@jungjaehoon/mama-core 1.6.0 → 1.8.1

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 (53) hide show
  1. package/db/migrations/037-create-context-packets.sql +32 -0
  2. package/db/migrations/038-create-vnext-operator-contracts.sql +57 -0
  3. package/db/migrations/039-add-connector-event-operator-ingest-seq.sql +96 -0
  4. package/db/migrations/040-create-operator-memory-commit-intents.sql +19 -0
  5. package/db/migrations/041-enforce-operator-memory-commit-claim-invariant.sql +74 -0
  6. package/db/migrations/042-embedding-prefix-scheme.sql +46 -0
  7. package/dist/cases/wiki-page-index.js +6 -2
  8. package/dist/context-compile/boundary-defaults.d.ts +12 -0
  9. package/dist/context-compile/boundary-defaults.js +105 -0
  10. package/dist/context-compile/compiler-policy.d.ts +39 -0
  11. package/dist/context-compile/compiler-policy.js +147 -0
  12. package/dist/context-compile/compiler.d.ts +18 -0
  13. package/dist/context-compile/compiler.js +401 -0
  14. package/dist/context-compile/index.d.ts +9 -0
  15. package/dist/context-compile/index.js +25 -0
  16. package/dist/context-compile/packet-store.d.ts +9 -0
  17. package/dist/context-compile/packet-store.js +255 -0
  18. package/dist/context-compile/ref.d.ts +7 -0
  19. package/dist/context-compile/ref.js +102 -0
  20. package/dist/context-compile/source-readers.d.ts +69 -0
  21. package/dist/context-compile/source-readers.js +707 -0
  22. package/dist/context-compile/types.d.ts +138 -0
  23. package/dist/context-compile/types.js +11 -0
  24. package/dist/context-compile/visibility.d.ts +21 -0
  25. package/dist/context-compile/visibility.js +200 -0
  26. package/dist/db-adapter/node-sqlite-adapter.d.ts +17 -1
  27. package/dist/db-adapter/node-sqlite-adapter.js +399 -6
  28. package/dist/db-manager.d.ts +9 -10
  29. package/dist/db-manager.js +59 -4
  30. package/dist/edges/ref-validation.js +55 -16
  31. package/dist/edges/types.d.ts +2 -0
  32. package/dist/embedding-client.d.ts +3 -1
  33. package/dist/embedding-client.js +3 -2
  34. package/dist/embedding-server/index.d.ts +2 -0
  35. package/dist/embedding-server/index.js +10 -2
  36. package/dist/embeddings.d.ts +8 -3
  37. package/dist/embeddings.js +48 -15
  38. package/dist/entities/store.d.ts +4 -1
  39. package/dist/entities/store.js +206 -78
  40. package/dist/entities/types.d.ts +5 -1
  41. package/dist/entities/types.js +1 -1
  42. package/dist/index.d.ts +4 -2
  43. package/dist/index.js +8 -4
  44. package/dist/mama-api.js +1 -1
  45. package/dist/memory/api.d.ts +6 -1
  46. package/dist/memory/api.js +223 -29
  47. package/dist/memory/provenance.d.ts +1 -0
  48. package/dist/memory/provenance.js +2 -0
  49. package/dist/memory/types.d.ts +1 -0
  50. package/dist/memory-inject.js +1 -1
  51. package/dist/provenance/source-ref.d.ts +29 -0
  52. package/dist/provenance/source-ref.js +204 -0
  53. package/package.json +4 -2
@@ -8,6 +8,7 @@ exports.getLexicalQueryTokens = getLexicalQueryTokens;
8
8
  exports.loadEdgesForIds = loadEdgesForIds;
9
9
  exports.saveMemory = saveMemory;
10
10
  exports.saveMemoryWithTrustedProvenance = saveMemoryWithTrustedProvenance;
11
+ exports.promoteMemoryStatus = promoteMemoryStatus;
11
12
  exports.buildProfile = buildProfile;
12
13
  exports.recallMemory = recallMemory;
13
14
  exports.ingestMemory = ingestMemory;
