agent-working-memory 0.4.1 → 0.4.3

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 (123) hide show
  1. package/LICENSE +190 -21
  2. package/README.md +175 -191
  3. package/dist/api/index.d.ts.map +1 -1
  4. package/dist/api/index.js +2 -0
  5. package/dist/api/index.js.map +1 -1
  6. package/dist/api/routes.d.ts.map +1 -1
  7. package/dist/api/routes.js +7 -0
  8. package/dist/api/routes.js.map +1 -1
  9. package/dist/cli.d.ts +0 -9
  10. package/dist/cli.d.ts.map +1 -1
  11. package/dist/cli.js +85 -56
  12. package/dist/cli.js.map +1 -1
  13. package/dist/core/decay.d.ts.map +1 -1
  14. package/dist/core/decay.js +2 -0
  15. package/dist/core/decay.js.map +1 -1
  16. package/dist/core/embeddings.d.ts.map +1 -1
  17. package/dist/core/embeddings.js +2 -0
  18. package/dist/core/embeddings.js.map +1 -1
  19. package/dist/core/hebbian.d.ts.map +1 -1
  20. package/dist/core/hebbian.js +2 -0
  21. package/dist/core/hebbian.js.map +1 -1
  22. package/dist/core/index.d.ts.map +1 -1
  23. package/dist/core/index.js +2 -0
  24. package/dist/core/index.js.map +1 -1
  25. package/dist/core/logger.d.ts.map +1 -1
  26. package/dist/core/logger.js +2 -0
  27. package/dist/core/logger.js.map +1 -1
  28. package/dist/core/query-expander.d.ts.map +1 -1
  29. package/dist/core/query-expander.js +2 -0
  30. package/dist/core/query-expander.js.map +1 -1
  31. package/dist/core/reranker.d.ts.map +1 -1
  32. package/dist/core/reranker.js +2 -0
  33. package/dist/core/reranker.js.map +1 -1
  34. package/dist/core/salience.d.ts.map +1 -1
  35. package/dist/core/salience.js +12 -8
  36. package/dist/core/salience.js.map +1 -1
  37. package/dist/engine/activation.d.ts.map +1 -1
  38. package/dist/engine/activation.js +2 -0
  39. package/dist/engine/activation.js.map +1 -1
  40. package/dist/engine/connections.d.ts.map +1 -1
  41. package/dist/engine/connections.js +2 -0
  42. package/dist/engine/connections.js.map +1 -1
  43. package/dist/engine/consolidation-scheduler.d.ts.map +1 -1
  44. package/dist/engine/consolidation-scheduler.js +2 -0
  45. package/dist/engine/consolidation-scheduler.js.map +1 -1
  46. package/dist/engine/consolidation.d.ts.map +1 -1
  47. package/dist/engine/consolidation.js +12 -3
  48. package/dist/engine/consolidation.js.map +1 -1
  49. package/dist/engine/eval.d.ts.map +1 -1
  50. package/dist/engine/eval.js +2 -0
  51. package/dist/engine/eval.js.map +1 -1
  52. package/dist/engine/eviction.d.ts.map +1 -1
  53. package/dist/engine/eviction.js +2 -0
  54. package/dist/engine/eviction.js.map +1 -1
  55. package/dist/engine/index.d.ts.map +1 -1
  56. package/dist/engine/index.js +2 -0
  57. package/dist/engine/index.js.map +1 -1
  58. package/dist/engine/retraction.d.ts.map +1 -1
  59. package/dist/engine/retraction.js +2 -0
  60. package/dist/engine/retraction.js.map +1 -1
  61. package/dist/engine/staging.d.ts.map +1 -1
  62. package/dist/engine/staging.js +2 -0
  63. package/dist/engine/staging.js.map +1 -1
  64. package/dist/hooks/sidecar.d.ts.map +1 -1
  65. package/dist/hooks/sidecar.js +29 -0
  66. package/dist/hooks/sidecar.js.map +1 -1
  67. package/dist/index.js +2 -0
  68. package/dist/index.js.map +1 -1
  69. package/dist/mcp.d.ts.map +1 -1
  70. package/dist/mcp.js +2 -0
  71. package/dist/mcp.js.map +1 -1
  72. package/dist/storage/index.d.ts.map +1 -1
  73. package/dist/storage/index.js +2 -0
  74. package/dist/storage/index.js.map +1 -1
  75. package/dist/storage/sqlite.d.ts.map +1 -1
  76. package/dist/storage/sqlite.js +12 -2
  77. package/dist/storage/sqlite.js.map +1 -1
  78. package/dist/types/agent.d.ts.map +1 -1
  79. package/dist/types/agent.js +2 -0
  80. package/dist/types/agent.js.map +1 -1
  81. package/dist/types/checkpoint.d.ts.map +1 -1
  82. package/dist/types/checkpoint.js +2 -0
  83. package/dist/types/checkpoint.js.map +1 -1
  84. package/dist/types/engram.d.ts.map +1 -1
  85. package/dist/types/engram.js +2 -0
  86. package/dist/types/engram.js.map +1 -1
  87. package/dist/types/eval.d.ts.map +1 -1
  88. package/dist/types/eval.js +2 -0
  89. package/dist/types/eval.js.map +1 -1
  90. package/dist/types/index.d.ts.map +1 -1
  91. package/dist/types/index.js +2 -0
  92. package/dist/types/index.js.map +1 -1
  93. package/package.json +2 -2
  94. package/src/api/index.ts +2 -0
  95. package/src/api/routes.ts +8 -0
  96. package/src/cli.ts +385 -355
  97. package/src/core/decay.ts +2 -0
  98. package/src/core/embeddings.ts +2 -0
  99. package/src/core/hebbian.ts +2 -0
  100. package/src/core/index.ts +2 -0
  101. package/src/core/logger.ts +2 -0
  102. package/src/core/query-expander.ts +2 -0
  103. package/src/core/reranker.ts +2 -0
  104. package/src/core/salience.ts +14 -10
  105. package/src/engine/activation.ts +2 -0
  106. package/src/engine/connections.ts +2 -0
  107. package/src/engine/consolidation-scheduler.ts +125 -123
  108. package/src/engine/consolidation.ts +11 -3
  109. package/src/engine/eval.ts +2 -0
  110. package/src/engine/eviction.ts +2 -0
  111. package/src/engine/index.ts +2 -0
  112. package/src/engine/retraction.ts +2 -0
  113. package/src/engine/staging.ts +2 -0
  114. package/src/hooks/sidecar.ts +31 -0
  115. package/src/index.ts +2 -0
  116. package/src/mcp.ts +2 -0
  117. package/src/storage/index.ts +2 -0
  118. package/src/storage/sqlite.ts +12 -2
  119. package/src/types/agent.ts +2 -0
  120. package/src/types/checkpoint.ts +46 -44
  121. package/src/types/engram.ts +2 -0
  122. package/src/types/eval.ts +2 -0
  123. package/src/types/index.ts +2 -0
