agent-working-memory 0.5.5 → 0.6.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 (82) hide show
  1. package/README.md +428 -399
  2. package/dist/api/routes.d.ts.map +1 -1
  3. package/dist/api/routes.js +60 -5
  4. package/dist/api/routes.js.map +1 -1
  5. package/dist/cli.js +468 -68
  6. package/dist/cli.js.map +1 -1
  7. package/dist/coordination/index.d.ts +11 -0
  8. package/dist/coordination/index.d.ts.map +1 -0
  9. package/dist/coordination/index.js +39 -0
  10. package/dist/coordination/index.js.map +1 -0
  11. package/dist/coordination/mcp-tools.d.ts +8 -0
  12. package/dist/coordination/mcp-tools.d.ts.map +1 -0
  13. package/dist/coordination/mcp-tools.js +221 -0
  14. package/dist/coordination/mcp-tools.js.map +1 -0
  15. package/dist/coordination/routes.d.ts +9 -0
  16. package/dist/coordination/routes.d.ts.map +1 -0
  17. package/dist/coordination/routes.js +573 -0
  18. package/dist/coordination/routes.js.map +1 -0
  19. package/dist/coordination/schema.d.ts +12 -0
  20. package/dist/coordination/schema.d.ts.map +1 -0
  21. package/dist/coordination/schema.js +125 -0
  22. package/dist/coordination/schema.js.map +1 -0
  23. package/dist/coordination/schemas.d.ts +227 -0
  24. package/dist/coordination/schemas.d.ts.map +1 -0
  25. package/dist/coordination/schemas.js +125 -0
  26. package/dist/coordination/schemas.js.map +1 -0
  27. package/dist/coordination/stale.d.ts +27 -0
  28. package/dist/coordination/stale.d.ts.map +1 -0
  29. package/dist/coordination/stale.js +58 -0
  30. package/dist/coordination/stale.js.map +1 -0
  31. package/dist/engine/activation.d.ts.map +1 -1
  32. package/dist/engine/activation.js +119 -23
  33. package/dist/engine/activation.js.map +1 -1
  34. package/dist/engine/consolidation.d.ts.map +1 -1
  35. package/dist/engine/consolidation.js +27 -6
  36. package/dist/engine/consolidation.js.map +1 -1
  37. package/dist/index.js +100 -4
  38. package/dist/index.js.map +1 -1
  39. package/dist/mcp.js +149 -80
  40. package/dist/mcp.js.map +1 -1
  41. package/dist/storage/sqlite.d.ts +21 -0
  42. package/dist/storage/sqlite.d.ts.map +1 -1
  43. package/dist/storage/sqlite.js +331 -282
  44. package/dist/storage/sqlite.js.map +1 -1
  45. package/dist/types/engram.d.ts +24 -0
  46. package/dist/types/engram.d.ts.map +1 -1
  47. package/dist/types/engram.js.map +1 -1
  48. package/package.json +57 -55
  49. package/src/api/index.ts +3 -3
  50. package/src/api/routes.ts +600 -536
  51. package/src/cli.ts +850 -397
  52. package/src/coordination/index.ts +47 -0
  53. package/src/coordination/mcp-tools.ts +318 -0
  54. package/src/coordination/routes.ts +846 -0
  55. package/src/coordination/schema.ts +120 -0
  56. package/src/coordination/schemas.ts +155 -0
  57. package/src/coordination/stale.ts +97 -0
  58. package/src/core/decay.ts +63 -63
  59. package/src/core/embeddings.ts +88 -88
  60. package/src/core/hebbian.ts +93 -93
  61. package/src/core/index.ts +5 -5
  62. package/src/core/logger.ts +36 -36
  63. package/src/core/query-expander.ts +66 -66
  64. package/src/core/reranker.ts +101 -101
  65. package/src/engine/activation.ts +758 -656
  66. package/src/engine/connections.ts +103 -103
  67. package/src/engine/consolidation-scheduler.ts +125 -125
  68. package/src/engine/consolidation.ts +29 -6
  69. package/src/engine/eval.ts +102 -102
  70. package/src/engine/eviction.ts +101 -101
  71. package/src/engine/index.ts +8 -8
  72. package/src/engine/retraction.ts +100 -100
  73. package/src/engine/staging.ts +74 -74
  74. package/src/index.ts +208 -121
  75. package/src/mcp.ts +1093 -1013
  76. package/src/storage/index.ts +3 -3
  77. package/src/storage/sqlite.ts +1017 -963
  78. package/src/types/agent.ts +67 -67
  79. package/src/types/checkpoint.ts +46 -46
  80. package/src/types/engram.ts +245 -217
  81. package/src/types/eval.ts +100 -100
  82. package/src/types/index.ts +6 -6
