agentic-qe 2.8.0 → 2.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.
- package/CHANGELOG.md +105 -0
- package/README.md +1 -1
- package/dist/agents/BaseAgent.d.ts +128 -0
- package/dist/agents/BaseAgent.d.ts.map +1 -1
- package/dist/agents/BaseAgent.js +256 -0
- package/dist/agents/BaseAgent.js.map +1 -1
- package/dist/cli/commands/supabase/index.d.ts +20 -0
- package/dist/cli/commands/supabase/index.d.ts.map +1 -0
- package/dist/cli/commands/supabase/index.js +632 -0
- package/dist/cli/commands/supabase/index.js.map +1 -0
- package/dist/cli/index.js +3 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/memory/HNSWVectorMemory.js +1 -1
- package/dist/mcp/handlers/NewDomainToolsHandler.d.ts +8 -8
- package/dist/mcp/handlers/NewDomainToolsHandler.d.ts.map +1 -1
- package/dist/mcp/handlers/NewDomainToolsHandler.js.map +1 -1
- package/dist/mcp/handlers/ruvector/RuVectorHandler.d.ts +54 -0
- package/dist/mcp/handlers/ruvector/RuVectorHandler.d.ts.map +1 -0
- package/dist/mcp/handlers/ruvector/RuVectorHandler.js +325 -0
- package/dist/mcp/handlers/ruvector/RuVectorHandler.js.map +1 -0
- package/dist/mcp/handlers/ruvector/index.d.ts +5 -0
- package/dist/mcp/handlers/ruvector/index.d.ts.map +1 -0
- package/dist/mcp/handlers/ruvector/index.js +9 -0
- package/dist/mcp/handlers/ruvector/index.js.map +1 -0
- package/dist/mcp/server-instructions.d.ts +1 -1
- package/dist/mcp/server-instructions.js +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +100 -22
- package/dist/mcp/server.js.map +1 -1
- package/dist/nervous-system/adapters/BTSPAdapter.d.ts +342 -0
- package/dist/nervous-system/adapters/BTSPAdapter.d.ts.map +1 -0
- package/dist/nervous-system/adapters/BTSPAdapter.js +494 -0
- package/dist/nervous-system/adapters/BTSPAdapter.js.map +1 -0
- package/dist/nervous-system/adapters/CircadianController.d.ts +560 -0
- package/dist/nervous-system/adapters/CircadianController.d.ts.map +1 -0
- package/dist/nervous-system/adapters/CircadianController.js +882 -0
- package/dist/nervous-system/adapters/CircadianController.js.map +1 -0
- package/dist/nervous-system/adapters/GlobalWorkspaceAdapter.d.ts +337 -0
- package/dist/nervous-system/adapters/GlobalWorkspaceAdapter.d.ts.map +1 -0
- package/dist/nervous-system/adapters/GlobalWorkspaceAdapter.js +532 -0
- package/dist/nervous-system/adapters/GlobalWorkspaceAdapter.js.map +1 -0
- package/dist/nervous-system/adapters/HdcMemoryAdapter.d.ts +444 -0
- package/dist/nervous-system/adapters/HdcMemoryAdapter.d.ts.map +1 -0
- package/dist/nervous-system/adapters/HdcMemoryAdapter.js +715 -0
- package/dist/nervous-system/adapters/HdcMemoryAdapter.js.map +1 -0
- package/dist/nervous-system/adapters/ReflexLayer.d.ts +231 -0
- package/dist/nervous-system/adapters/ReflexLayer.d.ts.map +1 -0
- package/dist/nervous-system/adapters/ReflexLayer.js +309 -0
- package/dist/nervous-system/adapters/ReflexLayer.js.map +1 -0
- package/dist/nervous-system/index.d.ts +25 -0
- package/dist/nervous-system/index.d.ts.map +1 -0
- package/dist/nervous-system/index.js +80 -0
- package/dist/nervous-system/index.js.map +1 -0
- package/dist/nervous-system/integration/BTSPLearningEngine.d.ts +266 -0
- package/dist/nervous-system/integration/BTSPLearningEngine.d.ts.map +1 -0
- package/dist/nervous-system/integration/BTSPLearningEngine.js +587 -0
- package/dist/nervous-system/integration/BTSPLearningEngine.js.map +1 -0
- package/dist/nervous-system/integration/CircadianAgent.d.ts +389 -0
- package/dist/nervous-system/integration/CircadianAgent.d.ts.map +1 -0
- package/dist/nervous-system/integration/CircadianAgent.js +696 -0
- package/dist/nervous-system/integration/CircadianAgent.js.map +1 -0
- package/dist/nervous-system/integration/HybridPatternStore.d.ts +244 -0
- package/dist/nervous-system/integration/HybridPatternStore.d.ts.map +1 -0
- package/dist/nervous-system/integration/HybridPatternStore.js +622 -0
- package/dist/nervous-system/integration/HybridPatternStore.js.map +1 -0
- package/dist/nervous-system/integration/NervousSystemEnhancement.d.ts +459 -0
- package/dist/nervous-system/integration/NervousSystemEnhancement.d.ts.map +1 -0
- package/dist/nervous-system/integration/NervousSystemEnhancement.js +921 -0
- package/dist/nervous-system/integration/NervousSystemEnhancement.js.map +1 -0
- package/dist/nervous-system/integration/WorkspaceAgent.d.ts +398 -0
- package/dist/nervous-system/integration/WorkspaceAgent.d.ts.map +1 -0
- package/dist/nervous-system/integration/WorkspaceAgent.js +722 -0
- package/dist/nervous-system/integration/WorkspaceAgent.js.map +1 -0
- package/dist/nervous-system/integration/index.d.ts +22 -0
- package/dist/nervous-system/integration/index.d.ts.map +1 -0
- package/dist/nervous-system/integration/index.js +44 -0
- package/dist/nervous-system/integration/index.js.map +1 -0
- package/dist/nervous-system/persistence/BTSPSerializer.d.ts +96 -0
- package/dist/nervous-system/persistence/BTSPSerializer.d.ts.map +1 -0
- package/dist/nervous-system/persistence/BTSPSerializer.js +223 -0
- package/dist/nervous-system/persistence/BTSPSerializer.js.map +1 -0
- package/dist/nervous-system/persistence/CircadianSerializer.d.ts +90 -0
- package/dist/nervous-system/persistence/CircadianSerializer.d.ts.map +1 -0
- package/dist/nervous-system/persistence/CircadianSerializer.js +239 -0
- package/dist/nervous-system/persistence/CircadianSerializer.js.map +1 -0
- package/dist/nervous-system/persistence/HdcSerializer.d.ts +100 -0
- package/dist/nervous-system/persistence/HdcSerializer.d.ts.map +1 -0
- package/dist/nervous-system/persistence/HdcSerializer.js +259 -0
- package/dist/nervous-system/persistence/HdcSerializer.js.map +1 -0
- package/dist/nervous-system/persistence/INervousSystemStore.d.ts +208 -0
- package/dist/nervous-system/persistence/INervousSystemStore.d.ts.map +1 -0
- package/dist/nervous-system/persistence/INervousSystemStore.js +11 -0
- package/dist/nervous-system/persistence/INervousSystemStore.js.map +1 -0
- package/dist/nervous-system/persistence/NervousSystemPersistenceManager.d.ts +187 -0
- package/dist/nervous-system/persistence/NervousSystemPersistenceManager.d.ts.map +1 -0
- package/dist/nervous-system/persistence/NervousSystemPersistenceManager.js +411 -0
- package/dist/nervous-system/persistence/NervousSystemPersistenceManager.js.map +1 -0
- package/dist/nervous-system/persistence/SQLiteNervousSystemStore.d.ts +98 -0
- package/dist/nervous-system/persistence/SQLiteNervousSystemStore.d.ts.map +1 -0
- package/dist/nervous-system/persistence/SQLiteNervousSystemStore.js +510 -0
- package/dist/nervous-system/persistence/SQLiteNervousSystemStore.js.map +1 -0
- package/dist/nervous-system/persistence/index.d.ts +22 -0
- package/dist/nervous-system/persistence/index.d.ts.map +1 -0
- package/dist/nervous-system/persistence/index.js +45 -0
- package/dist/nervous-system/persistence/index.js.map +1 -0
- package/dist/nervous-system/wasm-loader.d.ts +52 -0
- package/dist/nervous-system/wasm-loader.d.ts.map +1 -0
- package/dist/nervous-system/wasm-loader.js +188 -0
- package/dist/nervous-system/wasm-loader.js.map +1 -0
- package/dist/persistence/HybridPersistenceProvider.d.ts +184 -0
- package/dist/persistence/HybridPersistenceProvider.d.ts.map +1 -0
- package/dist/persistence/HybridPersistenceProvider.js +1086 -0
- package/dist/persistence/HybridPersistenceProvider.js.map +1 -0
- package/dist/persistence/IPersistenceProvider.d.ts +657 -0
- package/dist/persistence/IPersistenceProvider.d.ts.map +1 -0
- package/dist/persistence/IPersistenceProvider.js +11 -0
- package/dist/persistence/IPersistenceProvider.js.map +1 -0
- package/dist/persistence/SupabaseConfig.d.ts +176 -0
- package/dist/persistence/SupabaseConfig.d.ts.map +1 -0
- package/dist/persistence/SupabaseConfig.js +277 -0
- package/dist/persistence/SupabaseConfig.js.map +1 -0
- package/dist/persistence/SupabasePersistenceProvider.d.ts +143 -0
- package/dist/persistence/SupabasePersistenceProvider.d.ts.map +1 -0
- package/dist/persistence/SupabasePersistenceProvider.js +955 -0
- package/dist/persistence/SupabasePersistenceProvider.js.map +1 -0
- package/dist/persistence/adapters/CodeIntelligenceSyncAdapter.d.ts +213 -0
- package/dist/persistence/adapters/CodeIntelligenceSyncAdapter.d.ts.map +1 -0
- package/dist/persistence/adapters/CodeIntelligenceSyncAdapter.js +468 -0
- package/dist/persistence/adapters/CodeIntelligenceSyncAdapter.js.map +1 -0
- package/dist/persistence/adapters/MemorySyncAdapter.d.ts +115 -0
- package/dist/persistence/adapters/MemorySyncAdapter.d.ts.map +1 -0
- package/dist/persistence/adapters/MemorySyncAdapter.js +291 -0
- package/dist/persistence/adapters/MemorySyncAdapter.js.map +1 -0
- package/dist/persistence/adapters/index.d.ts +11 -0
- package/dist/persistence/adapters/index.d.ts.map +1 -0
- package/dist/persistence/adapters/index.js +20 -0
- package/dist/persistence/adapters/index.js.map +1 -0
- package/dist/persistence/index.d.ts +14 -0
- package/dist/persistence/index.d.ts.map +1 -1
- package/dist/persistence/index.js +36 -1
- package/dist/persistence/index.js.map +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,882 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CircadianController - Bio-inspired 4-phase duty cycling for compute efficiency
|
|
4
|
+
*
|
|
5
|
+
* Implements circadian rhythm patterns for AI agents to achieve 5-50x compute savings
|
|
6
|
+
* by intelligently cycling between active and rest phases. Based on biological
|
|
7
|
+
* circadian rhythms that regulate activity levels in living organisms.
|
|
8
|
+
*
|
|
9
|
+
* ## WASM Integration
|
|
10
|
+
* This controller uses the Winner-Take-All (WTA) layer from @ruvector/nervous-system-wasm
|
|
11
|
+
* for biologically-plausible phase selection. The WTA mechanism implements lateral
|
|
12
|
+
* inhibition where phases compete for activation - only one phase can be "active"
|
|
13
|
+
* (winning) at any time, similar to how neural populations compete in biological
|
|
14
|
+
* circadian nuclei like the suprachiasmatic nucleus (SCN).
|
|
15
|
+
*
|
|
16
|
+
* **Current WASM Usage:**
|
|
17
|
+
* - WTALayer: Phase competition and selection via lateral inhibition (compete() method)
|
|
18
|
+
* - Hypervector: Available for future phase state encoding enhancements
|
|
19
|
+
*
|
|
20
|
+
* **Future WASM Enhancements:**
|
|
21
|
+
* When a dedicated circadian oscillator WASM component becomes available
|
|
22
|
+
* (e.g., Kuramoto oscillators, suprachiasmatic nucleus models), it should replace
|
|
23
|
+
* the current time-based phase calculation with true oscillator dynamics.
|
|
24
|
+
*
|
|
25
|
+
* ## Phases
|
|
26
|
+
* - **Active**: Full compute, run tests, make decisions, process requests
|
|
27
|
+
* - **Dawn**: Ramping up, pre-fetch likely patterns, warm caches
|
|
28
|
+
* - **Dusk**: Ramping down, process backlog, prepare reports, batch operations
|
|
29
|
+
* - **Rest**: Memory consolidation, cleanup, minimal compute, only critical reactions
|
|
30
|
+
*
|
|
31
|
+
* ## Compute Savings
|
|
32
|
+
* The duty factor represents the fraction of full compute being used:
|
|
33
|
+
* - Active: 1.0 (100%)
|
|
34
|
+
* - Dawn: 0.6 (60%)
|
|
35
|
+
* - Dusk: 0.4 (40%)
|
|
36
|
+
* - Rest: 0.1 (10%)
|
|
37
|
+
*
|
|
38
|
+
* Average duty factor with default phase durations: ~0.52 (48% savings)
|
|
39
|
+
* Best case with extended rest: ~0.20 (80% savings, 5x reduction)
|
|
40
|
+
*
|
|
41
|
+
* @module nervous-system/adapters/CircadianController
|
|
42
|
+
* @version 2.0.0
|
|
43
|
+
*/
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.CircadianController = exports.DEFAULT_CIRCADIAN_CONFIG = exports.DEFAULT_PHASE_CONFIGS = void 0;
|
|
46
|
+
exports.createTestingController = createTestingController;
|
|
47
|
+
exports.createEfficientController = createEfficientController;
|
|
48
|
+
exports.createResponsiveController = createResponsiveController;
|
|
49
|
+
exports.createBudgetedController = createBudgetedController;
|
|
50
|
+
exports.createPureTypeScriptController = createPureTypeScriptController;
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// WASM Imports
|
|
53
|
+
// ============================================================================
|
|
54
|
+
const wasm_loader_js_1 = require("../wasm-loader.js");
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// Default Configuration
|
|
57
|
+
// ============================================================================
|
|
58
|
+
/**
|
|
59
|
+
* Default phase configurations based on biological circadian rhythms
|
|
60
|
+
* Total durations: Active (40%) + Dawn (15%) + Dusk (15%) + Rest (30%) = 100%
|
|
61
|
+
*/
|
|
62
|
+
exports.DEFAULT_PHASE_CONFIGS = {
|
|
63
|
+
Active: {
|
|
64
|
+
duration: 0.40, // 40% of cycle
|
|
65
|
+
dutyFactor: 1.0, // Full compute
|
|
66
|
+
importanceThreshold: 0.0, // React to everything
|
|
67
|
+
allowLearning: true,
|
|
68
|
+
allowConsolidation: false,
|
|
69
|
+
allowCompute: true,
|
|
70
|
+
},
|
|
71
|
+
Dawn: {
|
|
72
|
+
duration: 0.15, // 15% of cycle
|
|
73
|
+
dutyFactor: 0.6, // 60% compute
|
|
74
|
+
importanceThreshold: 0.2, // React to moderately important+
|
|
75
|
+
allowLearning: true,
|
|
76
|
+
allowConsolidation: false,
|
|
77
|
+
allowCompute: true,
|
|
78
|
+
},
|
|
79
|
+
Dusk: {
|
|
80
|
+
duration: 0.15, // 15% of cycle
|
|
81
|
+
dutyFactor: 0.4, // 40% compute
|
|
82
|
+
importanceThreshold: 0.4, // React to important+
|
|
83
|
+
allowLearning: false,
|
|
84
|
+
allowConsolidation: true,
|
|
85
|
+
allowCompute: true,
|
|
86
|
+
},
|
|
87
|
+
Rest: {
|
|
88
|
+
duration: 0.30, // 30% of cycle
|
|
89
|
+
dutyFactor: 0.1, // 10% compute (minimal)
|
|
90
|
+
importanceThreshold: 0.8, // Only react to critical
|
|
91
|
+
allowLearning: false,
|
|
92
|
+
allowConsolidation: true,
|
|
93
|
+
allowCompute: false,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Default configuration for CircadianController
|
|
98
|
+
*/
|
|
99
|
+
exports.DEFAULT_CIRCADIAN_CONFIG = {
|
|
100
|
+
cyclePeriodMs: 24 * 60 * 60 * 1000, // 24 hours in real time (adjustable for simulation)
|
|
101
|
+
phases: exports.DEFAULT_PHASE_CONFIGS,
|
|
102
|
+
hysteresisMs: 5000, // 5 seconds hysteresis
|
|
103
|
+
initialPhase: 'Active',
|
|
104
|
+
energyBudget: 0, // 0 = unlimited
|
|
105
|
+
computeEnergyCost: 1,
|
|
106
|
+
useWasmPhaseSelection: true, // Enable WASM by default
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Phase order for cycle progression
|
|
110
|
+
*/
|
|
111
|
+
const PHASE_ORDER = ['Dawn', 'Active', 'Dusk', 'Rest'];
|
|
112
|
+
/**
|
|
113
|
+
* Index to phase mapping for WTA layer output
|
|
114
|
+
* The WTA layer returns the index of the winning neuron (0-3),
|
|
115
|
+
* which maps to phases in PHASE_ORDER.
|
|
116
|
+
*/
|
|
117
|
+
const INDEX_TO_PHASE = ['Dawn', 'Active', 'Dusk', 'Rest'];
|
|
118
|
+
// ============================================================================
|
|
119
|
+
// Implementation
|
|
120
|
+
// ============================================================================
|
|
121
|
+
/**
|
|
122
|
+
* CircadianController implements bio-inspired duty cycling with WASM K-WTA phase selection
|
|
123
|
+
*
|
|
124
|
+
* The K-WTA (K-Winner-Take-All) mechanism from the nervous system WASM module provides
|
|
125
|
+
* biologically-plausible phase selection through lateral inhibition. Each phase is
|
|
126
|
+
* represented as a "neuron" in the K-WTA layer, and phases compete based on their
|
|
127
|
+
* activation strength (derived from cycle position and phase duration).
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```typescript
|
|
131
|
+
* // Create controller (WASM initializes automatically)
|
|
132
|
+
* const controller = await CircadianController.create({
|
|
133
|
+
* cyclePeriodMs: 60000, // 1 minute cycles for testing
|
|
134
|
+
* });
|
|
135
|
+
*
|
|
136
|
+
* // Advance simulation time
|
|
137
|
+
* controller.advance(1000);
|
|
138
|
+
*
|
|
139
|
+
* // Check if we should run expensive operations
|
|
140
|
+
* if (controller.shouldCompute()) {
|
|
141
|
+
* await runInference();
|
|
142
|
+
* }
|
|
143
|
+
*
|
|
144
|
+
* // Check if we should react to an event
|
|
145
|
+
* const importance = 0.5;
|
|
146
|
+
* if (controller.shouldReact(importance)) {
|
|
147
|
+
* handleEvent();
|
|
148
|
+
* }
|
|
149
|
+
*
|
|
150
|
+
* // Get compute savings
|
|
151
|
+
* const savings = controller.getCostReductionFactor();
|
|
152
|
+
* console.log(`Current savings: ${((1 - 1/savings) * 100).toFixed(1)}%`);
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
class CircadianController {
|
|
156
|
+
/**
|
|
157
|
+
* Create a new CircadianController
|
|
158
|
+
*
|
|
159
|
+
* Note: For WASM initialization, use the static `create()` factory method instead.
|
|
160
|
+
*
|
|
161
|
+
* @param config - Partial configuration (merged with defaults)
|
|
162
|
+
*/
|
|
163
|
+
constructor(config = {}) {
|
|
164
|
+
this.cycleTime = 0;
|
|
165
|
+
this.phaseTime = 0;
|
|
166
|
+
this.lastPhaseChange = 0;
|
|
167
|
+
this.cyclesCompleted = 0;
|
|
168
|
+
this.activeModulation = null;
|
|
169
|
+
this.modulationStartTime = 0;
|
|
170
|
+
// WASM components
|
|
171
|
+
this.wtaLayer = null;
|
|
172
|
+
this.wasmEnabled = false;
|
|
173
|
+
this.totalDutyFactorSum = 0;
|
|
174
|
+
this.totalDutyFactorSamples = 0;
|
|
175
|
+
this.totalEnergyConsumed = 0;
|
|
176
|
+
this.phaseTransitions = 0;
|
|
177
|
+
this.hysteresisActivations = 0;
|
|
178
|
+
this.wtaCompetitions = 0;
|
|
179
|
+
// Merge with defaults
|
|
180
|
+
this.config = {
|
|
181
|
+
...exports.DEFAULT_CIRCADIAN_CONFIG,
|
|
182
|
+
...config,
|
|
183
|
+
phases: {
|
|
184
|
+
...exports.DEFAULT_CIRCADIAN_CONFIG.phases,
|
|
185
|
+
...config.phases,
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
// Validate phase durations sum to 1.0
|
|
189
|
+
const totalDuration = Object.values(this.config.phases).reduce((sum, phase) => sum + phase.duration, 0);
|
|
190
|
+
if (Math.abs(totalDuration - 1.0) > 0.001) {
|
|
191
|
+
throw new Error(`Phase durations must sum to 1.0, got ${totalDuration.toFixed(4)}`);
|
|
192
|
+
}
|
|
193
|
+
// Initialize state
|
|
194
|
+
this.currentPhase = this.config.initialPhase;
|
|
195
|
+
this.energyRemaining = this.config.energyBudget;
|
|
196
|
+
// Initialize metrics
|
|
197
|
+
this.phaseTimeMetrics = { Active: 0, Dawn: 0, Dusk: 0, Rest: 0 };
|
|
198
|
+
this.reactionsPerPhase = { Active: 0, Dawn: 0, Dusk: 0, Rest: 0 };
|
|
199
|
+
this.rejectionsPerPhase = { Active: 0, Dawn: 0, Dusk: 0, Rest: 0 };
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Factory method to create a CircadianController with WASM initialization
|
|
203
|
+
*
|
|
204
|
+
* This is the preferred way to create a CircadianController as it ensures
|
|
205
|
+
* WASM is properly initialized before use.
|
|
206
|
+
*
|
|
207
|
+
* @param config - Partial configuration (merged with defaults)
|
|
208
|
+
* @returns Initialized CircadianController
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* ```typescript
|
|
212
|
+
* const controller = await CircadianController.create({
|
|
213
|
+
* cyclePeriodMs: 60000,
|
|
214
|
+
* useWasmPhaseSelection: true,
|
|
215
|
+
* });
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
static async create(config = {}) {
|
|
219
|
+
const controller = new CircadianController(config);
|
|
220
|
+
await controller.initializeWasm();
|
|
221
|
+
return controller;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Initialize WASM components for bio-inspired phase selection
|
|
225
|
+
*
|
|
226
|
+
* Creates a WTA (Winner-Take-All) layer with 4 neurons (one per phase).
|
|
227
|
+
* The WTA mechanism implements lateral inhibition where phases compete
|
|
228
|
+
* for activation - only one phase can be "active" (winning) at any time,
|
|
229
|
+
* similar to how neural populations compete in biological circadian nuclei.
|
|
230
|
+
*
|
|
231
|
+
* WTA Parameters:
|
|
232
|
+
* - size: 4 (one neuron per phase: Dawn, Active, Dusk, Rest)
|
|
233
|
+
* - threshold: 0.1 (low threshold to ensure a winner is always selected)
|
|
234
|
+
* - inhibition: 0.8 (strong lateral inhibition for clean phase separation)
|
|
235
|
+
*/
|
|
236
|
+
async initializeWasm() {
|
|
237
|
+
if (!this.config.useWasmPhaseSelection) {
|
|
238
|
+
this.wasmEnabled = false;
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
try {
|
|
242
|
+
// Initialize WASM module if not already done
|
|
243
|
+
if (!(0, wasm_loader_js_1.isWasmInitialized)()) {
|
|
244
|
+
await (0, wasm_loader_js_1.initNervousSystem)();
|
|
245
|
+
}
|
|
246
|
+
// Create WTA layer with 4 neurons (one per phase)
|
|
247
|
+
// - threshold: 0.1 (low, so we always get a winner)
|
|
248
|
+
// - inhibition: 0.8 (strong lateral inhibition for clean phase transitions)
|
|
249
|
+
this.wtaLayer = new wasm_loader_js_1.WTALayer(4, 0.1, 0.8);
|
|
250
|
+
this.wasmEnabled = true;
|
|
251
|
+
}
|
|
252
|
+
catch (error) {
|
|
253
|
+
// Log warning but continue without WASM
|
|
254
|
+
console.warn('[CircadianController] WASM initialization failed, falling back to pure TypeScript:', error instanceof Error ? error.message : error);
|
|
255
|
+
this.wasmEnabled = false;
|
|
256
|
+
this.wtaLayer = null;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Advance time by dt milliseconds
|
|
261
|
+
*
|
|
262
|
+
* This method updates the internal state, transitioning between phases
|
|
263
|
+
* as needed and tracking metrics.
|
|
264
|
+
*
|
|
265
|
+
* @param dt - Time to advance in milliseconds (must be positive)
|
|
266
|
+
*/
|
|
267
|
+
advance(dt) {
|
|
268
|
+
if (dt <= 0) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
// Handle modulation expiry
|
|
272
|
+
if (this.activeModulation?.duration && this.activeModulation.duration > 0) {
|
|
273
|
+
const modulationElapsed = this.cycleTime - this.modulationStartTime;
|
|
274
|
+
if (modulationElapsed >= this.activeModulation.duration) {
|
|
275
|
+
this.activeModulation = null;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// Update times
|
|
279
|
+
this.cycleTime += dt;
|
|
280
|
+
this.phaseTime += dt;
|
|
281
|
+
this.lastPhaseChange += dt;
|
|
282
|
+
// Track phase time metrics
|
|
283
|
+
this.phaseTimeMetrics[this.currentPhase] += dt;
|
|
284
|
+
// Track duty factor
|
|
285
|
+
this.totalDutyFactorSum += this.getDutyFactor() * dt;
|
|
286
|
+
this.totalDutyFactorSamples += dt;
|
|
287
|
+
// Check for cycle completion
|
|
288
|
+
while (this.cycleTime >= this.config.cyclePeriodMs) {
|
|
289
|
+
this.cycleTime -= this.config.cyclePeriodMs;
|
|
290
|
+
this.cyclesCompleted++;
|
|
291
|
+
// Reset energy budget at cycle start
|
|
292
|
+
if (this.config.energyBudget > 0) {
|
|
293
|
+
this.energyRemaining = this.config.energyBudget;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
// Check for phase transition
|
|
297
|
+
this.updatePhase();
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Update the current phase based on cycle time
|
|
301
|
+
*
|
|
302
|
+
* If WASM is enabled, uses K-WTA competition for phase selection.
|
|
303
|
+
* Otherwise, falls back to time-based phase calculation.
|
|
304
|
+
*/
|
|
305
|
+
updatePhase() {
|
|
306
|
+
// If modulation forces a phase, use it
|
|
307
|
+
if (this.activeModulation?.forcePhase) {
|
|
308
|
+
const newPhase = this.activeModulation.forcePhase;
|
|
309
|
+
if (newPhase !== this.currentPhase) {
|
|
310
|
+
this.transitionToPhase(newPhase);
|
|
311
|
+
}
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
// Calculate target phase
|
|
315
|
+
const cyclePosition = this.cycleTime / this.config.cyclePeriodMs;
|
|
316
|
+
let targetPhase;
|
|
317
|
+
if (this.wasmEnabled && this.wtaLayer) {
|
|
318
|
+
// Use WTA competition for biologically-plausible phase selection
|
|
319
|
+
targetPhase = this.calculatePhaseWithWTA(cyclePosition);
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
// Fallback to pure time-based calculation
|
|
323
|
+
targetPhase = this.calculatePhaseForPosition(cyclePosition);
|
|
324
|
+
}
|
|
325
|
+
// Check if we need to transition
|
|
326
|
+
if (targetPhase !== this.currentPhase) {
|
|
327
|
+
// Apply hysteresis
|
|
328
|
+
if (this.lastPhaseChange < this.config.hysteresisMs) {
|
|
329
|
+
this.hysteresisActivations++;
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
this.transitionToPhase(targetPhase);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Calculate phase using WTA (Winner-Take-All) competition
|
|
337
|
+
*
|
|
338
|
+
* Each phase's activation strength is based on how close the current
|
|
339
|
+
* cycle position is to the phase's peak time. The WTA layer then
|
|
340
|
+
* selects the winning phase through lateral inhibition.
|
|
341
|
+
*
|
|
342
|
+
* The activation function uses a Gaussian profile centered on each phase's
|
|
343
|
+
* midpoint, with sigma proportional to phase duration for smooth transitions.
|
|
344
|
+
*
|
|
345
|
+
* @param cyclePosition - Position in cycle (0-1)
|
|
346
|
+
* @returns The winning phase from WTA competition
|
|
347
|
+
*/
|
|
348
|
+
calculatePhaseWithWTA(cyclePosition) {
|
|
349
|
+
if (!this.wtaLayer) {
|
|
350
|
+
return this.calculatePhaseForPosition(cyclePosition);
|
|
351
|
+
}
|
|
352
|
+
// Calculate activation strength for each phase based on cycle position
|
|
353
|
+
// Each phase has strongest activation during its duration window
|
|
354
|
+
const activations = new Float32Array(4);
|
|
355
|
+
let accumulatedDuration = 0;
|
|
356
|
+
for (let i = 0; i < PHASE_ORDER.length; i++) {
|
|
357
|
+
const phase = PHASE_ORDER[i];
|
|
358
|
+
const phaseConfig = this.config.phases[phase];
|
|
359
|
+
const phaseStart = accumulatedDuration;
|
|
360
|
+
const phaseEnd = accumulatedDuration + phaseConfig.duration;
|
|
361
|
+
const phaseMidpoint = (phaseStart + phaseEnd) / 2;
|
|
362
|
+
// Calculate distance from cycle position to phase midpoint (circular distance)
|
|
363
|
+
let distance = Math.abs(cyclePosition - phaseMidpoint);
|
|
364
|
+
if (distance > 0.5) {
|
|
365
|
+
distance = 1 - distance; // Handle wrap-around
|
|
366
|
+
}
|
|
367
|
+
// Convert distance to activation (closer = higher activation)
|
|
368
|
+
// Using Gaussian-like falloff: activation = exp(-distance^2 / (2 * sigma^2))
|
|
369
|
+
// sigma is proportional to phase duration for smoother transitions
|
|
370
|
+
const sigma = phaseConfig.duration * 0.5;
|
|
371
|
+
activations[i] = Math.exp(-(distance * distance) / (2 * sigma * sigma));
|
|
372
|
+
// Scale by duty factor to give active phases stronger drive
|
|
373
|
+
activations[i] *= phaseConfig.dutyFactor;
|
|
374
|
+
accumulatedDuration = phaseEnd;
|
|
375
|
+
}
|
|
376
|
+
// Run WTA competition - returns index of winning neuron or -1 if none
|
|
377
|
+
const winnerIndex = this.wtaLayer.compete(activations);
|
|
378
|
+
this.wtaCompetitions++;
|
|
379
|
+
// If no winner (all below threshold), fall back to time-based
|
|
380
|
+
if (winnerIndex < 0 || winnerIndex >= INDEX_TO_PHASE.length) {
|
|
381
|
+
return this.calculatePhaseForPosition(cyclePosition);
|
|
382
|
+
}
|
|
383
|
+
return INDEX_TO_PHASE[winnerIndex];
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Calculate which phase corresponds to a cycle position (pure time-based)
|
|
387
|
+
*
|
|
388
|
+
* @param position - Position in cycle (0-1)
|
|
389
|
+
* @returns The phase for that position
|
|
390
|
+
*/
|
|
391
|
+
calculatePhaseForPosition(position) {
|
|
392
|
+
let accumulated = 0;
|
|
393
|
+
for (const phase of PHASE_ORDER) {
|
|
394
|
+
accumulated += this.config.phases[phase].duration;
|
|
395
|
+
if (position < accumulated) {
|
|
396
|
+
return phase;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
// Edge case: exactly at end of cycle
|
|
400
|
+
return PHASE_ORDER[PHASE_ORDER.length - 1];
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Transition to a new phase
|
|
404
|
+
*
|
|
405
|
+
* @param newPhase - The phase to transition to
|
|
406
|
+
*/
|
|
407
|
+
transitionToPhase(newPhase) {
|
|
408
|
+
this.currentPhase = newPhase;
|
|
409
|
+
this.phaseTime = 0;
|
|
410
|
+
this.lastPhaseChange = 0;
|
|
411
|
+
this.phaseTransitions++;
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Should run inference/tests?
|
|
415
|
+
*
|
|
416
|
+
* @returns true if in a phase that allows compute
|
|
417
|
+
*/
|
|
418
|
+
shouldCompute() {
|
|
419
|
+
const phaseConfig = this.config.phases[this.currentPhase];
|
|
420
|
+
return phaseConfig.allowCompute;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Should update learning models?
|
|
424
|
+
*
|
|
425
|
+
* @returns true if in a phase that allows learning
|
|
426
|
+
*/
|
|
427
|
+
shouldLearn() {
|
|
428
|
+
const phaseConfig = this.config.phases[this.currentPhase];
|
|
429
|
+
return phaseConfig.allowLearning;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Should run memory consolidation?
|
|
433
|
+
*
|
|
434
|
+
* @returns true if in a phase that allows consolidation
|
|
435
|
+
*/
|
|
436
|
+
shouldConsolidate() {
|
|
437
|
+
const phaseConfig = this.config.phases[this.currentPhase];
|
|
438
|
+
return phaseConfig.allowConsolidation;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Should react to event given importance?
|
|
442
|
+
*
|
|
443
|
+
* Events with importance >= threshold will be processed.
|
|
444
|
+
* Modulation can adjust the threshold.
|
|
445
|
+
*
|
|
446
|
+
* @param importance - Event importance (0-1, where 1 = critical)
|
|
447
|
+
* @returns true if the event should be processed
|
|
448
|
+
*/
|
|
449
|
+
shouldReact(importance) {
|
|
450
|
+
const phaseConfig = this.config.phases[this.currentPhase];
|
|
451
|
+
let threshold = phaseConfig.importanceThreshold;
|
|
452
|
+
// Apply modulation
|
|
453
|
+
if (this.activeModulation?.importanceMultiplier) {
|
|
454
|
+
threshold *= this.activeModulation.importanceMultiplier;
|
|
455
|
+
// Clamp to valid range
|
|
456
|
+
threshold = Math.max(0, Math.min(1, threshold));
|
|
457
|
+
}
|
|
458
|
+
const shouldReact = importance >= threshold;
|
|
459
|
+
// Track metrics
|
|
460
|
+
if (shouldReact) {
|
|
461
|
+
this.reactionsPerPhase[this.currentPhase]++;
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
this.rejectionsPerPhase[this.currentPhase]++;
|
|
465
|
+
}
|
|
466
|
+
return shouldReact;
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Get current circadian phase
|
|
470
|
+
*
|
|
471
|
+
* @returns Current phase
|
|
472
|
+
*/
|
|
473
|
+
getPhase() {
|
|
474
|
+
return this.currentPhase;
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Get current duty factor (0-1)
|
|
478
|
+
*
|
|
479
|
+
* The duty factor represents the fraction of full compute being used.
|
|
480
|
+
* Modified by active modulation if present.
|
|
481
|
+
*
|
|
482
|
+
* @returns Duty factor where 1 = full compute
|
|
483
|
+
*/
|
|
484
|
+
getDutyFactor() {
|
|
485
|
+
const phaseConfig = this.config.phases[this.currentPhase];
|
|
486
|
+
let dutyFactor = phaseConfig.dutyFactor;
|
|
487
|
+
// Apply modulation
|
|
488
|
+
if (this.activeModulation?.dutyAdjustment) {
|
|
489
|
+
dutyFactor += this.activeModulation.dutyAdjustment;
|
|
490
|
+
// Clamp to valid range
|
|
491
|
+
dutyFactor = Math.max(0, Math.min(1, dutyFactor));
|
|
492
|
+
}
|
|
493
|
+
return dutyFactor;
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Get compute cost reduction factor
|
|
497
|
+
*
|
|
498
|
+
* This returns a multiplier representing savings.
|
|
499
|
+
* For example, 2.0 means 50% savings (using half the compute).
|
|
500
|
+
*
|
|
501
|
+
* @returns Factor where higher = more savings (1/dutyFactor)
|
|
502
|
+
*/
|
|
503
|
+
getCostReductionFactor() {
|
|
504
|
+
const dutyFactor = this.getDutyFactor();
|
|
505
|
+
// Avoid division by zero
|
|
506
|
+
if (dutyFactor <= 0) {
|
|
507
|
+
return 100; // Max savings
|
|
508
|
+
}
|
|
509
|
+
return 1 / dutyFactor;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Apply external modulation
|
|
513
|
+
*
|
|
514
|
+
* Modulation allows external systems to adjust circadian behavior.
|
|
515
|
+
* For example, during high-priority periods, modulation can force
|
|
516
|
+
* the Active phase or lower importance thresholds.
|
|
517
|
+
*
|
|
518
|
+
* @param mod - Modulation parameters
|
|
519
|
+
*/
|
|
520
|
+
modulate(mod) {
|
|
521
|
+
this.activeModulation = { ...mod };
|
|
522
|
+
this.modulationStartTime = this.cycleTime;
|
|
523
|
+
// If forcing a phase, transition immediately (bypassing hysteresis)
|
|
524
|
+
if (mod.forcePhase && mod.forcePhase !== this.currentPhase) {
|
|
525
|
+
this.transitionToPhase(mod.forcePhase);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Clear any active modulation
|
|
530
|
+
*/
|
|
531
|
+
clearModulation() {
|
|
532
|
+
this.activeModulation = null;
|
|
533
|
+
// Recalculate phase based on current cycle position
|
|
534
|
+
this.updatePhase();
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Get current state
|
|
538
|
+
*
|
|
539
|
+
* @returns Complete state snapshot
|
|
540
|
+
*/
|
|
541
|
+
getState() {
|
|
542
|
+
const cyclePosition = this.cycleTime / this.config.cyclePeriodMs;
|
|
543
|
+
let accumulated = 0;
|
|
544
|
+
let timeToNextPhase = 0;
|
|
545
|
+
// Find time to next phase
|
|
546
|
+
for (const phase of PHASE_ORDER) {
|
|
547
|
+
const phaseEnd = accumulated + this.config.phases[phase].duration;
|
|
548
|
+
if (cyclePosition < phaseEnd) {
|
|
549
|
+
timeToNextPhase = (phaseEnd - cyclePosition) * this.config.cyclePeriodMs;
|
|
550
|
+
break;
|
|
551
|
+
}
|
|
552
|
+
accumulated = phaseEnd;
|
|
553
|
+
}
|
|
554
|
+
return {
|
|
555
|
+
phase: this.currentPhase,
|
|
556
|
+
cycleTime: this.cycleTime,
|
|
557
|
+
phaseTime: this.phaseTime,
|
|
558
|
+
energyRemaining: this.energyRemaining,
|
|
559
|
+
cyclesCompleted: this.cyclesCompleted,
|
|
560
|
+
activeModulation: this.activeModulation ? { ...this.activeModulation } : null,
|
|
561
|
+
timeToNextPhase,
|
|
562
|
+
wasmEnabled: this.wasmEnabled,
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Get collected metrics
|
|
567
|
+
*
|
|
568
|
+
* @returns Metrics snapshot
|
|
569
|
+
*/
|
|
570
|
+
getMetrics() {
|
|
571
|
+
return {
|
|
572
|
+
phaseTime: { ...this.phaseTimeMetrics },
|
|
573
|
+
reactionsPerPhase: { ...this.reactionsPerPhase },
|
|
574
|
+
rejectionsPerPhase: { ...this.rejectionsPerPhase },
|
|
575
|
+
averageDutyFactor: this.totalDutyFactorSamples > 0
|
|
576
|
+
? this.totalDutyFactorSum / this.totalDutyFactorSamples
|
|
577
|
+
: 1,
|
|
578
|
+
totalEnergyConsumed: this.totalEnergyConsumed,
|
|
579
|
+
phaseTransitions: this.phaseTransitions,
|
|
580
|
+
hysteresisActivations: this.hysteresisActivations,
|
|
581
|
+
wtaCompetitions: this.wtaCompetitions,
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Reset the controller to initial state
|
|
586
|
+
*/
|
|
587
|
+
reset() {
|
|
588
|
+
this.currentPhase = this.config.initialPhase;
|
|
589
|
+
this.cycleTime = 0;
|
|
590
|
+
this.phaseTime = 0;
|
|
591
|
+
this.lastPhaseChange = 0;
|
|
592
|
+
this.cyclesCompleted = 0;
|
|
593
|
+
this.energyRemaining = this.config.energyBudget;
|
|
594
|
+
this.activeModulation = null;
|
|
595
|
+
this.modulationStartTime = 0;
|
|
596
|
+
// Reset metrics
|
|
597
|
+
this.phaseTimeMetrics.Active = 0;
|
|
598
|
+
this.phaseTimeMetrics.Dawn = 0;
|
|
599
|
+
this.phaseTimeMetrics.Dusk = 0;
|
|
600
|
+
this.phaseTimeMetrics.Rest = 0;
|
|
601
|
+
this.reactionsPerPhase.Active = 0;
|
|
602
|
+
this.reactionsPerPhase.Dawn = 0;
|
|
603
|
+
this.reactionsPerPhase.Dusk = 0;
|
|
604
|
+
this.reactionsPerPhase.Rest = 0;
|
|
605
|
+
this.rejectionsPerPhase.Active = 0;
|
|
606
|
+
this.rejectionsPerPhase.Dawn = 0;
|
|
607
|
+
this.rejectionsPerPhase.Dusk = 0;
|
|
608
|
+
this.rejectionsPerPhase.Rest = 0;
|
|
609
|
+
this.totalDutyFactorSum = 0;
|
|
610
|
+
this.totalDutyFactorSamples = 0;
|
|
611
|
+
this.totalEnergyConsumed = 0;
|
|
612
|
+
this.phaseTransitions = 0;
|
|
613
|
+
this.hysteresisActivations = 0;
|
|
614
|
+
this.wtaCompetitions = 0;
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Consume energy for a compute operation
|
|
618
|
+
*
|
|
619
|
+
* When energy budget is enabled, this tracks energy consumption.
|
|
620
|
+
* Operations can check if energy is available before proceeding.
|
|
621
|
+
*
|
|
622
|
+
* @param amount - Energy to consume (defaults to computeEnergyCost)
|
|
623
|
+
* @returns true if energy was available and consumed
|
|
624
|
+
*/
|
|
625
|
+
consumeEnergy(amount) {
|
|
626
|
+
const cost = amount ?? this.config.computeEnergyCost;
|
|
627
|
+
// If no budget, always allow
|
|
628
|
+
if (this.config.energyBudget <= 0) {
|
|
629
|
+
this.totalEnergyConsumed += cost;
|
|
630
|
+
return true;
|
|
631
|
+
}
|
|
632
|
+
// Check if energy is available
|
|
633
|
+
if (this.energyRemaining >= cost) {
|
|
634
|
+
this.energyRemaining -= cost;
|
|
635
|
+
this.totalEnergyConsumed += cost;
|
|
636
|
+
return true;
|
|
637
|
+
}
|
|
638
|
+
return false;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Get the configuration
|
|
642
|
+
*
|
|
643
|
+
* @returns Current configuration (read-only)
|
|
644
|
+
*/
|
|
645
|
+
getConfig() {
|
|
646
|
+
return this.config;
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Calculate theoretical average duty factor based on phase durations
|
|
650
|
+
*
|
|
651
|
+
* @returns Weighted average of duty factors
|
|
652
|
+
*/
|
|
653
|
+
getTheoreticalAverageDutyFactor() {
|
|
654
|
+
return Object.entries(this.config.phases).reduce((sum, [, config]) => sum + config.duration * config.dutyFactor, 0);
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Calculate theoretical cost reduction factor
|
|
658
|
+
*
|
|
659
|
+
* @returns Expected savings factor (e.g., 2.0 = 50% savings)
|
|
660
|
+
*/
|
|
661
|
+
getTheoreticalCostReduction() {
|
|
662
|
+
const avgDuty = this.getTheoreticalAverageDutyFactor();
|
|
663
|
+
return avgDuty > 0 ? 1 / avgDuty : 100;
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Check if WASM phase selection is active
|
|
667
|
+
*
|
|
668
|
+
* @returns true if K-WTA competition is being used
|
|
669
|
+
*/
|
|
670
|
+
isWasmEnabled() {
|
|
671
|
+
return this.wasmEnabled;
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Cleanup WASM resources
|
|
675
|
+
*
|
|
676
|
+
* Call this when the controller is no longer needed to free WASM memory.
|
|
677
|
+
*/
|
|
678
|
+
dispose() {
|
|
679
|
+
if (this.wtaLayer) {
|
|
680
|
+
// WTALayer has a free() method for WASM memory cleanup
|
|
681
|
+
try {
|
|
682
|
+
this.wtaLayer.free();
|
|
683
|
+
}
|
|
684
|
+
catch {
|
|
685
|
+
// Ignore cleanup errors
|
|
686
|
+
}
|
|
687
|
+
this.wtaLayer = null;
|
|
688
|
+
}
|
|
689
|
+
this.wasmEnabled = false;
|
|
690
|
+
}
|
|
691
|
+
// ============================================
|
|
692
|
+
// Serialization Methods for Persistence
|
|
693
|
+
// ============================================
|
|
694
|
+
/**
|
|
695
|
+
* Get the last phase change timestamp
|
|
696
|
+
* @returns Timestamp in milliseconds
|
|
697
|
+
*/
|
|
698
|
+
getLastPhaseChangeTime() {
|
|
699
|
+
return this.lastPhaseChange;
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* Get the modulation start time (if active)
|
|
703
|
+
* @returns Timestamp or undefined
|
|
704
|
+
*/
|
|
705
|
+
getModulationStartTime() {
|
|
706
|
+
return this.activeModulation ? this.modulationStartTime : undefined;
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Restore state from serialized values
|
|
710
|
+
* @param phase Current phase
|
|
711
|
+
* @param cycleTime Time in current cycle
|
|
712
|
+
* @param phaseTime Time in current phase
|
|
713
|
+
* @param cyclesCompleted Number of completed cycles
|
|
714
|
+
* @param energyRemaining Remaining energy
|
|
715
|
+
* @param modulation Active modulation or null
|
|
716
|
+
*/
|
|
717
|
+
restoreState(phase, cycleTime, phaseTime, cyclesCompleted, energyRemaining, modulation) {
|
|
718
|
+
this.currentPhase = phase;
|
|
719
|
+
this.cycleTime = cycleTime;
|
|
720
|
+
this.phaseTime = phaseTime;
|
|
721
|
+
this.cyclesCompleted = cyclesCompleted;
|
|
722
|
+
this.energyRemaining = energyRemaining;
|
|
723
|
+
this.activeModulation = modulation;
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Restore metrics from serialized values
|
|
727
|
+
* @param metrics Metrics to restore
|
|
728
|
+
*/
|
|
729
|
+
restoreMetrics(metrics) {
|
|
730
|
+
// Restore phase time metrics
|
|
731
|
+
this.phaseTimeMetrics.Active = metrics.phaseTime.Active;
|
|
732
|
+
this.phaseTimeMetrics.Dawn = metrics.phaseTime.Dawn;
|
|
733
|
+
this.phaseTimeMetrics.Dusk = metrics.phaseTime.Dusk;
|
|
734
|
+
this.phaseTimeMetrics.Rest = metrics.phaseTime.Rest;
|
|
735
|
+
// Restore reactions per phase
|
|
736
|
+
this.reactionsPerPhase.Active = metrics.reactionsPerPhase.Active;
|
|
737
|
+
this.reactionsPerPhase.Dawn = metrics.reactionsPerPhase.Dawn;
|
|
738
|
+
this.reactionsPerPhase.Dusk = metrics.reactionsPerPhase.Dusk;
|
|
739
|
+
this.reactionsPerPhase.Rest = metrics.reactionsPerPhase.Rest;
|
|
740
|
+
// Restore rejections per phase
|
|
741
|
+
this.rejectionsPerPhase.Active = metrics.rejectionsPerPhase.Active;
|
|
742
|
+
this.rejectionsPerPhase.Dawn = metrics.rejectionsPerPhase.Dawn;
|
|
743
|
+
this.rejectionsPerPhase.Dusk = metrics.rejectionsPerPhase.Dusk;
|
|
744
|
+
this.rejectionsPerPhase.Rest = metrics.rejectionsPerPhase.Rest;
|
|
745
|
+
// Restore aggregate metrics
|
|
746
|
+
this.totalDutyFactorSum = metrics.averageDutyFactor;
|
|
747
|
+
this.totalDutyFactorSamples = 1; // Will be recalculated on next sample
|
|
748
|
+
this.totalEnergyConsumed = metrics.totalEnergyConsumed;
|
|
749
|
+
this.phaseTransitions = metrics.phaseTransitions;
|
|
750
|
+
this.hysteresisActivations = metrics.hysteresisActivations;
|
|
751
|
+
// WTA competitions if available
|
|
752
|
+
if ('wtaCompetitions' in metrics) {
|
|
753
|
+
this.wtaCompetitions = metrics.wtaCompetitions;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Restore last phase change time
|
|
758
|
+
* @param time Timestamp in milliseconds
|
|
759
|
+
*/
|
|
760
|
+
restoreLastPhaseChangeTime(time) {
|
|
761
|
+
this.lastPhaseChange = time;
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* Restore modulation start time
|
|
765
|
+
* @param time Timestamp or undefined
|
|
766
|
+
*/
|
|
767
|
+
restoreModulationStartTime(time) {
|
|
768
|
+
if (time !== undefined) {
|
|
769
|
+
this.modulationStartTime = time;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
exports.CircadianController = CircadianController;
|
|
774
|
+
// ============================================================================
|
|
775
|
+
// Factory Functions
|
|
776
|
+
// ============================================================================
|
|
777
|
+
/**
|
|
778
|
+
* Create a CircadianController optimized for testing (fast cycles)
|
|
779
|
+
*
|
|
780
|
+
* @param cyclePeriodMs - Cycle period in milliseconds (default: 1 minute)
|
|
781
|
+
* @returns Promise resolving to configured CircadianController
|
|
782
|
+
*/
|
|
783
|
+
async function createTestingController(cyclePeriodMs = 60000) {
|
|
784
|
+
return CircadianController.create({
|
|
785
|
+
cyclePeriodMs,
|
|
786
|
+
hysteresisMs: 100, // Low hysteresis for fast testing
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Create a CircadianController optimized for maximum savings
|
|
791
|
+
*
|
|
792
|
+
* Extends rest phase and reduces active phase for up to 80% savings.
|
|
793
|
+
*
|
|
794
|
+
* @param cyclePeriodMs - Cycle period in milliseconds
|
|
795
|
+
* @returns Promise resolving to configured CircadianController
|
|
796
|
+
*/
|
|
797
|
+
async function createEfficientController(cyclePeriodMs = exports.DEFAULT_CIRCADIAN_CONFIG.cyclePeriodMs) {
|
|
798
|
+
return CircadianController.create({
|
|
799
|
+
cyclePeriodMs,
|
|
800
|
+
phases: {
|
|
801
|
+
Active: {
|
|
802
|
+
...exports.DEFAULT_PHASE_CONFIGS.Active,
|
|
803
|
+
duration: 0.20, // Reduced to 20%
|
|
804
|
+
},
|
|
805
|
+
Dawn: {
|
|
806
|
+
...exports.DEFAULT_PHASE_CONFIGS.Dawn,
|
|
807
|
+
duration: 0.10, // Reduced to 10%
|
|
808
|
+
},
|
|
809
|
+
Dusk: {
|
|
810
|
+
...exports.DEFAULT_PHASE_CONFIGS.Dusk,
|
|
811
|
+
duration: 0.10, // Reduced to 10%
|
|
812
|
+
},
|
|
813
|
+
Rest: {
|
|
814
|
+
...exports.DEFAULT_PHASE_CONFIGS.Rest,
|
|
815
|
+
duration: 0.60, // Extended to 60%
|
|
816
|
+
},
|
|
817
|
+
},
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Create a CircadianController optimized for responsiveness
|
|
822
|
+
*
|
|
823
|
+
* Extended active phase with lower importance thresholds.
|
|
824
|
+
*
|
|
825
|
+
* @param cyclePeriodMs - Cycle period in milliseconds
|
|
826
|
+
* @returns Promise resolving to configured CircadianController
|
|
827
|
+
*/
|
|
828
|
+
async function createResponsiveController(cyclePeriodMs = exports.DEFAULT_CIRCADIAN_CONFIG.cyclePeriodMs) {
|
|
829
|
+
return CircadianController.create({
|
|
830
|
+
cyclePeriodMs,
|
|
831
|
+
phases: {
|
|
832
|
+
Active: {
|
|
833
|
+
...exports.DEFAULT_PHASE_CONFIGS.Active,
|
|
834
|
+
duration: 0.60, // Extended to 60%
|
|
835
|
+
},
|
|
836
|
+
Dawn: {
|
|
837
|
+
...exports.DEFAULT_PHASE_CONFIGS.Dawn,
|
|
838
|
+
duration: 0.15,
|
|
839
|
+
importanceThreshold: 0.1, // More responsive
|
|
840
|
+
},
|
|
841
|
+
Dusk: {
|
|
842
|
+
...exports.DEFAULT_PHASE_CONFIGS.Dusk,
|
|
843
|
+
duration: 0.10, // Reduced
|
|
844
|
+
importanceThreshold: 0.2, // More responsive
|
|
845
|
+
},
|
|
846
|
+
Rest: {
|
|
847
|
+
...exports.DEFAULT_PHASE_CONFIGS.Rest,
|
|
848
|
+
duration: 0.15, // Reduced
|
|
849
|
+
importanceThreshold: 0.5, // More responsive
|
|
850
|
+
},
|
|
851
|
+
},
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* Create a CircadianController with energy budgeting
|
|
856
|
+
*
|
|
857
|
+
* @param energyBudget - Energy budget per cycle
|
|
858
|
+
* @param cyclePeriodMs - Cycle period in milliseconds
|
|
859
|
+
* @returns Promise resolving to configured CircadianController
|
|
860
|
+
*/
|
|
861
|
+
async function createBudgetedController(energyBudget, cyclePeriodMs = exports.DEFAULT_CIRCADIAN_CONFIG.cyclePeriodMs) {
|
|
862
|
+
return CircadianController.create({
|
|
863
|
+
cyclePeriodMs,
|
|
864
|
+
energyBudget,
|
|
865
|
+
computeEnergyCost: 1,
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Create a CircadianController without WASM (pure TypeScript)
|
|
870
|
+
*
|
|
871
|
+
* Use this when WASM is not available or not desired.
|
|
872
|
+
*
|
|
873
|
+
* @param config - Partial configuration
|
|
874
|
+
* @returns Configured CircadianController (synchronous, no WASM)
|
|
875
|
+
*/
|
|
876
|
+
function createPureTypeScriptController(config = {}) {
|
|
877
|
+
return new CircadianController({
|
|
878
|
+
...config,
|
|
879
|
+
useWasmPhaseSelection: false,
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
//# sourceMappingURL=CircadianController.js.map
|