@jungjaehoon/mama-core 1.6.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 (35) hide show
  1. package/db/migrations/037-create-context-packets.sql +32 -0
  2. package/dist/cases/wiki-page-index.js +6 -2
  3. package/dist/context-compile/boundary-defaults.d.ts +12 -0
  4. package/dist/context-compile/boundary-defaults.js +105 -0
  5. package/dist/context-compile/compiler-policy.d.ts +39 -0
  6. package/dist/context-compile/compiler-policy.js +147 -0
  7. package/dist/context-compile/compiler.d.ts +18 -0
  8. package/dist/context-compile/compiler.js +401 -0
  9. package/dist/context-compile/index.d.ts +9 -0
  10. package/dist/context-compile/index.js +25 -0
  11. package/dist/context-compile/packet-store.d.ts +9 -0
  12. package/dist/context-compile/packet-store.js +255 -0
  13. package/dist/context-compile/ref.d.ts +7 -0
  14. package/dist/context-compile/ref.js +102 -0
  15. package/dist/context-compile/source-readers.d.ts +69 -0
  16. package/dist/context-compile/source-readers.js +707 -0
  17. package/dist/context-compile/types.d.ts +138 -0
  18. package/dist/context-compile/types.js +11 -0
  19. package/dist/context-compile/visibility.d.ts +21 -0
  20. package/dist/context-compile/visibility.js +200 -0
  21. package/dist/db-adapter/node-sqlite-adapter.d.ts +3 -0
  22. package/dist/db-adapter/node-sqlite-adapter.js +76 -0
  23. package/dist/edges/ref-validation.js +55 -16
  24. package/dist/edges/types.d.ts +2 -0
  25. package/dist/embeddings.d.ts +3 -0
  26. package/dist/embeddings.js +37 -10
  27. package/dist/entities/store.d.ts +4 -1
  28. package/dist/entities/store.js +206 -78
  29. package/dist/entities/types.d.ts +5 -1
  30. package/dist/entities/types.js +1 -1
  31. package/dist/index.d.ts +1 -0
  32. package/dist/index.js +1 -0
  33. package/dist/memory/provenance.js +1 -0
  34. package/dist/memory/types.d.ts +1 -0
  35. package/package.json +3 -2
@@ -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;
@@ -33,6 +34,9 @@ Object.defineProperty(exports, "getQuantized", { enumerable: true, get: function
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');
35
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;
36
40
  // Singleton pattern for model loading
37
41
  let embeddingPipeline = null;
38
42
  let currentModelName = null;
@@ -46,6 +50,19 @@ function assertEmbeddingsEnabled() {
46
50
  'Tier 3 test mode must use lexical/no-vector fallback instead of loading the embedding model.');
47
51
  }
48
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
+ }
49
66
  /**
50
67
  * Load embedding model (configurable)
51
68
  *
@@ -112,12 +129,13 @@ async function loadModel() {
112
129
  * @throws Error if text is empty or embedding fails
113
130
  */