package/src/core/decay.ts CHANGED
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  /**
2
4
  * ACT-R Base-Level Activation
3
5
  *
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  /**
2
4
  * Embedding Engine — local vector embeddings via transformers.js
3
5
  *
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  /**
2
4
  * Hebbian Learning — "neurons that fire together wire together"
3
5
  *
package/src/core/index.ts CHANGED
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  export * from './decay.js';
2
4
  export * from './hebbian.js';
3
5
  export * from './salience.js';
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  /**
2
4
  * Simple file logger for AWM activity.
3
5
  *
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  /**
2
4
  * Query Expander — rewrites queries with synonyms and related terms.
3
5
  *
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  /**
2
4
  * Cross-Encoder Re-Ranker — scores (query, passage) pairs for relevance.
3
5
  *
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  /**
2
4
  * Salience Filter — decides what's worth remembering.
3
5
  *
@@ -118,22 +120,24 @@ export function evaluateSalience(
118
120
  * The check is cheap (~1ms) because BM25 is synchronous SQLite FTS5.
119
121
  */
120
122
  export function computeNovelty(store: EngramStore, agentId: string, concept: string, content: string): number {
121
- // Search using concept + first 100 chars of content (enough to detect duplicates, fast)
122
- const searchText = `${concept} ${content.slice(0, 100)}`;
123
-
124
123
  try {
124
+ // Search using concept + first 100 chars of content (enough to detect duplicates, fast)
125
+ const contentStr = typeof content === 'string' ? content : '';
126
+ const conceptStr = typeof concept === 'string' ? concept : '';
127
+ const searchText = `${conceptStr} ${contentStr.slice(0, 100)}`;
128
+
125
129
  const results = store.searchBM25WithRank(agentId, searchText, 3);
126
130
  if (results.length === 0) return 1.0; // Nothing similar — fully novel
127
131
 
128
- // BM25 scores are unbounded. Normalize the top score relative to a "strong match" threshold.
129
- // A BM25 score > 15 typically indicates very high overlap. > 25 is near-duplicate.
132
+ // searchBM25WithRank normalizes scores to 0..1 via |rank|/(1+|rank|).
133
+ // Higher score = stronger match = less novel.
130
134
  const topScore = results[0].bm25Score;
131
135
 
132
- if (topScore > 25) return 0.1; // Near-duplicate
133
- if (topScore > 15) return 0.3; // High overlap
134
- if (topScore > 8) return 0.5; // Moderate overlap
135
- if (topScore > 3) return 0.7; // Some overlap
136
- return 0.9; // Minimal overlap — mostly novel
136
+ if (topScore > 0.95) return 0.1; // Near-duplicate
137
+ if (topScore > 0.85) return 0.3; // High overlap
138
+ if (topScore > 0.70) return 0.5; // Moderate overlap
139
+ if (topScore > 0.50) return 0.7; // Some overlap
140
+ return 0.9; // Minimal overlap — mostly novel
137
141
  } catch {
138
142
  // If BM25 search fails (e.g., FTS not ready), assume novel
139
143
  return 0.8;
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  /**
2
4
  * Activation Pipeline — the core retrieval engine.
3
5
  *
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  /**
2
4
  * Connection Engine — discovers links between memories.
3
5
  *
@@ -1,123 +1,125 @@
1
- /**
2
- * Consolidation Scheduler — automatically triggers sleep cycles.
3
- *
4
- * Four triggers:
5
- * 1. Idle — agent inactive >10min → full consolidation
6
- * 2. Volume — 50+ writes since last consolidation → full consolidation
7
- * 3. Time30min since last consolidation → full consolidation
8
- * 4. Adaptiveretrieval precision <0.4 → full consolidation
9
- *
10
- * Also provides mini-consolidation for restore (fire-and-forget, lightweight).
11
- * Checks every 30 seconds across all active agents.
12
- */
13
-
14
- import type { EngramStore } from '../storage/sqlite.js';
15
- import type { ConsolidationEngine } from './consolidation.js';
16
-
17
- const TICK_INTERVAL_MS = 30_000; // Check every 30s
18
- const IDLE_THRESHOLD_MS = 10 * 60_000; // 10 minutes
19
- const VOLUME_THRESHOLD = 50; // 50 writes
20
- const TIME_THRESHOLD_MS = 30 * 60_000; // 30 minutes
21
- const PRECISION_THRESHOLD = 0.4; // Below this triggers consolidation
22
-
23
- export class ConsolidationScheduler {
24
- private timer: ReturnType<typeof setInterval> | null = null;
25
- private running = false;
26
-
27
- constructor(
28
- private store: EngramStore,
29
- private consolidationEngine: ConsolidationEngine,
30
- ) {}
31
-
32
- start(): void {
33
- if (this.timer) return;
34
- this.timer = setInterval(() => this.tick(), TICK_INTERVAL_MS);
35
- console.log('ConsolidationScheduler started (30s tick)');
36
- }
37
-
38
- stop(): void {
39
- if (this.timer) {
40
- clearInterval(this.timer);
41
- this.timer = null;
42
- }
43
- console.log('ConsolidationScheduler stopped');
44
- }
45
-
46
- /**
47
- * Mini-consolidation — lightweight, called from restore path.
48
- * Only runs replay + strengthen (phases 1-2), skips heavy phases.
49
- */
50
- async runMiniConsolidation(agentId: string): Promise<void> {
51
- if (this.running) return;
52
- this.running = true;
53
- try {
54
- console.log(`[scheduler] mini-consolidation for ${agentId}`);
55
- this.consolidationEngine.consolidate(agentId);
56
- this.store.markConsolidation(agentId, true);
57
- } catch (err) {
58
- console.error(`[scheduler] mini-consolidation failed for ${agentId}:`, err);
59
- } finally {
60
- this.running = false;
61
- }
62
- }
63
-
64
- private tick(): void {
65
- if (this.running) return;
66
-
67
- const agents = this.store.getActiveAgents();
68
- const now = Date.now();
69
-
70
- for (const agent of agents) {
71
- const idleMs = now - agent.lastActivityAt.getTime();
72
- const sinceConsolidation = agent.lastConsolidationAt
73
- ? now - agent.lastConsolidationAt.getTime()
74
- : Infinity;
75
-
76
- let trigger: string | null = null;
77
-
78
- // 1. Idle trigger agent stopped writing/recalling >10min ago
79
- if (idleMs > IDLE_THRESHOLD_MS && sinceConsolidation > IDLE_THRESHOLD_MS) {
80
- trigger = `idle (${Math.round(idleMs / 60_000)}min)`;
81
- }
82
-
83
- // 2. Volume trigger — many writes accumulated
84
- if (!trigger && agent.writeCount >= VOLUME_THRESHOLD) {
85
- trigger = `volume (${agent.writeCount} writes)`;
86
- }
87
-
88
- // 3. Time trigger — been too long since last consolidation
89
- if (!trigger && sinceConsolidation > TIME_THRESHOLD_MS) {
90
- trigger = `time (${Math.round(sinceConsolidation / 60_000)}min)`;
91
- }
92
-
93
- // 4. Adaptive trigger — precision is low
94
- if (!trigger) {
95
- try {
96
- const precision = this.store.getRetrievalPrecision(agent.agentId, 1);
97
- if (precision > 0 && precision < PRECISION_THRESHOLD) {
98
- trigger = `adaptive (precision ${(precision * 100).toFixed(0)}%)`;
99
- }
100
- } catch { /* precision check is non-fatal */ }
101
- }
102
-
103
- if (trigger) {
104
- this.runFullConsolidation(agent.agentId, trigger);
105
- return; // One consolidation per tick to avoid overload
106
- }
107
- }
108
- }
109
-
110
- private runFullConsolidation(agentId: string, reason: string): void {
111
- this.running = true;
112
- try {
113
- console.log(`[scheduler] full consolidation for ${agentId} — trigger: ${reason}`);
114
- const result = this.consolidationEngine.consolidate(agentId);
115
- this.store.markConsolidation(agentId, false);
116
- console.log(`[scheduler] consolidation done: ${result.edgesStrengthened} strengthened, ${result.memoriesForgotten} forgotten`);
117
- } catch (err) {
118
- console.error(`[scheduler] consolidation failed for ${agentId}:`, err);
119
- } finally {
120
- this.running = false;
121
- }
122
- }
123
- }
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Consolidation Scheduler — automatically triggers sleep cycles.
5
+ *
6
+ * Four triggers:
7
+ * 1. Idleagent inactive >10min → full consolidation
8
+ * 2. Volume50+ writes since last consolidation → full consolidation
9
+ * 3. Time — 30min since last consolidation → full consolidation
10
+ * 4. Adaptive retrieval precision <0.4 full consolidation
11
+ *
12
+ * Also provides mini-consolidation for restore (fire-and-forget, lightweight).
13
+ * Checks every 30 seconds across all active agents.
14
+ */
15
+
16
+ import type { EngramStore } from '../storage/sqlite.js';
17
+ import type { ConsolidationEngine } from './consolidation.js';
18
+
19
+ const TICK_INTERVAL_MS = 30_000; // Check every 30s
20
+ const IDLE_THRESHOLD_MS = 10 * 60_000; // 10 minutes
21
+ const VOLUME_THRESHOLD = 50; // 50 writes
22
+ const TIME_THRESHOLD_MS = 30 * 60_000; // 30 minutes
23
+ const PRECISION_THRESHOLD = 0.4; // Below this triggers consolidation
24
+
25
+ export class ConsolidationScheduler {
26
+ private timer: ReturnType<typeof setInterval> | null = null;
27
+ private running = false;
28
+
29
+ constructor(
30
+ private store: EngramStore,
31
+ private consolidationEngine: ConsolidationEngine,
32
+ ) {}
33
+
34
+ start(): void {
35
+ if (this.timer) return;
36
+ this.timer = setInterval(() => this.tick(), TICK_INTERVAL_MS);
37
+ console.log('ConsolidationScheduler started (30s tick)');
38
+ }
39
+
40
+ stop(): void {
41
+ if (this.timer) {
42
+ clearInterval(this.timer);
43
+ this.timer = null;
44
+ }
45
+ console.log('ConsolidationScheduler stopped');
46
+ }
47
+
48
+ /**
49
+ * Mini-consolidation — lightweight, called from restore path.
50
+ * Only runs replay + strengthen (phases 1-2), skips heavy phases.
51
+ */
52
+ async runMiniConsolidation(agentId: string): Promise<void> {
53
+ if (this.running) return;
54
+ this.running = true;
55
+ try {
56
+ console.log(`[scheduler] mini-consolidation for ${agentId}`);
57
+ this.consolidationEngine.consolidate(agentId);
58
+ this.store.markConsolidation(agentId, true);
59
+ } catch (err) {
60
+ console.error(`[scheduler] mini-consolidation failed for ${agentId}:`, err);
61
+ } finally {
62
+ this.running = false;
63
+ }
64
+ }
65
+
66
+ private tick(): void {
67
+ if (this.running) return;
68
+
69
+ const agents = this.store.getActiveAgents();
70
+ const now = Date.now();
71
+
72
+ for (const agent of agents) {
73
+ const idleMs = now - agent.lastActivityAt.getTime();
74
+ const sinceConsolidation = agent.lastConsolidationAt
75
+ ? now - agent.lastConsolidationAt.getTime()
76
+ : Infinity;
77
+
78
+ let trigger: string | null = null;
79
+
80
+ // 1. Idle trigger agent stopped writing/recalling >10min ago
81
+ if (idleMs > IDLE_THRESHOLD_MS && sinceConsolidation > IDLE_THRESHOLD_MS) {
82
+ trigger = `idle (${Math.round(idleMs / 60_000)}min)`;
83
+ }
84
+
85
+ // 2. Volume trigger many writes accumulated
86
+ if (!trigger && agent.writeCount >= VOLUME_THRESHOLD) {
87
+ trigger = `volume (${agent.writeCount} writes)`;
88
+ }
89
+
90
+ // 3. Time trigger been too long since last consolidation
91
+ if (!trigger && sinceConsolidation > TIME_THRESHOLD_MS) {
92
+ trigger = `time (${Math.round(sinceConsolidation / 60_000)}min)`;
93
+ }
94
+
95
+ // 4. Adaptive trigger — precision is low
96
+ if (!trigger) {
97
+ try {
98
+ const precision = this.store.getRetrievalPrecision(agent.agentId, 1);
99
+ if (precision > 0 && precision < PRECISION_THRESHOLD) {
100
+ trigger = `adaptive (precision ${(precision * 100).toFixed(0)}%)`;
101
+ }
102
+ } catch { /* precision check is non-fatal */ }
103
+ }
104
+
105
+ if (trigger) {
106
+ this.runFullConsolidation(agent.agentId, trigger);
107
+ return; // One consolidation per tick to avoid overload
108
+ }
109
+ }
110
+ }
111
+
112
+ private runFullConsolidation(agentId: string, reason: string): void {
113
+ this.running = true;
114
+ try {
115
+ console.log(`[scheduler] full consolidation for ${agentId} — trigger: ${reason}`);
116
+ const result = this.consolidationEngine.consolidate(agentId);
117
+ this.store.markConsolidation(agentId, false);
118
+ console.log(`[scheduler] consolidation done: ${result.edgesStrengthened} strengthened, ${result.memoriesForgotten} forgotten`);
119
+ } catch (err) {
120
+ console.error(`[scheduler] consolidation failed for ${agentId}:`, err);
121
+ } finally {
122
+ this.running = false;
123
+ }
124
+ }
125
+ }
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  /**
2
4
  * Sleep Cycle — offline memory consolidation.
3
5
  *
@@ -226,13 +228,19 @@ export class ConsolidationEngine {
226
228
  // --- Phase 5: Synaptic homeostasis ---
227
229
  // Normalize total outgoing edge weight per node to prevent hub explosion.
228
230
  // Nodes with many strong edges get scaled down so relative weights stay meaningful.
231
+ // Track processed edge IDs to avoid rescaling the same edge from both endpoints.
229
232
  const engramIds = new Set(engrams.map(e => e.id));
233
+ const processedEdgeIds = new Set<string>();
230
234
  for (const id of engramIds) {
231
- const edges = this.store.getAssociationsFor(id);
232
- const totalWeight = edges.reduce((sum, a) => sum + a.weight, 0);
235
+ // Only outgoing edges filter to edges originating from this node
236
+ const allEdges = this.store.getAssociationsFor(id);
237
+ const outgoing = allEdges.filter(e => e.fromEngramId === id);
238
+ const totalWeight = outgoing.reduce((sum, a) => sum + a.weight, 0);
233
239
  if (totalWeight > HOMEOSTASIS_TARGET) {
234
240
  const scale = HOMEOSTASIS_TARGET / totalWeight;
235
- for (const edge of edges) {
241
+ for (const edge of outgoing) {
242
+ if (processedEdgeIds.has(edge.id)) continue;
243
+ processedEdgeIds.add(edge.id);
236
244
  const newWeight = edge.weight * scale;
237
245
  if (newWeight < PRUNE_THRESHOLD) {
238
246
  this.store.deleteAssociation(edge.id);
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  /**
2
4
  * Evaluation Engine — measures whether memory actually helps.
3
5
  *
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  /**
2
4
  * Eviction Engine — capacity enforcement and edge pruning.
3
5
  *
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  export * from './activation.js';
2
4
  export * from './staging.js';
3
5
  export * from './connections.js';
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  /**
2
4
  * Retraction Engine — negative memory / invalidation.
3
5
  *
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  /**
2
4
  * Staging Buffer — weak signal handler.
3
5
  *
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  /**
2
4
  * Hook Sidecar — lightweight HTTP server that runs alongside the MCP process.
3
5
  *
@@ -130,6 +132,8 @@ function json(res: ServerResponse, status: number, body: Record<string, unknown>
130
132
  res.end(JSON.stringify(body));
131
133
  }
132
134
 
135
+ const AUTO_CHECKPOINT_INTERVAL_MS = 15 * 60_000; // 15 minutes
136
+
133
137
  export function startSidecar(deps: SidecarDeps): { close: () => void } {
134
138
  const { store, agentId, secret, port, onConsolidate } = deps;
135
139
 
@@ -250,8 +254,35 @@ export function startSidecar(deps: SidecarDeps): { close: () => void } {
250
254
  }
251
255
  });
252
256
 
257
+ // --- Silent auto-checkpoint every 15 minutes ---
258
+ const autoCheckpointTimer = setInterval(() => {
259
+ try {
260
+ const checkpoint = store.getCheckpoint(agentId);
261
+ if (!checkpoint) return; // No state to save yet
262
+
263
+ // Only checkpoint if there's been activity since last auto-checkpoint
264
+ const lastActivity = checkpoint.auto.lastActivityAt?.getTime() ?? 0;
265
+ const sinceActivity = Date.now() - lastActivity;
266
+ if (sinceActivity > AUTO_CHECKPOINT_INTERVAL_MS) return; // Idle — skip
267
+
268
+ const state: ConsciousState = {
269
+ currentTask: checkpoint.executionState?.currentTask ?? 'Active session (auto-checkpoint)',
270
+ decisions: checkpoint.executionState?.decisions ?? [],
271
+ activeFiles: checkpoint.executionState?.activeFiles ?? [],
272
+ nextSteps: checkpoint.executionState?.nextSteps ?? [],
273
+ relatedMemoryIds: checkpoint.executionState?.relatedMemoryIds ?? [],
274
+ notes: `Auto-checkpoint (15min timer). Last activity: ${Math.round(sinceActivity / 60_000)}min ago.`,
275
+ episodeId: checkpoint.executionState?.episodeId ?? null,
276
+ };
277
+
278
+ store.saveCheckpoint(agentId, state);
279
+ log(agentId, 'hook:timer', `auto-checkpoint (${Math.round(sinceActivity / 60_000)}min since activity)`);
280
+ } catch { /* timer failure is non-fatal */ }
281
+ }, AUTO_CHECKPOINT_INTERVAL_MS);
282
+
253
283
  return {
254
284
  close: () => {
285
+ clearInterval(autoCheckpointTimer);
255
286
  server.close();
256
287
  },
257
288
  };
