@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.
Files changed (103) hide show
  1. package/README.md +48 -10
  2. package/db/migrations/032-add-memory-provenance-columns.sql +21 -0
  3. package/db/migrations/033-create-model-runs-and-tool-traces.sql +53 -0
  4. package/db/migrations/034-add-connector-event-scope-columns.sql +14 -0
  5. package/db/migrations/035-create-twin-edges.sql +63 -0
  6. package/db/migrations/036-create-agent-situation-packets.sql +61 -0
  7. package/db/migrations/037-create-context-packets.sql +32 -0
  8. package/dist/agent-graph/alias-write.d.ts +3 -0
  9. package/dist/agent-graph/alias-write.js +231 -0
  10. package/dist/agent-graph/entity-resolve.d.ts +3 -0
  11. package/dist/agent-graph/entity-resolve.js +275 -0
  12. package/dist/agent-graph/errors.d.ts +4 -0
  13. package/dist/agent-graph/errors.js +11 -0
  14. package/dist/agent-graph/graph-query.d.ts +5 -0
  15. package/dist/agent-graph/graph-query.js +359 -0
  16. package/dist/agent-graph/index.d.ts +6 -0
  17. package/dist/agent-graph/index.js +22 -0
  18. package/dist/agent-graph/types.d.ts +162 -0
  19. package/dist/agent-graph/types.js +3 -0
  20. package/dist/agent-situation/builder.d.ts +8 -0
  21. package/dist/agent-situation/builder.js +239 -0
  22. package/dist/agent-situation/cache-key.d.ts +4 -0
  23. package/dist/agent-situation/cache-key.js +64 -0
  24. package/dist/agent-situation/index.d.ts +7 -0
  25. package/dist/agent-situation/index.js +28 -0
  26. package/dist/agent-situation/packet-store.d.ts +27 -0
  27. package/dist/agent-situation/packet-store.js +228 -0
  28. package/dist/agent-situation/ranking-policy.d.ts +18 -0
  29. package/dist/agent-situation/ranking-policy.js +75 -0
  30. package/dist/agent-situation/source-readers.d.ts +52 -0
  31. package/dist/agent-situation/source-readers.js +300 -0
  32. package/dist/agent-situation/types.d.ts +195 -0
  33. package/dist/agent-situation/types.js +3 -0
  34. package/dist/cases/search-rollup.d.ts +4 -0
  35. package/dist/cases/search-rollup.js +84 -0
  36. package/dist/cases/wiki-page-index.js +6 -2
  37. package/dist/connectors/event-index.d.ts +1 -0
  38. package/dist/connectors/event-index.js +19 -4
  39. package/dist/connectors/raw-query.d.ts +18 -0
  40. package/dist/connectors/raw-query.js +305 -0
  41. package/dist/connectors/types.d.ts +40 -0
  42. package/dist/context-compile/boundary-defaults.d.ts +12 -0
  43. package/dist/context-compile/boundary-defaults.js +105 -0
  44. package/dist/context-compile/compiler-policy.d.ts +39 -0
  45. package/dist/context-compile/compiler-policy.js +147 -0
  46. package/dist/context-compile/compiler.d.ts +18 -0
  47. package/dist/context-compile/compiler.js +401 -0
  48. package/dist/context-compile/index.d.ts +9 -0
  49. package/dist/context-compile/index.js +25 -0
  50. package/dist/context-compile/packet-store.d.ts +9 -0
  51. package/dist/context-compile/packet-store.js +255 -0
  52. package/dist/context-compile/ref.d.ts +7 -0
  53. package/dist/context-compile/ref.js +102 -0
  54. package/dist/context-compile/source-readers.d.ts +69 -0
  55. package/dist/context-compile/source-readers.js +707 -0
  56. package/dist/context-compile/types.d.ts +138 -0
  57. package/dist/context-compile/types.js +11 -0
  58. package/dist/context-compile/visibility.d.ts +21 -0
  59. package/dist/context-compile/visibility.js +200 -0
  60. package/dist/db-adapter/node-sqlite-adapter.d.ts +9 -0
  61. package/dist/db-adapter/node-sqlite-adapter.js +207 -0
  62. package/dist/db-manager.d.ts +8 -1
  63. package/dist/db-manager.js +53 -5
  64. package/dist/edges/ref-validation.d.ts +8 -0
  65. package/dist/edges/ref-validation.js +301 -0
  66. package/dist/edges/store.d.ts +10 -0
  67. package/dist/edges/store.js +343 -0
  68. package/dist/edges/types.d.ts +88 -0
  69. package/dist/edges/types.js +18 -0
  70. package/dist/embeddings.d.ts +3 -0
  71. package/dist/embeddings.js +49 -9
  72. package/dist/entities/store.d.ts +4 -1
  73. package/dist/entities/store.js +206 -78
  74. package/dist/entities/types.d.ts +5 -1
  75. package/dist/entities/types.js +1 -1
  76. package/dist/index.d.ts +18 -3
  77. package/dist/index.js +54 -4
  78. package/dist/mama-api.d.ts +32 -35
  79. package/dist/mama-api.js +173 -22
  80. package/dist/memory/api.d.ts +17 -56
  81. package/dist/memory/api.js +494 -52
  82. package/dist/memory/event-store.d.ts +3 -0
  83. package/dist/memory/event-store.js +21 -2
  84. package/dist/memory/provenance-audit.d.ts +22 -0
  85. package/dist/memory/provenance-audit.js +112 -0
  86. package/dist/memory/provenance-query.d.ts +11 -0
  87. package/dist/memory/provenance-query.js +193 -0
  88. package/dist/memory/provenance.d.ts +29 -0
  89. package/dist/memory/provenance.js +137 -0
  90. package/dist/memory/scope-backfill.d.ts +17 -0
  91. package/dist/memory/scope-backfill.js +77 -0
  92. package/dist/memory/types.d.ts +87 -14
  93. package/dist/model-runs/store.d.ts +13 -0
  94. package/dist/model-runs/store.js +457 -0
  95. package/dist/model-runs/tool-trace-store.d.ts +4 -0
  96. package/dist/model-runs/tool-trace-store.js +114 -0
  97. package/dist/model-runs/types.d.ts +69 -0
  98. package/dist/model-runs/types.js +5 -0
  99. package/dist/runtime/trusted-provenance.d.ts +3 -0
  100. package/dist/runtime/trusted-provenance.js +6 -0
  101. package/dist/search/search-quality.d.ts +36 -0
  102. package/dist/search/search-quality.js +29 -0
  103. 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
@@ -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
  */
@@ -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 (!text || text.trim().length === 0) {
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(text);
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(text, {
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(text, embedding);
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 (!text || text.trim().length === 0) {
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(texts, {
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 = [];
@@ -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 UpsertEntityObservationInput = Omit<EntityObservation, 'created_at'>;
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;