claude-flow-novice 2.0.3 → 2.0.4
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/dist/src/cli/commands/guidance.js +487 -668
- package/dist/src/cli/commands/index-validate.js +18 -29
- package/dist/src/cli/commands/mcp-troubleshoot.js +230 -282
- package/dist/src/cli/commands/neural-goal-init.js +92 -125
- package/dist/src/cli/commands/swarm-exec.js +317 -393
- package/dist/src/cli/commands/swarm.js +1 -1
- package/dist/src/cli/commands/validate-framework.js +983 -1100
- package/dist/src/cli/commands/validate.js +144 -223
- package/dist/src/cli/simple-commands/__tests__/agent.test.js +265 -277
- package/dist/src/cli/simple-commands/__tests__/memory.test.js +6 -7
- package/dist/src/cli/simple-commands/__tests__/swarm.test.js +373 -356
- package/dist/src/cli/simple-commands/__tests__/task.test.js +6 -7
- package/dist/src/cli/simple-commands/agent.js +157 -193
- package/dist/src/cli/simple-commands/analysis.js +336 -446
- package/dist/src/cli/simple-commands/automation-executor.js +1095 -1339
- package/dist/src/cli/simple-commands/automation.js +481 -469
- package/dist/src/cli/simple-commands/batch-manager.js +261 -313
- package/dist/src/cli/simple-commands/claude-telemetry.js +241 -267
- package/dist/src/cli/simple-commands/claude-track.js +68 -90
- package/dist/src/cli/simple-commands/concurrent-display.js +266 -320
- package/dist/src/cli/simple-commands/config.js +245 -290
- package/dist/src/cli/simple-commands/coordination.js +182 -234
- package/dist/src/cli/simple-commands/enhanced-ui-views.js +812 -615
- package/dist/src/cli/simple-commands/enhanced-webui-complete.js +922 -981
- package/dist/src/cli/simple-commands/fix-hook-variables.js +274 -294
- package/dist/src/cli/simple-commands/github/gh-coordinator.js +378 -457
- package/dist/src/cli/simple-commands/github/github-api.js +535 -574
- package/dist/src/cli/simple-commands/github/init.js +276 -303
- package/dist/src/cli/simple-commands/github.js +222 -247
- package/dist/src/cli/simple-commands/goal.js +51 -63
- package/dist/src/cli/simple-commands/hive-mind/auto-save-middleware.js +208 -278
- package/dist/src/cli/simple-commands/hive-mind/communication.js +601 -696
- package/dist/src/cli/simple-commands/hive-mind/core.js +907 -979
- package/dist/src/cli/simple-commands/hive-mind/db-optimizer.js +406 -655
- package/dist/src/cli/simple-commands/hive-mind/mcp-wrapper.js +1125 -1245
- package/dist/src/cli/simple-commands/hive-mind/memory.js +854 -1090
- package/dist/src/cli/simple-commands/hive-mind/performance-optimizer.js +459 -574
- package/dist/src/cli/simple-commands/hive-mind/performance-test.js +263 -347
- package/dist/src/cli/simple-commands/hive-mind/queen.js +727 -768
- package/dist/src/cli/simple-commands/hive-mind/session-manager.js +745 -1049
- package/dist/src/cli/simple-commands/hive-mind-optimize.js +227 -283
- package/dist/src/cli/simple-commands/hive-mind-wizard.js +174 -217
- package/dist/src/cli/simple-commands/hive-mind.js +1842 -2283
- package/dist/src/cli/simple-commands/hive.js +90 -79
- package/dist/src/cli/simple-commands/hook-safety.js +431 -521
- package/dist/src/cli/simple-commands/hooks/session-start-soul.js +203 -254
- package/dist/src/cli/simple-commands/hooks.js +1064 -1204
- package/dist/src/cli/simple-commands/init/agent-copier.js +294 -319
- package/dist/src/cli/simple-commands/init/batch-init.js +496 -562
- package/dist/src/cli/simple-commands/init/claude-commands/claude-flow-commands.js +13 -19
- package/dist/src/cli/simple-commands/init/claude-commands/optimized-claude-flow-commands.js +13 -19
- package/dist/src/cli/simple-commands/init/claude-commands/optimized-slash-commands.js +61 -88
- package/dist/src/cli/simple-commands/init/claude-commands/optimized-sparc-commands.js +125 -150
- package/dist/src/cli/simple-commands/init/claude-commands/slash-commands.js +42 -49
- package/dist/src/cli/simple-commands/init/claude-commands/sparc-commands.js +43 -61
- package/dist/src/cli/simple-commands/init/copy-revised-templates.js +141 -147
- package/dist/src/cli/simple-commands/init/executable-wrapper.js +31 -44
- package/dist/src/cli/simple-commands/init/gitignore-updater.js +64 -90
- package/dist/src/cli/simple-commands/init/help.js +104 -107
- package/dist/src/cli/simple-commands/init/hive-mind-init.js +509 -528
- package/dist/src/cli/simple-commands/init/index.js +1510 -1759
- package/dist/src/cli/simple-commands/init/performance-monitor.js +234 -317
- package/dist/src/cli/simple-commands/init/rollback/backup-manager.js +441 -504
- package/dist/src/cli/simple-commands/init/rollback/index.js +289 -364
- package/dist/src/cli/simple-commands/init/rollback/recovery-manager.js +652 -728
- package/dist/src/cli/simple-commands/init/rollback/rollback-executor.js +416 -481
- package/dist/src/cli/simple-commands/init/rollback/state-tracker.js +369 -448
- package/dist/src/cli/simple-commands/init/sparc/roo-readme.js +1 -2
- package/dist/src/cli/simple-commands/init/sparc/roomodes-config.js +122 -99
- package/dist/src/cli/simple-commands/init/sparc/workflows.js +32 -37
- package/dist/src/cli/simple-commands/init/sparc-structure.js +55 -62
- package/dist/src/cli/simple-commands/init/template-copier.js +421 -533
- package/dist/src/cli/simple-commands/init/templates/coordination-md.js +3 -6
- package/dist/src/cli/simple-commands/init/templates/enhanced-templates.js +344 -318
- package/dist/src/cli/simple-commands/init/templates/github-safe-enhanced.js +173 -218
- package/dist/src/cli/simple-commands/init/templates/github-safe.js +65 -75
- package/dist/src/cli/simple-commands/init/templates/memory-bank-md.js +3 -6
- package/dist/src/cli/simple-commands/init/templates/readme-files.js +2 -4
- package/dist/src/cli/simple-commands/init/templates/safe-hook-patterns.js +187 -230
- package/dist/src/cli/simple-commands/init/templates/sparc-modes.js +53 -80
- package/dist/src/cli/simple-commands/init/templates/verification-claude-md.js +101 -85
- package/dist/src/cli/simple-commands/init/validation/config-validator.js +283 -330
- package/dist/src/cli/simple-commands/init/validation/health-checker.js +495 -561
- package/dist/src/cli/simple-commands/init/validation/index.js +302 -358
- package/dist/src/cli/simple-commands/init/validation/mode-validator.js +308 -359
- package/dist/src/cli/simple-commands/init/validation/post-init-validator.js +389 -366
- package/dist/src/cli/simple-commands/init/validation/pre-init-validator.js +270 -268
- package/dist/src/cli/simple-commands/init/validation/test-runner.js +427 -447
- package/dist/src/cli/simple-commands/init.js +1 -2
- package/dist/src/cli/simple-commands/mcp-health.js +131 -158
- package/dist/src/cli/simple-commands/mcp-integration-layer.js +533 -634
- package/dist/src/cli/simple-commands/mcp.js +345 -400
- package/dist/src/cli/simple-commands/memory-consolidation.js +426 -537
- package/dist/src/cli/simple-commands/memory.js +247 -311
- package/dist/src/cli/simple-commands/migrate-hooks.js +39 -46
- package/dist/src/cli/simple-commands/monitor.js +294 -363
- package/dist/src/cli/simple-commands/neural.js +51 -65
- package/dist/src/cli/simple-commands/pair-autofix-only.js +538 -662
- package/dist/src/cli/simple-commands/pair-basic.js +528 -656
- package/dist/src/cli/simple-commands/pair-old.js +430 -543
- package/dist/src/cli/simple-commands/pair-working.js +615 -751
- package/dist/src/cli/simple-commands/pair.js +615 -751
- package/dist/src/cli/simple-commands/performance-hooks.js +83 -111
- package/dist/src/cli/simple-commands/performance-metrics.js +348 -433
- package/dist/src/cli/simple-commands/process-ui-enhanced.js +708 -787
- package/dist/src/cli/simple-commands/process-ui.js +230 -254
- package/dist/src/cli/simple-commands/realtime-update-system.js +525 -611
- package/dist/src/cli/simple-commands/sparc/architecture.js +1704 -1530
- package/dist/src/cli/simple-commands/sparc/commands.js +438 -516
- package/dist/src/cli/simple-commands/sparc/completion.js +1224 -1481
- package/dist/src/cli/simple-commands/sparc/coordinator.js +913 -978
- package/dist/src/cli/simple-commands/sparc/index.js +241 -298
- package/dist/src/cli/simple-commands/sparc/phase-base.js +314 -390
- package/dist/src/cli/simple-commands/sparc/pseudocode.js +965 -869
- package/dist/src/cli/simple-commands/sparc/refinement.js +980 -1273
- package/dist/src/cli/simple-commands/sparc/specification.js +559 -645
- package/dist/src/cli/simple-commands/sparc-modes/architect.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/ask.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/code.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/debug.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/devops.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/docs-writer.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/generic.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/index.js +47 -55
- package/dist/src/cli/simple-commands/sparc-modes/integration.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/mcp.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/monitoring.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/optimization.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/security-review.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/sparc-orchestrator.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/spec-pseudocode.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/supabase-admin.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/swarm.js +101 -87
- package/dist/src/cli/simple-commands/sparc-modes/tdd.js +1 -1
- package/dist/src/cli/simple-commands/sparc-modes/tutorial.js +1 -1
- package/dist/src/cli/simple-commands/sparc.js +465 -493
- package/dist/src/cli/simple-commands/start-ui.js +108 -132
- package/dist/src/cli/simple-commands/start-wrapper.js +240 -268
- package/dist/src/cli/simple-commands/start.js +1 -1
- package/dist/src/cli/simple-commands/status.js +254 -275
- package/dist/src/cli/simple-commands/stream-chain-clean.js +128 -171
- package/dist/src/cli/simple-commands/stream-chain-fixed.js +61 -82
- package/dist/src/cli/simple-commands/stream-chain-real.js +267 -331
- package/dist/src/cli/simple-commands/stream-chain-working.js +211 -263
- package/dist/src/cli/simple-commands/stream-chain.js +260 -318
- package/dist/src/cli/simple-commands/stream-processor.js +290 -315
- package/dist/src/cli/simple-commands/swarm-executor.js +189 -222
- package/dist/src/cli/simple-commands/swarm-metrics-integration.js +208 -300
- package/dist/src/cli/simple-commands/swarm-ui.js +623 -703
- package/dist/src/cli/simple-commands/swarm-webui-integration.js +258 -286
- package/dist/src/cli/simple-commands/swarm.js +887 -1082
- package/dist/src/cli/simple-commands/task.js +161 -206
- package/dist/src/cli/simple-commands/timestamp-fix.js +59 -89
- package/dist/src/cli/simple-commands/token-tracker.js +258 -316
- package/dist/src/cli/simple-commands/tool-execution-framework.js +433 -519
- package/dist/src/cli/simple-commands/train-and-stream.js +275 -331
- package/dist/src/cli/simple-commands/training-pipeline.js +619 -725
- package/dist/src/cli/simple-commands/training.js +170 -227
- package/dist/src/cli/simple-commands/verification-hooks.js +261 -284
- package/dist/src/cli/simple-commands/verification-integration.js +389 -417
- package/dist/src/cli/simple-commands/verification-training-integration.js +486 -606
- package/dist/src/cli/simple-commands/verification.js +493 -513
- package/dist/src/cli/simple-commands/web-server.js +766 -836
- package/dist/src/cli/simple-commands/webui-validator.js +106 -124
- package/dist/src/coordination/event-bus/demo-wasm-integration.js +212 -251
- package/dist/src/coordination/event-bus/qe-event-bus.js +608 -748
- package/dist/src/coordination/event-bus/qe-event-bus.test.js +379 -454
- package/dist/src/coordination/iteration-tracker.js +363 -454
- package/dist/src/enterprise/analytics-manager.js +1135 -0
- package/dist/src/enterprise/audit-manager.js +1115 -0
- package/dist/src/enterprise/cloud-manager.js +891 -0
- package/dist/src/enterprise/deployment-manager.js +966 -0
- package/dist/src/enterprise/index.js +6 -0
- package/dist/src/enterprise/project-manager.js +584 -0
- package/dist/src/enterprise/security-manager.js +991 -0
- package/dist/src/index.js +1 -1
- package/dist/src/mcp/DEPRECATED.js +46 -60
- package/dist/src/mcp/fixes/mcp-error-fixes.js +115 -134
- package/dist/src/mcp/implementations/agent-tracker.js +114 -128
- package/dist/src/mcp/implementations/daa-tools.js +292 -350
- package/dist/src/mcp/implementations/workflow-tools.js +329 -361
- package/dist/src/mcp/mcp-config-manager.js +1183 -1331
- package/dist/src/mcp/mcp-server-novice-simplified.js +11 -17
- package/dist/src/mcp/mcp-server-novice.js +11 -17
- package/dist/src/mcp/mcp-server-sdk.js +11 -17
- package/dist/src/mcp/mcp-server.js +1620 -1484
- package/dist/src/mcp/ruv-swarm-wrapper.js +209 -239
- package/dist/src/memory/advanced-serializer.js +609 -589
- package/dist/src/memory/enhanced-examples.js +220 -305
- package/dist/src/memory/enhanced-memory.js +295 -336
- package/dist/src/memory/enhanced-session-serializer.js +408 -492
- package/dist/src/memory/fallback-memory-system.js +900 -1021
- package/dist/src/memory/fallback-store.js +93 -131
- package/dist/src/memory/high-performance-serialization.js +592 -730
- package/dist/src/memory/in-memory-store.js +161 -213
- package/dist/src/memory/index.js +123 -157
- package/dist/src/memory/lock-free-structures.js +578 -764
- package/dist/src/memory/memory-mapped-persistence.js +585 -766
- package/dist/src/memory/memory-pressure-manager.js +569 -707
- package/dist/src/memory/migration.js +358 -445
- package/dist/src/memory/shared-memory.js +641 -768
- package/dist/src/memory/sqlite-store.js +245 -325
- package/dist/src/memory/sqlite-wrapper.js +122 -151
- package/dist/src/memory/swarm-memory.js +470 -603
- package/dist/src/memory/test-example.js +126 -134
- package/dist/src/memory/ultra-fast-memory-store.js +622 -821
- package/dist/src/memory/unified-memory-manager.js +356 -437
- package/dist/src/migration/index.js +92 -0
- package/dist/src/migration/logger.js +121 -0
- package/dist/src/migration/migration-analyzer.js +268 -0
- package/dist/src/migration/migration-runner.js +522 -0
- package/dist/src/migration/migration-validator.js +285 -0
- package/dist/src/migration/progress-reporter.js +150 -0
- package/dist/src/migration/rollback-manager.js +321 -0
- package/dist/src/migration/tests/migration-system.test.js +7 -0
- package/dist/src/migration/types.js +3 -0
- package/dist/src/swarm/CodeRefactoringSwarm.js +777 -952
- package/dist/src/swarm/__tests__/integration.test.js +227 -0
- package/dist/src/swarm/__tests__/prompt-copier.test.js +344 -0
- package/dist/src/swarm/advanced-orchestrator.js +1095 -0
- package/dist/src/swarm/claude-code-interface.js +961 -0
- package/dist/src/swarm/claude-flow-executor.js +229 -0
- package/dist/src/swarm/consensus-coordinator.js +475 -0
- package/dist/src/swarm/coordinator.js +2993 -0
- package/dist/src/swarm/direct-executor.js +1180 -0
- package/dist/src/swarm/error-recovery/advanced-error-detection.js +691 -0
- package/dist/src/swarm/error-recovery/automated-recovery-workflows.js +998 -0
- package/dist/src/swarm/error-recovery/error-recovery-coordinator.js +1197 -0
- package/dist/src/swarm/error-recovery/recovery-monitoring.js +772 -0
- package/dist/src/swarm/error-recovery/resilience-architecture.js +714 -0
- package/dist/src/swarm/error-recovery/self-healing-mechanisms.js +1319 -0
- package/dist/src/swarm/error-recovery/test-error-recovery-effectiveness.js +808 -0
- package/dist/src/swarm/executor-v2.js +322 -0
- package/dist/src/swarm/executor.js +815 -0
- package/dist/src/swarm/hive-mind-integration.js +703 -0
- package/dist/src/swarm/index.js +41 -0
- package/dist/src/swarm/json-output-aggregator.js +267 -0
- package/dist/src/swarm/large-scale-coordinator.js +542 -0
- package/dist/src/swarm/mcp-integration-wrapper.js +628 -0
- package/dist/src/swarm/memory.js +1117 -0
- package/dist/src/swarm/optimizations/__tests__/optimization.test.js +348 -0
- package/dist/src/swarm/optimizations/async-file-manager.js +285 -0
- package/dist/src/swarm/optimizations/circular-buffer.js +162 -0
- package/dist/src/swarm/optimizations/connection-pool.js +244 -0
- package/dist/src/swarm/optimizations/index.js +28 -0
- package/dist/src/swarm/optimizations/optimized-executor.js +320 -0
- package/dist/src/swarm/optimizations/ttl-map.js +234 -0
- package/dist/src/swarm/prompt-cli.js +200 -0
- package/dist/src/swarm/prompt-copier-enhanced.js +202 -0
- package/dist/src/swarm/prompt-copier.js +381 -0
- package/dist/src/swarm/prompt-manager.js +295 -0
- package/dist/src/swarm/prompt-utils.js +310 -0
- package/dist/src/swarm/result-aggregator.js +718 -0
- package/dist/src/swarm/sparc-executor.js +1568 -0
- package/dist/src/swarm/strategies/auto.js +758 -0
- package/dist/src/swarm/strategies/base.js +128 -0
- package/dist/src/swarm/strategies/research.js +914 -0
- package/dist/src/swarm/strategies/strategy-metrics-patch.js +2 -0
- package/dist/src/swarm/types.js +52 -0
- package/dist/src/swarm/workers/copy-worker.js +56 -0
- package/dist/src/utils/__tests__/github-cli-safety-wrapper.test.js +332 -400
- package/dist/src/utils/github-cli-safe.js +56 -64
- package/dist/src/utils/github-cli-safety-wrapper.js +451 -546
- package/dist/src/utils/npx-isolated-cache.js +104 -119
- package/dist/src/utils/preference-manager.js +622 -652
- package/dist/src/utils/timezone-utils.js +86 -105
- package/dist/src/validators/epic-config-schema.js +214 -0
- package/dist/src/validators/index.js +10 -0
- package/dist/src/validators/swarm-init-validator.js +259 -0
- package/dist/src/validators/todowrite-batching-validator.js +215 -0
- package/dist/src/validators/todowrite-integration.js +187 -0
- package/package.json +2 -2
|
@@ -1,115 +1,85 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Hive Mind Session Manager
|
|
3
3
|
* Handles session persistence and resume functionality for swarms
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import { cwd } from '../../node-compat.js';
|
|
11
|
-
import { createDatabase, isSQLiteAvailable, isWindows } from '../../../memory/sqlite-wrapper.js';
|
|
12
|
-
import { sessionSerializer } from '../../../memory/enhanced-session-serializer.js';
|
|
13
|
-
import { SerializationError, DeserializationError } from '../../../memory/advanced-serializer.js';
|
|
14
|
-
|
|
4
|
+
*/ import path from "path";
|
|
5
|
+
import { existsSync, mkdirSync } from "fs";
|
|
6
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
7
|
+
import { cwd } from "../../node-compat.js";
|
|
8
|
+
import { createDatabase, isSQLiteAvailable, isWindows } from "../../../memory/sqlite-wrapper.js";
|
|
9
|
+
import { sessionSerializer } from "../../../memory/enhanced-session-serializer.js";
|
|
15
10
|
export class HiveMindSessionManager {
|
|
16
|
-
|
|
17
|
-
this.hiveMindDir = hiveMindDir || path.join(cwd(), '.hive-mind');
|
|
18
|
-
this.sessionsDir = path.join(this.hiveMindDir, 'sessions');
|
|
19
|
-
this.dbPath = path.join(this.hiveMindDir, 'hive.db');
|
|
20
|
-
this.db = null;
|
|
21
|
-
this.isInMemory = false;
|
|
22
|
-
this.memoryStore = null;
|
|
23
|
-
this.initializationPromise = null;
|
|
24
|
-
|
|
25
|
-
// Ensure directories exist
|
|
26
|
-
this.ensureDirectories();
|
|
27
|
-
|
|
28
|
-
// Initialize database connection (store promise for later)
|
|
29
|
-
this.initializationPromise = this.initializeDatabase();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
11
|
+
/**
|
|
33
12
|
* Initialize database with fallback support
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
console.warn('Falling back to in-memory session storage');
|
|
54
|
-
this.initializeInMemoryFallback();
|
|
13
|
+
*/ async initializeDatabase() {
|
|
14
|
+
try {
|
|
15
|
+
const sqliteAvailable = await isSQLiteAvailable();
|
|
16
|
+
if (!sqliteAvailable) {
|
|
17
|
+
console.warn('SQLite not available, using in-memory session storage');
|
|
18
|
+
this.initializeInMemoryFallback();
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
this.db = await createDatabase(this.dbPath);
|
|
22
|
+
if (this.db) {
|
|
23
|
+
this.initializeSchema();
|
|
24
|
+
} else {
|
|
25
|
+
throw new Error('Failed to create database instance');
|
|
26
|
+
}
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error('Failed to create SQLite database:', error.message);
|
|
29
|
+
console.warn('Falling back to in-memory session storage');
|
|
30
|
+
this.initializeInMemoryFallback();
|
|
31
|
+
}
|
|
55
32
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
/**
|
|
33
|
+
/**
|
|
59
34
|
* Ensure database is initialized before use
|
|
60
|
-
*/
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
await this.initializeDatabase();
|
|
35
|
+
*/ async ensureInitialized() {
|
|
36
|
+
if (this.initializationPromise) {
|
|
37
|
+
await this.initializationPromise;
|
|
38
|
+
this.initializationPromise = null;
|
|
39
|
+
}
|
|
40
|
+
if (this.db === null && !this.isInMemory) {
|
|
41
|
+
await this.initializeDatabase();
|
|
42
|
+
}
|
|
69
43
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
/**
|
|
44
|
+
/**
|
|
73
45
|
* Initialize in-memory fallback for session storage
|
|
74
|
-
*/
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (isWindows()) {
|
|
84
|
-
console.info(`
|
|
46
|
+
*/ initializeInMemoryFallback() {
|
|
47
|
+
this.isInMemory = true;
|
|
48
|
+
this.memoryStore = {
|
|
49
|
+
sessions: new Map(),
|
|
50
|
+
checkpoints: new Map(),
|
|
51
|
+
logs: new Map()
|
|
52
|
+
};
|
|
53
|
+
if (isWindows()) {
|
|
54
|
+
console.info(`
|
|
85
55
|
Note: Session data will not persist between runs on Windows without SQLite.
|
|
86
56
|
To enable persistence, see: https://github.com/ruvnet/claude-code-flow/docs/windows-installation.md
|
|
87
57
|
`);
|
|
58
|
+
}
|
|
88
59
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
/**
|
|
60
|
+
/**
|
|
92
61
|
* Ensure required directories exist
|
|
93
|
-
*/
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
62
|
+
*/ ensureDirectories() {
|
|
63
|
+
if (!existsSync(this.hiveMindDir)) {
|
|
64
|
+
mkdirSync(this.hiveMindDir, {
|
|
65
|
+
recursive: true
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (!existsSync(this.sessionsDir)) {
|
|
69
|
+
mkdirSync(this.sessionsDir, {
|
|
70
|
+
recursive: true
|
|
71
|
+
});
|
|
72
|
+
}
|
|
100
73
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
/**
|
|
74
|
+
/**
|
|
104
75
|
* Initialize database schema for sessions
|
|
105
|
-
*/
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
this.db.exec(`
|
|
76
|
+
*/ initializeSchema() {
|
|
77
|
+
if (!this.db) {
|
|
78
|
+
console.error('Database not initialized');
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Create the base schema
|
|
82
|
+
this.db.exec(`
|
|
113
83
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
114
84
|
id TEXT PRIMARY KEY,
|
|
115
85
|
swarm_id TEXT NOT NULL,
|
|
@@ -148,247 +118,189 @@ To enable persistence, see: https://github.com/ruvnet/claude-code-flow/docs/wind
|
|
|
148
118
|
FOREIGN KEY (session_id) REFERENCES sessions(id)
|
|
149
119
|
);
|
|
150
120
|
`);
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
this.runMigrations();
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Run database migrations
|
|
158
|
-
*/
|
|
159
|
-
runMigrations() {
|
|
160
|
-
if (!this.db) {
|
|
161
|
-
console.error('Database not initialized for migrations');
|
|
162
|
-
return;
|
|
121
|
+
// Run migrations to add new columns
|
|
122
|
+
this.runMigrations();
|
|
163
123
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if (!hasCompletionPercentage) {
|
|
230
|
-
this.db.exec('ALTER TABLE sessions ADD COLUMN completion_percentage REAL DEFAULT 0');
|
|
231
|
-
console.log('Added completion_percentage column to sessions table');
|
|
232
|
-
}
|
|
233
|
-
} catch (error) {
|
|
234
|
-
console.error('Migration error:', error);
|
|
124
|
+
/**
|
|
125
|
+
* Run database migrations
|
|
126
|
+
*/ runMigrations() {
|
|
127
|
+
if (!this.db) {
|
|
128
|
+
console.error('Database not initialized for migrations');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
// Check if required columns exist
|
|
133
|
+
const columns = this.db.prepare('PRAGMA table_info(sessions)').all();
|
|
134
|
+
// Core columns
|
|
135
|
+
const hasObjective = columns.some((col)=>col.name === 'objective');
|
|
136
|
+
const hasSwarmName = columns.some((col)=>col.name === 'swarm_name');
|
|
137
|
+
const hasCheckpointData = columns.some((col)=>col.name === 'checkpoint_data');
|
|
138
|
+
const hasMetadata = columns.some((col)=>col.name === 'metadata');
|
|
139
|
+
const hasParentPid = columns.some((col)=>col.name === 'parent_pid');
|
|
140
|
+
const hasChildPids = columns.some((col)=>col.name === 'child_pids');
|
|
141
|
+
// Timestamp columns
|
|
142
|
+
const hasUpdatedAt = columns.some((col)=>col.name === 'updated_at');
|
|
143
|
+
const hasPausedAt = columns.some((col)=>col.name === 'paused_at');
|
|
144
|
+
const hasResumedAt = columns.some((col)=>col.name === 'resumed_at');
|
|
145
|
+
const hasCompletionPercentage = columns.some((col)=>col.name === 'completion_percentage');
|
|
146
|
+
if (!hasObjective) {
|
|
147
|
+
this.db.exec('ALTER TABLE sessions ADD COLUMN objective TEXT');
|
|
148
|
+
console.log('Added objective column to sessions table');
|
|
149
|
+
}
|
|
150
|
+
if (!hasSwarmName) {
|
|
151
|
+
this.db.exec('ALTER TABLE sessions ADD COLUMN swarm_name TEXT');
|
|
152
|
+
console.log('Added swarm_name column to sessions table');
|
|
153
|
+
}
|
|
154
|
+
if (!hasCheckpointData) {
|
|
155
|
+
this.db.exec('ALTER TABLE sessions ADD COLUMN checkpoint_data TEXT');
|
|
156
|
+
console.log('Added checkpoint_data column to sessions table');
|
|
157
|
+
}
|
|
158
|
+
if (!hasMetadata) {
|
|
159
|
+
this.db.exec('ALTER TABLE sessions ADD COLUMN metadata TEXT');
|
|
160
|
+
console.log('Added metadata column to sessions table');
|
|
161
|
+
}
|
|
162
|
+
if (!hasParentPid) {
|
|
163
|
+
this.db.exec('ALTER TABLE sessions ADD COLUMN parent_pid INTEGER');
|
|
164
|
+
console.log('Added parent_pid column to sessions table');
|
|
165
|
+
}
|
|
166
|
+
if (!hasChildPids) {
|
|
167
|
+
this.db.exec('ALTER TABLE sessions ADD COLUMN child_pids TEXT');
|
|
168
|
+
console.log('Added child_pids column to sessions table');
|
|
169
|
+
}
|
|
170
|
+
if (!hasUpdatedAt) {
|
|
171
|
+
this.db.exec('ALTER TABLE sessions ADD COLUMN updated_at DATETIME DEFAULT CURRENT_TIMESTAMP');
|
|
172
|
+
console.log('Added updated_at column to sessions table');
|
|
173
|
+
}
|
|
174
|
+
if (!hasPausedAt) {
|
|
175
|
+
this.db.exec('ALTER TABLE sessions ADD COLUMN paused_at DATETIME');
|
|
176
|
+
console.log('Added paused_at column to sessions table');
|
|
177
|
+
}
|
|
178
|
+
if (!hasResumedAt) {
|
|
179
|
+
this.db.exec('ALTER TABLE sessions ADD COLUMN resumed_at DATETIME');
|
|
180
|
+
console.log('Added resumed_at column to sessions table');
|
|
181
|
+
}
|
|
182
|
+
if (!hasCompletionPercentage) {
|
|
183
|
+
this.db.exec('ALTER TABLE sessions ADD COLUMN completion_percentage REAL DEFAULT 0');
|
|
184
|
+
console.log('Added completion_percentage column to sessions table');
|
|
185
|
+
}
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.error('Migration error:', error);
|
|
188
|
+
}
|
|
235
189
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
/**
|
|
190
|
+
/**
|
|
239
191
|
* Create a new session for a swarm
|
|
240
|
-
*/
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
} else {
|
|
262
|
-
// Use SQLite
|
|
263
|
-
const stmt = this.db.prepare(`
|
|
192
|
+
*/ async createSession(swarmId, swarmName, objective, metadata = {}) {
|
|
193
|
+
await this.ensureInitialized();
|
|
194
|
+
const sessionId = `session-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
195
|
+
if (this.isInMemory) {
|
|
196
|
+
// Use in-memory storage
|
|
197
|
+
const sessionData = {
|
|
198
|
+
id: sessionId,
|
|
199
|
+
swarm_id: swarmId,
|
|
200
|
+
swarm_name: swarmName,
|
|
201
|
+
objective,
|
|
202
|
+
status: 'active',
|
|
203
|
+
created_at: new Date().toISOString(),
|
|
204
|
+
updated_at: new Date().toISOString(),
|
|
205
|
+
metadata: sessionSerializer.serializeMetadata(metadata),
|
|
206
|
+
parent_pid: process.pid,
|
|
207
|
+
child_pids: '[]'
|
|
208
|
+
};
|
|
209
|
+
this.memoryStore.sessions.set(sessionId, sessionData);
|
|
210
|
+
} else {
|
|
211
|
+
// Use SQLite
|
|
212
|
+
const stmt = this.db.prepare(`
|
|
264
213
|
INSERT INTO sessions (id, swarm_id, swarm_name, objective, metadata, parent_pid)
|
|
265
214
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
266
215
|
`);
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
await this.logSessionEvent(sessionId, 'info', 'Session created', null, {
|
|
280
|
-
swarmId,
|
|
281
|
-
swarmName,
|
|
282
|
-
objective,
|
|
283
|
-
parentPid: process.pid,
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
return sessionId;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
216
|
+
stmt.run(sessionId, swarmId, swarmName, objective, sessionSerializer.serializeMetadata(metadata), process.pid);
|
|
217
|
+
}
|
|
218
|
+
// Log session creation
|
|
219
|
+
await this.logSessionEvent(sessionId, 'info', 'Session created', null, {
|
|
220
|
+
swarmId,
|
|
221
|
+
swarmName,
|
|
222
|
+
objective,
|
|
223
|
+
parentPid: process.pid
|
|
224
|
+
});
|
|
225
|
+
return sessionId;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
290
228
|
* Save session checkpoint
|
|
291
|
-
*/
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
session.updated_at = new Date().toISOString();
|
|
317
|
-
}
|
|
318
|
-
} else {
|
|
319
|
-
// Save to database
|
|
320
|
-
const stmt = this.db.prepare(`
|
|
229
|
+
*/ async saveCheckpoint(sessionId, checkpointName, checkpointData) {
|
|
230
|
+
await this.ensureInitialized();
|
|
231
|
+
const checkpointId = `checkpoint-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
232
|
+
if (this.isInMemory) {
|
|
233
|
+
// Use in-memory storage
|
|
234
|
+
const checkpointEntry = {
|
|
235
|
+
id: checkpointId,
|
|
236
|
+
session_id: sessionId,
|
|
237
|
+
checkpoint_name: checkpointName,
|
|
238
|
+
checkpoint_data: sessionSerializer.serializeCheckpointData(checkpointData),
|
|
239
|
+
created_at: new Date().toISOString()
|
|
240
|
+
};
|
|
241
|
+
if (!this.memoryStore.checkpoints.has(sessionId)) {
|
|
242
|
+
this.memoryStore.checkpoints.set(sessionId, []);
|
|
243
|
+
}
|
|
244
|
+
this.memoryStore.checkpoints.get(sessionId).push(checkpointEntry);
|
|
245
|
+
// Update session data
|
|
246
|
+
const session = this.memoryStore.sessions.get(sessionId);
|
|
247
|
+
if (session) {
|
|
248
|
+
session.checkpoint_data = sessionSerializer.serializeCheckpointData(checkpointData);
|
|
249
|
+
session.updated_at = new Date().toISOString();
|
|
250
|
+
}
|
|
251
|
+
} else {
|
|
252
|
+
// Save to database
|
|
253
|
+
const stmt = this.db.prepare(`
|
|
321
254
|
INSERT INTO session_checkpoints (id, session_id, checkpoint_name, checkpoint_data)
|
|
322
255
|
VALUES (?, ?, ?, ?)
|
|
323
256
|
`);
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
sessionId,
|
|
328
|
-
checkpointName,
|
|
329
|
-
sessionSerializer.serializeCheckpointData(checkpointData),
|
|
330
|
-
);
|
|
331
|
-
|
|
332
|
-
// Update session checkpoint data and timestamp
|
|
333
|
-
const updateStmt = this.db.prepare(`
|
|
257
|
+
stmt.run(checkpointId, sessionId, checkpointName, sessionSerializer.serializeCheckpointData(checkpointData));
|
|
258
|
+
// Update session checkpoint data and timestamp
|
|
259
|
+
const updateStmt = this.db.prepare(`
|
|
334
260
|
UPDATE sessions
|
|
335
261
|
SET checkpoint_data = ?, updated_at = CURRENT_TIMESTAMP
|
|
336
262
|
WHERE id = ?
|
|
337
263
|
`);
|
|
338
|
-
|
|
339
|
-
updateStmt.run(sessionSerializer.serializeCheckpointData(checkpointData), sessionId);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// Save checkpoint file for backup
|
|
343
|
-
const checkpointFile = path.join(this.sessionsDir, `${sessionId}-${checkpointName}.json`);
|
|
344
|
-
await writeFile(
|
|
345
|
-
checkpointFile,
|
|
346
|
-
sessionSerializer.serializeSessionData({
|
|
347
|
-
sessionId,
|
|
348
|
-
checkpointId,
|
|
349
|
-
checkpointName,
|
|
350
|
-
timestamp: new Date().toISOString(),
|
|
351
|
-
data: checkpointData,
|
|
352
|
-
}),
|
|
353
|
-
);
|
|
354
|
-
|
|
355
|
-
await this.logSessionEvent(sessionId, 'info', `Checkpoint saved: ${checkpointName}`, null, {
|
|
356
|
-
checkpointId,
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
return checkpointId;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
/**
|
|
363
|
-
* Get active sessions
|
|
364
|
-
*/
|
|
365
|
-
async getActiveSessions() {
|
|
366
|
-
await this.ensureInitialized();
|
|
367
|
-
|
|
368
|
-
if (this.isInMemory) {
|
|
369
|
-
// Use in-memory storage
|
|
370
|
-
const sessions = [];
|
|
371
|
-
for (const [sessionId, session] of this.memoryStore.sessions) {
|
|
372
|
-
if (session.status === 'active' || session.status === 'paused') {
|
|
373
|
-
sessions.push({
|
|
374
|
-
...session,
|
|
375
|
-
metadata: session.metadata
|
|
376
|
-
? sessionSerializer.deserializeMetadata(session.metadata)
|
|
377
|
-
: {},
|
|
378
|
-
checkpoint_data: session.checkpoint_data
|
|
379
|
-
? sessionSerializer.deserializeCheckpointData(session.checkpoint_data)
|
|
380
|
-
: null,
|
|
381
|
-
agent_count: 0, // Not tracked in memory mode
|
|
382
|
-
task_count: 0, // Not tracked in memory mode
|
|
383
|
-
completed_tasks: 0, // Not tracked in memory mode
|
|
384
|
-
completion_percentage: 0,
|
|
385
|
-
});
|
|
264
|
+
updateStmt.run(sessionSerializer.serializeCheckpointData(checkpointData), sessionId);
|
|
386
265
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
266
|
+
// Save checkpoint file for backup
|
|
267
|
+
const checkpointFile = path.join(this.sessionsDir, `${sessionId}-${checkpointName}.json`);
|
|
268
|
+
await writeFile(checkpointFile, sessionSerializer.serializeSessionData({
|
|
269
|
+
sessionId,
|
|
270
|
+
checkpointId,
|
|
271
|
+
checkpointName,
|
|
272
|
+
timestamp: new Date().toISOString(),
|
|
273
|
+
data: checkpointData
|
|
274
|
+
}));
|
|
275
|
+
await this.logSessionEvent(sessionId, 'info', `Checkpoint saved: ${checkpointName}`, null, {
|
|
276
|
+
checkpointId
|
|
277
|
+
});
|
|
278
|
+
return checkpointId;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Get active sessions
|
|
282
|
+
*/ async getActiveSessions() {
|
|
283
|
+
await this.ensureInitialized();
|
|
284
|
+
if (this.isInMemory) {
|
|
285
|
+
// Use in-memory storage
|
|
286
|
+
const sessions = [];
|
|
287
|
+
for (const [sessionId, session] of this.memoryStore.sessions){
|
|
288
|
+
if (session.status === 'active' || session.status === 'paused') {
|
|
289
|
+
sessions.push({
|
|
290
|
+
...session,
|
|
291
|
+
metadata: session.metadata ? sessionSerializer.deserializeMetadata(session.metadata) : {},
|
|
292
|
+
checkpoint_data: session.checkpoint_data ? sessionSerializer.deserializeCheckpointData(session.checkpoint_data) : null,
|
|
293
|
+
agent_count: 0,
|
|
294
|
+
task_count: 0,
|
|
295
|
+
completed_tasks: 0,
|
|
296
|
+
completion_percentage: 0
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return sessions.sort((a, b)=>new Date(b.updated_at) - new Date(a.updated_at));
|
|
301
|
+
} else {
|
|
302
|
+
// Use SQLite
|
|
303
|
+
const stmt = this.db.prepare(`
|
|
392
304
|
SELECT s.*,
|
|
393
305
|
COUNT(DISTINCT a.id) as agent_count,
|
|
394
306
|
COUNT(DISTINCT t.id) as task_count,
|
|
@@ -400,824 +312,608 @@ To enable persistence, see: https://github.com/ruvnet/claude-code-flow/docs/wind
|
|
|
400
312
|
GROUP BY s.id
|
|
401
313
|
ORDER BY s.updated_at DESC
|
|
402
314
|
`);
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
: null,
|
|
413
|
-
completion_percentage:
|
|
414
|
-
session.task_count > 0
|
|
415
|
-
? Math.round((session.completed_tasks / session.task_count) * 100)
|
|
416
|
-
: 0,
|
|
417
|
-
}));
|
|
315
|
+
const sessions = stmt.all();
|
|
316
|
+
// Parse JSON fields
|
|
317
|
+
return sessions.map((session)=>({
|
|
318
|
+
...session,
|
|
319
|
+
metadata: session.metadata ? sessionSerializer.deserializeMetadata(session.metadata) : {},
|
|
320
|
+
checkpoint_data: session.checkpoint_data ? sessionSerializer.deserializeCheckpointData(session.checkpoint_data) : null,
|
|
321
|
+
completion_percentage: session.task_count > 0 ? Math.round(session.completed_tasks / session.task_count * 100) : 0
|
|
322
|
+
}));
|
|
323
|
+
}
|
|
418
324
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
/**
|
|
325
|
+
/**
|
|
422
326
|
* Get session by ID with full details
|
|
423
|
-
*/
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
completionPercentage: session.completion_percentage || 0,
|
|
454
|
-
},
|
|
455
|
-
};
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
const session = this.db
|
|
459
|
-
.prepare(
|
|
460
|
-
`
|
|
327
|
+
*/ async getSession(sessionId) {
|
|
328
|
+
await this.ensureInitialized();
|
|
329
|
+
if (this.isInMemory) {
|
|
330
|
+
// Use in-memory storage
|
|
331
|
+
const session = this.memoryStore.sessions.get(sessionId);
|
|
332
|
+
if (!session) {
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
// Return simplified session data for in-memory mode
|
|
336
|
+
return {
|
|
337
|
+
...session,
|
|
338
|
+
metadata: session.metadata ? sessionSerializer.deserializeMetadata(session.metadata) : {},
|
|
339
|
+
checkpoint_data: session.checkpoint_data ? sessionSerializer.deserializeCheckpointData(session.checkpoint_data) : null,
|
|
340
|
+
swarm: null,
|
|
341
|
+
agents: [],
|
|
342
|
+
tasks: [],
|
|
343
|
+
checkpoints: this.memoryStore.checkpoints.get(sessionId) || [],
|
|
344
|
+
recentLogs: this.memoryStore.logs.get(sessionId) || [],
|
|
345
|
+
statistics: {
|
|
346
|
+
totalAgents: 0,
|
|
347
|
+
activeAgents: 0,
|
|
348
|
+
totalTasks: 0,
|
|
349
|
+
completedTasks: 0,
|
|
350
|
+
pendingTasks: 0,
|
|
351
|
+
inProgressTasks: 0,
|
|
352
|
+
completionPercentage: session.completion_percentage || 0
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
const session = this.db.prepare(`
|
|
461
357
|
SELECT * FROM sessions WHERE id = ?
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// Get associated swarm data
|
|
471
|
-
const swarm = this.db
|
|
472
|
-
.prepare(
|
|
473
|
-
`
|
|
358
|
+
`).get(sessionId);
|
|
359
|
+
if (!session) {
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
// Get associated swarm data
|
|
363
|
+
const swarm = this.db.prepare(`
|
|
474
364
|
SELECT * FROM swarms WHERE id = ?
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
// Get agents
|
|
480
|
-
const agents = this.db
|
|
481
|
-
.prepare(
|
|
482
|
-
`
|
|
365
|
+
`).get(session.swarm_id);
|
|
366
|
+
// Get agents
|
|
367
|
+
const agents = this.db.prepare(`
|
|
483
368
|
SELECT * FROM agents WHERE swarm_id = ?
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
// Get tasks
|
|
489
|
-
const tasks = this.db
|
|
490
|
-
.prepare(
|
|
491
|
-
`
|
|
369
|
+
`).all(session.swarm_id);
|
|
370
|
+
// Get tasks
|
|
371
|
+
const tasks = this.db.prepare(`
|
|
492
372
|
SELECT * FROM tasks WHERE swarm_id = ?
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
// Get checkpoints
|
|
498
|
-
const checkpoints = this.db
|
|
499
|
-
.prepare(
|
|
500
|
-
`
|
|
373
|
+
`).all(session.swarm_id);
|
|
374
|
+
// Get checkpoints
|
|
375
|
+
const checkpoints = this.db.prepare(`
|
|
501
376
|
SELECT * FROM session_checkpoints
|
|
502
377
|
WHERE session_id = ?
|
|
503
378
|
ORDER BY created_at DESC
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
// Get recent logs
|
|
509
|
-
const recentLogs = this.db
|
|
510
|
-
.prepare(
|
|
511
|
-
`
|
|
379
|
+
`).all(sessionId);
|
|
380
|
+
// Get recent logs
|
|
381
|
+
const recentLogs = this.db.prepare(`
|
|
512
382
|
SELECT * FROM session_logs
|
|
513
383
|
WHERE session_id = ?
|
|
514
384
|
ORDER BY timestamp DESC
|
|
515
385
|
LIMIT 50
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
completionPercentage:
|
|
542
|
-
tasks.length > 0
|
|
543
|
-
? Math.round(
|
|
544
|
-
(tasks.filter((t) => t.status === 'completed').length / tasks.length) * 100,
|
|
545
|
-
)
|
|
546
|
-
: 0,
|
|
547
|
-
},
|
|
548
|
-
};
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
/**
|
|
386
|
+
`).all(sessionId);
|
|
387
|
+
return {
|
|
388
|
+
...session,
|
|
389
|
+
metadata: session.metadata ? sessionSerializer.deserializeMetadata(session.metadata) : {},
|
|
390
|
+
checkpoint_data: session.checkpoint_data ? sessionSerializer.deserializeCheckpointData(session.checkpoint_data) : null,
|
|
391
|
+
swarm,
|
|
392
|
+
agents,
|
|
393
|
+
tasks,
|
|
394
|
+
checkpoints: checkpoints.map((cp)=>({
|
|
395
|
+
...cp,
|
|
396
|
+
checkpoint_data: sessionSerializer.deserializeCheckpointData(cp.checkpoint_data)
|
|
397
|
+
})),
|
|
398
|
+
recentLogs,
|
|
399
|
+
statistics: {
|
|
400
|
+
totalAgents: agents.length,
|
|
401
|
+
activeAgents: agents.filter((a)=>a.status === 'active' || a.status === 'busy').length,
|
|
402
|
+
totalTasks: tasks.length,
|
|
403
|
+
completedTasks: tasks.filter((t)=>t.status === 'completed').length,
|
|
404
|
+
pendingTasks: tasks.filter((t)=>t.status === 'pending').length,
|
|
405
|
+
inProgressTasks: tasks.filter((t)=>t.status === 'in_progress').length,
|
|
406
|
+
completionPercentage: tasks.length > 0 ? Math.round(tasks.filter((t)=>t.status === 'completed').length / tasks.length * 100) : 0
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
552
411
|
* Pause a session
|
|
553
|
-
*/
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
} else {
|
|
570
|
-
// Use SQLite
|
|
571
|
-
const stmt = this.db.prepare(`
|
|
412
|
+
*/ async pauseSession(sessionId) {
|
|
413
|
+
await this.ensureInitialized();
|
|
414
|
+
if (this.isInMemory) {
|
|
415
|
+
// Use in-memory storage
|
|
416
|
+
const session = this.memoryStore.sessions.get(sessionId);
|
|
417
|
+
if (session) {
|
|
418
|
+
session.status = 'paused';
|
|
419
|
+
session.paused_at = new Date().toISOString();
|
|
420
|
+
session.updated_at = new Date().toISOString();
|
|
421
|
+
await this.logSessionEvent(sessionId, 'info', 'Session paused');
|
|
422
|
+
return true;
|
|
423
|
+
}
|
|
424
|
+
return false;
|
|
425
|
+
} else {
|
|
426
|
+
// Use SQLite
|
|
427
|
+
const stmt = this.db.prepare(`
|
|
572
428
|
UPDATE sessions
|
|
573
429
|
SET status = 'paused', paused_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP
|
|
574
430
|
WHERE id = ?
|
|
575
431
|
`);
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
if (session) {
|
|
587
|
-
this.db
|
|
588
|
-
.prepare('UPDATE swarms SET status = ? WHERE id = ?')
|
|
589
|
-
.run('paused', session.swarm_id);
|
|
432
|
+
const result = stmt.run(sessionId);
|
|
433
|
+
if (result.changes > 0) {
|
|
434
|
+
await this.logSessionEvent(sessionId, 'info', 'Session paused');
|
|
435
|
+
// Update swarm status
|
|
436
|
+
const session = this.db.prepare('SELECT swarm_id FROM sessions WHERE id = ?').get(sessionId);
|
|
437
|
+
if (session) {
|
|
438
|
+
this.db.prepare('UPDATE swarms SET status = ? WHERE id = ?').run('paused', session.swarm_id);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return result.changes > 0;
|
|
590
442
|
}
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
return result.changes > 0;
|
|
594
443
|
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
/**
|
|
444
|
+
/**
|
|
598
445
|
* Resume any previous session (paused, stopped, or inactive)
|
|
599
|
-
*/
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
const sessionData = this.memoryStore.sessions.get(sessionId);
|
|
623
|
-
if (sessionData) {
|
|
624
|
-
sessionData.status = 'active';
|
|
625
|
-
sessionData.resumed_at = new Date().toISOString();
|
|
626
|
-
sessionData.updated_at = new Date().toISOString();
|
|
627
|
-
}
|
|
628
|
-
} else {
|
|
629
|
-
// Use SQLite
|
|
630
|
-
const stmt = this.db.prepare(`
|
|
446
|
+
*/ async resumeSession(sessionId) {
|
|
447
|
+
const session = await this.getSession(sessionId);
|
|
448
|
+
if (!session) {
|
|
449
|
+
throw new Error(`Session ${sessionId} not found`);
|
|
450
|
+
}
|
|
451
|
+
// Allow resuming any session regardless of status
|
|
452
|
+
console.log(`Resuming session ${sessionId} from status: ${session.status}`);
|
|
453
|
+
// If session was stopped, log that we're restarting it
|
|
454
|
+
if (session.status === 'stopped') {
|
|
455
|
+
await this.logSessionEvent(sessionId, 'info', `Restarting stopped session with original configuration`);
|
|
456
|
+
}
|
|
457
|
+
// Update session status
|
|
458
|
+
if (this.isInMemory) {
|
|
459
|
+
// Use in-memory storage
|
|
460
|
+
const sessionData = this.memoryStore.sessions.get(sessionId);
|
|
461
|
+
if (sessionData) {
|
|
462
|
+
sessionData.status = 'active';
|
|
463
|
+
sessionData.resumed_at = new Date().toISOString();
|
|
464
|
+
sessionData.updated_at = new Date().toISOString();
|
|
465
|
+
}
|
|
466
|
+
} else {
|
|
467
|
+
// Use SQLite
|
|
468
|
+
const stmt = this.db.prepare(`
|
|
631
469
|
UPDATE sessions
|
|
632
470
|
SET status = 'active', resumed_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP
|
|
633
471
|
WHERE id = ?
|
|
634
472
|
`);
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
// Update agent statuses
|
|
642
|
-
this.db
|
|
643
|
-
.prepare(
|
|
644
|
-
`
|
|
473
|
+
stmt.run(sessionId);
|
|
474
|
+
// Update swarm status
|
|
475
|
+
this.db.prepare('UPDATE swarms SET status = ? WHERE id = ?').run('active', session.swarm_id);
|
|
476
|
+
// Update agent statuses
|
|
477
|
+
this.db.prepare(`
|
|
645
478
|
UPDATE agents
|
|
646
479
|
SET status = CASE
|
|
647
480
|
WHEN role = 'queen' THEN 'active'
|
|
648
481
|
ELSE 'idle'
|
|
649
482
|
END
|
|
650
483
|
WHERE swarm_id = ?
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
.
|
|
484
|
+
`).run(session.swarm_id);
|
|
485
|
+
}
|
|
486
|
+
await this.logSessionEvent(sessionId, 'info', 'Session resumed', null, {
|
|
487
|
+
pausedDuration: session.paused_at ? new Date() - new Date(session.paused_at) : null
|
|
488
|
+
});
|
|
489
|
+
return session;
|
|
654
490
|
}
|
|
655
|
-
|
|
656
|
-
await this.logSessionEvent(sessionId, 'info', 'Session resumed', null, {
|
|
657
|
-
pausedDuration: session.paused_at ? new Date() - new Date(session.paused_at) : null,
|
|
658
|
-
});
|
|
659
|
-
|
|
660
|
-
return session;
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
/**
|
|
491
|
+
/**
|
|
664
492
|
* Mark session as completed
|
|
665
|
-
*/
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
} else {
|
|
682
|
-
// Use SQLite
|
|
683
|
-
const stmt = this.db.prepare(`
|
|
493
|
+
*/ async completeSession(sessionId) {
|
|
494
|
+
await this.ensureInitialized();
|
|
495
|
+
if (this.isInMemory) {
|
|
496
|
+
// Use in-memory storage
|
|
497
|
+
const session = this.memoryStore.sessions.get(sessionId);
|
|
498
|
+
if (session) {
|
|
499
|
+
session.status = 'completed';
|
|
500
|
+
session.updated_at = new Date().toISOString();
|
|
501
|
+
session.completion_percentage = 100;
|
|
502
|
+
await this.logSessionEvent(sessionId, 'info', 'Session completed');
|
|
503
|
+
return true;
|
|
504
|
+
}
|
|
505
|
+
return false;
|
|
506
|
+
} else {
|
|
507
|
+
// Use SQLite
|
|
508
|
+
const stmt = this.db.prepare(`
|
|
684
509
|
UPDATE sessions
|
|
685
510
|
SET status = 'completed', updated_at = CURRENT_TIMESTAMP, completion_percentage = 100
|
|
686
511
|
WHERE id = ?
|
|
687
512
|
`);
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
if (session) {
|
|
699
|
-
this.db
|
|
700
|
-
.prepare('UPDATE swarms SET status = ? WHERE id = ?')
|
|
701
|
-
.run('completed', session.swarm_id);
|
|
513
|
+
const result = stmt.run(sessionId);
|
|
514
|
+
if (result.changes > 0) {
|
|
515
|
+
await this.logSessionEvent(sessionId, 'info', 'Session completed');
|
|
516
|
+
// Update swarm status
|
|
517
|
+
const session = this.db.prepare('SELECT swarm_id FROM sessions WHERE id = ?').get(sessionId);
|
|
518
|
+
if (session) {
|
|
519
|
+
this.db.prepare('UPDATE swarms SET status = ? WHERE id = ?').run('completed', session.swarm_id);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
return result.changes > 0;
|
|
702
523
|
}
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
return result.changes > 0;
|
|
706
524
|
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
/**
|
|
525
|
+
/**
|
|
710
526
|
* Archive old sessions
|
|
711
|
-
*/
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
const cutoffDate = new Date();
|
|
722
|
-
cutoffDate.setDate(cutoffDate.getDate() - daysOld);
|
|
723
|
-
|
|
724
|
-
const sessionsToArchive = this.db
|
|
725
|
-
.prepare(
|
|
726
|
-
`
|
|
527
|
+
*/ async archiveSessions(daysOld = 30) {
|
|
528
|
+
await this.ensureInitialized();
|
|
529
|
+
if (this.isInMemory) {
|
|
530
|
+
// In-memory mode doesn't support archiving
|
|
531
|
+
console.warn('Session archiving not supported in in-memory mode');
|
|
532
|
+
return 0;
|
|
533
|
+
}
|
|
534
|
+
const cutoffDate = new Date();
|
|
535
|
+
cutoffDate.setDate(cutoffDate.getDate() - daysOld);
|
|
536
|
+
const sessionsToArchive = this.db.prepare(`
|
|
727
537
|
SELECT * FROM sessions
|
|
728
538
|
WHERE status = 'completed' AND updated_at < ?
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
this.db.prepare('DELETE FROM session_checkpoints WHERE session_id = ?').run(session.id);
|
|
747
|
-
this.db.prepare('DELETE FROM sessions WHERE id = ?').run(session.id);
|
|
539
|
+
`).all(cutoffDate.toISOString());
|
|
540
|
+
const archiveDir = path.join(this.sessionsDir, 'archive');
|
|
541
|
+
if (!existsSync(archiveDir)) {
|
|
542
|
+
mkdirSync(archiveDir, {
|
|
543
|
+
recursive: true
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
for (const session of sessionsToArchive){
|
|
547
|
+
const sessionData = await this.getSession(session.id);
|
|
548
|
+
const archiveFile = path.join(archiveDir, `${session.id}-archive.json`);
|
|
549
|
+
await writeFile(archiveFile, sessionSerializer.serializeSessionData(sessionData));
|
|
550
|
+
// Remove from database
|
|
551
|
+
this.db.prepare('DELETE FROM session_logs WHERE session_id = ?').run(session.id);
|
|
552
|
+
this.db.prepare('DELETE FROM session_checkpoints WHERE session_id = ?').run(session.id);
|
|
553
|
+
this.db.prepare('DELETE FROM sessions WHERE id = ?').run(session.id);
|
|
554
|
+
}
|
|
555
|
+
return sessionsToArchive.length;
|
|
748
556
|
}
|
|
749
|
-
|
|
750
|
-
return sessionsToArchive.length;
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
/**
|
|
557
|
+
/**
|
|
754
558
|
* Log session event
|
|
755
|
-
*/
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
} else {
|
|
777
|
-
// Use SQLite
|
|
778
|
-
const stmt = this.db.prepare(`
|
|
559
|
+
*/ async logSessionEvent(sessionId, logLevel, message, agentId = null, data = null) {
|
|
560
|
+
await this.ensureInitialized();
|
|
561
|
+
if (this.isInMemory) {
|
|
562
|
+
// Use in-memory storage for logs
|
|
563
|
+
const logId = `log-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
564
|
+
const logEntry = {
|
|
565
|
+
id: logId,
|
|
566
|
+
session_id: sessionId,
|
|
567
|
+
timestamp: new Date().toISOString(),
|
|
568
|
+
log_level: logLevel,
|
|
569
|
+
message,
|
|
570
|
+
agent_id: agentId,
|
|
571
|
+
data: data ? sessionSerializer.serializeLogData(data) : null
|
|
572
|
+
};
|
|
573
|
+
if (!this.memoryStore.logs.has(sessionId)) {
|
|
574
|
+
this.memoryStore.logs.set(sessionId, []);
|
|
575
|
+
}
|
|
576
|
+
this.memoryStore.logs.get(sessionId).push(logEntry);
|
|
577
|
+
} else {
|
|
578
|
+
// Use SQLite
|
|
579
|
+
const stmt = this.db.prepare(`
|
|
779
580
|
INSERT INTO session_logs (session_id, log_level, message, agent_id, data)
|
|
780
581
|
VALUES (?, ?, ?, ?, ?)
|
|
781
582
|
`);
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
sessionId,
|
|
785
|
-
logLevel,
|
|
786
|
-
message,
|
|
787
|
-
agentId,
|
|
788
|
-
data ? sessionSerializer.serializeLogData(data) : null,
|
|
789
|
-
);
|
|
583
|
+
stmt.run(sessionId, logLevel, message, agentId, data ? sessionSerializer.serializeLogData(data) : null);
|
|
584
|
+
}
|
|
790
585
|
}
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
/**
|
|
586
|
+
/**
|
|
794
587
|
* Get session logs
|
|
795
|
-
*/
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
const stmt = this.db.prepare(`
|
|
588
|
+
*/ async getSessionLogs(sessionId, limit = 100, offset = 0) {
|
|
589
|
+
await this.ensureInitialized();
|
|
590
|
+
if (this.isInMemory) {
|
|
591
|
+
// Use in-memory storage
|
|
592
|
+
const logs = this.memoryStore.logs.get(sessionId) || [];
|
|
593
|
+
return logs.slice(offset, offset + limit).map((log)=>({
|
|
594
|
+
...log,
|
|
595
|
+
data: log.data ? sessionSerializer.deserializeLogData(log.data) : null
|
|
596
|
+
}));
|
|
597
|
+
}
|
|
598
|
+
const stmt = this.db.prepare(`
|
|
809
599
|
SELECT * FROM session_logs
|
|
810
600
|
WHERE session_id = ?
|
|
811
601
|
ORDER BY timestamp DESC
|
|
812
602
|
LIMIT ? OFFSET ?
|
|
813
603
|
`);
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
/**
|
|
604
|
+
const logs = stmt.all(sessionId, limit, offset);
|
|
605
|
+
return logs.map((log)=>({
|
|
606
|
+
...log,
|
|
607
|
+
data: log.data ? sessionSerializer.deserializeLogData(log.data) : null
|
|
608
|
+
}));
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
824
611
|
* Update session progress
|
|
825
|
-
*/
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
// Use SQLite
|
|
838
|
-
const stmt = this.db.prepare(`
|
|
612
|
+
*/ async updateSessionProgress(sessionId, completionPercentage) {
|
|
613
|
+
await this.ensureInitialized();
|
|
614
|
+
if (this.isInMemory) {
|
|
615
|
+
// Use in-memory storage
|
|
616
|
+
const session = this.memoryStore.sessions.get(sessionId);
|
|
617
|
+
if (session) {
|
|
618
|
+
session.completion_percentage = completionPercentage;
|
|
619
|
+
session.updated_at = new Date().toISOString();
|
|
620
|
+
}
|
|
621
|
+
} else {
|
|
622
|
+
// Use SQLite
|
|
623
|
+
const stmt = this.db.prepare(`
|
|
839
624
|
UPDATE sessions
|
|
840
625
|
SET completion_percentage = ?, updated_at = CURRENT_TIMESTAMP
|
|
841
626
|
WHERE id = ?
|
|
842
627
|
`);
|
|
843
|
-
|
|
844
|
-
|
|
628
|
+
stmt.run(completionPercentage, sessionId);
|
|
629
|
+
}
|
|
845
630
|
}
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
/**
|
|
631
|
+
/**
|
|
849
632
|
* Generate session summary
|
|
850
|
-
*/
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
633
|
+
*/ async generateSessionSummary(sessionId) {
|
|
634
|
+
const session = await this.getSession(sessionId);
|
|
635
|
+
if (!session) {
|
|
636
|
+
return null;
|
|
637
|
+
}
|
|
638
|
+
const duration = session.paused_at && session.resumed_at ? new Date(session.updated_at) - new Date(session.created_at) - (new Date(session.resumed_at) - new Date(session.paused_at)) : new Date(session.updated_at) - new Date(session.created_at);
|
|
639
|
+
const tasksByType = session.agents.reduce((acc, agent)=>{
|
|
640
|
+
const agentTasks = session.tasks.filter((t)=>t.agent_id === agent.id);
|
|
641
|
+
if (!acc[agent.type]) {
|
|
642
|
+
acc[agent.type] = {
|
|
643
|
+
total: 0,
|
|
644
|
+
completed: 0,
|
|
645
|
+
inProgress: 0,
|
|
646
|
+
pending: 0
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
acc[agent.type].total += agentTasks.length;
|
|
650
|
+
acc[agent.type].completed += agentTasks.filter((t)=>t.status === 'completed').length;
|
|
651
|
+
acc[agent.type].inProgress += agentTasks.filter((t)=>t.status === 'in_progress').length;
|
|
652
|
+
acc[agent.type].pending += agentTasks.filter((t)=>t.status === 'pending').length;
|
|
653
|
+
return acc;
|
|
654
|
+
}, {});
|
|
655
|
+
return {
|
|
656
|
+
sessionId: session.id,
|
|
657
|
+
swarmName: session.swarm_name,
|
|
658
|
+
objective: session.objective,
|
|
659
|
+
status: session.status,
|
|
660
|
+
duration: Math.round(duration / 1000 / 60),
|
|
661
|
+
statistics: session.statistics,
|
|
662
|
+
tasksByType,
|
|
663
|
+
checkpointCount: session.checkpoints.length,
|
|
664
|
+
lastCheckpoint: session.checkpoints[0] || null,
|
|
665
|
+
timeline: {
|
|
666
|
+
created: session.created_at,
|
|
667
|
+
lastUpdated: session.updated_at,
|
|
668
|
+
paused: session.paused_at,
|
|
669
|
+
resumed: session.resumed_at
|
|
670
|
+
}
|
|
873
671
|
};
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
acc[agent.type].completed += agentTasks.filter((t) => t.status === 'completed').length;
|
|
877
|
-
acc[agent.type].inProgress += agentTasks.filter((t) => t.status === 'in_progress').length;
|
|
878
|
-
acc[agent.type].pending += agentTasks.filter((t) => t.status === 'pending').length;
|
|
879
|
-
return acc;
|
|
880
|
-
}, {});
|
|
881
|
-
|
|
882
|
-
return {
|
|
883
|
-
sessionId: session.id,
|
|
884
|
-
swarmName: session.swarm_name,
|
|
885
|
-
objective: session.objective,
|
|
886
|
-
status: session.status,
|
|
887
|
-
duration: Math.round(duration / 1000 / 60), // minutes
|
|
888
|
-
statistics: session.statistics,
|
|
889
|
-
tasksByType,
|
|
890
|
-
checkpointCount: session.checkpoints.length,
|
|
891
|
-
lastCheckpoint: session.checkpoints[0] || null,
|
|
892
|
-
timeline: {
|
|
893
|
-
created: session.created_at,
|
|
894
|
-
lastUpdated: session.updated_at,
|
|
895
|
-
paused: session.paused_at,
|
|
896
|
-
resumed: session.resumed_at,
|
|
897
|
-
},
|
|
898
|
-
};
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
/**
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
902
674
|
* Export session data
|
|
903
|
-
*/
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
675
|
+
*/ async exportSession(sessionId, exportPath = null) {
|
|
676
|
+
const session = await this.getSession(sessionId);
|
|
677
|
+
if (!session) {
|
|
678
|
+
throw new Error(`Session ${sessionId} not found`);
|
|
679
|
+
}
|
|
680
|
+
const exportFile = exportPath || path.join(this.sessionsDir, `${sessionId}-export.json`);
|
|
681
|
+
await writeFile(exportFile, sessionSerializer.serializeSessionData(session));
|
|
682
|
+
return exportFile;
|
|
909
683
|
}
|
|
910
|
-
|
|
911
|
-
const exportFile = exportPath || path.join(this.sessionsDir, `${sessionId}-export.json`);
|
|
912
|
-
|
|
913
|
-
await writeFile(exportFile, sessionSerializer.serializeSessionData(session));
|
|
914
|
-
|
|
915
|
-
return exportFile;
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
/**
|
|
684
|
+
/**
|
|
919
685
|
* Import session data
|
|
920
|
-
*/
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
// Import checkpoints
|
|
935
|
-
for (const checkpoint of sessionData.checkpoints || []) {
|
|
936
|
-
await this.saveCheckpoint(
|
|
937
|
-
newSessionId,
|
|
938
|
-
checkpoint.checkpoint_name,
|
|
939
|
-
checkpoint.checkpoint_data,
|
|
940
|
-
);
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
// Import logs
|
|
944
|
-
for (const log of sessionData.recentLogs || []) {
|
|
945
|
-
await this.logSessionEvent(
|
|
946
|
-
newSessionId,
|
|
947
|
-
log.log_level,
|
|
948
|
-
log.message,
|
|
949
|
-
log.agent_id,
|
|
950
|
-
log.data ? sessionSerializer.deserializeLogData(log.data) : null,
|
|
951
|
-
);
|
|
686
|
+
*/ async importSession(importPath) {
|
|
687
|
+
const sessionData = sessionSerializer.deserializeSessionData(await readFile(importPath, 'utf8'));
|
|
688
|
+
// Create new session with imported data
|
|
689
|
+
const newSessionId = this.createSession(sessionData.swarm_id, sessionData.swarm_name, sessionData.objective, sessionData.metadata);
|
|
690
|
+
// Import checkpoints
|
|
691
|
+
for (const checkpoint of sessionData.checkpoints || []){
|
|
692
|
+
await this.saveCheckpoint(newSessionId, checkpoint.checkpoint_name, checkpoint.checkpoint_data);
|
|
693
|
+
}
|
|
694
|
+
// Import logs
|
|
695
|
+
for (const log of sessionData.recentLogs || []){
|
|
696
|
+
await this.logSessionEvent(newSessionId, log.log_level, log.message, log.agent_id, log.data ? sessionSerializer.deserializeLogData(log.data) : null);
|
|
697
|
+
}
|
|
698
|
+
return newSessionId;
|
|
952
699
|
}
|
|
953
|
-
|
|
954
|
-
return newSessionId;
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
/**
|
|
700
|
+
/**
|
|
958
701
|
* Add a child process PID to session
|
|
959
|
-
*/
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
const childPids = session.child_pids
|
|
985
|
-
? sessionSerializer.deserializeLogData(session.child_pids)
|
|
986
|
-
: [];
|
|
987
|
-
if (!childPids.includes(pid)) {
|
|
988
|
-
childPids.push(pid);
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
const stmt = this.db.prepare(`
|
|
702
|
+
*/ async addChildPid(sessionId, pid) {
|
|
703
|
+
await this.ensureInitialized();
|
|
704
|
+
if (this.isInMemory) {
|
|
705
|
+
// Use in-memory storage
|
|
706
|
+
const session = this.memoryStore.sessions.get(sessionId);
|
|
707
|
+
if (!session) return false;
|
|
708
|
+
const childPids = session.child_pids ? sessionSerializer.deserializeLogData(session.child_pids) : [];
|
|
709
|
+
if (!childPids.includes(pid)) {
|
|
710
|
+
childPids.push(pid);
|
|
711
|
+
}
|
|
712
|
+
session.child_pids = sessionSerializer.serializeLogData(childPids);
|
|
713
|
+
session.updated_at = new Date().toISOString();
|
|
714
|
+
await this.logSessionEvent(sessionId, 'info', 'Child process added', null, {
|
|
715
|
+
pid
|
|
716
|
+
});
|
|
717
|
+
return true;
|
|
718
|
+
}
|
|
719
|
+
const session = this.db.prepare('SELECT child_pids FROM sessions WHERE id = ?').get(sessionId);
|
|
720
|
+
if (!session) return false;
|
|
721
|
+
const childPids = session.child_pids ? sessionSerializer.deserializeLogData(session.child_pids) : [];
|
|
722
|
+
if (!childPids.includes(pid)) {
|
|
723
|
+
childPids.push(pid);
|
|
724
|
+
}
|
|
725
|
+
const stmt = this.db.prepare(`
|
|
992
726
|
UPDATE sessions
|
|
993
727
|
SET child_pids = ?, updated_at = CURRENT_TIMESTAMP
|
|
994
728
|
WHERE id = ?
|
|
995
729
|
`);
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
/**
|
|
1004
|
-
* Remove a child process PID from session
|
|
1005
|
-
*/
|
|
1006
|
-
async removeChildPid(sessionId, pid) {
|
|
1007
|
-
await this.ensureInitialized();
|
|
1008
|
-
|
|
1009
|
-
if (this.isInMemory) {
|
|
1010
|
-
// Use in-memory storage
|
|
1011
|
-
const session = this.memoryStore.sessions.get(sessionId);
|
|
1012
|
-
if (!session) return false;
|
|
1013
|
-
|
|
1014
|
-
const childPids = session.child_pids
|
|
1015
|
-
? sessionSerializer.deserializeLogData(session.child_pids)
|
|
1016
|
-
: [];
|
|
1017
|
-
const index = childPids.indexOf(pid);
|
|
1018
|
-
if (index > -1) {
|
|
1019
|
-
childPids.splice(index, 1);
|
|
1020
|
-
}
|
|
1021
|
-
session.child_pids = sessionSerializer.serializeLogData(childPids);
|
|
1022
|
-
session.updated_at = new Date().toISOString();
|
|
1023
|
-
|
|
1024
|
-
await this.logSessionEvent(sessionId, 'info', 'Child process removed', null, { pid });
|
|
1025
|
-
return true;
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
// Check if database connection is still open before operations
|
|
1029
|
-
if (!this.db || !this.db.open) {
|
|
1030
|
-
console.warn('Database connection closed, cannot remove child PID during cleanup');
|
|
1031
|
-
return false;
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
const session = this.db.prepare('SELECT child_pids FROM sessions WHERE id = ?').get(sessionId);
|
|
1035
|
-
if (!session) return false;
|
|
1036
|
-
|
|
1037
|
-
const childPids = session.child_pids
|
|
1038
|
-
? sessionSerializer.deserializeLogData(session.child_pids)
|
|
1039
|
-
: [];
|
|
1040
|
-
const index = childPids.indexOf(pid);
|
|
1041
|
-
if (index > -1) {
|
|
1042
|
-
childPids.splice(index, 1);
|
|
730
|
+
stmt.run(sessionSerializer.serializeLogData(childPids), sessionId);
|
|
731
|
+
await this.logSessionEvent(sessionId, 'info', 'Child process added', null, {
|
|
732
|
+
pid
|
|
733
|
+
});
|
|
734
|
+
return true;
|
|
1043
735
|
}
|
|
1044
|
-
|
|
1045
|
-
|
|
736
|
+
/**
|
|
737
|
+
* Remove a child process PID from session
|
|
738
|
+
*/ async removeChildPid(sessionId, pid) {
|
|
739
|
+
await this.ensureInitialized();
|
|
740
|
+
if (this.isInMemory) {
|
|
741
|
+
// Use in-memory storage
|
|
742
|
+
const session = this.memoryStore.sessions.get(sessionId);
|
|
743
|
+
if (!session) return false;
|
|
744
|
+
const childPids = session.child_pids ? sessionSerializer.deserializeLogData(session.child_pids) : [];
|
|
745
|
+
const index = childPids.indexOf(pid);
|
|
746
|
+
if (index > -1) {
|
|
747
|
+
childPids.splice(index, 1);
|
|
748
|
+
}
|
|
749
|
+
session.child_pids = sessionSerializer.serializeLogData(childPids);
|
|
750
|
+
session.updated_at = new Date().toISOString();
|
|
751
|
+
await this.logSessionEvent(sessionId, 'info', 'Child process removed', null, {
|
|
752
|
+
pid
|
|
753
|
+
});
|
|
754
|
+
return true;
|
|
755
|
+
}
|
|
756
|
+
// Check if database connection is still open before operations
|
|
757
|
+
if (!this.db || !this.db.open) {
|
|
758
|
+
console.warn('Database connection closed, cannot remove child PID during cleanup');
|
|
759
|
+
return false;
|
|
760
|
+
}
|
|
761
|
+
const session = this.db.prepare('SELECT child_pids FROM sessions WHERE id = ?').get(sessionId);
|
|
762
|
+
if (!session) return false;
|
|
763
|
+
const childPids = session.child_pids ? sessionSerializer.deserializeLogData(session.child_pids) : [];
|
|
764
|
+
const index = childPids.indexOf(pid);
|
|
765
|
+
if (index > -1) {
|
|
766
|
+
childPids.splice(index, 1);
|
|
767
|
+
}
|
|
768
|
+
const stmt = this.db.prepare(`
|
|
1046
769
|
UPDATE sessions
|
|
1047
770
|
SET child_pids = ?, updated_at = CURRENT_TIMESTAMP
|
|
1048
771
|
WHERE id = ?
|
|
1049
772
|
`);
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
/**
|
|
773
|
+
stmt.run(sessionSerializer.serializeLogData(childPids), sessionId);
|
|
774
|
+
await this.logSessionEvent(sessionId, 'info', 'Child process removed', null, {
|
|
775
|
+
pid
|
|
776
|
+
});
|
|
777
|
+
return true;
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
1058
780
|
* Get all child PIDs for a session
|
|
1059
|
-
*/
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
.prepare('SELECT child_pids FROM sessions WHERE id = ?')
|
|
1078
|
-
.get(sessionId);
|
|
1079
|
-
if (!session || !session.child_pids) return [];
|
|
1080
|
-
return sessionSerializer.deserializeLogData(session.child_pids);
|
|
781
|
+
*/ async getChildPids(sessionId) {
|
|
782
|
+
await this.ensureInitialized();
|
|
783
|
+
if (this.isInMemory) {
|
|
784
|
+
// Use in-memory storage
|
|
785
|
+
const session = this.memoryStore.sessions.get(sessionId);
|
|
786
|
+
if (!session || !session.child_pids) return [];
|
|
787
|
+
return sessionSerializer.deserializeLogData(session.child_pids);
|
|
788
|
+
} else {
|
|
789
|
+
// Check if database connection is still open
|
|
790
|
+
if (!this.db || !this.db.open) {
|
|
791
|
+
console.warn('Database connection closed, cannot get child PIDs during cleanup');
|
|
792
|
+
return [];
|
|
793
|
+
}
|
|
794
|
+
// Use SQLite
|
|
795
|
+
const session = this.db.prepare('SELECT child_pids FROM sessions WHERE id = ?').get(sessionId);
|
|
796
|
+
if (!session || !session.child_pids) return [];
|
|
797
|
+
return sessionSerializer.deserializeLogData(session.child_pids);
|
|
798
|
+
}
|
|
1081
799
|
}
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
/**
|
|
800
|
+
/**
|
|
1085
801
|
* Stop a session and terminate all child processes
|
|
1086
|
-
*/
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
const sessionData = this.memoryStore.sessions.get(sessionId);
|
|
1120
|
-
if (sessionData) {
|
|
1121
|
-
sessionData.status = 'stopped';
|
|
1122
|
-
sessionData.updated_at = new Date().toISOString();
|
|
1123
|
-
}
|
|
1124
|
-
} else {
|
|
1125
|
-
// Use SQLite
|
|
1126
|
-
const stmt = this.db.prepare(`
|
|
802
|
+
*/ async stopSession(sessionId) {
|
|
803
|
+
const session = await this.getSession(sessionId);
|
|
804
|
+
if (!session) {
|
|
805
|
+
throw new Error(`Session ${sessionId} not found`);
|
|
806
|
+
}
|
|
807
|
+
// Get child PIDs
|
|
808
|
+
const childPids = await this.getChildPids(sessionId);
|
|
809
|
+
// Terminate child processes
|
|
810
|
+
for (const pid of childPids){
|
|
811
|
+
try {
|
|
812
|
+
process.kill(pid, 'SIGTERM');
|
|
813
|
+
await this.logSessionEvent(sessionId, 'info', 'Child process terminated', null, {
|
|
814
|
+
pid
|
|
815
|
+
});
|
|
816
|
+
} catch (err) {
|
|
817
|
+
// Process might already be dead
|
|
818
|
+
await this.logSessionEvent(sessionId, 'warning', 'Failed to terminate child process', null, {
|
|
819
|
+
pid,
|
|
820
|
+
error: err.message
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
// Update session status
|
|
825
|
+
if (this.isInMemory) {
|
|
826
|
+
// Use in-memory storage
|
|
827
|
+
const sessionData = this.memoryStore.sessions.get(sessionId);
|
|
828
|
+
if (sessionData) {
|
|
829
|
+
sessionData.status = 'stopped';
|
|
830
|
+
sessionData.updated_at = new Date().toISOString();
|
|
831
|
+
}
|
|
832
|
+
} else {
|
|
833
|
+
// Use SQLite
|
|
834
|
+
const stmt = this.db.prepare(`
|
|
1127
835
|
UPDATE sessions
|
|
1128
836
|
SET status = 'stopped', updated_at = CURRENT_TIMESTAMP
|
|
1129
837
|
WHERE id = ?
|
|
1130
838
|
`);
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
839
|
+
stmt.run(sessionId);
|
|
840
|
+
// Update swarm status
|
|
841
|
+
this.db.prepare('UPDATE swarms SET status = ? WHERE id = ?').run('stopped', session.swarm_id);
|
|
842
|
+
}
|
|
843
|
+
await this.logSessionEvent(sessionId, 'info', 'Session stopped');
|
|
844
|
+
return true;
|
|
1136
845
|
}
|
|
1137
|
-
|
|
1138
|
-
await this.logSessionEvent(sessionId, 'info', 'Session stopped');
|
|
1139
|
-
|
|
1140
|
-
return true;
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
|
-
/**
|
|
846
|
+
/**
|
|
1144
847
|
* Get active sessions with process information
|
|
1145
|
-
*/
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
child_pids: aliveChildPids,
|
|
1170
|
-
total_processes: 1 + aliveChildPids.length,
|
|
1171
|
-
};
|
|
1172
|
-
});
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
|
-
/**
|
|
848
|
+
*/ async getActiveSessionsWithProcessInfo() {
|
|
849
|
+
const sessions = await this.getActiveSessions();
|
|
850
|
+
// Add process info to each session
|
|
851
|
+
return sessions.map((session)=>{
|
|
852
|
+
const childPids = session.child_pids ? sessionSerializer.deserializeLogData(session.child_pids) : [];
|
|
853
|
+
const aliveChildPids = [];
|
|
854
|
+
// Check which child processes are still alive
|
|
855
|
+
for (const pid of childPids){
|
|
856
|
+
try {
|
|
857
|
+
process.kill(pid, 0); // Signal 0 just checks if process exists
|
|
858
|
+
aliveChildPids.push(pid);
|
|
859
|
+
} catch (err) {
|
|
860
|
+
// Process is dead
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
return {
|
|
864
|
+
...session,
|
|
865
|
+
parent_pid: session.parent_pid,
|
|
866
|
+
child_pids: aliveChildPids,
|
|
867
|
+
total_processes: 1 + aliveChildPids.length
|
|
868
|
+
};
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
1176
872
|
* Clean up orphaned processes
|
|
1177
|
-
*/
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
|
-
const sessions = this.db
|
|
1187
|
-
.prepare(
|
|
1188
|
-
`
|
|
873
|
+
*/ async cleanupOrphanedProcesses() {
|
|
874
|
+
await this.ensureInitialized();
|
|
875
|
+
if (this.isInMemory) {
|
|
876
|
+
// In-memory mode doesn't track orphaned processes
|
|
877
|
+
return 0;
|
|
878
|
+
}
|
|
879
|
+
const sessions = this.db.prepare(`
|
|
1189
880
|
SELECT * FROM sessions
|
|
1190
881
|
WHERE status IN ('active', 'paused')
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
cleanedCount
|
|
1205
|
-
await this.logSessionEvent(session.id, 'info', 'Orphaned session cleaned up');
|
|
1206
|
-
}
|
|
882
|
+
`).all();
|
|
883
|
+
let cleanedCount = 0;
|
|
884
|
+
for (const session of sessions){
|
|
885
|
+
// Check if parent process is still alive
|
|
886
|
+
try {
|
|
887
|
+
process.kill(session.parent_pid, 0);
|
|
888
|
+
} catch (err) {
|
|
889
|
+
// Parent is dead, clean up session
|
|
890
|
+
await this.stopSession(session.id);
|
|
891
|
+
cleanedCount++;
|
|
892
|
+
await this.logSessionEvent(session.id, 'info', 'Orphaned session cleaned up');
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
return cleanedCount;
|
|
1207
896
|
}
|
|
1208
|
-
|
|
1209
|
-
return cleanedCount;
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
/**
|
|
897
|
+
/**
|
|
1213
898
|
* Clean up and close database connection
|
|
1214
|
-
*/
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
899
|
+
*/ close() {
|
|
900
|
+
if (this.db && !this.isInMemory) {
|
|
901
|
+
this.db.close();
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
constructor(hiveMindDir = null){
|
|
905
|
+
this.hiveMindDir = hiveMindDir || path.join(cwd(), '.hive-mind');
|
|
906
|
+
this.sessionsDir = path.join(this.hiveMindDir, 'sessions');
|
|
907
|
+
this.dbPath = path.join(this.hiveMindDir, 'hive.db');
|
|
908
|
+
this.db = null;
|
|
909
|
+
this.isInMemory = false;
|
|
910
|
+
this.memoryStore = null;
|
|
911
|
+
this.initializationPromise = null;
|
|
912
|
+
// Ensure directories exist
|
|
913
|
+
this.ensureDirectories();
|
|
914
|
+
// Initialize database connection (store promise for later)
|
|
915
|
+
this.initializationPromise = this.initializeDatabase();
|
|
1218
916
|
}
|
|
1219
|
-
}
|
|
1220
917
|
}
|
|
1221
|
-
|
|
1222
918
|
// Export for use in other modules
|
|
1223
919
|
export default HiveMindSessionManager;
|