genesis-ai-cli 7.4.5
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/.env.example +78 -0
- package/README.md +282 -0
- package/dist/src/active-inference/actions.d.ts +75 -0
- package/dist/src/active-inference/actions.js +250 -0
- package/dist/src/active-inference/autonomous-loop.d.ts +103 -0
- package/dist/src/active-inference/autonomous-loop.js +289 -0
- package/dist/src/active-inference/core.d.ts +85 -0
- package/dist/src/active-inference/core.js +555 -0
- package/dist/src/active-inference/demo-autonomous-loop.d.ts +8 -0
- package/dist/src/active-inference/demo-autonomous-loop.js +338 -0
- package/dist/src/active-inference/demo-value-integration.d.ts +8 -0
- package/dist/src/active-inference/demo-value-integration.js +174 -0
- package/dist/src/active-inference/index.d.ts +32 -0
- package/dist/src/active-inference/index.js +88 -0
- package/dist/src/active-inference/integration.d.ts +114 -0
- package/dist/src/active-inference/integration.js +698 -0
- package/dist/src/active-inference/memory-integration.d.ts +51 -0
- package/dist/src/active-inference/memory-integration.js +232 -0
- package/dist/src/active-inference/observations.d.ts +67 -0
- package/dist/src/active-inference/observations.js +147 -0
- package/dist/src/active-inference/test-active-inference.d.ts +8 -0
- package/dist/src/active-inference/test-active-inference.js +320 -0
- package/dist/src/active-inference/test-value-integration.d.ts +6 -0
- package/dist/src/active-inference/test-value-integration.js +168 -0
- package/dist/src/active-inference/types.d.ts +150 -0
- package/dist/src/active-inference/types.js +59 -0
- package/dist/src/active-inference/value-integration.d.ts +164 -0
- package/dist/src/active-inference/value-integration.js +459 -0
- package/dist/src/agents/base-agent.d.ts +53 -0
- package/dist/src/agents/base-agent.js +178 -0
- package/dist/src/agents/builder.d.ts +67 -0
- package/dist/src/agents/builder.js +537 -0
- package/dist/src/agents/critic.d.ts +35 -0
- package/dist/src/agents/critic.js +322 -0
- package/dist/src/agents/ethicist.d.ts +54 -0
- package/dist/src/agents/ethicist.js +393 -0
- package/dist/src/agents/explorer.d.ts +26 -0
- package/dist/src/agents/explorer.js +216 -0
- package/dist/src/agents/feeling.d.ts +41 -0
- package/dist/src/agents/feeling.js +320 -0
- package/dist/src/agents/index.d.ts +111 -0
- package/dist/src/agents/index.js +222 -0
- package/dist/src/agents/memory.d.ts +69 -0
- package/dist/src/agents/memory.js +404 -0
- package/dist/src/agents/message-bus.d.ts +88 -0
- package/dist/src/agents/message-bus.js +267 -0
- package/dist/src/agents/narrator.d.ts +90 -0
- package/dist/src/agents/narrator.js +473 -0
- package/dist/src/agents/planner.d.ts +38 -0
- package/dist/src/agents/planner.js +341 -0
- package/dist/src/agents/predictor.d.ts +73 -0
- package/dist/src/agents/predictor.js +506 -0
- package/dist/src/agents/sensor.d.ts +88 -0
- package/dist/src/agents/sensor.js +377 -0
- package/dist/src/agents/test-agents.d.ts +6 -0
- package/dist/src/agents/test-agents.js +73 -0
- package/dist/src/agents/types.d.ts +194 -0
- package/dist/src/agents/types.js +7 -0
- package/dist/src/brain/index.d.ts +185 -0
- package/dist/src/brain/index.js +843 -0
- package/dist/src/brain/trace.d.ts +91 -0
- package/dist/src/brain/trace.js +327 -0
- package/dist/src/brain/types.d.ts +165 -0
- package/dist/src/brain/types.js +51 -0
- package/dist/src/cli/chat.d.ts +237 -0
- package/dist/src/cli/chat.js +1959 -0
- package/dist/src/cli/dispatcher.d.ts +182 -0
- package/dist/src/cli/dispatcher.js +718 -0
- package/dist/src/cli/human-loop.d.ts +170 -0
- package/dist/src/cli/human-loop.js +543 -0
- package/dist/src/cli/index.d.ts +12 -0
- package/dist/src/cli/index.js +28 -0
- package/dist/src/cli/interactive.d.ts +141 -0
- package/dist/src/cli/interactive.js +757 -0
- package/dist/src/cli/ui.d.ts +205 -0
- package/dist/src/cli/ui.js +632 -0
- package/dist/src/consciousness/attention-schema.d.ts +154 -0
- package/dist/src/consciousness/attention-schema.js +432 -0
- package/dist/src/consciousness/global-workspace.d.ts +149 -0
- package/dist/src/consciousness/global-workspace.js +422 -0
- package/dist/src/consciousness/index.d.ts +186 -0
- package/dist/src/consciousness/index.js +476 -0
- package/dist/src/consciousness/phi-calculator.d.ts +119 -0
- package/dist/src/consciousness/phi-calculator.js +445 -0
- package/dist/src/consciousness/phi-decisions.d.ts +169 -0
- package/dist/src/consciousness/phi-decisions.js +383 -0
- package/dist/src/consciousness/phi-monitor.d.ts +153 -0
- package/dist/src/consciousness/phi-monitor.js +465 -0
- package/dist/src/consciousness/types.d.ts +260 -0
- package/dist/src/consciousness/types.js +44 -0
- package/dist/src/daemon/dream-mode.d.ts +115 -0
- package/dist/src/daemon/dream-mode.js +470 -0
- package/dist/src/daemon/index.d.ts +162 -0
- package/dist/src/daemon/index.js +542 -0
- package/dist/src/daemon/maintenance.d.ts +139 -0
- package/dist/src/daemon/maintenance.js +549 -0
- package/dist/src/daemon/process.d.ts +82 -0
- package/dist/src/daemon/process.js +442 -0
- package/dist/src/daemon/scheduler.d.ts +90 -0
- package/dist/src/daemon/scheduler.js +494 -0
- package/dist/src/daemon/types.d.ts +213 -0
- package/dist/src/daemon/types.js +50 -0
- package/dist/src/epistemic/index.d.ts +74 -0
- package/dist/src/epistemic/index.js +225 -0
- package/dist/src/grounding/epistemic-stack.d.ts +100 -0
- package/dist/src/grounding/epistemic-stack.js +408 -0
- package/dist/src/grounding/feedback.d.ts +98 -0
- package/dist/src/grounding/feedback.js +276 -0
- package/dist/src/grounding/index.d.ts +123 -0
- package/dist/src/grounding/index.js +224 -0
- package/dist/src/grounding/verifier.d.ts +149 -0
- package/dist/src/grounding/verifier.js +484 -0
- package/dist/src/healing/detector.d.ts +110 -0
- package/dist/src/healing/detector.js +436 -0
- package/dist/src/healing/fixer.d.ts +138 -0
- package/dist/src/healing/fixer.js +572 -0
- package/dist/src/healing/index.d.ts +23 -0
- package/dist/src/healing/index.js +43 -0
- package/dist/src/hooks/index.d.ts +135 -0
- package/dist/src/hooks/index.js +317 -0
- package/dist/src/index.d.ts +23 -0
- package/dist/src/index.js +1266 -0
- package/dist/src/kernel/index.d.ts +155 -0
- package/dist/src/kernel/index.js +795 -0
- package/dist/src/kernel/invariants.d.ts +153 -0
- package/dist/src/kernel/invariants.js +355 -0
- package/dist/src/kernel/test-kernel.d.ts +6 -0
- package/dist/src/kernel/test-kernel.js +108 -0
- package/dist/src/kernel/test-real-mcp.d.ts +10 -0
- package/dist/src/kernel/test-real-mcp.js +295 -0
- package/dist/src/llm/index.d.ts +146 -0
- package/dist/src/llm/index.js +428 -0
- package/dist/src/llm/router.d.ts +136 -0
- package/dist/src/llm/router.js +510 -0
- package/dist/src/mcp/index.d.ts +85 -0
- package/dist/src/mcp/index.js +657 -0
- package/dist/src/mcp/resilient.d.ts +139 -0
- package/dist/src/mcp/resilient.js +417 -0
- package/dist/src/memory/cache.d.ts +118 -0
- package/dist/src/memory/cache.js +356 -0
- package/dist/src/memory/cognitive-workspace.d.ts +231 -0
- package/dist/src/memory/cognitive-workspace.js +521 -0
- package/dist/src/memory/consolidation.d.ts +99 -0
- package/dist/src/memory/consolidation.js +443 -0
- package/dist/src/memory/episodic.d.ts +114 -0
- package/dist/src/memory/episodic.js +394 -0
- package/dist/src/memory/forgetting.d.ts +134 -0
- package/dist/src/memory/forgetting.js +324 -0
- package/dist/src/memory/index.d.ts +211 -0
- package/dist/src/memory/index.js +367 -0
- package/dist/src/memory/indexer.d.ts +123 -0
- package/dist/src/memory/indexer.js +479 -0
- package/dist/src/memory/procedural.d.ts +136 -0
- package/dist/src/memory/procedural.js +479 -0
- package/dist/src/memory/semantic.d.ts +132 -0
- package/dist/src/memory/semantic.js +497 -0
- package/dist/src/memory/types.d.ts +193 -0
- package/dist/src/memory/types.js +15 -0
- package/dist/src/orchestrator.d.ts +65 -0
- package/dist/src/orchestrator.js +317 -0
- package/dist/src/persistence/index.d.ts +257 -0
- package/dist/src/persistence/index.js +763 -0
- package/dist/src/pipeline/executor.d.ts +51 -0
- package/dist/src/pipeline/executor.js +695 -0
- package/dist/src/pipeline/index.d.ts +7 -0
- package/dist/src/pipeline/index.js +11 -0
- package/dist/src/self-production.d.ts +67 -0
- package/dist/src/self-production.js +205 -0
- package/dist/src/subagents/executor.d.ts +58 -0
- package/dist/src/subagents/executor.js +283 -0
- package/dist/src/subagents/index.d.ts +37 -0
- package/dist/src/subagents/index.js +53 -0
- package/dist/src/subagents/registry.d.ts +23 -0
- package/dist/src/subagents/registry.js +167 -0
- package/dist/src/subagents/types.d.ts +79 -0
- package/dist/src/subagents/types.js +14 -0
- package/dist/src/tools/bash.d.ts +139 -0
- package/dist/src/tools/bash.js +583 -0
- package/dist/src/tools/edit.d.ts +125 -0
- package/dist/src/tools/edit.js +424 -0
- package/dist/src/tools/git.d.ts +179 -0
- package/dist/src/tools/git.js +504 -0
- package/dist/src/tools/index.d.ts +21 -0
- package/dist/src/tools/index.js +163 -0
- package/dist/src/types.d.ts +145 -0
- package/dist/src/types.js +7 -0
- package/dist/src/world-model/decoder.d.ts +163 -0
- package/dist/src/world-model/decoder.js +517 -0
- package/dist/src/world-model/digital-twin.d.ts +219 -0
- package/dist/src/world-model/digital-twin.js +695 -0
- package/dist/src/world-model/encoder.d.ts +141 -0
- package/dist/src/world-model/encoder.js +564 -0
- package/dist/src/world-model/index.d.ts +221 -0
- package/dist/src/world-model/index.js +772 -0
- package/dist/src/world-model/predictor.d.ts +161 -0
- package/dist/src/world-model/predictor.js +681 -0
- package/dist/src/world-model/test-value-jepa.d.ts +8 -0
- package/dist/src/world-model/test-value-jepa.js +430 -0
- package/dist/src/world-model/types.d.ts +341 -0
- package/dist/src/world-model/types.js +69 -0
- package/dist/src/world-model/value-jepa.d.ts +247 -0
- package/dist/src/world-model/value-jepa.js +622 -0
- package/dist/test/brain.test.d.ts +11 -0
- package/dist/test/brain.test.js +358 -0
- package/dist/test/cli/dispatcher.test.d.ts +4 -0
- package/dist/test/cli/dispatcher.test.js +332 -0
- package/dist/test/cli/human-loop.test.d.ts +4 -0
- package/dist/test/cli/human-loop.test.js +270 -0
- package/dist/test/grounding/feedback.test.d.ts +4 -0
- package/dist/test/grounding/feedback.test.js +462 -0
- package/dist/test/grounding/verifier.test.d.ts +4 -0
- package/dist/test/grounding/verifier.test.js +442 -0
- package/dist/test/grounding.test.d.ts +6 -0
- package/dist/test/grounding.test.js +246 -0
- package/dist/test/healing/detector.test.d.ts +4 -0
- package/dist/test/healing/detector.test.js +266 -0
- package/dist/test/healing/fixer.test.d.ts +4 -0
- package/dist/test/healing/fixer.test.js +369 -0
- package/dist/test/integration.test.d.ts +5 -0
- package/dist/test/integration.test.js +290 -0
- package/dist/test/tools/bash.test.d.ts +4 -0
- package/dist/test/tools/bash.test.js +348 -0
- package/dist/test/tools/edit.test.d.ts +4 -0
- package/dist/test/tools/edit.test.js +350 -0
- package/dist/test/tools/git.test.d.ts +4 -0
- package/dist/test/tools/git.test.js +350 -0
- package/package.json +60 -0
|
@@ -0,0 +1,763 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Genesis 6.0 - State Persistence
|
|
4
|
+
*
|
|
5
|
+
* Save and load Genesis state to/from disk.
|
|
6
|
+
* Supports JSON format with optional compression.
|
|
7
|
+
*
|
|
8
|
+
* State includes:
|
|
9
|
+
* - Memory (episodic, semantic, procedural)
|
|
10
|
+
* - Conversation history
|
|
11
|
+
* - Session metadata
|
|
12
|
+
* - Configuration
|
|
13
|
+
*/
|
|
14
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
17
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
18
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
19
|
+
}
|
|
20
|
+
Object.defineProperty(o, k2, desc);
|
|
21
|
+
}) : (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
o[k2] = m[k];
|
|
24
|
+
}));
|
|
25
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
26
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
27
|
+
}) : function(o, v) {
|
|
28
|
+
o["default"] = v;
|
|
29
|
+
});
|
|
30
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
31
|
+
var ownKeys = function(o) {
|
|
32
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
33
|
+
var ar = [];
|
|
34
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
35
|
+
return ar;
|
|
36
|
+
};
|
|
37
|
+
return ownKeys(o);
|
|
38
|
+
};
|
|
39
|
+
return function (mod) {
|
|
40
|
+
if (mod && mod.__esModule) return mod;
|
|
41
|
+
var result = {};
|
|
42
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
43
|
+
__setModuleDefault(result, mod);
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
})();
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.SessionManager = exports.StateStore = void 0;
|
|
49
|
+
exports.createStateStore = createStateStore;
|
|
50
|
+
exports.getStateStore = getStateStore;
|
|
51
|
+
exports.resetStateStore = resetStateStore;
|
|
52
|
+
exports.getSessionManager = getSessionManager;
|
|
53
|
+
const fs = __importStar(require("fs"));
|
|
54
|
+
const path = __importStar(require("path"));
|
|
55
|
+
const crypto = __importStar(require("crypto"));
|
|
56
|
+
// ============================================================================
|
|
57
|
+
// Default Paths
|
|
58
|
+
// ============================================================================
|
|
59
|
+
const DEFAULT_DATA_DIR = path.join(process.env.HOME || '.', '.genesis');
|
|
60
|
+
const STATE_FILE = 'state.json';
|
|
61
|
+
const BACKUP_DIR = 'backups';
|
|
62
|
+
// ============================================================================
|
|
63
|
+
// State Store Class
|
|
64
|
+
// ============================================================================
|
|
65
|
+
class StateStore {
|
|
66
|
+
dataDir;
|
|
67
|
+
statePath;
|
|
68
|
+
backupDir;
|
|
69
|
+
options;
|
|
70
|
+
autoSaveTimer = null;
|
|
71
|
+
dirty = false;
|
|
72
|
+
// In-memory state
|
|
73
|
+
state;
|
|
74
|
+
constructor(options = {}) {
|
|
75
|
+
this.options = options;
|
|
76
|
+
this.dataDir = options.dataDir || DEFAULT_DATA_DIR;
|
|
77
|
+
this.statePath = path.join(this.dataDir, STATE_FILE);
|
|
78
|
+
this.backupDir = path.join(this.dataDir, BACKUP_DIR);
|
|
79
|
+
// Initialize with empty state
|
|
80
|
+
this.state = this.createEmptyState();
|
|
81
|
+
// Ensure directories exist
|
|
82
|
+
this.ensureDirectories();
|
|
83
|
+
// Try to load existing state
|
|
84
|
+
this.loadSync();
|
|
85
|
+
// Start auto-save if enabled
|
|
86
|
+
if (options.autoSave) {
|
|
87
|
+
this.startAutoSave(options.autoSaveIntervalMs || 60000);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// ============================================================================
|
|
91
|
+
// Core Methods
|
|
92
|
+
// ============================================================================
|
|
93
|
+
/**
|
|
94
|
+
* Save state to disk
|
|
95
|
+
*/
|
|
96
|
+
async save() {
|
|
97
|
+
try {
|
|
98
|
+
// Update metadata
|
|
99
|
+
this.state.lastModified = new Date();
|
|
100
|
+
this.state.checksum = this.calculateChecksum(this.state);
|
|
101
|
+
// Create backup before overwriting
|
|
102
|
+
await this.backup();
|
|
103
|
+
// Write state
|
|
104
|
+
const json = JSON.stringify(this.state, null, 2);
|
|
105
|
+
await fs.promises.writeFile(this.statePath, json, 'utf-8');
|
|
106
|
+
this.dirty = false;
|
|
107
|
+
this.options.onSave?.(this.state);
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
111
|
+
this.options.onError?.(err);
|
|
112
|
+
throw err;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Save state synchronously
|
|
117
|
+
*/
|
|
118
|
+
saveSync() {
|
|
119
|
+
try {
|
|
120
|
+
this.state.lastModified = new Date();
|
|
121
|
+
this.state.checksum = this.calculateChecksum(this.state);
|
|
122
|
+
// Backup
|
|
123
|
+
this.backupSync();
|
|
124
|
+
// Write
|
|
125
|
+
const json = JSON.stringify(this.state, null, 2);
|
|
126
|
+
fs.writeFileSync(this.statePath, json, 'utf-8');
|
|
127
|
+
this.dirty = false;
|
|
128
|
+
this.options.onSave?.(this.state);
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
132
|
+
this.options.onError?.(err);
|
|
133
|
+
throw err;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Load state from disk
|
|
138
|
+
*/
|
|
139
|
+
async load() {
|
|
140
|
+
try {
|
|
141
|
+
if (!fs.existsSync(this.statePath)) {
|
|
142
|
+
return this.state;
|
|
143
|
+
}
|
|
144
|
+
const json = await fs.promises.readFile(this.statePath, 'utf-8');
|
|
145
|
+
const loaded = JSON.parse(json);
|
|
146
|
+
// Validate checksum
|
|
147
|
+
const expectedChecksum = this.calculateChecksum(loaded);
|
|
148
|
+
if (loaded.checksum && loaded.checksum !== expectedChecksum) {
|
|
149
|
+
console.warn('[StateStore] Checksum mismatch - state may be corrupted');
|
|
150
|
+
}
|
|
151
|
+
// Merge with empty state to ensure all fields exist
|
|
152
|
+
this.state = this.mergeState(this.createEmptyState(), loaded);
|
|
153
|
+
this.options.onLoad?.(this.state);
|
|
154
|
+
return this.state;
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
158
|
+
this.options.onError?.(err);
|
|
159
|
+
throw err;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Load state synchronously
|
|
164
|
+
*/
|
|
165
|
+
loadSync() {
|
|
166
|
+
try {
|
|
167
|
+
if (!fs.existsSync(this.statePath)) {
|
|
168
|
+
return this.state;
|
|
169
|
+
}
|
|
170
|
+
const json = fs.readFileSync(this.statePath, 'utf-8');
|
|
171
|
+
const loaded = JSON.parse(json);
|
|
172
|
+
this.state = this.mergeState(this.createEmptyState(), loaded);
|
|
173
|
+
this.options.onLoad?.(this.state);
|
|
174
|
+
return this.state;
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
// If loading fails, keep empty state
|
|
178
|
+
console.warn('[StateStore] Failed to load state:', error);
|
|
179
|
+
return this.state;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Get current state
|
|
184
|
+
*/
|
|
185
|
+
getState() {
|
|
186
|
+
return this.state;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Update state (partial update)
|
|
190
|
+
*/
|
|
191
|
+
update(updates) {
|
|
192
|
+
this.state = this.mergeState(this.state, updates);
|
|
193
|
+
this.dirty = true;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Check if state has unsaved changes
|
|
197
|
+
*/
|
|
198
|
+
isDirty() {
|
|
199
|
+
return this.dirty;
|
|
200
|
+
}
|
|
201
|
+
// ============================================================================
|
|
202
|
+
// Memory Operations
|
|
203
|
+
// ============================================================================
|
|
204
|
+
/**
|
|
205
|
+
* Update memory state
|
|
206
|
+
*/
|
|
207
|
+
updateMemory(memory) {
|
|
208
|
+
this.state.memory = { ...this.state.memory, ...memory };
|
|
209
|
+
this.dirty = true;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Add episodic memory
|
|
213
|
+
*/
|
|
214
|
+
addEpisode(episode) {
|
|
215
|
+
this.state.memory.episodic.push(episode);
|
|
216
|
+
this.state.memory.stats.totalEpisodes++;
|
|
217
|
+
this.dirty = true;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Add semantic memory
|
|
221
|
+
*/
|
|
222
|
+
addFact(fact) {
|
|
223
|
+
this.state.memory.semantic.push(fact);
|
|
224
|
+
this.state.memory.stats.totalFacts++;
|
|
225
|
+
this.dirty = true;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Add procedural memory
|
|
229
|
+
*/
|
|
230
|
+
addSkill(skill) {
|
|
231
|
+
this.state.memory.procedural.push(skill);
|
|
232
|
+
this.state.memory.stats.totalSkills++;
|
|
233
|
+
this.dirty = true;
|
|
234
|
+
}
|
|
235
|
+
// ============================================================================
|
|
236
|
+
// Conversation Operations
|
|
237
|
+
// ============================================================================
|
|
238
|
+
/**
|
|
239
|
+
* Update conversation history
|
|
240
|
+
*/
|
|
241
|
+
updateConversation(messages, tokens) {
|
|
242
|
+
this.state.conversation.history = messages;
|
|
243
|
+
this.state.conversation.totalMessages = messages.length;
|
|
244
|
+
if (tokens) {
|
|
245
|
+
this.state.conversation.totalTokens += tokens;
|
|
246
|
+
}
|
|
247
|
+
this.dirty = true;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Add message to conversation
|
|
251
|
+
*/
|
|
252
|
+
addMessage(message) {
|
|
253
|
+
this.state.conversation.history.push(message);
|
|
254
|
+
this.state.conversation.totalMessages++;
|
|
255
|
+
this.dirty = true;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Clear conversation history
|
|
259
|
+
*/
|
|
260
|
+
clearConversation() {
|
|
261
|
+
this.state.conversation.history = [];
|
|
262
|
+
this.dirty = true;
|
|
263
|
+
}
|
|
264
|
+
// ============================================================================
|
|
265
|
+
// Session Operations
|
|
266
|
+
// ============================================================================
|
|
267
|
+
/**
|
|
268
|
+
* Record an interaction
|
|
269
|
+
*/
|
|
270
|
+
recordInteraction() {
|
|
271
|
+
this.state.session.interactions++;
|
|
272
|
+
this.state.session.lastActivity = new Date();
|
|
273
|
+
this.dirty = true;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Start new session
|
|
277
|
+
*/
|
|
278
|
+
newSession() {
|
|
279
|
+
this.state.session = {
|
|
280
|
+
id: crypto.randomUUID(),
|
|
281
|
+
startTime: new Date(),
|
|
282
|
+
interactions: 0,
|
|
283
|
+
lastActivity: new Date(),
|
|
284
|
+
};
|
|
285
|
+
this.dirty = true;
|
|
286
|
+
}
|
|
287
|
+
// ============================================================================
|
|
288
|
+
// Backup Operations
|
|
289
|
+
// ============================================================================
|
|
290
|
+
/**
|
|
291
|
+
* Create backup of current state
|
|
292
|
+
*/
|
|
293
|
+
async backup() {
|
|
294
|
+
if (!fs.existsSync(this.statePath)) {
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
298
|
+
const backupPath = path.join(this.backupDir, `state-${timestamp}.json`);
|
|
299
|
+
await fs.promises.copyFile(this.statePath, backupPath);
|
|
300
|
+
// Clean old backups
|
|
301
|
+
await this.cleanOldBackups();
|
|
302
|
+
return backupPath;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Create backup synchronously
|
|
306
|
+
*/
|
|
307
|
+
backupSync() {
|
|
308
|
+
if (!fs.existsSync(this.statePath)) {
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
312
|
+
const backupPath = path.join(this.backupDir, `state-${timestamp}.json`);
|
|
313
|
+
fs.copyFileSync(this.statePath, backupPath);
|
|
314
|
+
this.cleanOldBackupsSync();
|
|
315
|
+
return backupPath;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* List available backups
|
|
319
|
+
*/
|
|
320
|
+
listBackups() {
|
|
321
|
+
if (!fs.existsSync(this.backupDir)) {
|
|
322
|
+
return [];
|
|
323
|
+
}
|
|
324
|
+
return fs.readdirSync(this.backupDir)
|
|
325
|
+
.filter(f => f.endsWith('.json'))
|
|
326
|
+
.sort()
|
|
327
|
+
.reverse();
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Restore from backup
|
|
331
|
+
*/
|
|
332
|
+
async restoreBackup(backupName) {
|
|
333
|
+
const backupPath = path.join(this.backupDir, backupName);
|
|
334
|
+
if (!fs.existsSync(backupPath)) {
|
|
335
|
+
throw new Error(`Backup not found: ${backupName}`);
|
|
336
|
+
}
|
|
337
|
+
// Backup current state first
|
|
338
|
+
await this.backup();
|
|
339
|
+
// Copy backup to state file
|
|
340
|
+
await fs.promises.copyFile(backupPath, this.statePath);
|
|
341
|
+
// Reload
|
|
342
|
+
await this.load();
|
|
343
|
+
}
|
|
344
|
+
// ============================================================================
|
|
345
|
+
// Export/Import
|
|
346
|
+
// ============================================================================
|
|
347
|
+
/**
|
|
348
|
+
* Export state to a portable format
|
|
349
|
+
*/
|
|
350
|
+
async export(outputPath) {
|
|
351
|
+
const exportData = {
|
|
352
|
+
...this.state,
|
|
353
|
+
exportedAt: new Date(),
|
|
354
|
+
exportVersion: '1.0',
|
|
355
|
+
};
|
|
356
|
+
const json = JSON.stringify(exportData, null, 2);
|
|
357
|
+
await fs.promises.writeFile(outputPath, json, 'utf-8');
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Import state from file
|
|
361
|
+
*/
|
|
362
|
+
async import(inputPath, merge = false) {
|
|
363
|
+
const json = await fs.promises.readFile(inputPath, 'utf-8');
|
|
364
|
+
const imported = JSON.parse(json);
|
|
365
|
+
if (merge) {
|
|
366
|
+
// Merge with current state
|
|
367
|
+
this.state = this.mergeState(this.state, imported);
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
// Replace state
|
|
371
|
+
this.state = this.mergeState(this.createEmptyState(), imported);
|
|
372
|
+
}
|
|
373
|
+
this.dirty = true;
|
|
374
|
+
await this.save();
|
|
375
|
+
}
|
|
376
|
+
// ============================================================================
|
|
377
|
+
// Auto-Save
|
|
378
|
+
// ============================================================================
|
|
379
|
+
/**
|
|
380
|
+
* Start auto-save timer
|
|
381
|
+
*/
|
|
382
|
+
startAutoSave(intervalMs) {
|
|
383
|
+
this.stopAutoSave();
|
|
384
|
+
this.autoSaveTimer = setInterval(async () => {
|
|
385
|
+
if (this.dirty) {
|
|
386
|
+
try {
|
|
387
|
+
await this.save();
|
|
388
|
+
}
|
|
389
|
+
catch (error) {
|
|
390
|
+
console.error('[StateStore] Auto-save failed:', error);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}, intervalMs);
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Stop auto-save timer
|
|
397
|
+
*/
|
|
398
|
+
stopAutoSave() {
|
|
399
|
+
if (this.autoSaveTimer) {
|
|
400
|
+
clearInterval(this.autoSaveTimer);
|
|
401
|
+
this.autoSaveTimer = null;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
// ============================================================================
|
|
405
|
+
// Cleanup
|
|
406
|
+
// ============================================================================
|
|
407
|
+
/**
|
|
408
|
+
* Reset state to empty
|
|
409
|
+
*/
|
|
410
|
+
reset() {
|
|
411
|
+
this.state = this.createEmptyState();
|
|
412
|
+
this.dirty = true;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Delete all persisted data
|
|
416
|
+
*/
|
|
417
|
+
async purge() {
|
|
418
|
+
this.stopAutoSave();
|
|
419
|
+
if (fs.existsSync(this.statePath)) {
|
|
420
|
+
await fs.promises.unlink(this.statePath);
|
|
421
|
+
}
|
|
422
|
+
if (fs.existsSync(this.backupDir)) {
|
|
423
|
+
const files = await fs.promises.readdir(this.backupDir);
|
|
424
|
+
for (const file of files) {
|
|
425
|
+
await fs.promises.unlink(path.join(this.backupDir, file));
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
this.reset();
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Close and cleanup
|
|
432
|
+
*/
|
|
433
|
+
close() {
|
|
434
|
+
this.stopAutoSave();
|
|
435
|
+
if (this.dirty) {
|
|
436
|
+
this.saveSync();
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
// ============================================================================
|
|
440
|
+
// Helpers
|
|
441
|
+
// ============================================================================
|
|
442
|
+
createEmptyState() {
|
|
443
|
+
return {
|
|
444
|
+
version: '6.0',
|
|
445
|
+
created: new Date(),
|
|
446
|
+
lastModified: new Date(),
|
|
447
|
+
checksum: '',
|
|
448
|
+
memory: {
|
|
449
|
+
episodic: [],
|
|
450
|
+
semantic: [],
|
|
451
|
+
procedural: [],
|
|
452
|
+
stats: {
|
|
453
|
+
totalEpisodes: 0,
|
|
454
|
+
totalFacts: 0,
|
|
455
|
+
totalSkills: 0,
|
|
456
|
+
},
|
|
457
|
+
},
|
|
458
|
+
conversation: {
|
|
459
|
+
history: [],
|
|
460
|
+
totalMessages: 0,
|
|
461
|
+
totalTokens: 0,
|
|
462
|
+
},
|
|
463
|
+
session: {
|
|
464
|
+
id: crypto.randomUUID(),
|
|
465
|
+
startTime: new Date(),
|
|
466
|
+
interactions: 0,
|
|
467
|
+
lastActivity: new Date(),
|
|
468
|
+
},
|
|
469
|
+
config: {
|
|
470
|
+
llm: {},
|
|
471
|
+
persistence: {},
|
|
472
|
+
},
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
mergeState(base, updates) {
|
|
476
|
+
return {
|
|
477
|
+
...base,
|
|
478
|
+
...updates,
|
|
479
|
+
memory: {
|
|
480
|
+
...base.memory,
|
|
481
|
+
...(updates.memory || {}),
|
|
482
|
+
stats: {
|
|
483
|
+
...base.memory.stats,
|
|
484
|
+
...(updates.memory?.stats || {}),
|
|
485
|
+
},
|
|
486
|
+
},
|
|
487
|
+
conversation: {
|
|
488
|
+
...base.conversation,
|
|
489
|
+
...(updates.conversation || {}),
|
|
490
|
+
},
|
|
491
|
+
session: {
|
|
492
|
+
...base.session,
|
|
493
|
+
...(updates.session || {}),
|
|
494
|
+
},
|
|
495
|
+
config: {
|
|
496
|
+
...base.config,
|
|
497
|
+
...(updates.config || {}),
|
|
498
|
+
llm: {
|
|
499
|
+
...base.config.llm,
|
|
500
|
+
...(updates.config?.llm || {}),
|
|
501
|
+
},
|
|
502
|
+
persistence: {
|
|
503
|
+
...base.config.persistence,
|
|
504
|
+
...(updates.config?.persistence || {}),
|
|
505
|
+
},
|
|
506
|
+
},
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
calculateChecksum(state) {
|
|
510
|
+
const data = JSON.stringify({
|
|
511
|
+
memory: state.memory,
|
|
512
|
+
conversation: state.conversation,
|
|
513
|
+
});
|
|
514
|
+
return crypto.createHash('sha256').update(data).digest('hex').slice(0, 16);
|
|
515
|
+
}
|
|
516
|
+
ensureDirectories() {
|
|
517
|
+
if (!fs.existsSync(this.dataDir)) {
|
|
518
|
+
fs.mkdirSync(this.dataDir, { recursive: true });
|
|
519
|
+
}
|
|
520
|
+
if (!fs.existsSync(this.backupDir)) {
|
|
521
|
+
fs.mkdirSync(this.backupDir, { recursive: true });
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
async cleanOldBackups() {
|
|
525
|
+
const maxBackups = this.options.maxBackups || 10;
|
|
526
|
+
const backups = this.listBackups();
|
|
527
|
+
if (backups.length > maxBackups) {
|
|
528
|
+
const toDelete = backups.slice(maxBackups);
|
|
529
|
+
for (const backup of toDelete) {
|
|
530
|
+
await fs.promises.unlink(path.join(this.backupDir, backup));
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
cleanOldBackupsSync() {
|
|
535
|
+
const maxBackups = this.options.maxBackups || 10;
|
|
536
|
+
const backups = this.listBackups();
|
|
537
|
+
if (backups.length > maxBackups) {
|
|
538
|
+
const toDelete = backups.slice(maxBackups);
|
|
539
|
+
for (const backup of toDelete) {
|
|
540
|
+
fs.unlinkSync(path.join(this.backupDir, backup));
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Get data directory path
|
|
546
|
+
*/
|
|
547
|
+
getDataDir() {
|
|
548
|
+
return this.dataDir;
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Get stats
|
|
552
|
+
*/
|
|
553
|
+
stats() {
|
|
554
|
+
const stateExists = fs.existsSync(this.statePath);
|
|
555
|
+
const stateSize = stateExists ? fs.statSync(this.statePath).size : 0;
|
|
556
|
+
return {
|
|
557
|
+
dataDir: this.dataDir,
|
|
558
|
+
stateExists,
|
|
559
|
+
stateSize,
|
|
560
|
+
backupCount: this.listBackups().length,
|
|
561
|
+
isDirty: this.dirty,
|
|
562
|
+
lastModified: this.state.lastModified,
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
exports.StateStore = StateStore;
|
|
567
|
+
// ============================================================================
|
|
568
|
+
// Factory & Singleton
|
|
569
|
+
// ============================================================================
|
|
570
|
+
let stateStoreInstance = null;
|
|
571
|
+
function createStateStore(options) {
|
|
572
|
+
return new StateStore(options);
|
|
573
|
+
}
|
|
574
|
+
function getStateStore(options) {
|
|
575
|
+
if (!stateStoreInstance) {
|
|
576
|
+
stateStoreInstance = createStateStore(options);
|
|
577
|
+
}
|
|
578
|
+
return stateStoreInstance;
|
|
579
|
+
}
|
|
580
|
+
function resetStateStore() {
|
|
581
|
+
if (stateStoreInstance) {
|
|
582
|
+
stateStoreInstance.close();
|
|
583
|
+
}
|
|
584
|
+
stateStoreInstance = null;
|
|
585
|
+
}
|
|
586
|
+
const SESSIONS_DIR = 'sessions';
|
|
587
|
+
class SessionManager {
|
|
588
|
+
dataDir;
|
|
589
|
+
sessionsDir;
|
|
590
|
+
options;
|
|
591
|
+
constructor(options = {}) {
|
|
592
|
+
this.options = options;
|
|
593
|
+
this.dataDir = options.dataDir || path.join(process.env.HOME || '.', '.genesis');
|
|
594
|
+
this.sessionsDir = path.join(this.dataDir, SESSIONS_DIR);
|
|
595
|
+
this.ensureDirectory();
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* List all saved sessions
|
|
599
|
+
*/
|
|
600
|
+
listSessions() {
|
|
601
|
+
if (!fs.existsSync(this.sessionsDir)) {
|
|
602
|
+
return [];
|
|
603
|
+
}
|
|
604
|
+
const files = fs.readdirSync(this.sessionsDir)
|
|
605
|
+
.filter(f => f.endsWith('.json'))
|
|
606
|
+
.sort()
|
|
607
|
+
.reverse(); // Most recent first
|
|
608
|
+
const sessions = [];
|
|
609
|
+
for (const file of files) {
|
|
610
|
+
try {
|
|
611
|
+
const filePath = path.join(this.sessionsDir, file);
|
|
612
|
+
const data = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
613
|
+
// Extract first user message as summary
|
|
614
|
+
const firstUserMsg = data.conversation?.history?.find(m => m.role === 'user');
|
|
615
|
+
const summary = firstUserMsg?.content?.slice(0, 80);
|
|
616
|
+
sessions.push({
|
|
617
|
+
id: data.session?.id || file.replace('.json', ''),
|
|
618
|
+
name: data.sessionName, // Optional session name
|
|
619
|
+
created: new Date(data.created),
|
|
620
|
+
lastModified: new Date(data.lastModified),
|
|
621
|
+
messageCount: data.conversation?.totalMessages || 0,
|
|
622
|
+
tokenCount: data.conversation?.totalTokens || 0,
|
|
623
|
+
summary,
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
catch {
|
|
627
|
+
// Skip invalid files
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
return sessions;
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Get the most recent session ID
|
|
634
|
+
*/
|
|
635
|
+
getLastSessionId() {
|
|
636
|
+
const sessions = this.listSessions();
|
|
637
|
+
return sessions.length > 0 ? sessions[0].id : null;
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Save current state as a session
|
|
641
|
+
*/
|
|
642
|
+
saveSession(state, name) {
|
|
643
|
+
const sessionId = state.session.id;
|
|
644
|
+
const sessionPath = this.getSessionPath(sessionId);
|
|
645
|
+
const sessionData = {
|
|
646
|
+
...state,
|
|
647
|
+
sessionName: name,
|
|
648
|
+
};
|
|
649
|
+
fs.writeFileSync(sessionPath, JSON.stringify(sessionData, null, 2));
|
|
650
|
+
// Cleanup old sessions if needed
|
|
651
|
+
this.cleanupOldSessions();
|
|
652
|
+
return sessionId;
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Load a session by ID (or 'last' for most recent)
|
|
656
|
+
*/
|
|
657
|
+
loadSession(sessionIdOrLast) {
|
|
658
|
+
let sessionId = sessionIdOrLast;
|
|
659
|
+
// Handle 'last' keyword
|
|
660
|
+
if (sessionIdOrLast === 'last') {
|
|
661
|
+
const lastId = this.getLastSessionId();
|
|
662
|
+
if (!lastId)
|
|
663
|
+
return null;
|
|
664
|
+
sessionId = lastId;
|
|
665
|
+
}
|
|
666
|
+
// Try exact match first
|
|
667
|
+
let sessionPath = this.getSessionPath(sessionId);
|
|
668
|
+
// If not found, try partial match (first 8 chars like git)
|
|
669
|
+
if (!fs.existsSync(sessionPath)) {
|
|
670
|
+
const sessions = this.listSessions();
|
|
671
|
+
const match = sessions.find(s => s.id.startsWith(sessionId));
|
|
672
|
+
if (match) {
|
|
673
|
+
sessionPath = this.getSessionPath(match.id);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
if (!fs.existsSync(sessionPath)) {
|
|
677
|
+
return null;
|
|
678
|
+
}
|
|
679
|
+
try {
|
|
680
|
+
const data = JSON.parse(fs.readFileSync(sessionPath, 'utf-8'));
|
|
681
|
+
return data;
|
|
682
|
+
}
|
|
683
|
+
catch {
|
|
684
|
+
return null;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Fork a session (create a copy with new ID)
|
|
689
|
+
*/
|
|
690
|
+
forkSession(sessionId, newName) {
|
|
691
|
+
const originalState = this.loadSession(sessionId);
|
|
692
|
+
if (!originalState)
|
|
693
|
+
return null;
|
|
694
|
+
// Create new session with new ID
|
|
695
|
+
const forkedState = {
|
|
696
|
+
...originalState,
|
|
697
|
+
session: {
|
|
698
|
+
...originalState.session,
|
|
699
|
+
id: crypto.randomUUID(),
|
|
700
|
+
startTime: new Date(),
|
|
701
|
+
},
|
|
702
|
+
created: new Date(),
|
|
703
|
+
lastModified: new Date(),
|
|
704
|
+
};
|
|
705
|
+
return this.saveSession(forkedState, newName || `Fork of ${sessionId.slice(0, 8)}`);
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Delete a session
|
|
709
|
+
*/
|
|
710
|
+
deleteSession(sessionId) {
|
|
711
|
+
const sessionPath = this.getSessionPath(sessionId);
|
|
712
|
+
if (!fs.existsSync(sessionPath)) {
|
|
713
|
+
// Try partial match
|
|
714
|
+
const sessions = this.listSessions();
|
|
715
|
+
const match = sessions.find(s => s.id.startsWith(sessionId));
|
|
716
|
+
if (match) {
|
|
717
|
+
fs.unlinkSync(this.getSessionPath(match.id));
|
|
718
|
+
return true;
|
|
719
|
+
}
|
|
720
|
+
return false;
|
|
721
|
+
}
|
|
722
|
+
fs.unlinkSync(sessionPath);
|
|
723
|
+
return true;
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Rename a session
|
|
727
|
+
*/
|
|
728
|
+
renameSession(sessionId, newName) {
|
|
729
|
+
const state = this.loadSession(sessionId);
|
|
730
|
+
if (!state)
|
|
731
|
+
return false;
|
|
732
|
+
this.saveSession(state, newName);
|
|
733
|
+
return true;
|
|
734
|
+
}
|
|
735
|
+
getSessionPath(sessionId) {
|
|
736
|
+
return path.join(this.sessionsDir, `${sessionId}.json`);
|
|
737
|
+
}
|
|
738
|
+
ensureDirectory() {
|
|
739
|
+
if (!fs.existsSync(this.sessionsDir)) {
|
|
740
|
+
fs.mkdirSync(this.sessionsDir, { recursive: true });
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
cleanupOldSessions() {
|
|
744
|
+
const maxSessions = this.options.maxSessions || 50;
|
|
745
|
+
const sessions = this.listSessions();
|
|
746
|
+
if (sessions.length > maxSessions) {
|
|
747
|
+
// Delete oldest sessions
|
|
748
|
+
const toDelete = sessions.slice(maxSessions);
|
|
749
|
+
for (const session of toDelete) {
|
|
750
|
+
this.deleteSession(session.id);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
exports.SessionManager = SessionManager;
|
|
756
|
+
// Singleton
|
|
757
|
+
let sessionManagerInstance = null;
|
|
758
|
+
function getSessionManager(options) {
|
|
759
|
+
if (!sessionManagerInstance) {
|
|
760
|
+
sessionManagerInstance = new SessionManager(options);
|
|
761
|
+
}
|
|
762
|
+
return sessionManagerInstance;
|
|
763
|
+
}
|