@jungjaehoon/mama-core 4.1.1 → 5.0.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 (37) hide show
  1. package/README.md +10 -10
  2. package/db/migrations/099-commitment-revision-graph.sql +3 -0
  3. package/dist/api/catalog.js +3 -6
  4. package/dist/db-adapter/node-sqlite-adapter.d.ts +5 -1
  5. package/dist/db-adapter/node-sqlite-adapter.js +27 -24
  6. package/dist/db-manager.d.ts +2 -13
  7. package/dist/index.d.ts +2 -2
  8. package/dist/index.js +3 -6
  9. package/dist/knowledge/commitments.d.ts +2 -0
  10. package/dist/knowledge/commitments.js +1 -0
  11. package/dist/knowledge/graph-query.d.ts +10 -1
  12. package/dist/knowledge/graph-query.js +118 -25
  13. package/dist/knowledge/index.d.ts +11 -3
  14. package/dist/knowledge/index.js +9 -10
  15. package/dist/knowledge/judgments.d.ts +3 -1
  16. package/dist/knowledge/judgments.js +46 -26
  17. package/dist/knowledge/links.d.ts +50 -0
  18. package/dist/knowledge/links.js +152 -0
  19. package/dist/mama-api.d.ts +22 -7
  20. package/dist/mama-api.js +64 -15
  21. package/dist/memory/api.d.ts +16 -11
  22. package/dist/memory/api.js +376 -400
  23. package/dist/memory/decision-links.d.ts +54 -0
  24. package/dist/memory/decision-links.js +195 -0
  25. package/dist/memory/graph-read.d.ts +0 -16
  26. package/dist/memory/graph-read.js +10 -51
  27. package/dist/memory/judgment-types.d.ts +1 -20
  28. package/dist/memory/types.d.ts +13 -0
  29. package/dist/memory/write-adapters.d.ts +1 -39
  30. package/dist/memory/write-adapters.js +0 -98
  31. package/package.json +1 -1
  32. package/dist/knowledge/commitment-revision-migration.d.ts +0 -4
  33. package/dist/knowledge/commitment-revision-migration.js +0 -50
  34. package/dist/knowledge/decision-edges.d.ts +0 -70
  35. package/dist/knowledge/decision-edges.js +0 -123
  36. package/dist/memory/evolution-engine.d.ts +0 -22
  37. package/dist/memory/evolution-engine.js +0 -133