@@ -1,102 +1,102 @@
1
- // Copyright 2026 Robert Winter / Complete Ideas
2
- // SPDX-License-Identifier: Apache-2.0
3
- /**
4
- * Evaluation Engine — measures whether memory actually helps.
5
- *
6
- * Four dimensions (from Codex):
7
- * 1. Retrieval quality — precision@k, latency
8
- * 2. Connection quality — edge utility, stability
9
- * 3. Staging accuracy — promotion precision, discard regret
10
- * 4. Memory health — contamination tracking, confidence distribution
11
- *
12
- * Task impact (with/without memory) is measured externally via TaskTrial records.
13
- */
14
-
15
- import type { EngramStore } from '../storage/sqlite.js';
16
- import type { EvalMetrics } from '../types/index.js';
17
-
18
- export class EvalEngine {
19
- private store: EngramStore;
20
-
21
- constructor(store: EngramStore) {
22
- this.store = store;
23
- }
24
-
25
- /**
26
- * Compute aggregate metrics for an agent over a time window.
27
- */
28
- computeMetrics(agentId: string, windowHours: number = 24): EvalMetrics {
29
- const window = windowHours <= 24 ? '24h' : `${Math.round(windowHours / 24)}d`;
30
-
31
- // Retrieval quality
32
- const precision = this.store.getRetrievalPrecision(agentId, windowHours);
33
-
34
- // Staging accuracy
35
- const stagingMetrics = this.store.getStagingMetrics(agentId);
36
- const totalStaged = stagingMetrics.promoted + stagingMetrics.discarded + stagingMetrics.expired;
37
- const promotionPrecision = totalStaged > 0 ? stagingMetrics.promoted / totalStaged : 0;
38
-
39
- // Memory health
40
- const activeEngrams = this.store.getEngramsByAgent(agentId, 'active');
41
- const stagingEngrams = this.store.getEngramsByAgent(agentId, 'staging');
42
- const retractedEngrams = this.store.getEngramsByAgent(agentId, undefined, true)
43
- .filter(e => e.retracted);
44
- const allAssociations = this.store.getAllAssociations(agentId);
45
-
46
- const avgConfidence = activeEngrams.length > 0
47
- ? activeEngrams.reduce((sum, e) => sum + e.confidence, 0) / activeEngrams.length
48
- : 0;
49
-
50
- // Edge utility — % of edges that have been used in activation
51
- const usedEdges = allAssociations.filter(a => a.activationCount > 0);
52
- const edgeUtility = allAssociations.length > 0
53
- ? usedEdges.length / allAssociations.length
54
- : 0;
55
-
56
- // Edge survival — average age of edges that are still above minimum weight
57
- const livingEdges = allAssociations.filter(a => a.weight > 0.01);
58
- const avgSurvival = livingEdges.length > 0
59
- ? livingEdges.reduce((sum, a) =>
60
- sum + (Date.now() - a.createdAt.getTime()) / (1000 * 60 * 60 * 24), 0
61
- ) / livingEdges.length
62
- : 0;
63
-
64
- // Activation performance stats
65
- const activationStats = this.store.getActivationStats(agentId, windowHours);
66
-
67
- // Consolidated count
68
- const consolidatedCount = this.store.getConsolidatedCount(agentId);
69
-
70
- return {
71
- agentId,
72
- timestamp: new Date(),
73
- window,
74
-
75
- activationCount: activationStats.count,
76
- avgPrecisionAtK: precision,
77
- avgLatencyMs: activationStats.avgLatencyMs,
78
- p95LatencyMs: activationStats.p95LatencyMs,
79
-
80
- totalEdges: allAssociations.length,
81
- edgesUsedInActivation: usedEdges.length,
82
- edgeUtilityRate: edgeUtility,
83
- avgEdgeSurvivalDays: avgSurvival,
84
-
85
- totalStaged: totalStaged,
86
- promotedCount: stagingMetrics.promoted,
87
- discardedCount: stagingMetrics.discarded,
88
- promotionPrecision,
89
- discardRegret: 0, // Requires tracking discarded-then-rediscovered items
90
-
91
- activeEngramCount: activeEngrams.length,
92
- stagingEngramCount: stagingEngrams.length,
93
- retractedCount: retractedEngrams.length,
94
- consolidatedCount,
95
- avgConfidence,
96
-
97
- staleUsageCount: 0, // Requires per-activation age/confidence tracking
98
- retractionRate: retractedEngrams.length /
99
- Math.max(activeEngrams.length + retractedEngrams.length, 1),
100
- };
101
- }
102
- }
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Evaluation Engine — measures whether memory actually helps.
5
+ *
6
+ * Four dimensions (from Codex):
7
+ * 1. Retrieval quality — precision@k, latency
8
+ * 2. Connection quality — edge utility, stability
9
+ * 3. Staging accuracy — promotion precision, discard regret
10
+ * 4. Memory health — contamination tracking, confidence distribution
11
+ *
12
+ * Task impact (with/without memory) is measured externally via TaskTrial records.
13
+ */
14
+
15
+ import type { EngramStore } from '../storage/sqlite.js';
16
+ import type { EvalMetrics } from '../types/index.js';
17
+
18
+ export class EvalEngine {
19
+ private store: EngramStore;
20
+
21
+ constructor(store: EngramStore) {
22
+ this.store = store;
23
+ }
24
+
25
+ /**
26
+ * Compute aggregate metrics for an agent over a time window.
27
+ */
28
+ computeMetrics(agentId: string, windowHours: number = 24): EvalMetrics {
29
+ const window = windowHours <= 24 ? '24h' : `${Math.round(windowHours / 24)}d`;
30
+
31
+ // Retrieval quality
32
+ const precision = this.store.getRetrievalPrecision(agentId, windowHours);
33
+
34
+ // Staging accuracy
35
+ const stagingMetrics = this.store.getStagingMetrics(agentId);
36
+ const totalStaged = stagingMetrics.promoted + stagingMetrics.discarded + stagingMetrics.expired;
37
+ const promotionPrecision = totalStaged > 0 ? stagingMetrics.promoted / totalStaged : 0;
38
+
39
+ // Memory health
40
+ const activeEngrams = this.store.getEngramsByAgent(agentId, 'active');
41
+ const stagingEngrams = this.store.getEngramsByAgent(agentId, 'staging');
42
+ const retractedEngrams = this.store.getEngramsByAgent(agentId, undefined, true)
43
+ .filter(e => e.retracted);
44
+ const allAssociations = this.store.getAllAssociations(agentId);
45
+
46
+ const avgConfidence = activeEngrams.length > 0
47
+ ? activeEngrams.reduce((sum, e) => sum + e.confidence, 0) / activeEngrams.length
48
+ : 0;
49
+
50
+ // Edge utility — % of edges that have been used in activation
51
+ const usedEdges = allAssociations.filter(a => a.activationCount > 0);
52
+ const edgeUtility = allAssociations.length > 0
53
+ ? usedEdges.length / allAssociations.length
54
+ : 0;
55
+
56
+ // Edge survival — average age of edges that are still above minimum weight
57
+ const livingEdges = allAssociations.filter(a => a.weight > 0.01);
58
+ const avgSurvival = livingEdges.length > 0
59
+ ? livingEdges.reduce((sum, a) =>
60
+ sum + (Date.now() - a.createdAt.getTime()) / (1000 * 60 * 60 * 24), 0
61
+ ) / livingEdges.length
62
+ : 0;
63
+
64
+ // Activation performance stats
65
+ const activationStats = this.store.getActivationStats(agentId, windowHours);
66
+
67
+ // Consolidated count
68
+ const consolidatedCount = this.store.getConsolidatedCount(agentId);
69
+
70
+ return {
71
+ agentId,
72
+ timestamp: new Date(),
73
+ window,
74
+
75
+ activationCount: activationStats.count,
76
+ avgPrecisionAtK: precision,
77
+ avgLatencyMs: activationStats.avgLatencyMs,
78
+ p95LatencyMs: activationStats.p95LatencyMs,
79
+
80
+ totalEdges: allAssociations.length,
81
+ edgesUsedInActivation: usedEdges.length,
82
+ edgeUtilityRate: edgeUtility,
83
+ avgEdgeSurvivalDays: avgSurvival,
84
+
85
+ totalStaged: totalStaged,
86
+ promotedCount: stagingMetrics.promoted,
87
+ discardedCount: stagingMetrics.discarded,
88
+ promotionPrecision,
89
+ discardRegret: 0, // Requires tracking discarded-then-rediscovered items
90
+
91
+ activeEngramCount: activeEngrams.length,
92
+ stagingEngramCount: stagingEngrams.length,
93
+ retractedCount: retractedEngrams.length,
94
+ consolidatedCount,
95
+ avgConfidence,
96
+
97
+ staleUsageCount: 0, // Requires per-activation age/confidence tracking
98
+ retractionRate: retractedEngrams.length /
99
+ Math.max(activeEngrams.length + retractedEngrams.length, 1),
100
+ };
101
+ }
102
+ }
@@ -1,101 +1,101 @@
1
- // Copyright 2026 Robert Winter / Complete Ideas
2
- // SPDX-License-Identifier: Apache-2.0
3
- /**
4
- * Eviction Engine — capacity enforcement and edge pruning.
5
- *
6
- * When memory budgets are exceeded:
7
- * 1. Archive lowest-value active engrams
8
- * 2. Delete expired staging engrams
9
- * 3. Prune weakest edges when per-engram cap exceeded
10
- * 4. Decay unused association weights over time
11
- */
12
-
13
- import type { EngramStore } from '../storage/sqlite.js';
14
- import type { AgentConfig } from '../types/agent.js';
15
- import { decayAssociation } from '../core/hebbian.js';
16
-
17
- export class EvictionEngine {
18
- private store: EngramStore;
19
-
20
- constructor(store: EngramStore) {
21
- this.store = store;
22
- }
23
-
24
- /**
25
- * Check capacity budgets and evict if needed.
26
- * Returns count of evicted engrams.
27
- */
28
- enforceCapacity(agentId: string, config: AgentConfig): { evicted: number; edgesPruned: number } {
29
- let evicted = 0;
30
- let edgesPruned = 0;
31
-
32
- // Active engram budget
33
- const activeCount = this.store.getActiveCount(agentId);
34
- if (activeCount > config.maxActiveEngrams) {
35
- const excess = activeCount - config.maxActiveEngrams;
36
- const candidates = this.store.getEvictionCandidates(agentId, excess);
37
- for (const engram of candidates) {
38
- this.store.updateStage(engram.id, 'archived');
39
- evicted++;
40
- }
41
- }
42
-
43
- // Staging budget
44
- const stagingCount = this.store.getStagingCount(agentId);
45
- if (stagingCount > config.maxStagingEngrams) {
46
- const expired = this.store.getExpiredStaging();
47
- for (const engram of expired) {
48
- this.store.deleteEngram(engram.id);
49
- evicted++;
50
- }
51
- }
52
-
53
- // Edge pruning — cap per engram
54
- const engrams = this.store.getEngramsByAgent(agentId, 'active');
55
- for (const engram of engrams) {
56
- const edgeCount = this.store.countAssociationsFor(engram.id);
57
- if (edgeCount > config.maxEdgesPerEngram) {
58
- // Remove weakest edges until under cap
59
- let toRemove = edgeCount - config.maxEdgesPerEngram;
60
- while (toRemove > 0) {
61
- const weakest = this.store.getWeakestAssociation(engram.id);
62
- if (weakest) {
63
- this.store.deleteAssociation(weakest.id);
64
- edgesPruned++;
65
- }
66
- toRemove--;
67
- }
68
- }
69
- }
70
-
71
- return { evicted, edgesPruned };
72
- }
73
-
74
- /**
75
- * Decay all association weights based on time since last activation.
76
- * Run periodically (e.g., daily).
77
- */
78
- decayEdges(agentId: string, halfLifeDays: number = 7): number {
79
- const associations = this.store.getAllAssociations(agentId);
80
- let decayed = 0;
81
-
82
- for (const assoc of associations) {
83
- const daysSince = (Date.now() - assoc.lastActivated.getTime()) / (1000 * 60 * 60 * 24);
84
- if (daysSince < 0.5) continue; // Skip recently activated
85
-
86
- const newWeight = decayAssociation(assoc.weight, daysSince, halfLifeDays);
87
- if (newWeight < 0.01) {
88
- // Below minimum useful weight — prune
89
- this.store.deleteAssociation(assoc.id);
90
- decayed++;
91
- } else if (Math.abs(newWeight - assoc.weight) > 0.001) {
92
- this.store.upsertAssociation(
93
- assoc.fromEngramId, assoc.toEngramId, newWeight, assoc.type, assoc.confidence
94
- );
95
- decayed++;
96
- }
97
- }
98
-
99
- return decayed;
100
- }
101
- }
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Eviction Engine — capacity enforcement and edge pruning.
5
+ *
6
+ * When memory budgets are exceeded:
7
+ * 1. Archive lowest-value active engrams
8
+ * 2. Delete expired staging engrams
9
+ * 3. Prune weakest edges when per-engram cap exceeded
10
+ * 4. Decay unused association weights over time
11
+ */
12
+
13
+ import type { EngramStore } from '../storage/sqlite.js';
14
+ import type { AgentConfig } from '../types/agent.js';
15
+ import { decayAssociation } from '../core/hebbian.js';
16
+
17
+ export class EvictionEngine {
18
+ private store: EngramStore;
19
+
20
+ constructor(store: EngramStore) {
21
+ this.store = store;
22
+ }
23
+
24
+ /**
25
+ * Check capacity budgets and evict if needed.
26
+ * Returns count of evicted engrams.
27
+ */
28
+ enforceCapacity(agentId: string, config: AgentConfig): { evicted: number; edgesPruned: number } {
29
+ let evicted = 0;
30
+ let edgesPruned = 0;
31
+
32
+ // Active engram budget
33
+ const activeCount = this.store.getActiveCount(agentId);
34
+ if (activeCount > config.maxActiveEngrams) {
35
+ const excess = activeCount - config.maxActiveEngrams;
36
+ const candidates = this.store.getEvictionCandidates(agentId, excess);
37
+ for (const engram of candidates) {
38
+ this.store.updateStage(engram.id, 'archived');
39
+ evicted++;
40
+ }
41
+ }
42
+
43
+ // Staging budget
44
+ const stagingCount = this.store.getStagingCount(agentId);
45
+ if (stagingCount > config.maxStagingEngrams) {
46
+ const expired = this.store.getExpiredStaging();
47
+ for (const engram of expired) {
48
+ this.store.deleteEngram(engram.id);
49
+ evicted++;
50
+ }
51
+ }
52
+
53
+ // Edge pruning — cap per engram
54
+ const engrams = this.store.getEngramsByAgent(agentId, 'active');
55
+ for (const engram of engrams) {
56
+ const edgeCount = this.store.countAssociationsFor(engram.id);
57
+ if (edgeCount > config.maxEdgesPerEngram) {
58
+ // Remove weakest edges until under cap
59
+ let toRemove = edgeCount - config.maxEdgesPerEngram;
60
+ while (toRemove > 0) {
61
+ const weakest = this.store.getWeakestAssociation(engram.id);
62
+ if (weakest) {
63
+ this.store.deleteAssociation(weakest.id);
64
+ edgesPruned++;
65
+ }
66
+ toRemove--;
67
+ }
68
+ }
69
+ }
70
+
71
+ return { evicted, edgesPruned };
72
+ }
73
+
74
+ /**
75
+ * Decay all association weights based on time since last activation.
76
+ * Run periodically (e.g., daily).
77
+ */
78
+ decayEdges(agentId: string, halfLifeDays: number = 7): number {
79
+ const associations = this.store.getAllAssociations(agentId);
80
+ let decayed = 0;
81
+
82
+ for (const assoc of associations) {
83
+ const daysSince = (Date.now() - assoc.lastActivated.getTime()) / (1000 * 60 * 60 * 24);
84
+ if (daysSince < 0.5) continue; // Skip recently activated
85
+
86
+ const newWeight = decayAssociation(assoc.weight, daysSince, halfLifeDays);
87
+ if (newWeight < 0.01) {
88
+ // Below minimum useful weight — prune
89
+ this.store.deleteAssociation(assoc.id);
90
+ decayed++;
91
+ } else if (Math.abs(newWeight - assoc.weight) > 0.001) {
92
+ this.store.upsertAssociation(
93
+ assoc.fromEngramId, assoc.toEngramId, newWeight, assoc.type, assoc.confidence
94
+ );
95
+ decayed++;
96
+ }
97
+ }
98
+
99
+ return decayed;
100
+ }
101
+ }
@@ -1,8 +1,8 @@
1
- // Copyright 2026 Robert Winter / Complete Ideas
2
- // SPDX-License-Identifier: Apache-2.0
3
- export * from './activation.js';
4
- export * from './staging.js';
5
- export * from './connections.js';
6
- export * from './eviction.js';
7
- export * from './retraction.js';
8
- export * from './eval.js';
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ export * from './activation.js';
4
+ export * from './staging.js';
5
+ export * from './connections.js';
6
+ export * from './eviction.js';
7
+ export * from './retraction.js';
8
+ export * from './eval.js';
@@ -1,100 +1,100 @@
1
- // Copyright 2026 Robert Winter / Complete Ideas
2
- // SPDX-License-Identifier: Apache-2.0
3
- /**
4
- * Retraction Engine — negative memory / invalidation.
5
- *
6
- * Codex critique: "You need explicit anti-salience for wrong info.
7
- * Otherwise wrong memories persist and compound mistakes."
8
- *
9
- * When an agent discovers a memory is wrong:
10
- * 1. The original engram is marked retracted (not deleted — audit trail)
11
- * 2. An invalidation association is created
12
- * 3. Optionally, a counter-engram with correct info is created
13
- * 4. Confidence of associated engrams is reduced (contamination check)
14
- */
15
-
16
- import type { EngramStore } from '../storage/sqlite.js';
17
- import type { Retraction } from '../types/index.js';
18
-
19
- export class RetractionEngine {
20
- private store: EngramStore;
21
-
22
- constructor(store: EngramStore) {
23
- this.store = store;
24
- }
25
-
26
- /**
27
- * Retract a memory — mark it invalid and optionally create a correction.
28
- */
29
- retract(retraction: Retraction): { retractedId: string; correctionId: string | null; associatesAffected: number } {
30
- const target = this.store.getEngram(retraction.targetEngramId);
31
- if (!target) {
32
- throw new Error(`Engram ${retraction.targetEngramId} not found`);
33
- }
34
-
35
- // Mark the original as retracted
36
- this.store.retractEngram(target.id, null);
37
-
38
- let correctionId: string | null = null;
39
-
40
- // Create counter-engram if correction content provided
41
- if (retraction.counterContent) {
42
- const correction = this.store.createEngram({
43
- agentId: retraction.agentId,
44
- concept: `correction:${target.concept}`,
45
- content: retraction.counterContent,
46
- tags: [...target.tags, 'correction', 'retraction'],
47
- salience: Math.max(target.salience, 0.6), // Corrections are at least moderately salient
48
- confidence: 0.7,
49
- reasonCodes: ['retraction_correction', `invalidates:${target.id}`],
50
- });
51
-
52
- correctionId = correction.id;
53
-
54
- // Create invalidation link
55
- this.store.upsertAssociation(
56
- correction.id, target.id, 1.0, 'invalidation', 1.0
57
- );
58
-
59
- // Update retracted_by to point to correction
60
- this.store.retractEngram(target.id, correction.id);
61
- }
62
-
63
- // Reduce confidence of closely associated engrams (contamination spread)
64
- const associatesAffected = this.propagateConfidenceReduction(target.id, 0.1, 1);
65
-
66
- return { retractedId: target.id, correctionId, associatesAffected };
67
- }
68
-
69
- /**
70
- * Reduce confidence of engrams associated with a retracted engram.
71
- * Shallow propagation (depth 1) to avoid over-penalizing.
72
- */
73
- private propagateConfidenceReduction(
74
- engramId: string,
75
- penalty: number,
76
- maxDepth: number,
77
- currentDepth: number = 0
78
- ): number {
79
- if (currentDepth >= maxDepth) return 0;
80
-
81
- let affected = 0;
82
- const associations = this.store.getAssociationsFor(engramId);
83
- for (const assoc of associations) {
84
- if (assoc.type === 'invalidation') continue; // Don't penalize corrections
85
-
86
- const neighborId = assoc.fromEngramId === engramId
87
- ? assoc.toEngramId
88
- : assoc.fromEngramId;
89
- const neighbor = this.store.getEngram(neighborId);
90
- if (!neighbor || neighbor.retracted) continue;
91
-
92
- // Scale penalty by association weight
93
- const scaledPenalty = penalty * assoc.weight;
94
- const newConfidence = Math.max(0.1, neighbor.confidence - scaledPenalty);
95
- this.store.updateConfidence(neighborId, newConfidence);
96
- affected++;
97
- }
98
- return affected;
99
- }
100
- }
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Retraction Engine — negative memory / invalidation.
5
+ *
6
+ * Codex critique: "You need explicit anti-salience for wrong info.
7
+ * Otherwise wrong memories persist and compound mistakes."
8
+ *
9
+ * When an agent discovers a memory is wrong:
10
+ * 1. The original engram is marked retracted (not deleted — audit trail)
11
+ * 2. An invalidation association is created
12
+ * 3. Optionally, a counter-engram with correct info is created
13
+ * 4. Confidence of associated engrams is reduced (contamination check)
14
+ */
15
+
16
+ import type { EngramStore } from '../storage/sqlite.js';
17
+ import type { Retraction } from '../types/index.js';
18
+
19
+ export class RetractionEngine {
20
+ private store: EngramStore;
21
+
22
+ constructor(store: EngramStore) {
23
+ this.store = store;
24
+ }
25
+
26
+ /**
27
+ * Retract a memory — mark it invalid and optionally create a correction.
28
+ */
29
+ retract(retraction: Retraction): { retractedId: string; correctionId: string | null; associatesAffected: number } {
30
+ const target = this.store.getEngram(retraction.targetEngramId);
31
+ if (!target) {
32
+ throw new Error(`Engram ${retraction.targetEngramId} not found`);
33
+ }
34
+
35
+ // Mark the original as retracted
36
+ this.store.retractEngram(target.id, null);
37
+
38
+ let correctionId: string | null = null;
39
+
40
+ // Create counter-engram if correction content provided
41
+ if (retraction.counterContent) {
42
+ const correction = this.store.createEngram({
43
+ agentId: retraction.agentId,
44
+ concept: `correction:${target.concept}`,
45
+ content: retraction.counterContent,
46
+ tags: [...target.tags, 'correction', 'retraction'],
47
+ salience: Math.max(target.salience, 0.6), // Corrections are at least moderately salient
48
+ confidence: 0.7,
49
+ reasonCodes: ['retraction_correction', `invalidates:${target.id}`],
50
+ });
51
+
52
+ correctionId = correction.id;
53
+
54
+ // Create invalidation link
55
+ this.store.upsertAssociation(
56
+ correction.id, target.id, 1.0, 'invalidation', 1.0
57
+ );
58
+
59
+ // Update retracted_by to point to correction
60
+ this.store.retractEngram(target.id, correction.id);
61
+ }
62
+
63
+ // Reduce confidence of closely associated engrams (contamination spread)
64
+ const associatesAffected = this.propagateConfidenceReduction(target.id, 0.1, 1);
65
+
66
+ return { retractedId: target.id, correctionId, associatesAffected };
67
+ }
68
+
69
+ /**
70
+ * Reduce confidence of engrams associated with a retracted engram.
71
+ * Shallow propagation (depth 1) to avoid over-penalizing.
72
+ */
73
+ private propagateConfidenceReduction(
74
+ engramId: string,
75
+ penalty: number,
76
+ maxDepth: number,
77
+ currentDepth: number = 0
78
+ ): number {
79
+ if (currentDepth >= maxDepth) return 0;
80
+
81
+ let affected = 0;
82
+ const associations = this.store.getAssociationsFor(engramId);
83
+ for (const assoc of associations) {
84
+ if (assoc.type === 'invalidation') continue; // Don't penalize corrections
85
+
86
+ const neighborId = assoc.fromEngramId === engramId
87
+ ? assoc.toEngramId
88
+ : assoc.fromEngramId;
89
+ const neighbor = this.store.getEngram(neighborId);
90
+ if (!neighbor || neighbor.retracted) continue;
91
+
92
+ // Scale penalty by association weight
93
+ const scaledPenalty = penalty * assoc.weight;
94
+ const newConfidence = Math.max(0.1, neighbor.confidence - scaledPenalty);
95
+ this.store.updateConfidence(neighborId, newConfidence);
96
+ affected++;
97
+ }
98
+ return affected;
99
+ }
100
+ }