@@ -442,11 +443,16 @@ async function saveMemoryInternal(input, options) {
442
443
  const id = buildDecisionId(input.topic);
443
444
  const now = Date.now();
444
445
  const provenance = (0, provenance_js_1.normalizeMemoryWriteProvenance)(options);
446
+ const targetStatus = input.status ?? 'active';
447
+ const shouldApplyEvolution = targetStatus === 'active';
445
448
  // Find evolution candidates: exact topic match first, then semantic search fallback
446
449
  const primaryScope = input.scopes.length > 0 ? input.scopes[0] : null;
447
450
  const excludeIds = new Set(input.excludeIds ?? []);
448
451
  let existingCandidates;
449
- if (primaryScope) {
452
+ if (!shouldApplyEvolution) {
453
+ existingCandidates = [];
454
+ }
455
+ else if (primaryScope) {
450
456
  const scopeId = await (0, db_manager_js_1.ensureMemoryScope)(primaryScope.kind, primaryScope.id);
451
457
  existingCandidates = adapter
452
458
  .prepare(`
@@ -475,11 +481,14 @@ async function saveMemoryInternal(input, options) {
475
481
  .all(input.topic).filter((c) => !excludeIds.has(c.id));
476
482
  }
477
483
  // Semantic fallback: if no exact topic match, find similar memories via vector search
478
- if (existingCandidates.length === 0) {
484
+ if (shouldApplyEvolution && existingCandidates.length === 0) {
479
485
  try {
480
486
  const queryText = `${input.topic} ${input.summary}`;
481
- const embedding = await (0, embeddings_js_1.generateEmbedding)(queryText);
482
- const semanticResults = await (0, db_manager_js_1.vectorSearch)(embedding, 3, 0.82);
487
+ const embedding = await (0, embeddings_js_1.generateEmbedding)(queryText, 'query');
488
+ // Exclude superseded history so a dense supersede chain cannot fill all 3
489
+ // candidate slots and hide the prior ACTIVE decision (which would break the
490
+ // evolution link and create an active duplicate).
491
+ const semanticResults = await (0, db_manager_js_1.vectorSearch)(embedding, 3, 0.82, undefined, Array.from(EXCLUDED_STATUSES));
483
492
  // Scope-filter semantic candidates when a primary scope is available
484
493
  let scopeFiltered = semanticResults;
485
494
  if (primaryScope) {
@@ -509,13 +518,15 @@ async function saveMemoryInternal(input, options) {
509
518
  // Semantic search unavailable — proceed with empty candidates
510
519
  }
511
520
  }
512
- const evolution = (0, evolution_engine_js_1.resolveMemoryEvolution)({
513
- incoming: { topic: input.topic, summary: input.summary, kind: input.kind },
514
- existing: existingCandidates.map((c) => ({
515
- ...c,
516
- kind: (c.kind || 'fact'),
517
- })),
518
- });
521
+ const evolution = shouldApplyEvolution
522
+ ? (0, evolution_engine_js_1.resolveMemoryEvolution)({
523
+ incoming: { topic: input.topic, summary: input.summary, kind: input.kind },
524
+ existing: existingCandidates.map((c) => ({
525
+ ...c,
526
+ kind: (c.kind || 'fact'),
527
+ })),
528
+ })
529
+ : { edges: [] };
519
530
  const supersedesTarget = evolution.edges.find((edge) => edge.type === 'supersedes')?.to_id ?? null;
520
531
  const entityObservationIds = Array.from(new Set(input.entityObservationIds ?? []));
521
532
  const eventDateTime = typeof input.eventDateTime === 'number'
@@ -556,7 +567,7 @@ async function saveMemoryInternal(input, options) {
556
567
  SET kind = ?, status = ?, summary = ?, is_static = ?, trust_context = ?, updated_at = ?
557
568
  WHERE id = ?
558
569
  `)
559
- .run(input.kind, input.status ?? 'active', input.summary, input.kind === 'preference' || input.kind === 'constraint' ? 1 : 0, JSON.stringify({ source: input.source }), now, id);
570
+ .run(input.kind, targetStatus, input.summary, input.kind === 'preference' || input.kind === 'constraint' ? 1 : 0, JSON.stringify({ source: input.source }), now, id);
560
571
  for (const { scopeId, isPrimary } of resolvedScopeIds) {
561
572
  adapter
562
573
  .prepare(`
@@ -609,22 +620,41 @@ async function saveMemoryInternal(input, options) {
609
620
  rollbackError.memoryId = id;
610
621
  throw rollbackError;
611
622
  }
612
- // Project to memory_truth table (best-effort; failure should not break save)
613
- try {
614
- await (0, truth_store_js_1.projectMemoryTruth)({
615
- memory_id: id,
616
- topic: input.topic,
617
- truth_status: (input.status ?? 'active'),
618
- effective_summary: input.summary,
619
- effective_details: input.details,
620
- trust_score: input.confidence ?? 0.5,
621
- scope_refs: input.scopes,
622
- supporting_event_ids: [],
623
- superseded_by: undefined,
624
- });
623
+ // Sync the adapter's status cache for every row whose status changed in the
624
+ // post-insert transaction: the saved row itself (status is written AFTER
625
+ // insertEmbedding populated the cache) and any rows just superseded. This makes
626
+ // the vectorSearch pre-filter correct within this session, not only after reload.
627
+ if (adapter.refreshDecisionStatusCache) {
628
+ const changedIds = [
629
+ id,
630
+ ...evolution.edges.filter((e) => e.type === 'supersedes').map((e) => e.to_id),
631
+ ];
632
+ const ridStmt = adapter.prepare('SELECT rowid FROM decisions WHERE id = ?');
633
+ for (const changedId of changedIds) {
634
+ const ridRow = ridStmt.get(changedId);
635
+ if (ridRow) {
636
+ adapter.refreshDecisionStatusCache(ridRow.rowid);
637
+ }
638
+ }
625
639
  }
626
- catch {
627
- // Truth projection is best-effort; do not fail the save
640
+ if (options?.projectTruth !== false) {
641
+ // Project to memory_truth table (best-effort; failure should not break save)
642
+ try {
643
+ await (0, truth_store_js_1.projectMemoryTruth)({
644
+ memory_id: id,
645
+ topic: input.topic,
646
+ truth_status: targetStatus,
647
+ effective_summary: input.summary,
648
+ effective_details: input.details,
649
+ trust_score: input.confidence ?? 0.5,
650
+ scope_refs: input.scopes,
651
+ supporting_event_ids: [],
652
+ superseded_by: undefined,
653
+ });
654
+ }
655
+ catch {
656
+ // Truth projection is best-effort; do not fail the save
657
+ }
628
658
  }
629
659
  return {
630
660
  success: true,
@@ -640,6 +670,156 @@ async function saveMemory(input) {
640
670
  async function saveMemoryWithTrustedProvenance(input, options) {
641
671
  return saveMemoryInternal((0, provenance_js_1.sanitizePublicSaveMemoryInput)(input), options);
642
672
  }
673
+ async function promoteMemoryStatus(input) {
674
+ await (0, db_manager_js_1.initDB)();
675
+ const adapter = (0, db_manager_js_1.getAdapter)();
676
+ const memoryId = input.memoryId;
677
+ const now = input.nowMs ?? Date.now();
678
+ const targetStatus = input.status;
679
+ const row = adapter
680
+ .prepare(`
681
+ SELECT id, topic, decision, reasoning, confidence, kind, summary, supersedes
682
+ FROM decisions
683
+ WHERE id = ?
684
+ `)
685
+ .get(memoryId);
686
+ if (!row) {
687
+ throw new Error(`Cannot promote missing memory ${memoryId}`);
688
+ }
689
+ const topic = String(row.topic);
690
+ const summary = String(row.summary ?? row.decision ?? '');
691
+ const details = String(row.reasoning ?? row.decision ?? '');
692
+ const kind = (row.kind ?? 'fact') || 'fact';
693
+ const scopes = batchLoadScopes(adapter, [memoryId]).get(memoryId) ?? [];
694
+ let evolution = { edges: [] };
695
+ if (targetStatus === 'active') {
696
+ const primaryScope = scopes[0] ?? null;
697
+ let existingCandidates;
698
+ if (primaryScope) {
699
+ const scopeId = await (0, db_manager_js_1.ensureMemoryScope)(primaryScope.kind, primaryScope.id);
700
+ existingCandidates = adapter
701
+ .prepare(`
702
+ SELECT d.id, d.topic, d.summary, d.kind
703
+ FROM decisions d
704
+ JOIN memory_scope_bindings msb ON msb.memory_id = d.id
705
+ WHERE d.topic = ? AND msb.scope_id = ? AND d.id <> ?
706
+ AND (d.status = 'active' OR d.status IS NULL)
707
+ AND d.superseded_by IS NULL
708
+ ORDER BY d.created_at DESC
709
+ LIMIT 5
710
+ `)
711
+ .all(topic, scopeId, memoryId);
712
+ }
713
+ else {
714
+ existingCandidates = adapter
715
+ .prepare(`
716
+ SELECT id, topic, summary, kind
717
+ FROM decisions
718
+ WHERE topic = ? AND id <> ?
719
+ AND (status = 'active' OR status IS NULL)
720
+ AND superseded_by IS NULL
721
+ ORDER BY created_at DESC
722
+ LIMIT 5
723
+ `)
724
+ .all(topic, memoryId);
725
+ }
726
+ if (existingCandidates.length === 0) {
727
+ try {
728
+ const queryText = `${topic} ${summary}`;
729
+ const embedding = await (0, embeddings_js_1.generateEmbedding)(queryText, 'query');
730
+ // Same exclusion as saveMemoryInternal's fallback: superseded history must
731
+ // not crowd out the prior ACTIVE decision from the 3 candidate slots.
732
+ const semanticResults = await (0, db_manager_js_1.vectorSearch)(embedding, 3, 0.82, undefined, Array.from(EXCLUDED_STATUSES));
733
+ let scopeFiltered = semanticResults;
734
+ if (primaryScope) {
735
+ const semIds = semanticResults.map((result) => String(result.id));
736
+ const semScopeMap = batchLoadScopes(adapter, semIds);
737
+ const scopeKey = `${primaryScope.kind}:${primaryScope.id}`;
738
+ scopeFiltered = semanticResults.filter((result) => {
739
+ const resultScopes = semScopeMap.get(String(result.id)) ?? [];
740
+ return (resultScopes.length === 0 ||
741
+ resultScopes.some((scope) => `${scope.kind}:${scope.id}` === scopeKey));
742
+ });
743
+ }
744
+ existingCandidates = scopeFiltered
745
+ .filter((result) => String(result.id) !== memoryId)
746
+ .filter((result) => {
747
+ const status = String(result.status || '');
748
+ return !status || status === 'active' || status === '';
749
+ })
750
+ .map((result) => ({
751
+ id: String(result.id),
752
+ topic: String(result.topic || ''),
753
+ summary: String(result.decision || ''),
754
+ kind: 'fact',
755
+ _semanticMatch: true,
756
+ }));
757
+ }
758
+ catch {
759
+ // Semantic search unavailable — proceed with exact-match candidates only.
760
+ }
761
+ }
762
+ evolution = (0, evolution_engine_js_1.resolveMemoryEvolution)({
763
+ incoming: { topic, summary, kind },
764
+ existing: existingCandidates.map((candidate) => ({
765
+ ...candidate,
766
+ kind: (candidate.kind || 'fact'),
767
+ })),
768
+ });
769
+ }
770
+ const existingSupersedesTarget = typeof row.supersedes === 'string' && row.supersedes.length > 0 ? row.supersedes : null;
771
+ const supersedesTarget = evolution.edges.find((edge) => edge.type === 'supersedes')?.to_id ??
772
+ (targetStatus === 'active' ? existingSupersedesTarget : null);
773
+ adapter.transaction(() => {
774
+ const updated = adapter
775
+ .prepare('UPDATE decisions SET status = ?, supersedes = ?, updated_at = ? WHERE id = ?')
776
+ .run(targetStatus, supersedesTarget, now, memoryId);
777
+ if (updated.changes !== 1) {
778
+ throw new Error(`Cannot promote missing memory ${memoryId}`);
779
+ }
780
+ adapter
781
+ .prepare(`
782
+ INSERT OR REPLACE INTO memory_truth (
783
+ memory_id, topic, truth_status, effective_summary, effective_details, trust_score,
784
+ scope_refs, supporting_event_ids, superseded_by, contradicted_by, created_at, updated_at
785
+ )
786
+ VALUES (?, ?, ?, ?, ?, ?, ?, COALESCE((SELECT supporting_event_ids FROM memory_truth WHERE memory_id = ?), '[]'), NULL, NULL, COALESCE((SELECT created_at FROM memory_truth WHERE memory_id = ?), ?), ?)
787
+ `)
788
+ .run(memoryId, topic, targetStatus, summary, details, row.confidence ?? 0.5, JSON.stringify(scopes), memoryId, memoryId, now, now);
789
+ for (const edge of evolution.edges) {
790
+ if (edge.type === 'supersedes') {
791
+ adapter
792
+ .prepare(`UPDATE decisions SET superseded_by = ?, status = 'superseded', updated_at = ? WHERE id = ?`)
793
+ .run(memoryId, now, edge.to_id);
794
+ adapter
795
+ .prepare(`UPDATE memory_truth SET truth_status = 'superseded', superseded_by = ?, updated_at = ? WHERE memory_id = ?`)
796
+ .run(memoryId, now, edge.to_id);
797
+ }
798
+ adapter
799
+ .prepare(`
800
+ INSERT OR REPLACE INTO decision_edges (from_id, to_id, relationship, reason, weight, created_at)
801
+ VALUES (?, ?, ?, ?, ?, ?)
802
+ `)
803
+ .run(memoryId, edge.to_id, edge.type, edge.reason ?? null, 1.0, now);
804
+ }
805
+ });
806
+ // Sync the adapter's status cache for every row whose status changed above.
807
+ // Without this, a row promoted OUT of an excluded status (e.g. stale->active)
808
+ // stays invisible to the vectorSearch pre-filter until the next full reload.
809
+ if (adapter.refreshDecisionStatusCache) {
810
+ const changedIds = [
811
+ memoryId,
812
+ ...evolution.edges.filter((e) => e.type === 'supersedes').map((e) => e.to_id),
813
+ ];
814
+ const ridStmt = adapter.prepare('SELECT rowid FROM decisions WHERE id = ?');
815
+ for (const id of changedIds) {
816
+ const ridRow = ridStmt.get(id);
817
+ if (ridRow) {
818
+ adapter.refreshDecisionStatusCache(ridRow.rowid);
819
+ }
820
+ }
821
+ }
822
+ }
643
823
  async function buildProfile(scopes) {
644
824
  const records = await loadScopedMemories(scopes);
645
825
  return (0, profile_builder_js_1.classifyProfileEntries)(records);
@@ -704,11 +884,14 @@ async function recallMemory(query, options = {}) {
704
884
  let primaryQueryEmbedding = null;
705
885
  try {
706
886
  for (const sq of subQueries) {
707
- const queryEmbedding = await (0, embeddings_js_1.generateEmbedding)(sq);
887
+ const queryEmbedding = await (0, embeddings_js_1.generateEmbedding)(sq, 'query');
708
888
  if (sq === query && primaryQueryEmbedding === null) {
709
889
  primaryQueryEmbedding = queryEmbedding;
710
890
  }
711
- const vectorResults = await (0, db_manager_js_1.vectorSearch)(queryEmbedding, vectorLimit, searchOptions.threshold, searchOptions.topicPrefix);
891
+ const vectorResults = await (0, db_manager_js_1.vectorSearch)(queryEmbedding, vectorLimit, searchOptions.threshold, searchOptions.topicPrefix,
892
+ // Keep superseded history out of the candidate top-K at search time; the
893
+ // post-filter below stays the authority (and includeHistory restores it).
894
+ options.includeHistory ? undefined : Array.from(EXCLUDED_STATUSES));
712
895
  let filtered = vectorResults;
713
896
  let vectorScopeMap = new Map();
714
897
  if (options.scopes && options.scopes.length > 0) {
@@ -1318,6 +1501,17 @@ async function recallMemory(query, options = {}) {
1318
1501
  ];
1319
1502
  });
1320
1503
  fusedHits = fusedHits.filter((hit) => hit.source_type !== 'decision' || acceptedPrimaryIds.has(hit.source_id));
1504
+ // Honor options.limit on the final memories (matched is RRF-rank-sorted, canonical
1505
+ // dual-write appends last). Without this cap the full fusion set (hundreds of records)
1506
+ // flowed into bundle.memories AND the per-record enrichment SQL loops below.
1507
+ if (matched.length > requestedLimit) {
1508
+ matched = matched.slice(0, requestedLimit);
1509
+ // Keep bundle.fused_hits consistent with the capped memories: drop decision hits
1510
+ // whose record no longer appears in `matched` (wiki hits are added below and are
1511
+ // not keyed to memories).
1512
+ const cappedIds = new Set(matched.map((record) => record.id));
1513
+ fusedHits = fusedHits.filter((hit) => hit.source_type !== 'decision' || cappedIds.has(hit.source_id));
1514
+ }
1321
1515
  const acceptedWikiFusedHits = Array.from(wikiScores.values())
1322
1516
  .sort((a, b) => b.score - a.score)
1323
1517
  .flatMap((entry) => {
@@ -5,6 +5,7 @@ export interface TrustedProvenanceCapability {
5
5
  export interface TrustedMemoryWriteOptions {
6
6
  provenance: MemoryWriteProvenance;
7
7
  capability: TrustedProvenanceCapability;
8
+ projectTruth?: boolean;
8
9
  }
9
10
  export interface NormalizedMemoryProvenance {
10
11
  actor: MemoryEventRecord['actor'];
@@ -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',
@@ -91,6 +92,7 @@ function appendProvenanceSourceRefs(options, refs) {
91
92
  assertTrustedProvenanceCapability(options.capability);
92
93
  return {
93
94
  capability: options.capability,
95
+ projectTruth: options.projectTruth,
94
96
  provenance: {
95
97
  ...options.provenance,
96
98
  source_refs: [...(options.provenance.source_refs ?? []), ...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[];
@@ -77,7 +77,7 @@ async function injectDecisionContext(userMessage) {
77
77
  async function performMemoryInjection(userMessage, startTime) {
78
78
  // 1. Generate query embedding
79
79
  const { generateEmbedding } = await import('./embeddings.js');
80
- const queryEmbedding = await generateEmbedding(userMessage);
80
+ const queryEmbedding = await generateEmbedding(userMessage, 'query');
81
81
  const embeddingLatency = Date.now() - startTime;
82
82
  (0, debug_logger_js_1.info)(`[MAMA] Embedding generation: ${embeddingLatency}ms`);
83
83
  // 2. Adaptive threshold (shorter queries need higher confidence)
@@ -0,0 +1,29 @@
1
+ import { type ContextRef } from '../context-compile/types.js';
2
+ export declare const STRICT_SOURCE_REF_KINDS: readonly ["memory", "raw", "entity", "case", "decision", "os_task", "agent_situation_packet", "report_slot", "context_packet", "model_run", "tool_trace", "wiki_page", "report"];
3
+ export type StrictSourceRefKind = (typeof STRICT_SOURCE_REF_KINDS)[number];
4
+ export declare const LEGACY_SOURCE_REF_KINDS: readonly ["message", "conversation", "raw_memory"];
5
+ export type LegacySourceRefKind = (typeof LEGACY_SOURCE_REF_KINDS)[number];
6
+ export type SourceRef = {
7
+ kind: Exclude<StrictSourceRefKind, 'raw'>;
8
+ id: string;
9
+ } | {
10
+ kind: 'raw';
11
+ id: string;
12
+ connector: string;
13
+ source_id?: string;
14
+ channel_id?: string | null;
15
+ } | {
16
+ kind: 'legacy';
17
+ legacy_kind: LegacySourceRefKind;
18
+ id: string;
19
+ };
20
+ export declare function fromContextRef(ref: ContextRef): SourceRef;
21
+ export declare function toContextRef(ref: SourceRef): ContextRef | null;
22
+ export declare function fromVerifyArtifact(artifact: {
23
+ type: string;
24
+ id: string;
25
+ }): SourceRef;
26
+ export declare function parseSourceRefString(value: string): SourceRef;
27
+ export declare function serializeSourceRef(ref: SourceRef): string;
28
+ export declare function assertNonEmptySourceRefs(refs: readonly SourceRef[]): void;
29
+ //# sourceMappingURL=source-ref.d.ts.map
@@ -0,0 +1,204 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LEGACY_SOURCE_REF_KINDS = exports.STRICT_SOURCE_REF_KINDS = void 0;
4
+ exports.fromContextRef = fromContextRef;
5
+ exports.toContextRef = toContextRef;
6
+ exports.fromVerifyArtifact = fromVerifyArtifact;
7
+ exports.parseSourceRefString = parseSourceRefString;
8
+ exports.serializeSourceRef = serializeSourceRef;
9
+ exports.assertNonEmptySourceRefs = assertNonEmptySourceRefs;
10
+ const types_js_1 = require("../context-compile/types.js");
11
+ exports.STRICT_SOURCE_REF_KINDS = [
12
+ 'memory',
13
+ 'raw',
14
+ 'entity',
15
+ 'case',
16
+ 'decision',
17
+ 'os_task',
18
+ 'agent_situation_packet',
19
+ 'report_slot',
20
+ 'context_packet',
21
+ 'model_run',
22
+ 'tool_trace',
23
+ 'wiki_page',
24
+ 'report',
25
+ ];
26
+ exports.LEGACY_SOURCE_REF_KINDS = ['message', 'conversation', 'raw_memory'];
27
+ const STRICT_SOURCE_REF_KIND_SET = new Set(exports.STRICT_SOURCE_REF_KINDS);
28
+ const CONTEXT_REF_KIND_SET = new Set(types_js_1.CONTEXT_REF_KINDS);
29
+ const LEGACY_SOURCE_REF_KIND_SET = new Set(exports.LEGACY_SOURCE_REF_KINDS);
30
+ const VERIFY_ARTIFACT_KIND_SET = new Set([
31
+ 'decision',
32
+ 'os_task',
33
+ 'agent_situation_packet',
34
+ 'report_slot',
35
+ 'context_packet',
36
+ 'model_run',
37
+ 'tool_trace',
38
+ ]);
39
+ function requiredString(value, field) {
40
+ if (typeof value !== 'string') {
41
+ throw new Error(`Source ref ${field} must be a string`);
42
+ }
43
+ const trimmed = value.trim();
44
+ if (trimmed.length === 0) {
45
+ throw new Error(`Source ref ${field} must not be empty`);
46
+ }
47
+ return trimmed;
48
+ }
49
+ function optionalString(value, field) {
50
+ if (value === undefined) {
51
+ return undefined;
52
+ }
53
+ return requiredString(value, field);
54
+ }
55
+ function optionalNullableString(value, field) {
56
+ if (value === null) {
57
+ return null;
58
+ }
59
+ return optionalString(value, field);
60
+ }
61
+ function assertStrictSourceRefKind(kind) {
62
+ if (!STRICT_SOURCE_REF_KIND_SET.has(kind)) {
63
+ throw new Error(`Unsupported source ref kind: ${kind}`);
64
+ }
65
+ }
66
+ function assertLegacySourceRefKind(kind) {
67
+ if (!LEGACY_SOURCE_REF_KIND_SET.has(kind)) {
68
+ throw new Error(`Unsupported legacy source ref kind: ${kind}`);
69
+ }
70
+ }
71
+ function assertContextSourceRefKind(kind) {
72
+ if (kind === 'raw' || !CONTEXT_REF_KIND_SET.has(kind)) {
73
+ throw new Error(`Unsupported context source ref kind: ${kind}`);
74
+ }
75
+ }
76
+ function fromContextRef(ref) {
77
+ if (ref === null || ref === undefined) {
78
+ throw new Error('Context ref must not be null or undefined');
79
+ }
80
+ const input = ref;
81
+ const kind = requiredString(input.kind, 'kind');
82
+ if (kind === 'raw') {
83
+ const normalized = {
84
+ kind: 'raw',
85
+ id: requiredString(input.raw_id, 'raw_id'),
86
+ connector: requiredString(input.connector, 'connector'),
87
+ };
88
+ const sourceId = optionalString(input.source_id, 'source_id');
89
+ const channelId = optionalNullableString(input.channel_id, 'channel_id');
90
+ if (sourceId !== undefined) {
91
+ normalized.source_id = sourceId;
92
+ }
93
+ if (channelId !== undefined) {
94
+ normalized.channel_id = channelId;
95
+ }
96
+ return normalized;
97
+ }
98
+ assertContextSourceRefKind(kind);
99
+ return {
100
+ kind,
101
+ id: requiredString(input.id, 'id'),
102
+ };
103
+ }
104
+ function toContextRef(ref) {
105
+ if (ref === null || ref === undefined) {
106
+ return null;
107
+ }
108
+ switch (ref.kind) {
109
+ case 'memory':
110
+ case 'entity':
111
+ case 'case':
112
+ return {
113
+ kind: ref.kind,
114
+ id: requiredString(ref.id, 'id'),
115
+ };
116
+ case 'raw': {
117
+ const normalized = {
118
+ kind: 'raw',
119
+ raw_id: requiredString(ref.id, 'id'),
120
+ connector: requiredString(ref.connector, 'connector'),
121
+ };
122
+ const sourceId = optionalString(ref.source_id, 'source_id');
123
+ const channelId = optionalNullableString(ref.channel_id, 'channel_id');
124
+ if (sourceId !== undefined) {
125
+ normalized.source_id = sourceId;
126
+ }
127
+ if (channelId !== undefined) {
128
+ normalized.channel_id = channelId;
129
+ }
130
+ return normalized;
131
+ }
132
+ default:
133
+ return null;
134
+ }
135
+ }
136
+ function fromVerifyArtifact(artifact) {
137
+ if (artifact === null || artifact === undefined) {
138
+ throw new Error('Artifact must not be null or undefined');
139
+ }
140
+ const kind = requiredString(artifact.type, 'type');
141
+ if (!VERIFY_ARTIFACT_KIND_SET.has(kind)) {
142
+ throw new Error(`Unsupported verify artifact source ref kind: ${kind}`);
143
+ }
144
+ assertStrictSourceRefKind(kind);
145
+ if (kind === 'raw') {
146
+ throw new Error('Verify artifact source refs cannot use raw kind');
147
+ }
148
+ return {
149
+ kind,
150
+ id: requiredString(artifact.id, 'id'),
151
+ };
152
+ }
153
+ function parseSourceRefString(value) {
154
+ const rawValue = requiredString(value, 'value');
155
+ const parts = rawValue.split(':');
156
+ const kind = requiredString(parts[0], 'kind');
157
+ if (kind === 'raw') {
158
+ const connector = requiredString(parts[1], 'connector');
159
+ const id = requiredString(parts.slice(2).join(':'), 'id');
160
+ return { kind: 'raw', connector, id };
161
+ }
162
+ if (LEGACY_SOURCE_REF_KIND_SET.has(kind)) {
163
+ assertLegacySourceRefKind(kind);
164
+ return {
165
+ kind: 'legacy',
166
+ legacy_kind: kind,
167
+ id: requiredString(parts.slice(1).join(':'), 'id'),
168
+ };
169
+ }
170
+ assertStrictSourceRefKind(kind);
171
+ if (kind === 'raw') {
172
+ throw new Error('Raw source refs must include connector and id');
173
+ }
174
+ const sourceKind = kind;
175
+ return {
176
+ kind: sourceKind,
177
+ id: requiredString(parts.slice(1).join(':'), 'id'),
178
+ };
179
+ }
180
+ function serializeSourceRef(ref) {
181
+ if (ref === null || ref === undefined) {
182
+ throw new Error('Source ref must not be null or undefined');
183
+ }
184
+ switch (ref.kind) {
185
+ case 'raw':
186
+ return `raw:${requiredString(ref.connector, 'connector')}:${requiredString(ref.id, 'id')}`;
187
+ case 'legacy':
188
+ assertLegacySourceRefKind(ref.legacy_kind);
189
+ return `${ref.legacy_kind}:${requiredString(ref.id, 'id')}`;
190
+ default: {
191
+ assertStrictSourceRefKind(ref.kind);
192
+ return `${ref.kind}:${requiredString(ref.id, 'id')}`;
193
+ }
194
+ }
195
+ }
196
+ function assertNonEmptySourceRefs(refs) {
197
+ if (!Array.isArray(refs) || refs.length === 0) {
198
+ throw new Error('Source refs must not be empty');
199
+ }
200
+ for (const ref of refs) {
201
+ serializeSourceRef(ref);
202
+ }
203
+ }
204
+ //# sourceMappingURL=source-ref.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jungjaehoon/mama-core",
3
- "version": "1.6.0",
3
+ "version": "1.8.1",
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,8 @@
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",
53
+ "./provenance/source-ref": "./dist/provenance/source-ref.js",
52
54
  "./edges/types": "./dist/edges/types.js",
53
55
  "./edges/store": "./dist/edges/store.js",
54
56
  "./edges/ref-validation": "./dist/edges/ref-validation.js"
@@ -74,7 +76,7 @@
74
76
  "license": "MIT",
75
77
  "repository": {
76
78
  "type": "git",
77
- "url": "https://github.com/jungjaehoon-lifegamez/MAMA.git",
79
+ "url": "git+https://github.com/jungjaehoon-lifegamez/MAMA.git",
78
80
  "directory": "packages/mama-core"
79
81
  },
80
82
  "bugs": {