package/src/index.ts CHANGED
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  import { readFileSync } from 'node:fs';
2
4
  import { resolve } from 'node:path';
3
5
  import Fastify from 'fastify';
package/src/mcp.ts CHANGED
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  /**
2
4
  * MCP Server — Model Context Protocol interface for AgentWorkingMemory.
3
5
  *
@@ -1 +1,3 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  export * from './sqlite.js';
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  /**
2
4
  * SQLite storage layer — persistence for engrams, associations, and eval events.
3
5
  *
@@ -14,6 +16,14 @@ import type {
14
16
  ConsciousState, AutoCheckpoint, CheckpointRow,
15
17
  } from '../types/index.js';
16
18
 
19
+ /** Safely convert a Node Buffer to Float32Array, respecting byteOffset/byteLength. */
20
+ function bufferToFloat32Array(buf: Buffer | ArrayBuffer): Float32Array {
21
+ if (buf instanceof ArrayBuffer) return new Float32Array(buf);
22
+ // Node Buffer may share an underlying ArrayBuffer — slice to the exact region
23
+ const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
24
+ return new Float32Array(ab);
25
+ }
26
+
17
27
  const DEFAULT_SALIENCE_FEATURES: SalienceFeatures = {
18
28
  surprise: 0, decisionMade: false, causalDepth: 0, resolutionEffort: 0, eventType: 'observation',
19
29
  };