@@ -1,70 +0,0 @@
1
- import type { DatabaseAdapter } from '../db-manager.js';
2
- /**
3
- * decision_edges + link_audit_log write boundary.
4
- *
5
- * Every mutation of the decision-edge graph goes through this module, so
6
- * `decision_edges` and `link_audit_log` each have exactly one writing module
7
- * inside `knowledge/` (the sibling judgments.ts owns the judgment-command
8
- * edge insert). Each function performs the edge change and its audit row
9
- * inside ONE adapter transaction - a link write and its audit row commit or
10
- * fail together.
11
- */
12
- type EdgeWriter = Pick<DatabaseAdapter, 'prepare' | 'transaction'>;
13
- export interface DecisionEdgeKey {
14
- fromId: string;
15
- toId: string;
16
- relationship: string;
17
- }
18
- export interface DecisionEdgeRow extends DecisionEdgeKey {
19
- reason: string | null;
20
- createdBy: string | null;
21
- approvedByUser: number | null;
22
- decisionId: string | null;
23
- evidence: string | null;
24
- createdAt: number | null;
25
- }
26
- export interface ProposedDecisionEdge extends DecisionEdgeKey {
27
- reason: string;
28
- decisionId: string | null;
29
- evidence: string | null;
30
- }
31
- export interface DecisionEdgeDeleteFailure {
32
- link: string;
33
- error: string;
34
- }
35
- /**
36
- * Insert-or-replace one decision edge row with explicit governance columns.
37
- * Used by the public createEdge API (createdBy 'llm', approvedByUser 1) and by
38
- * the backup-restore path (columns replayed from the backup file).
39
- */
40
- export declare function upsertDecisionEdge(adapter: EdgeWriter, edge: DecisionEdgeRow): void;
41
- /**
42
- * Record a proposed edge pending user approval, plus its 'proposed' audit row.
43
- */
44
- export declare function proposeDecisionEdge(adapter: EdgeWriter, edge: ProposedDecisionEdge): void;
45
- /**
46
- * Approve a pending edge, plus its 'approved' audit row.
47
- */
48
- export declare function approveDecisionEdge(adapter: EdgeWriter, key: DecisionEdgeKey): void;
49
- /**
50
- * Reject a proposed edge: the 'rejected' audit row is written before the edge
51
- * is deleted, in the same transaction.
52
- */
53
- export declare function rejectDecisionEdge(adapter: EdgeWriter, key: DecisionEdgeKey, reason: string): void;
54
- /**
55
- * Remove the auto-generated edge population (created_by='user' with no
56
- * proposal context) and record one 'deprecated' audit row per removed link.
57
- * `links` is the already-scanned target set; it only feeds the audit rows.
58
- */
59
- export declare function deprecateAutoDecisionEdges(adapter: EdgeWriter, links: readonly DecisionEdgeKey[], reason: string): void;
60
- /**
61
- * Delete one batch of edges, writing a 'deprecated' audit row for each. A
62
- * per-link failure is collected and the batch continues, matching the cleanup
63
- * tool's accounting; a failure of the batch itself throws to the caller.
64
- */
65
- export declare function deleteDecisionEdgesWithAudit(adapter: EdgeWriter, links: readonly DecisionEdgeKey[], reason: string): {
66
- deleted: number;
67
- failures: DecisionEdgeDeleteFailure[];
68
- };
69
- export {};
70
- //# sourceMappingURL=decision-edges.d.ts.map
@@ -1,123 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.upsertDecisionEdge = upsertDecisionEdge;
4
- exports.proposeDecisionEdge = proposeDecisionEdge;
5
- exports.approveDecisionEdge = approveDecisionEdge;
6
- exports.rejectDecisionEdge = rejectDecisionEdge;
7
- exports.deprecateAutoDecisionEdges = deprecateAutoDecisionEdges;
8
- exports.deleteDecisionEdgesWithAudit = deleteDecisionEdgesWithAudit;
9
- /**
10
- * Insert-or-replace one decision edge row with explicit governance columns.
11
- * Used by the public createEdge API (createdBy 'llm', approvedByUser 1) and by
12
- * the backup-restore path (columns replayed from the backup file).
13
- */
14
- function upsertDecisionEdge(adapter, edge) {
15
- adapter
16
- .prepare(`INSERT OR REPLACE INTO decision_edges
17
- (from_id, to_id, relationship, reason, created_by, approved_by_user, decision_id, evidence, created_at)
18
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
19
- .run(edge.fromId, edge.toId, edge.relationship, edge.reason, edge.createdBy, edge.approvedByUser, edge.decisionId, edge.evidence, edge.createdAt);
20
- }
21
- /**
22
- * Record a proposed edge pending user approval, plus its 'proposed' audit row.
23
- */
24
- function proposeDecisionEdge(adapter, edge) {
25
- adapter.transaction(() => {
26
- adapter
27
- .prepare(`INSERT INTO decision_edges
28
- (from_id, to_id, relationship, reason, created_by, approved_by_user, decision_id, evidence, created_at)
29
- VALUES (?, ?, ?, ?, 'llm', 0, ?, ?, ?)`)
30
- .run(edge.fromId, edge.toId, edge.relationship, edge.reason, edge.decisionId, edge.evidence, Date.now());
31
- adapter
32
- .prepare(`INSERT INTO link_audit_log (from_id, to_id, relationship, action, actor, reason, created_at)
33
- VALUES (?, ?, ?, 'proposed', 'llm', ?, ?)`)
34
- .run(edge.fromId, edge.toId, edge.relationship, edge.reason, Date.now());
35
- });
36
- }
37
- /**
38
- * Approve a pending edge, plus its 'approved' audit row.
39
- */
40
- function approveDecisionEdge(adapter, key) {
41
- adapter.transaction(() => {
42
- adapter
43
- .prepare(`UPDATE decision_edges
44
- SET approved_by_user = 1, approved_at = ?
45
- WHERE from_id = ? AND to_id = ? AND relationship = ?`)
46
- .run(Date.now(), key.fromId, key.toId, key.relationship);
47
- adapter
48
- .prepare(`INSERT INTO link_audit_log (from_id, to_id, relationship, action, actor, created_at)
49
- VALUES (?, ?, ?, 'approved', 'user', ?)`)
50
- .run(key.fromId, key.toId, key.relationship, Date.now());
51
- });
52
- }
53
- /**
54
- * Reject a proposed edge: the 'rejected' audit row is written before the edge
55
- * is deleted, in the same transaction.
56
- */
57
- function rejectDecisionEdge(adapter, key, reason) {
58
- adapter.transaction(() => {
59
- adapter
60
- .prepare(`INSERT INTO link_audit_log (from_id, to_id, relationship, action, actor, reason, created_at)
61
- VALUES (?, ?, ?, 'rejected', 'user', ?, ?)`)
62
- .run(key.fromId, key.toId, key.relationship, reason, Date.now());
63
- adapter
64
- .prepare(`DELETE FROM decision_edges
65
- WHERE from_id = ? AND to_id = ? AND relationship = ?`)
66
- .run(key.fromId, key.toId, key.relationship);
67
- });
68
- }
69
- /**
70
- * Remove the auto-generated edge population (created_by='user' with no
71
- * proposal context) and record one 'deprecated' audit row per removed link.
72
- * `links` is the already-scanned target set; it only feeds the audit rows.
73
- */
74
- function deprecateAutoDecisionEdges(adapter, links, reason) {
75
- adapter.transaction(() => {
76
- adapter
77
- .prepare(`DELETE FROM decision_edges WHERE created_by = 'user' AND decision_id IS NULL`)
78
- .run();
79
- const auditStmt = adapter.prepare(`INSERT INTO link_audit_log (from_id, to_id, relationship, action, actor, reason, created_at)
80
- VALUES (?, ?, ?, 'deprecated', 'system', ?, ?)`);
81
- const timestamp = Date.now();
82
- for (const link of links) {
83
- auditStmt.run(link.fromId, link.toId, link.relationship, reason, timestamp);
84
- }
85
- });
86
- }
87
- /**
88
- * Delete one batch of edges, writing a 'deprecated' audit row for each. A
89
- * per-link failure is collected and the batch continues, matching the cleanup
90
- * tool's accounting; a failure of the batch itself throws to the caller.
91
- */
92
- function deleteDecisionEdgesWithAudit(adapter, links, reason) {
93
- const deleteStmt = adapter.prepare(`DELETE FROM decision_edges
94
- WHERE from_id = ? AND to_id = ? AND relationship = ?`);
95
- const auditStmt = adapter.prepare(`INSERT INTO link_audit_log (from_id, to_id, relationship, action, actor, reason, created_at)
96
- VALUES (?, ?, ?, 'deprecated', 'system', ?, ?)`);
97
- let deleted = 0;
98
- const failures = [];
99
- const processLinks = () => {
100
- for (const link of links) {
101
- try {
102
- deleteStmt.run(link.fromId, link.toId, link.relationship);
103
- auditStmt.run(link.fromId, link.toId, link.relationship, reason, Date.now());
104
- deleted++;
105
- }
106
- catch (error) {
107
- failures.push({
108
- link: `${link.fromId}->${link.toId}`,
109
- error: error instanceof Error ? error.message : String(error),
110
- });
111
- }
112
- }
113
- };
114
- // Use transaction if available, otherwise run directly
115
- if (adapter.transaction) {
116
- adapter.transaction(processLinks);
117
- }
118
- else {
119
- processLinks();
120
- }
121
- return { deleted, failures };
122
- }
123
- //# sourceMappingURL=decision-edges.js.map
@@ -1,22 +0,0 @@
1
- import type { MemoryEdge, MemoryRecord } from './types.js';
2
- interface EvolutionInput {
3
- incoming: Pick<MemoryRecord, 'topic' | 'summary' | 'kind'>;
4
- existing: Array<Pick<MemoryRecord, 'id' | 'topic' | 'summary' | 'kind'> & {
5
- _semanticMatch?: boolean;
6
- }>;
7
- }
8
- export interface EvolutionResult {
9
- edges: MemoryEdge[];
10
- }
11
- /**
12
- * Resolve how an incoming memory relates to existing memories.
13
- *
14
- * Supersede rules (conservative — avoid information loss):
15
- * 1. Raw → Extracted: structured fact replaces raw conversation (same topic, raw kind)
16
- * 2. Same fact update: same topic + high summary overlap (≥0.6) between two extracted facts
17
- *
18
- * Everything else → builds_on (preserves independent facts under same topic)
19
- */
20
- export declare function resolveMemoryEvolution(input: EvolutionInput): EvolutionResult;
21
- export {};
22
- //# sourceMappingURL=evolution-engine.d.ts.map
@@ -1,133 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolveMemoryEvolution = resolveMemoryEvolution;
4
- const STOP_WORDS = new Set([
5
- 'the',
6
- 'is',
7
- 'a',
8
- 'an',
9
- 'to',
10
- 'in',
11
- 'on',
12
- 'of',
13
- 'for',
14
- 'and',
15
- 'or',
16
- 'but',
17
- 'it',
18
- 'we',
19
- 'i',
20
- 'this',
21
- 'that',
22
- 'with',
23
- 'as',
24
- 'at',
25
- 'by',
26
- 'from',
27
- 'be',
28
- 'are',
29
- 'was',
30
- 'were',
31
- 'been',
32
- 'has',
33
- 'have',
34
- 'had',
35
- 'do',
36
- 'does',
37
- 'did',
38
- 'will',
39
- 'would',
40
- 'can',
41
- 'could',
42
- 'should',
43
- 'not',
44
- 'no',
45
- 'so',
46
- 'if',
47
- 'up',
48
- 'out',
49
- 'all',
50
- 'its',
51
- 'user',
52
- 'assistant',
53
- ]);
54
- const MIN_TOKEN_LENGTH = 3;
55
- function summaryOverlapRatio(left, right) {
56
- const tokenize = (s) => new Set(s
57
- .toLowerCase()
58
- .split(/[^a-z0-9_]+/)
59
- .filter((t) => t.length >= MIN_TOKEN_LENGTH && !STOP_WORDS.has(t)));
60
- const leftTokens = tokenize(left);
61
- const rightTokens = tokenize(right);
62
- if (leftTokens.size === 0 && rightTokens.size === 0) {
63
- return left.toLowerCase().trim() === right.toLowerCase().trim() ? 1 : 0;
64
- }
65
- const smaller = leftTokens.size <= rightTokens.size ? leftTokens : rightTokens;
66
- const larger = leftTokens.size > rightTokens.size ? leftTokens : rightTokens;
67
- let shared = 0;
68
- for (const t of smaller) {
69
- if (larger.has(t))
70
- shared++;
71
- }
72
- return shared / Math.max(smaller.size, 1);
73
- }
74
- /**
75
- * Resolve how an incoming memory relates to existing memories.
76
- *
77
- * Supersede rules (conservative — avoid information loss):
78
- * 1. Raw → Extracted: structured fact replaces raw conversation (same topic, raw kind)
79
- * 2. Same fact update: same topic + high summary overlap (≥0.6) between two extracted facts
80
- *
81
- * Everything else → builds_on (preserves independent facts under same topic)
82
- */
83
- function resolveMemoryEvolution(input) {
84
- const edges = [];
85
- for (const existing of input.existing) {
86
- if (existing.topic === input.incoming.topic) {
87
- // Rule 1: Raw → Extracted supersede (ingestConversation saves raw first, then extracted)
88
- // Raw records have kind='fact' but summary starts with conversation text (role prefixes)
89
- const existingIsRaw = existing.kind === 'fact' && /^(user:|assistant:)/i.test(existing.summary.trim());
90
- if (existingIsRaw) {
91
- edges.push({
92
- from_id: 'incoming',
93
- to_id: existing.id,
94
- type: 'supersedes',
95
- reason: 'Structured extraction replaces raw conversation',
96
- });
97
- continue;
98
- }
99
- // Rule 2: Same fact update — supersede if summaries share meaningful overlap.
100
- // Threshold 0.3 balances: "Use SQLite" → "Switch to PostgreSQL" (same topic, genuine update)
101
- // vs "User's cat Luna" / "User's wedding plan" (same topic, independent facts).
102
- const overlap = summaryOverlapRatio(existing.summary, input.incoming.summary);
103
- if (overlap >= 0.3) {
104
- edges.push({
105
- from_id: 'incoming',
106
- to_id: existing.id,
107
- type: 'supersedes',
108
- reason: `Updated fact (${(overlap * 100).toFixed(0)}% overlap)`,
109
- });
110
- continue;
111
- }
112
- // Different content under same topic → independent facts, link as builds_on
113
- edges.push({
114
- from_id: 'incoming',
115
- to_id: existing.id,
116
- type: 'builds_on',
117
- reason: `Related but distinct (${(overlap * 100).toFixed(0)}% overlap)`,
118
- });
119
- continue;
120
- }
121
- // Semantic candidates (vector similarity >= 0.82) → builds_on
122
- if (existing._semanticMatch) {
123
- edges.push({
124
- from_id: 'incoming',
125
- to_id: existing.id,
126
- type: 'builds_on',
127
- reason: 'Semantically similar memory detected via vector search',
128
- });
129
- }
130
- }
131
- return { edges };
132
- }
133
- //# sourceMappingURL=evolution-engine.js.map