@jungjaehoon/mama-core 1.5.0 → 1.7.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/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/db/migrations/037-create-context-packets.sql +32 -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/cases/search-rollup.d.ts +4 -0
- package/dist/cases/search-rollup.js +84 -0
- package/dist/cases/wiki-page-index.js +6 -2
- package/dist/connectors/event-index.d.ts +1 -0
- package/dist/connectors/event-index.js +19 -4
- package/dist/connectors/raw-query.d.ts +18 -0
- package/dist/connectors/raw-query.js +305 -0
- package/dist/connectors/types.d.ts +40 -0
- package/dist/context-compile/boundary-defaults.d.ts +12 -0
- package/dist/context-compile/boundary-defaults.js +105 -0
- package/dist/context-compile/compiler-policy.d.ts +39 -0
- package/dist/context-compile/compiler-policy.js +147 -0
- package/dist/context-compile/compiler.d.ts +18 -0
- package/dist/context-compile/compiler.js +401 -0
- package/dist/context-compile/index.d.ts +9 -0
- package/dist/context-compile/index.js +25 -0
- package/dist/context-compile/packet-store.d.ts +9 -0
- package/dist/context-compile/packet-store.js +255 -0
- package/dist/context-compile/ref.d.ts +7 -0
- package/dist/context-compile/ref.js +102 -0
- package/dist/context-compile/source-readers.d.ts +69 -0
- package/dist/context-compile/source-readers.js +707 -0
- package/dist/context-compile/types.d.ts +138 -0
- package/dist/context-compile/types.js +11 -0
- package/dist/context-compile/visibility.d.ts +21 -0
- package/dist/context-compile/visibility.js +200 -0
- package/dist/db-adapter/node-sqlite-adapter.d.ts +9 -0
- package/dist/db-adapter/node-sqlite-adapter.js +207 -0
- package/dist/db-manager.d.ts +8 -1
- package/dist/db-manager.js +53 -5
- package/dist/edges/ref-validation.d.ts +8 -0
- package/dist/edges/ref-validation.js +301 -0
- package/dist/edges/store.d.ts +10 -0
- package/dist/edges/store.js +343 -0
- package/dist/edges/types.d.ts +88 -0
- package/dist/edges/types.js +18 -0
- package/dist/embeddings.d.ts +3 -0
- package/dist/embeddings.js +49 -9
- package/dist/entities/store.d.ts +4 -1
- package/dist/entities/store.js +206 -78
- package/dist/entities/types.d.ts +5 -1
- package/dist/entities/types.js +1 -1
- package/dist/index.d.ts +18 -3
- package/dist/index.js +54 -4
- package/dist/mama-api.d.ts +32 -35
- package/dist/mama-api.js +173 -22
- package/dist/memory/api.d.ts +17 -56
- package/dist/memory/api.js +494 -52
- 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 +137 -0
- package/dist/memory/scope-backfill.d.ts +17 -0
- package/dist/memory/scope-backfill.js +77 -0
- package/dist/memory/types.d.ts +87 -14
- 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/search-quality.d.ts +36 -0
- package/dist/search/search-quality.js +29 -0
- package/package.json +11 -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,88 @@
|
|
|
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
|
+
startMs?: number | null;
|
|
31
|
+
asOfMs?: number | null;
|
|
32
|
+
}
|
|
33
|
+
export interface InsertTwinEdgeInput {
|
|
34
|
+
edge_id?: string;
|
|
35
|
+
edge_type: TwinEdgeType;
|
|
36
|
+
subject_ref: TwinEdgeSubjectRef;
|
|
37
|
+
object_ref: TwinRef;
|
|
38
|
+
relation_attrs?: unknown;
|
|
39
|
+
confidence?: number;
|
|
40
|
+
source: TwinEdgeSource;
|
|
41
|
+
agent_id?: string;
|
|
42
|
+
model_run_id?: string;
|
|
43
|
+
envelope_hash?: string;
|
|
44
|
+
request_idempotency_key?: string;
|
|
45
|
+
edge_idempotency_key?: string;
|
|
46
|
+
human_actor_id?: string;
|
|
47
|
+
human_actor_role?: 'commander' | 'configurator_elevated';
|
|
48
|
+
authority_scope_json?: unknown;
|
|
49
|
+
reason_classification?: 'factual_correction' | 'agent_inference_wrong' | 'privacy_redaction' | 'duplicate_merge' | 'state_override' | 'other';
|
|
50
|
+
reason_text?: string;
|
|
51
|
+
evidence_refs?: unknown;
|
|
52
|
+
}
|
|
53
|
+
export interface TwinEdgeRecord {
|
|
54
|
+
edge_id: string;
|
|
55
|
+
edge_type: TwinEdgeType;
|
|
56
|
+
subject_ref: TwinRef;
|
|
57
|
+
object_ref: TwinRef;
|
|
58
|
+
relation_attrs_json: string | null;
|
|
59
|
+
relation_attrs: unknown | null;
|
|
60
|
+
confidence: number;
|
|
61
|
+
source: TwinEdgeSource;
|
|
62
|
+
agent_id: string | null;
|
|
63
|
+
model_run_id: string | null;
|
|
64
|
+
envelope_hash: string | null;
|
|
65
|
+
human_actor_id: string | null;
|
|
66
|
+
human_actor_role: string | null;
|
|
67
|
+
authority_scope_json: string | null;
|
|
68
|
+
authority_scope: unknown | null;
|
|
69
|
+
reason_classification: string | null;
|
|
70
|
+
reason_text: string | null;
|
|
71
|
+
evidence_refs_json: string | null;
|
|
72
|
+
evidence_refs: unknown | null;
|
|
73
|
+
request_idempotency_key: string | null;
|
|
74
|
+
edge_idempotency_key: string | null;
|
|
75
|
+
content_hash: Buffer;
|
|
76
|
+
created_at: number;
|
|
77
|
+
}
|
|
78
|
+
export interface ListVisibleTwinEdgesOptions {
|
|
79
|
+
scopes?: TwinScopeRef[];
|
|
80
|
+
connectors?: string[];
|
|
81
|
+
projectRefs?: TwinProjectRef[];
|
|
82
|
+
tenantId?: string | null;
|
|
83
|
+
edgeTypes?: TwinEdgeType[];
|
|
84
|
+
startMs?: number | null;
|
|
85
|
+
asOfMs?: number | null;
|
|
86
|
+
limit?: number;
|
|
87
|
+
}
|
|
88
|
+
//# 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
|
package/dist/embeddings.d.ts
CHANGED
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { embeddingCache } from './embedding-cache.js';
|
|
13
13
|
import { loadConfig, getModelName, getEmbeddingDim, getQuantized } from './config-loader.js';
|
|
14
|
+
export declare const EMBEDDING_MODEL_MAX_LENGTH = 512;
|
|
15
|
+
export declare const EMBEDDING_MAX_TOKENISH_SEGMENTS = 480;
|
|
16
|
+
export declare function prepareEmbeddingText(text: string): string;
|
|
14
17
|
/**
|
|
15
18
|
* Decision object for enhanced embedding generation
|
|
16
19
|
*/
|
package/dist/embeddings.js
CHANGED
|
@@ -14,7 +14,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
14
14
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.getQuantized = exports.getEmbeddingDim = exports.getModelName = exports.loadConfig = exports.MODEL_NAME = exports.EMBEDDING_DIM = exports.embeddingCache = void 0;
|
|
17
|
+
exports.getQuantized = exports.getEmbeddingDim = exports.getModelName = exports.loadConfig = exports.MODEL_NAME = exports.EMBEDDING_DIM = exports.embeddingCache = exports.EMBEDDING_MAX_TOKENISH_SEGMENTS = exports.EMBEDDING_MODEL_MAX_LENGTH = void 0;
|
|
18
|
+
exports.prepareEmbeddingText = prepareEmbeddingText;
|
|
18
19
|
exports.generateEmbedding = generateEmbedding;
|
|
19
20
|
exports.generateEnhancedEmbedding = generateEnhancedEmbedding;
|
|
20
21
|
exports.generateBatchEmbeddings = generateBatchEmbeddings;
|
|
@@ -32,10 +33,36 @@ Object.defineProperty(exports, "getEmbeddingDim", { enumerable: true, get: funct
|
|
|
32
33
|
Object.defineProperty(exports, "getQuantized", { enumerable: true, get: function () { return config_loader_js_1.getQuantized; } });
|
|
33
34
|
// Shared cache directory (not in node_modules)
|
|
34
35
|
const DEFAULT_CACHE_DIR = path_1.default.join(os_1.default.homedir(), '.cache', 'huggingface', 'transformers');
|
|
36
|
+
const TIER3_ENV_VALUES = new Set(['1', 'true', 'yes']);
|
|
37
|
+
exports.EMBEDDING_MODEL_MAX_LENGTH = 512;
|
|
38
|
+
exports.EMBEDDING_MAX_TOKENISH_SEGMENTS = 480;
|
|
39
|
+
const TOKENISH_SEGMENT_PATTERN = /[\p{Script=Hangul}\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}]|\S+/gu;
|
|
35
40
|
// Singleton pattern for model loading
|
|
36
41
|
let embeddingPipeline = null;
|
|
37
42
|
let currentModelName = null;
|
|
38
43
|
let modelLoadFailed = false; // Cache load failures to avoid repeated slow retries
|
|
44
|
+
function isForceTier3Enabled() {
|
|
45
|
+
return TIER3_ENV_VALUES.has(String(process.env.MAMA_FORCE_TIER_3 || '').toLowerCase());
|
|
46
|
+
}
|
|
47
|
+
function assertEmbeddingsEnabled() {
|
|
48
|
+
if (isForceTier3Enabled()) {
|
|
49
|
+
throw new Error('Embedding generation disabled because MAMA_FORCE_TIER_3=true. ' +
|
|
50
|
+
'Tier 3 test mode must use lexical/no-vector fallback instead of loading the embedding model.');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function prepareEmbeddingText(text) {
|
|
54
|
+
const trimmed = text.trim();
|
|
55
|
+
let count = 0;
|
|
56
|
+
let cutIndex = trimmed.length;
|
|
57
|
+
for (const match of trimmed.matchAll(TOKENISH_SEGMENT_PATTERN)) {
|
|
58
|
+
count++;
|
|
59
|
+
if (count > exports.EMBEDDING_MAX_TOKENISH_SEGMENTS) {
|
|
60
|
+
cutIndex = match.index ?? trimmed.length;
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return cutIndex === trimmed.length ? trimmed : trimmed.slice(0, cutIndex).trimEnd();
|
|
65
|
+
}
|
|
39
66
|
/**
|
|
40
67
|
* Load embedding model (configurable)
|
|
41
68
|
*
|
|
@@ -45,6 +72,7 @@ let modelLoadFailed = false; // Cache load failures to avoid repeated slow retri
|
|
|
45
72
|
* @returns Embedding pipeline
|
|
46
73
|
*/
|
|
47
74
|
async function loadModel() {
|
|
75
|
+
assertEmbeddingsEnabled();
|
|
48
76
|
const modelName = (0, config_loader_js_1.getModelName)();
|
|
49
77
|
// Check if model has changed (Story M1.4 AC #3)
|
|
50
78
|
if (embeddingPipeline && currentModelName && currentModelName !== modelName) {
|
|
@@ -101,11 +129,13 @@ async function loadModel() {
|
|
|
101
129
|
* @throws Error if text is empty or embedding fails
|
|
102
130
|
*/
|
|
103
131
|
async function generateEmbedding(text) {
|
|
104
|
-
if (
|
|
132
|
+
if (typeof text !== 'string' || text.trim().length === 0) {
|
|
105
133
|
throw new Error('Text cannot be empty');
|
|
106
134
|
}
|
|
135
|
+
assertEmbeddingsEnabled();
|
|
136
|
+
const preparedText = prepareEmbeddingText(text);
|
|
107
137
|
// Task 2: Check cache first (AC #3)
|
|
108
|
-
const cached = embedding_cache_js_1.embeddingCache.get(
|
|
138
|
+
const cached = embedding_cache_js_1.embeddingCache.get(preparedText);
|
|
109
139
|
if (cached) {
|
|
110
140
|
return cached;
|
|
111
141
|
}
|
|
@@ -114,9 +144,11 @@ async function generateEmbedding(text) {
|
|
|
114
144
|
const model = await loadModel();
|
|
115
145
|
const expectedDim = (0, config_loader_js_1.getEmbeddingDim)();
|
|
116
146
|
// Generate embedding
|
|
117
|
-
const output = await model(
|
|
147
|
+
const output = await model(preparedText, {
|
|
118
148
|
pooling: 'mean', // Mean pooling over tokens
|
|
119
149
|
normalize: true, // L2 normalization
|
|
150
|
+
truncation: true,
|
|
151
|
+
max_length: exports.EMBEDDING_MODEL_MAX_LENGTH,
|
|
120
152
|
});
|
|
121
153
|
// Extract Float32Array
|
|
122
154
|
const embedding = output.data;
|
|
@@ -127,7 +159,7 @@ async function generateEmbedding(text) {
|
|
|
127
159
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
128
160
|
const _latency = Date.now() - startTime;
|
|
129
161
|
// Task 2: Store in cache (AC #3)
|
|
130
|
-
embedding_cache_js_1.embeddingCache.set(
|
|
162
|
+
embedding_cache_js_1.embeddingCache.set(preparedText, embedding);
|
|
131
163
|
return embedding;
|
|
132
164
|
}
|
|
133
165
|
catch (error) {
|
|
@@ -192,10 +224,16 @@ async function generateBatchEmbeddings(texts) {
|
|
|
192
224
|
if (!Array.isArray(texts) || texts.length === 0) {
|
|
193
225
|
throw new Error('Texts must be a non-empty array');
|
|
194
226
|
}
|
|
195
|
-
// Validate all texts
|
|
196
227
|
for (const text of texts) {
|
|
197
|
-
if (
|
|
198
|
-
throw new Error('All texts must be non-empty');
|
|
228
|
+
if (typeof text !== 'string' || text.trim().length === 0) {
|
|
229
|
+
throw new Error('All texts must be non-empty strings');
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
assertEmbeddingsEnabled();
|
|
233
|
+
const preparedTexts = texts.map((text) => prepareEmbeddingText(text));
|
|
234
|
+
for (const text of preparedTexts) {
|
|
235
|
+
if (!text || text.length === 0) {
|
|
236
|
+
throw new Error('All texts must be non-empty strings');
|
|
199
237
|
}
|
|
200
238
|
}
|
|
201
239
|
const startTime = Date.now();
|
|
@@ -204,9 +242,11 @@ async function generateBatchEmbeddings(texts) {
|
|
|
204
242
|
const expectedDim = (0, config_loader_js_1.getEmbeddingDim)();
|
|
205
243
|
// Native batch processing - single model forward pass
|
|
206
244
|
// This is significantly faster than sequential calls
|
|
207
|
-
const outputs = await model(
|
|
245
|
+
const outputs = await model(preparedTexts, {
|
|
208
246
|
pooling: 'mean',
|
|
209
247
|
normalize: true,
|
|
248
|
+
truncation: true,
|
|
249
|
+
max_length: exports.EMBEDDING_MODEL_MAX_LENGTH,
|
|
210
250
|
});
|
|
211
251
|
// Extract embeddings from batch output
|
|
212
252
|
const embeddings = [];
|
package/dist/entities/store.d.ts
CHANGED
|
@@ -13,9 +13,11 @@ export declare class EntityMergeError extends Error {
|
|
|
13
13
|
readonly code: string;
|
|
14
14
|
constructor(code: string, message: string);
|
|
15
15
|
}
|
|
16
|
+
declare const OBSERVATION_CONTEXT_KEY_COLUMNS: readonly ["workspace_context_key", "channel_context_key", "thread_context_key", "actor_context_key"];
|
|
16
17
|
type CreateEntityNodeInput = Omit<EntityNode, 'created_at' | 'updated_at'>;
|
|
17
18
|
type AttachEntityAliasInput = Omit<EntityAlias, 'created_at'>;
|
|
18
|
-
type
|
|
19
|
+
type ObservationContextKeyColumn = (typeof OBSERVATION_CONTEXT_KEY_COLUMNS)[number];
|
|
20
|
+
type UpsertEntityObservationInput = Omit<EntityObservation, 'created_at'> & Partial<Record<ObservationContextKeyColumn, string | null>>;
|
|
19
21
|
type AppendEntityTimelineEventInput = {
|
|
20
22
|
event: Omit<EntityTimelineEvent, 'id' | 'created_at'> & {
|
|
21
23
|
id?: string;
|
|
@@ -27,6 +29,7 @@ export interface UpsertEntityObservationResult {
|
|
|
27
29
|
id: string;
|
|
28
30
|
created: boolean;
|
|
29
31
|
}
|
|
32
|
+
export declare function clearEntityTableColumnCache(adapter: EntityStoreAdapter): void;
|
|
30
33
|
export declare function parseObservationRow(row: Record<string, unknown>): EntityObservation;
|
|
31
34
|
export declare function createEntityNode(input: CreateEntityNodeInput): Promise<EntityNode>;
|
|
32
35
|
export declare function getEntityNode(id: string, adapter?: EntityStoreAdapter): EntityNode | null;
|