@@ -641,7 +651,7 @@ export class EngramStore {
641
651
  concept: row.concept,
642
652
  content: row.content,
643
653
  embedding: row.embedding
644
- ? Array.from(new Float32Array(row.embedding.buffer ?? row.embedding))
654
+ ? Array.from(bufferToFloat32Array(row.embedding))
645
655
  : null,
646
656
  confidence: row.confidence,
647
657
  salience: row.salience,
@@ -751,7 +761,7 @@ export class EngramStore {
751
761
  agentId: row.agent_id,
752
762
  label: row.label,
753
763
  embedding: row.embedding
754
- ? Array.from(new Float32Array(row.embedding.buffer ?? row.embedding))
764
+ ? Array.from(bufferToFloat32Array(row.embedding))
755
765
  : null,
756
766
  engramCount: row.engram_count,
757
767
  startTime: new Date(row.start_time),
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  /**
2
4
  * Agent — a consciousness boundary.
3
5
  * Each agent has its own isolated memory space with capacity budgets.
@@ -1,44 +1,46 @@
1
- /**
2
- * Checkpoint types — conscious state preservation across compaction.
3
- *
4
- * ConsciousState: explicit structured snapshot (saved by agent)
5
- * AutoCheckpoint: implicit lightweight tracking (updated on every write/recall)
6
- */
7
-
8
- export interface ConsciousState {
9
- currentTask: string;
10
- decisions: string[];
11
- activeFiles: string[];
12
- nextSteps: string[];
13
- relatedMemoryIds: string[];
14
- notes: string;
15
- episodeId: string | null;
16
- }
17
-
18
- export interface AutoCheckpoint {
19
- lastWriteId: string | null;
20
- lastRecallContext: string | null;
21
- lastRecallIds: string[];
22
- lastActivityAt: Date;
23
- writeCountSinceConsolidation: number;
24
- recallCountSinceConsolidation: number;
25
- }
26
-
27
- export interface CheckpointRow {
28
- agentId: string;
29
- auto: AutoCheckpoint;
30
- executionState: ConsciousState | null;
31
- checkpointAt: Date | null;
32
- lastConsolidationAt: Date | null;
33
- lastMiniConsolidationAt: Date | null;
34
- updatedAt: Date;
35
- }
36
-
37
- export interface RestoreResult {
38
- executionState: ConsciousState | null;
39
- checkpointAt: Date | null;
40
- recalledMemories: Array<{ id: string; concept: string; content: string; score: number }>;
41
- lastWrite: { id: string; concept: string; content: string } | null;
42
- idleMs: number;
43
- miniConsolidationTriggered: boolean;
44
- }
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Checkpoint types conscious state preservation across compaction.
5
+ *
6
+ * ConsciousState: explicit structured snapshot (saved by agent)
7
+ * AutoCheckpoint: implicit lightweight tracking (updated on every write/recall)
8
+ */
9
+
10
+ export interface ConsciousState {
11
+ currentTask: string;
12
+ decisions: string[];
13
+ activeFiles: string[];
14
+ nextSteps: string[];
15
+ relatedMemoryIds: string[];
16
+ notes: string;
17
+ episodeId: string | null;
18
+ }
19
+
20
+ export interface AutoCheckpoint {
21
+ lastWriteId: string | null;
22
+ lastRecallContext: string | null;
23
+ lastRecallIds: string[];
24
+ lastActivityAt: Date;
25
+ writeCountSinceConsolidation: number;
26
+ recallCountSinceConsolidation: number;
27
+ }
28
+
29
+ export interface CheckpointRow {
30
+ agentId: string;
31
+ auto: AutoCheckpoint;
32
+ executionState: ConsciousState | null;
33
+ checkpointAt: Date | null;
34
+ lastConsolidationAt: Date | null;
35
+ lastMiniConsolidationAt: Date | null;
36
+ updatedAt: Date;
37
+ }
38
+
39
+ export interface RestoreResult {
40
+ executionState: ConsciousState | null;
41
+ checkpointAt: Date | null;
42
+ recalledMemories: Array<{ id: string; concept: string; content: string; score: number }>;
43
+ lastWrite: { id: string; concept: string; content: string } | null;
44
+ idleMs: number;
45
+ miniConsolidationTriggered: boolean;
46
+ }
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  /**
2
4
  * Engram — the fundamental unit of agent memory.
3
5
  *