114
131
  async function generateEmbedding(text) {
115
- if (!text || text.trim().length === 0) {
132
+ if (typeof text !== 'string' || text.trim().length === 0) {
116
133
  throw new Error('Text cannot be empty');
117
134
  }
118
135
  assertEmbeddingsEnabled();
136
+ const preparedText = prepareEmbeddingText(text);
119
137
  // Task 2: Check cache first (AC #3)
120
- const cached = embedding_cache_js_1.embeddingCache.get(text);
138
+ const cached = embedding_cache_js_1.embeddingCache.get(preparedText);
121
139
  if (cached) {
122
140
  return cached;
123
141
  }
@@ -126,9 +144,11 @@ async function generateEmbedding(text) {
126
144
  const model = await loadModel();
127
145
  const expectedDim = (0, config_loader_js_1.getEmbeddingDim)();
128
146
  // Generate embedding
129
- const output = await model(text, {
147
+ const output = await model(preparedText, {
130
148
  pooling: 'mean', // Mean pooling over tokens
131
149
  normalize: true, // L2 normalization
150
+ truncation: true,
151
+ max_length: exports.EMBEDDING_MODEL_MAX_LENGTH,
132
152
  });
133
153
  // Extract Float32Array
134
154
  const embedding = output.data;
@@ -139,7 +159,7 @@ async function generateEmbedding(text) {
139
159
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
140
160
  const _latency = Date.now() - startTime;
141
161
  // Task 2: Store in cache (AC #3)
142
- embedding_cache_js_1.embeddingCache.set(text, embedding);
162
+ embedding_cache_js_1.embeddingCache.set(preparedText, embedding);
143
163
  return embedding;
144
164
  }
145
165
  catch (error) {
@@ -201,14 +221,19 @@ async function generateEnhancedEmbedding(decision) {
201
221
  * @returns Array of embeddings
202
222
  */
203
223
  async function generateBatchEmbeddings(texts) {
204
- assertEmbeddingsEnabled();
205
224
  if (!Array.isArray(texts) || texts.length === 0) {
206
225
  throw new Error('Texts must be a non-empty array');
207
226
  }
208
- // Validate all texts
209
227
  for (const text of texts) {
210
- if (!text || text.trim().length === 0) {
211
- 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');
212
237
  }
213
238
  }
214
239
  const startTime = Date.now();
@@ -217,9 +242,11 @@ async function generateBatchEmbeddings(texts) {
217
242
  const expectedDim = (0, config_loader_js_1.getEmbeddingDim)();
218
243
  // Native batch processing - single model forward pass
219
244
  // This is significantly faster than sequential calls
220
- const outputs = await model(texts, {
245
+ const outputs = await model(preparedTexts, {
221
246
  pooling: 'mean',
222
247
  normalize: true,
248
+ truncation: true,
249
+ max_length: exports.EMBEDDING_MODEL_MAX_LENGTH,
223
250
  });
224
251
  // Extract embeddings from batch output
225
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;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.EntityMergeError = void 0;
4
+ exports.clearEntityTableColumnCache = clearEntityTableColumnCache;
4
5
  exports.parseObservationRow = parseObservationRow;
5
6
  exports.createEntityNode = createEntityNode;
6
7
  exports.getEntityNode = getEntityNode;
@@ -25,6 +26,13 @@ class EntityMergeError extends Error {
25
26
  }
26
27
  exports.EntityMergeError = EntityMergeError;
27
28
  const MAX_MERGE_CHAIN_DEPTH = 8;
29
+ const OBSERVATION_CONTEXT_KEY_COLUMNS = [
30
+ 'workspace_context_key',
31
+ 'channel_context_key',
32
+ 'thread_context_key',
33
+ 'actor_context_key',
34
+ ];
35
+ const tableColumnCache = new WeakMap();
28
36
  function now() {
29
37
  return Date.now();
30
38
  }
@@ -32,6 +40,58 @@ function normalizeSourceLocator(value) {
32
40
  // Write empty-string as the null sentinel so SQLite UNIQUE can collapse null-like locators; parseObservationRow maps it back to null on read.
33
41
  return value ?? '';
34
42
  }
43
+ function normalizeContextKey(value) {
44
+ return value ?? '';
45
+ }
46
+ function sourceLocatorForInput(input) {
47
+ return normalizeSourceLocator(input.source_locator);
48
+ }
49
+ function channelContextKey(input) {
50
+ if (input.channel_context_key !== undefined) {
51
+ return normalizeContextKey(input.channel_context_key);
52
+ }
53
+ if (input.scope_kind !== 'channel' || !input.scope_id) {
54
+ return '';
55
+ }
56
+ return input.scope_id.startsWith(`${input.source_connector}:`)
57
+ ? input.scope_id
58
+ : `${input.source_connector}:${input.scope_id}`;
59
+ }
60
+ function actorContextKey(input) {
61
+ if (input.actor_context_key !== undefined) {
62
+ return normalizeContextKey(input.actor_context_key);
63
+ }
64
+ if (input.observation_type === 'author') {
65
+ return input.surface_form;
66
+ }
67
+ return input.related_surface_forms.find((value) => value.trim().length > 0) ?? '';
68
+ }
69
+ function observationContextKeys(input) {
70
+ return {
71
+ workspace_context_key: normalizeContextKey(input.workspace_context_key ?? input.source_connector),
72
+ channel_context_key: channelContextKey(input),
73
+ thread_context_key: normalizeContextKey(input.thread_context_key),
74
+ actor_context_key: actorContextKey(input),
75
+ };
76
+ }
77
+ function tableColumnNames(adapter, tableName) {
78
+ let adapterCache = tableColumnCache.get(adapter);
79
+ if (!adapterCache) {
80
+ adapterCache = new Map();
81
+ tableColumnCache.set(adapter, adapterCache);
82
+ }
83
+ const cached = adapterCache.get(tableName);
84
+ if (cached) {
85
+ return cached;
86
+ }
87
+ const rows = adapter.prepare(`PRAGMA table_info(${tableName})`).all();
88
+ const columns = new Set(rows.map((row) => row.name));
89
+ adapterCache.set(tableName, columns);
90
+ return columns;
91
+ }
92
+ function clearEntityTableColumnCache(adapter) {
93
+ tableColumnCache.delete(adapter);
94
+ }
35
95
  function requireStringField(row, field) {
36
96
  if (typeof row[field] !== 'string' || row[field].length === 0) {
37
97
  throw new Error(`Invalid entity observation row: ${field} must be a non-empty string`);
@@ -134,6 +194,10 @@ function parseObservationRow(row) {
134
194
  source_connector: requireStringField(row, 'source_connector'),
135
195
  source_locator: sourceLocator && sourceLocator.length > 0 ? sourceLocator : null,
136
196
  source_raw_record_id: requireStringField(row, 'source_raw_record_id'),
197
+ workspace_context_key: optionalStringFieldIfPresent(row, 'workspace_context_key'),
198
+ channel_context_key: optionalStringFieldIfPresent(row, 'channel_context_key'),
199
+ thread_context_key: optionalStringFieldIfPresent(row, 'thread_context_key'),
200
+ actor_context_key: optionalStringFieldIfPresent(row, 'actor_context_key'),
137
201
  created_at: createdAt,
138
202
  };
139
203
  }
@@ -226,44 +290,152 @@ function listEntityAliases(entityId, adapter = (0, db_manager_js_1.getAdapter)()
226
290
  created_at: Number(row.created_at),
227
291
  }));
228
292
  }
229
- async function upsertEntityObservation(input) {
230
- await (0, db_manager_js_1.initDB)();
231
- const adapter = (0, db_manager_js_1.getAdapter)();
232
- const createdAt = now();
233
- const normalizedSourceLocator = normalizeSourceLocator(input.source_locator);
293
+ function observationContextColumns(columns) {
294
+ return OBSERVATION_CONTEXT_KEY_COLUMNS.filter((column) => columns.has(column));
295
+ }
296
+ function observationInsertValues(input, normalizedSourceLocator, contextColumns, contextKeys, createdAt) {
297
+ const columns = [
298
+ 'id',
299
+ 'observation_type',
300
+ 'entity_kind_hint',
301
+ 'surface_form',
302
+ 'normalized_form',
303
+ 'lang',
304
+ 'script',
305
+ 'context_summary',
306
+ 'related_surface_forms',
307
+ 'timestamp_observed',
308
+ 'scope_kind',
309
+ 'scope_id',
310
+ 'extractor_version',
311
+ 'embedding_model_version',
312
+ 'source_connector',
313
+ 'source_locator',
314
+ 'source_raw_record_id',
315
+ ...contextColumns,
316
+ 'created_at',
317
+ ];
318
+ const values = [
319
+ input.id,
320
+ input.observation_type,
321
+ input.entity_kind_hint,
322
+ input.surface_form,
323
+ input.normalized_form,
324
+ input.lang,
325
+ input.script,
326
+ input.context_summary,
327
+ JSON.stringify(input.related_surface_forms),
328
+ input.timestamp_observed,
329
+ input.scope_kind,
330
+ input.scope_id,
331
+ input.extractor_version,
332
+ input.embedding_model_version,
333
+ input.source_connector,
334
+ normalizedSourceLocator,
335
+ input.source_raw_record_id,
336
+ ...contextColumns.map((column) => contextKeys[column]),
337
+ createdAt,
338
+ ];
339
+ return { columns, values };
340
+ }
341
+ function findExistingObservation(adapter, input, normalizedSourceLocator, contextColumns, contextKeys) {
342
+ const conditions = [
343
+ 'source_connector = ?',
344
+ 'source_locator = ?',
345
+ 'source_raw_record_id = ?',
346
+ 'observation_type = ?',
347
+ ...contextColumns.map((column) => `COALESCE(${column}, '') = ?`),
348
+ ];
349
+ const params = [
350
+ input.source_connector,
351
+ normalizedSourceLocator,
352
+ input.source_raw_record_id,
353
+ input.observation_type,
354
+ ...contextColumns.map((column) => contextKeys[column]),
355
+ ];
356
+ return adapter
357
+ .prepare(`
358
+ SELECT id FROM entity_observations
359
+ WHERE ${conditions.join(' AND ')}
360
+ ORDER BY created_at ASC, id ASC
361
+ LIMIT 1
362
+ `)
363
+ .get(...params);
364
+ }
365
+ function insertObservation(adapter, input, normalizedSourceLocator, contextColumns, contextKeys, createdAt) {
366
+ const insert = observationInsertValues(input, normalizedSourceLocator, contextColumns, contextKeys, createdAt);
367
+ const placeholders = insert.columns.map(() => '?').join(', ');
234
368
  adapter
235
369
  .prepare(`
236
- INSERT INTO entity_observations (
237
- id, observation_type, entity_kind_hint, surface_form, normalized_form, lang, script,
238
- context_summary, related_surface_forms, timestamp_observed, scope_kind, scope_id,
239
- extractor_version, embedding_model_version, source_connector, source_locator,
240
- source_raw_record_id, created_at
241
- )
242
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
243
- ON CONFLICT(source_connector, source_locator, source_raw_record_id, observation_type)
244
- DO UPDATE SET
245
- entity_kind_hint = excluded.entity_kind_hint,
246
- surface_form = excluded.surface_form,
247
- normalized_form = excluded.normalized_form,
248
- lang = excluded.lang,
249
- script = excluded.script,
250
- context_summary = excluded.context_summary,
251
- related_surface_forms = excluded.related_surface_forms,
252
- timestamp_observed = excluded.timestamp_observed,
253
- scope_kind = excluded.scope_kind,
254
- scope_id = excluded.scope_id,
255
- extractor_version = excluded.extractor_version,
256
- embedding_model_version = excluded.embedding_model_version,
257
- source_locator = excluded.source_locator,
258
- created_at = entity_observations.created_at
370
+ INSERT INTO entity_observations (${insert.columns.join(', ')})
371
+ VALUES (${placeholders})
259
372
  `)
260
- .run(input.id, input.observation_type, input.entity_kind_hint, input.surface_form, input.normalized_form, input.lang, input.script, input.context_summary, JSON.stringify(input.related_surface_forms), input.timestamp_observed, input.scope_kind, input.scope_id, input.extractor_version, input.embedding_model_version, input.source_connector, normalizedSourceLocator, input.source_raw_record_id, createdAt);
261
- const saved = adapter
373
+ .run(...insert.values);
374
+ }
375
+ function updateObservation(adapter, id, input, normalizedSourceLocator, contextColumns, contextKeys) {
376
+ const assignments = [
377
+ 'entity_kind_hint = ?',
378
+ 'surface_form = ?',
379
+ 'normalized_form = ?',
380
+ 'lang = ?',
381
+ 'script = ?',
382
+ 'context_summary = ?',
383
+ 'related_surface_forms = ?',
384
+ 'timestamp_observed = ?',
385
+ 'scope_kind = ?',
386
+ 'scope_id = ?',
387
+ 'extractor_version = ?',
388
+ 'embedding_model_version = ?',
389
+ 'source_locator = ?',
390
+ ...contextColumns.map((column) => `${column} = ?`),
391
+ ];
392
+ const values = [
393
+ input.entity_kind_hint,
394
+ input.surface_form,
395
+ input.normalized_form,
396
+ input.lang,
397
+ input.script,
398
+ input.context_summary,
399
+ JSON.stringify(input.related_surface_forms),
400
+ input.timestamp_observed,
401
+ input.scope_kind,
402
+ input.scope_id,
403
+ input.extractor_version,
404
+ input.embedding_model_version,
405
+ normalizedSourceLocator,
406
+ ...contextColumns.map((column) => contextKeys[column]),
407
+ id,
408
+ ];
409
+ adapter
262
410
  .prepare(`
263
- SELECT * FROM entity_observations
264
- WHERE source_connector = ? AND source_locator = ? AND source_raw_record_id = ? AND observation_type = ?
411
+ UPDATE entity_observations
412
+ SET ${assignments.join(', ')}
413
+ WHERE id = ?
265
414
  `)
266
- .get(input.source_connector, normalizedSourceLocator, input.source_raw_record_id, input.observation_type);
415
+ .run(...values);
416
+ }
417
+ function saveObservation(adapter, input) {
418
+ const columns = tableColumnNames(adapter, 'entity_observations');
419
+ const contextColumns = observationContextColumns(columns);
420
+ const contextKeys = observationContextKeys(input);
421
+ const normalizedSourceLocator = sourceLocatorForInput(input);
422
+ const existing = findExistingObservation(adapter, input, normalizedSourceLocator, contextColumns, contextKeys);
423
+ if (existing) {
424
+ const id = requireStringField(existing, 'id');
425
+ updateObservation(adapter, id, input, normalizedSourceLocator, contextColumns, contextKeys);
426
+ return { id, created: false };
427
+ }
428
+ insertObservation(adapter, input, normalizedSourceLocator, contextColumns, contextKeys, now());
429
+ return { id: input.id, created: true };
430
+ }
431
+ async function upsertEntityObservation(input) {
432
+ await (0, db_manager_js_1.initDB)();
433
+ const adapter = (0, db_manager_js_1.getAdapter)();
434
+ const result = saveObservation(adapter, input);
435
+ const saved = adapter.prepare('SELECT * FROM entity_observations WHERE id = ?').get(result.id);
436
+ if (!saved) {
437
+ throw new Error(`Failed to load saved entity observation ${result.id}`);
438
+ }
267
439
  return parseObservationRow(saved);
268
440
  }
269
441
  async function upsertEntityObservations(inputs) {
@@ -271,51 +443,7 @@ async function upsertEntityObservations(inputs) {
271
443
  const adapter = (0, db_manager_js_1.getAdapter)();
272
444
  const observations = [];
273
445
  const upsertOne = (input) => {
274
- const normalizedSourceLocator = normalizeSourceLocator(input.source_locator);
275
- const existing = adapter
276
- .prepare(`
277
- SELECT id FROM entity_observations
278
- WHERE source_connector = ? AND source_locator = ? AND source_raw_record_id = ? AND observation_type = ?
279
- `)
280
- .get(input.source_connector, normalizedSourceLocator, input.source_raw_record_id, input.observation_type);
281
- const createdAt = now();
282
- adapter
283
- .prepare(`
284
- INSERT INTO entity_observations (
285
- id, observation_type, entity_kind_hint, surface_form, normalized_form, lang, script,
286
- context_summary, related_surface_forms, timestamp_observed, scope_kind, scope_id,
287
- extractor_version, embedding_model_version, source_connector, source_locator,
288
- source_raw_record_id, created_at
289
- )
290
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
291
- ON CONFLICT(source_connector, source_locator, source_raw_record_id, observation_type)
292
- DO UPDATE SET
293
- entity_kind_hint = excluded.entity_kind_hint,
294
- surface_form = excluded.surface_form,
295
- normalized_form = excluded.normalized_form,
296
- lang = excluded.lang,
297
- script = excluded.script,
298
- context_summary = excluded.context_summary,
299
- related_surface_forms = excluded.related_surface_forms,
300
- timestamp_observed = excluded.timestamp_observed,
301
- scope_kind = excluded.scope_kind,
302
- scope_id = excluded.scope_id,
303
- extractor_version = excluded.extractor_version,
304
- embedding_model_version = excluded.embedding_model_version,
305
- source_locator = excluded.source_locator,
306
- created_at = entity_observations.created_at
307
- `)
308
- .run(input.id, input.observation_type, input.entity_kind_hint, input.surface_form, input.normalized_form, input.lang, input.script, input.context_summary, JSON.stringify(input.related_surface_forms), input.timestamp_observed, input.scope_kind, input.scope_id, input.extractor_version, input.embedding_model_version, input.source_connector, normalizedSourceLocator, input.source_raw_record_id, createdAt);
309
- const saved = adapter
310
- .prepare(`
311
- SELECT * FROM entity_observations
312
- WHERE source_connector = ? AND source_locator = ? AND source_raw_record_id = ? AND observation_type = ?
313
- `)
314
- .get(input.source_connector, normalizedSourceLocator, input.source_raw_record_id, input.observation_type);
315
- observations.push({
316
- id: requireStringField(saved, 'id'),
317
- created: !existing,
318
- });
446
+ observations.push(saveObservation(adapter, input));
319
447
  };
320
448
  if ('transaction' in adapter && typeof adapter.transaction === 'function') {
321
449
  const txResult = adapter.transaction(() => {
@@ -12,7 +12,7 @@ export declare const ENTITY_NODE_STATUSES: readonly ["active", "merged", "archiv
12
12
  export type EntityNodeStatus = (typeof ENTITY_NODE_STATUSES)[number];
13
13
  export declare const ENTITY_ALIAS_STATUSES: readonly ["active", "suppressed"];
14
14
  export type EntityAliasStatus = (typeof ENTITY_ALIAS_STATUSES)[number];
15
- export declare const ENTITY_OBSERVATION_TYPES: readonly ["generic", "author", "channel"];
15
+ export declare const ENTITY_OBSERVATION_TYPES: readonly ["generic", "author", "channel", "thread"];
16
16
  export type EntityObservationType = (typeof ENTITY_OBSERVATION_TYPES)[number];
17
17
  export declare const ENTITY_CANDIDATE_KINDS: readonly ["alias_to_entity", "entity_to_entity", "cluster"];
18
18
  export type EntityCandidateKind = (typeof ENTITY_CANDIDATE_KINDS)[number];
@@ -71,6 +71,10 @@ export interface EntityObservation {
71
71
  source_connector: string;
72
72
  source_locator: string | null;
73
73
  source_raw_record_id: string;
74
+ workspace_context_key?: string | null;
75
+ channel_context_key?: string | null;
76
+ thread_context_key?: string | null;
77
+ actor_context_key?: string | null;
74
78
  created_at: number;
75
79
  }
76
80
  export interface EntityResolutionCandidate {
@@ -14,7 +14,7 @@ exports.ENTITY_MERGE_ACTION_TYPES = ['merge', 'reject', 'defer', 'split'];
14
14
  exports.ENTITY_SCOPE_KINDS = ['project', 'channel', 'user', 'global'];
15
15
  exports.ENTITY_NODE_STATUSES = ['active', 'merged', 'archived'];
16
16
  exports.ENTITY_ALIAS_STATUSES = ['active', 'suppressed'];
17
- exports.ENTITY_OBSERVATION_TYPES = ['generic', 'author', 'channel'];
17
+ exports.ENTITY_OBSERVATION_TYPES = ['generic', 'author', 'channel', 'thread'];
18
18
  exports.ENTITY_CANDIDATE_KINDS = ['alias_to_entity', 'entity_to_entity', 'cluster'];
19
19
  exports.ENTITY_ACTOR_TYPES = ['system', 'user', 'agent'];
20
20
  exports.ENTITY_INGEST_RUN_STATUSES = ['running', 'complete', 'failed'];
package/dist/index.d.ts CHANGED
@@ -29,6 +29,7 @@ export { appendToolTrace, listToolTracesForRun } from './model-runs/tool-trace-s
29
29
  export { TWIN_EDGE_SOURCES, TWIN_EDGE_TYPES, TWIN_REF_KINDS, type InsertTwinEdgeInput, type ListVisibleTwinEdgesOptions, type TwinEdgeRecord, type TwinEdgeSource, type TwinEdgeType, type TwinRef, type TwinRefKind, type TwinScopeRef, } from './edges/types.js';
30
30
  export { getTwinEdge, insertTwinEdge, listTwinEdgesForRefs, mapTwinEdgeRow, } from './edges/store.js';
31
31
  export { assertTwinRefsVisibleToScopes, listVisibleTwinEdgesForRefs, } from './edges/ref-validation.js';
32
+ export * from './context-compile/index.js';
32
33
  export * from './agent-situation/index.js';
33
34
  export * from './agent-graph/index.js';
34
35
  export * from './entities/types.js';
package/dist/index.js CHANGED
@@ -150,6 +150,7 @@ Object.defineProperty(exports, "mapTwinEdgeRow", { enumerable: true, get: functi
150
150
  var ref_validation_js_1 = require("./edges/ref-validation.js");
151
151
  Object.defineProperty(exports, "assertTwinRefsVisibleToScopes", { enumerable: true, get: function () { return ref_validation_js_1.assertTwinRefsVisibleToScopes; } });
152
152
  Object.defineProperty(exports, "listVisibleTwinEdgesForRefs", { enumerable: true, get: function () { return ref_validation_js_1.listVisibleTwinEdgesForRefs; } });
153
+ __exportStar(require("./context-compile/index.js"), exports);
153
154
  __exportStar(require("./agent-situation/index.js"), exports);
154
155
  __exportStar(require("./agent-graph/index.js"), exports);
155
156
  __exportStar(require("./entities/types.js"), exports);
@@ -16,6 +16,7 @@ const ALLOWED_PROVENANCE_FIELDS = new Set([
16
16
  'envelope_hash',
17
17
  'tool_name',
18
18
  'gateway_call_id',
19
+ 'context_packet_id',
19
20
  'source_turn_id',
20
21
  'source_message_ref',
21
22
  'source_refs',
@@ -119,6 +119,7 @@ export interface MemoryWriteProvenance {
119
119
  envelope_hash?: string;
120
120
  tool_name?: string;
121
121
  gateway_call_id?: string;
122
+ context_packet_id?: string;
122
123
  source_turn_id?: string;
123
124
  source_message_ref?: string;
124
125
  source_refs?: string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jungjaehoon/mama-core",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "MAMA Core - Shared modules for Memory-Augmented MCP Assistant",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -49,6 +49,7 @@
49
49
  "./connectors/types": "./dist/connectors/types.js",
50
50
  "./agent-situation": "./dist/agent-situation/index.js",
51
51
  "./agent-graph": "./dist/agent-graph/index.js",
52
+ "./context-compile": "./dist/context-compile/index.js",
52
53
  "./edges/types": "./dist/edges/types.js",
53
54
  "./edges/store": "./dist/edges/store.js",
54
55
  "./edges/ref-validation": "./dist/edges/ref-validation.js"
@@ -74,7 +75,7 @@
74
75
  "license": "MIT",
75
76
  "repository": {
76
77
  "type": "git",
77
- "url": "https://github.com/jungjaehoon-lifegamez/MAMA.git",
78
+ "url": "git+https://github.com/jungjaehoon-lifegamez/MAMA.git",
78
79
  "directory": "packages/mama-core"
79
80
  },
80
81
  "bugs": {