claude-flow-novice 1.6.3 → 1.6.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/.claude/commands/parse-epic.js +180 -0
- package/.claude/settings.json +12 -2
- package/.claude/settings.local.json +4 -2
- package/.claude-flow-novice/dist/mcp/mcp-server-novice.js +37 -2
- package/.claude-flow-novice/dist/mcp/transports/base.js +5 -0
- package/.claude-flow-novice/dist/mcp/transports/base.js.map +1 -0
- package/.claude-flow-novice/dist/mcp/transports/http.js +414 -0
- package/.claude-flow-novice/dist/mcp/transports/http.js.map +1 -0
- package/.claude-flow-novice/dist/mcp/transports/stdio.js +217 -0
- package/.claude-flow-novice/dist/mcp/transports/stdio.js.map +1 -0
- package/.claude-flow-novice/dist/src/cli/commands/parse-epic.js +129 -0
- package/.claude-flow-novice/dist/src/cli/commands/parse-epic.js.map +1 -0
- package/.claude-flow-novice/dist/src/cli/index.js +3 -0
- package/.claude-flow-novice/dist/src/cli/index.js.map +1 -1
- package/.claude-flow-novice/dist/src/cli/utils/epic-parser.js +266 -0
- package/.claude-flow-novice/dist/src/cli/utils/epic-parser.js.map +1 -0
- package/.claude-flow-novice/dist/src/communication/message-bus.js +105 -2
- package/.claude-flow-novice/dist/src/communication/message-bus.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/adapters/v1-coordinator-adapter.js +1 -1
- package/.claude-flow-novice/dist/src/coordination/adapters/v1-coordinator-adapter.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/cache/artifact-cache-optimizer.js +632 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/cache/artifact-cache-optimizer.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/cache/index.js +11 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/cache/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/checkpoints/checkpoint-compressor.js +318 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/checkpoints/checkpoint-compressor.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/completion/completion-detector.js +234 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/completion/completion-detector.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/completion/hierarchical-detector.js +347 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/completion/hierarchical-detector.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/completion/index.js +13 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/completion/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/completion/lamport-clock.js +173 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/completion/lamport-clock.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/completion/mesh-detector.js +526 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/completion/mesh-detector.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/completion/sdk-completion-detector.js +443 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/completion/sdk-completion-detector.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/completion/swarm-shutdown.js +366 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/completion/swarm-shutdown.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/coordinator-factory.js +287 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/coordinator-factory.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/coordinators/cascading-shutdown.example.js +364 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/coordinators/cascading-shutdown.example.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/coordinators/cascading-shutdown.js +492 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/coordinators/cascading-shutdown.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/coordinators/hierarchical-coordinator.js +786 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/coordinators/hierarchical-coordinator.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/coordinators/index.js +16 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/coordinators/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/coordinators/parent-child-manager.js +342 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/coordinators/parent-child-manager.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/coordinators/swarm-coordinator-v2.js +601 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/coordinators/swarm-coordinator-v2.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/agent-state.js +9 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/agent-state.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/dead-letter-queue.js +413 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/dead-letter-queue.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/dependency-graph.js +471 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/dependency-graph.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/dependency-node.js +379 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/dependency-node.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/dependency-resolver.js +335 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/dependency-resolver.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/help-request-metrics.js +211 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/help-request-metrics.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/index.js +33 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/message-broker.js +920 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/message-broker.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/message-router.js +385 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/message-router.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/message.js +138 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/message.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/resource-manager-safe.js +478 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/resource-manager-safe.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/state-machine-config.js +358 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/state-machine-config.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/state-machine.js +588 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/state-machine.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/state-transition.js +153 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/state-transition.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/task-scheduler.js +360 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/task-scheduler.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/topic-manager.js +337 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/core/topic-manager.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/deadlock/deadlock-detector.js +424 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/deadlock/deadlock-detector.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/deadlock/index.js +9 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/deadlock/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/deadlock/resource-manager.js +669 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/deadlock/resource-manager.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/dependency/artifact-storage.js +451 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/dependency/artifact-storage.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/dependency/cycle-detector.js +271 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/dependency/cycle-detector.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/dependency/dependency-graph.js +335 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/dependency/dependency-graph.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/dependency/dependency-manager.js +439 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/dependency/dependency-manager.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/dependency/dependency-request.js +92 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/dependency/dependency-request.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/dependency/index.js +21 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/dependency/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/dependency/topological-sort.js +223 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/dependency/topological-sort.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/help-system/help-coordinator.js +436 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/help-system/help-coordinator.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/help-system/help-matcher.js +278 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/help-system/help-matcher.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/help-system/help-request-handler.js +317 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/help-system/help-request-handler.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/help-system/help-request.js +273 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/help-system/help-request.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/help-system/index.js +15 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/help-system/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/help-system/waiting-agent-pool.js +512 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/help-system/waiting-agent-pool.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/index.js +67 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/integration/help-deadlock-integration.js +557 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/integration/help-deadlock-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/integration/index.js +14 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/integration/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/integration/message-bus-completion-integration.example.js +212 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/integration/message-bus-completion-integration.example.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/integration/message-bus-completion-integration.js +552 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/integration/message-bus-completion-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/integration/state-machine-integration.js +635 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/integration/state-machine-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/interfaces/IArtifactStorage.js +28 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/interfaces/IArtifactStorage.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/interfaces/ICoordinator.js +9 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/interfaces/ICoordinator.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/interfaces/ISessionStore.js +25 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/interfaces/ISessionStore.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/interfaces/index.js +14 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/interfaces/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/memory/artifact-adapter.js +308 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/memory/artifact-adapter.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/memory/completion-storage.js +439 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/memory/completion-storage.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/memory/dependency-graph-storage.js +540 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/memory/dependency-graph-storage.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/memory/dependency-storage.js +367 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/memory/dependency-storage.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/memory/index.js +14 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/memory/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/memory/message-storage.js +518 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/memory/message-storage.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/memory/state-storage.js +377 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/memory/state-storage.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/messaging/channel.js +371 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/messaging/channel.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/messaging/channels/dependency-channel.js +355 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/messaging/channels/dependency-channel.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/messaging/channels/help-channel.js +424 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/messaging/channels/help-channel.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/messaging/channels/index.js +16 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/messaging/channels/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/messaging/channels/state-channel.js +295 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/messaging/channels/state-channel.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/messaging/channels/task-channel.js +411 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/messaging/channels/task-channel.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/messaging/index.js +14 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/messaging/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/messaging/message-bus.js +387 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/messaging/message-bus.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/messaging/message-persistence.js +589 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/messaging/message-persistence.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/messaging/message-router.js +444 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/messaging/message-router.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/artifact-storage.js +560 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/artifact-storage.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/background-orchestrator.js +335 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/background-orchestrator.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/bash-output-monitor.js +104 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/bash-output-monitor.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/checkpoint-manager.js +847 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/checkpoint-manager.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/help-coordinator.js +470 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/help-coordinator.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/hierarchical-background-integration.js +450 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/hierarchical-background-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/index.js +13 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/message-bus-integration.js +625 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/message-bus-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/multi-level-control.js +545 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/multi-level-control.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/query-controller.js +740 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/query-controller.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/query-message-integration.js +415 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/query-message-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/session-pool-optimizer.js +615 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/session-pool-optimizer.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/state-machine-integration.js +547 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/state-machine-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/state-sdk-integration.js +342 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/sdk/state-sdk-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/security/payload-validator.js +259 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/security/payload-validator.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/truth/framework-registry.js +273 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/truth/framework-registry.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/truth/index.js +8 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/truth/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/truth/truth-config-manager.js +310 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/truth/truth-config-manager.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/truth/truth-validator.js +218 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/truth/truth-validator.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/types/sdk.js +9 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/types/sdk.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/utils/index.js +6 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/utils/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/utils/priority-queue.js +145 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/utils/priority-queue.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/utils/sdk-helpers.js +122 -0
- package/.claude-flow-novice/dist/src/coordination/archives/v2-sdk-typescript/v2/utils/sdk-helpers.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/config-translator.js +17 -43
- package/.claude-flow-novice/dist/src/coordination/config-translator.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/coordination-toggle.js +34 -76
- package/.claude-flow-novice/dist/src/coordination/coordination-toggle.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/index.js +3 -0
- package/.claude-flow-novice/dist/src/coordination/index.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/shared/core/agent-state.js +172 -0
- package/.claude-flow-novice/dist/src/coordination/shared/core/agent-state.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/shared/core/index.js +7 -0
- package/.claude-flow-novice/dist/src/coordination/shared/core/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/shared/index.js +19 -0
- package/.claude-flow-novice/dist/src/coordination/shared/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/shared/interfaces/ICoordinator.js +24 -0
- package/.claude-flow-novice/dist/src/coordination/shared/interfaces/ICoordinator.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/shared/interfaces/index.js +7 -0
- package/.claude-flow-novice/dist/src/coordination/shared/interfaces/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/shared/message-broker.js +920 -0
- package/.claude-flow-novice/dist/src/coordination/shared/message-broker.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/shared/message.js +138 -0
- package/.claude-flow-novice/dist/src/coordination/shared/message.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/shared/security/payload-validator.js +259 -0
- package/.claude-flow-novice/dist/src/coordination/shared/security/payload-validator.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/shared/transparency/index.js +17 -0
- package/.claude-flow-novice/dist/src/coordination/shared/transparency/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/shared/transparency/interfaces/transparency-system.js +19 -0
- package/.claude-flow-novice/dist/src/coordination/shared/transparency/interfaces/transparency-system.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/shared/transparency/transparency-integration.js +357 -0
- package/.claude-flow-novice/dist/src/coordination/shared/transparency/transparency-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/shared/transparency/transparency-system.js +679 -0
- package/.claude-flow-novice/dist/src/coordination/shared/transparency/transparency-system.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/shared/types/index.js +7 -0
- package/.claude-flow-novice/dist/src/coordination/shared/types/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/shared/types/sdk.js +10 -0
- package/.claude-flow-novice/dist/src/coordination/shared/types/sdk.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v1-transparency/interfaces/v1-transparency-system.js +12 -0
- package/.claude-flow-novice/dist/src/coordination/v1-transparency/interfaces/v1-transparency-system.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v1-transparency/v1-to-v2-bridge.js +433 -0
- package/.claude-flow-novice/dist/src/coordination/v1-transparency/v1-to-v2-bridge.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v1-transparency/v1-transparency-adapter.js +1468 -0
- package/.claude-flow-novice/dist/src/coordination/v1-transparency/v1-transparency-adapter.js.map +1 -0
- package/.claude-flow-novice/dist/src/feature-flags/core/FeatureFlagManager.js +52 -2
- package/.claude-flow-novice/dist/src/feature-flags/core/FeatureFlagManager.js.map +1 -1
- package/.claude-flow-novice/dist/src/mcp/mcp-server-novice.js +37 -2
- package/.claude-flow-novice/dist/src/mcp/session-manager.js +3 -1
- package/.claude-flow-novice/dist/src/mcp/session-manager.js.map +1 -1
- package/.claude-flow-novice/dist/src/monitoring/apm/apm-integration.js +5 -0
- package/.claude-flow-novice/dist/src/monitoring/apm/apm-integration.js.map +1 -1
- package/.claude-flow-novice/dist/src/providers/provider-manager.js +41 -6
- package/.claude-flow-novice/dist/src/providers/provider-manager.js.map +1 -1
- package/.claude-flow-novice/dist/src/providers/tiered-router.js +9 -17
- package/.claude-flow-novice/dist/src/providers/tiered-router.js.map +1 -1
- package/.claude-flow-novice/dist/src/utils/markdown-sanitizer.js +65 -41
- package/.claude-flow-novice/dist/src/utils/markdown-sanitizer.js.map +1 -1
- package/.claude-flow-novice/dist/src/web/api/server.js +1 -1
- package/.claude-flow-novice/dist/src/web/api/server.js.map +1 -1
- package/.claude-flow-novice/dist/src/workflows/progressive-rollout-manager.js +30 -0
- package/.claude-flow-novice/dist/src/workflows/progressive-rollout-manager.js.map +1 -1
- package/.claude-flow-novice/metrics.db +0 -0
- package/.claude-flow-novice/metrics.db-shm +0 -0
- package/.claude-flow-novice/metrics.db-wal +0 -0
- package/CLAUDE.md +72 -0
- package/config/hooks/post-edit-pipeline.js +68 -118
- package/config/hooks/pre-tool-memory-safety.js +209 -0
- package/package.json +7 -4
- package/scripts/cleanup-idle-sessions.sh +59 -0
- package/scripts/monitoring/alert-monitor.sh +220 -0
- package/scripts/monitoring/view-alerts.sh +307 -0
- package/scripts/test-provider-routing.cjs +7 -9
- package/scripts/test-zai-api.cjs +2 -2
- package/src/slash-commands/parse-epic.js +1 -1
- package/wiki/Provider-Routing.md +57 -69
- package/MEMORY_LEAK_ROOT_CAUSE.md +0 -149
|
@@ -0,0 +1,847 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Coordination V2 - SDK Checkpoint Manager
|
|
3
|
+
* Git-like state snapshots with message UUID tracking for instant recovery
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - <500ms checkpoint recovery (p99)
|
|
7
|
+
* - Auto-checkpoint on state transitions
|
|
8
|
+
* - 60% compression via MessagePack
|
|
9
|
+
* - Message UUID tracking for instant rollback
|
|
10
|
+
* - Zero data loss during recovery
|
|
11
|
+
*/ import { createHash, timingSafeEqual } from 'crypto';
|
|
12
|
+
import { promises as fs } from 'fs';
|
|
13
|
+
import * as path from 'path';
|
|
14
|
+
import * as msgpack from 'msgpack-lite';
|
|
15
|
+
import { AgentState } from '../../shared/types/sdk.js';
|
|
16
|
+
import { CheckpointCompressor } from '../checkpoints/checkpoint-compressor.js';
|
|
17
|
+
export class CheckpointManager {
|
|
18
|
+
MAX_IN_MEMORY_CHECKPOINTS = 100;
|
|
19
|
+
checkpoints = new Map();
|
|
20
|
+
sessions = new Map();
|
|
21
|
+
agentSessions = new Map();
|
|
22
|
+
sessionCheckpoints = new Map();
|
|
23
|
+
messageUUIDIndex = new Map();
|
|
24
|
+
storagePath;
|
|
25
|
+
metrics;
|
|
26
|
+
restoreLatencies = [];
|
|
27
|
+
autoCheckpointEnabled = true;
|
|
28
|
+
initialized = false;
|
|
29
|
+
storeMetrics;
|
|
30
|
+
initTime = Date.now();
|
|
31
|
+
cleanupInterval;
|
|
32
|
+
retentionHours = 24;
|
|
33
|
+
restoredCheckpoints = new Set();
|
|
34
|
+
maxCheckpointAge = 3600000;
|
|
35
|
+
compressor;
|
|
36
|
+
constructor(storagePath = '.claude-flow/checkpoints/v2', retentionHours = 24){
|
|
37
|
+
this.storagePath = storagePath;
|
|
38
|
+
this.retentionHours = retentionHours;
|
|
39
|
+
this.compressor = new CheckpointCompressor();
|
|
40
|
+
this.metrics = {
|
|
41
|
+
totalCheckpoints: 0,
|
|
42
|
+
totalRestores: 0,
|
|
43
|
+
averageCreateTimeMs: 0,
|
|
44
|
+
averageRestoreTimeMs: 0,
|
|
45
|
+
p95RestoreTimeMs: 0,
|
|
46
|
+
p99RestoreTimeMs: 0,
|
|
47
|
+
compressionRatio: 0,
|
|
48
|
+
totalStorageMb: 0
|
|
49
|
+
};
|
|
50
|
+
this.storeMetrics = {
|
|
51
|
+
totalSessions: 0,
|
|
52
|
+
totalCheckpoints: 0,
|
|
53
|
+
totalStorageMb: 0,
|
|
54
|
+
averageCompressionRatio: 0,
|
|
55
|
+
averageReadLatencyMs: 0,
|
|
56
|
+
averageWriteLatencyMs: 0,
|
|
57
|
+
totalReads: 0,
|
|
58
|
+
totalWrites: 0,
|
|
59
|
+
totalDeletions: 0,
|
|
60
|
+
uptimeMs: 0,
|
|
61
|
+
totalRestores: 0,
|
|
62
|
+
averageCreateTimeMs: 0,
|
|
63
|
+
averageRestoreTimeMs: 0,
|
|
64
|
+
compressionRatio: 0,
|
|
65
|
+
p95RestoreTimeMs: 0,
|
|
66
|
+
p99RestoreTimeMs: 0
|
|
67
|
+
};
|
|
68
|
+
this.ensureStorageDirectory();
|
|
69
|
+
// Start automatic cleanup (every 1 hour)
|
|
70
|
+
this.cleanupInterval = setInterval(()=>{
|
|
71
|
+
this.cleanExpiredCheckpoints().catch((error)=>{
|
|
72
|
+
console.error('Automatic checkpoint cleanup failed:', error);
|
|
73
|
+
});
|
|
74
|
+
}, 60 * 60 * 1000); // 1 hour
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Create a checkpoint snapshot with Git-like message UUID tracking
|
|
78
|
+
*/ async createCheckpoint(sessionId, agentId, messageUUID, state, stateSnapshot, metadata) {
|
|
79
|
+
const startTime = performance.now();
|
|
80
|
+
const checkpointId = this.generateCheckpointId(sessionId, messageUUID);
|
|
81
|
+
const timestamp = Date.now();
|
|
82
|
+
// Get previous checkpoint for delta compression
|
|
83
|
+
const previousCheckpointData = this.getLatestCheckpointData(sessionId);
|
|
84
|
+
const previousCheckpointId = previousCheckpointData?.id;
|
|
85
|
+
// Use multi-layer compression: MessagePack + Delta + Gzip
|
|
86
|
+
const compressionResult = await this.compressor.compress(stateSnapshot, sessionId, previousCheckpointId);
|
|
87
|
+
const compressedBuffer = compressionResult.compressedBuffer;
|
|
88
|
+
const compressedSize = compressionResult.compressedSize;
|
|
89
|
+
const uncompressedSize = compressionResult.uncompressedSize;
|
|
90
|
+
const compressionRatio = compressionResult.compressionRatio;
|
|
91
|
+
// Calculate checksum for integrity verification
|
|
92
|
+
const checksum = this.calculateChecksum(stateSnapshot);
|
|
93
|
+
const checkpoint = {
|
|
94
|
+
id: checkpointId,
|
|
95
|
+
sessionId,
|
|
96
|
+
agentId,
|
|
97
|
+
messageUUID,
|
|
98
|
+
state,
|
|
99
|
+
timestamp,
|
|
100
|
+
stateSnapshot,
|
|
101
|
+
metadata: {
|
|
102
|
+
reason: metadata?.reason || 'manual',
|
|
103
|
+
autoCheckpoint: metadata?.autoCheckpoint || false,
|
|
104
|
+
pendingDependencies: metadata?.pendingDependencies,
|
|
105
|
+
context: metadata?.context
|
|
106
|
+
},
|
|
107
|
+
checksum,
|
|
108
|
+
compressedSize,
|
|
109
|
+
uncompressedSize
|
|
110
|
+
};
|
|
111
|
+
// SEC-025 FIX: Enforce hard LRU limit BEFORE insertion to prevent memory exhaustion
|
|
112
|
+
// Synchronous eviction check happens BEFORE adding new checkpoint to prevent bypass
|
|
113
|
+
if (this.checkpoints.size >= this.MAX_IN_MEMORY_CHECKPOINTS) {
|
|
114
|
+
const oldest = Array.from(this.checkpoints.entries()).sort(([, a], [, b])=>a.timestamp - b.timestamp)[0];
|
|
115
|
+
if (oldest) {
|
|
116
|
+
const [oldestId, evictedCheckpoint] = oldest;
|
|
117
|
+
// Remove from memory IMMEDIATELY (synchronous enforcement)
|
|
118
|
+
this.checkpoints.delete(oldestId);
|
|
119
|
+
this.messageUUIDIndex.delete(evictedCheckpoint.messageUUID);
|
|
120
|
+
// Remove from session index
|
|
121
|
+
const sessionCheckpoints = this.sessionCheckpoints.get(evictedCheckpoint.sessionId);
|
|
122
|
+
if (sessionCheckpoints) {
|
|
123
|
+
const index = sessionCheckpoints.indexOf(oldestId);
|
|
124
|
+
if (index > -1) {
|
|
125
|
+
sessionCheckpoints.splice(index, 1);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Emit eviction event for monitoring
|
|
129
|
+
console.log(`🔄 LRU eviction: ${oldestId} (age: ${Date.now() - evictedCheckpoint.timestamp}ms, reason: MEMORY_LIMIT_ENFORCEMENT)`);
|
|
130
|
+
// Async disk cleanup (non-blocking, best-effort)
|
|
131
|
+
this.deleteCheckpointFile(oldestId).catch((error)=>{
|
|
132
|
+
console.warn(`Warning: Failed to delete evicted checkpoint file ${oldestId}: ${error}`);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// NOW safe to add new checkpoint (hard limit enforced above)
|
|
137
|
+
this.checkpoints.set(checkpointId, checkpoint);
|
|
138
|
+
// Update session index
|
|
139
|
+
if (!this.sessionCheckpoints.has(sessionId)) {
|
|
140
|
+
this.sessionCheckpoints.set(sessionId, []);
|
|
141
|
+
}
|
|
142
|
+
this.sessionCheckpoints.get(sessionId).push(checkpointId);
|
|
143
|
+
// Update message UUID index for instant lookup
|
|
144
|
+
this.messageUUIDIndex.set(messageUUID, checkpointId);
|
|
145
|
+
// Persist to disk with compression
|
|
146
|
+
await this.persistCheckpoint(checkpoint, compressedBuffer);
|
|
147
|
+
// Update metrics
|
|
148
|
+
const createTime = performance.now() - startTime;
|
|
149
|
+
this.metrics.totalCheckpoints++;
|
|
150
|
+
this.metrics.averageCreateTimeMs = (this.metrics.averageCreateTimeMs * (this.metrics.totalCheckpoints - 1) + createTime) / this.metrics.totalCheckpoints;
|
|
151
|
+
this.metrics.compressionRatio = (this.metrics.compressionRatio * (this.metrics.totalCheckpoints - 1) + compressionRatio) / this.metrics.totalCheckpoints;
|
|
152
|
+
console.log(`✅ Checkpoint created: ${checkpointId} (${createTime.toFixed(2)}ms, ${compressionRatio.toFixed(2)}x compression)`);
|
|
153
|
+
return checkpointId;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Restore from checkpoint with <500ms guarantee (p99)
|
|
157
|
+
* SEC-020: Now includes anti-replay protection
|
|
158
|
+
*/ async restoreCheckpoint(checkpointId) {
|
|
159
|
+
const startTime = performance.now();
|
|
160
|
+
// SEC-020: Anti-replay check #1 - Prevent duplicate restoration
|
|
161
|
+
if (this.restoredCheckpoints.has(checkpointId)) {
|
|
162
|
+
throw new Error(`Checkpoint ${checkpointId} already restored (replay attack detected)`);
|
|
163
|
+
}
|
|
164
|
+
const checkpoint = this.checkpoints.get(checkpointId);
|
|
165
|
+
if (!checkpoint) {
|
|
166
|
+
throw new Error(`Checkpoint ${checkpointId} not found`);
|
|
167
|
+
}
|
|
168
|
+
// SEC-020: Anti-replay check #2 - Reject stale checkpoints
|
|
169
|
+
const age = Date.now() - checkpoint.timestamp;
|
|
170
|
+
if (age > this.maxCheckpointAge) {
|
|
171
|
+
throw new Error(`Checkpoint ${checkpointId} expired (age: ${age}ms, max: ${this.maxCheckpointAge}ms)`);
|
|
172
|
+
}
|
|
173
|
+
// Verify checksum integrity
|
|
174
|
+
const currentChecksum = this.calculateChecksum(checkpoint.stateSnapshot);
|
|
175
|
+
if (currentChecksum !== checkpoint.checksum) {
|
|
176
|
+
throw new Error(`Checkpoint ${checkpointId} integrity check failed (checksum mismatch)`);
|
|
177
|
+
}
|
|
178
|
+
// SEC-020: Track restoration BEFORE applying state
|
|
179
|
+
this.restoredCheckpoints.add(checkpointId);
|
|
180
|
+
// SEC-020: Emit audit event for monitoring
|
|
181
|
+
console.log(`🔐 SEC-020: Checkpoint restoration audit - ID: ${checkpointId}, Age: ${age}ms, Timestamp: ${Date.now()}`);
|
|
182
|
+
// Restore state (already decompressed in memory)
|
|
183
|
+
const restoredState = checkpoint.stateSnapshot;
|
|
184
|
+
const latencyMs = performance.now() - startTime;
|
|
185
|
+
// Update metrics
|
|
186
|
+
this.restoreLatencies.push(latencyMs);
|
|
187
|
+
this.metrics.totalRestores++;
|
|
188
|
+
this.metrics.averageRestoreTimeMs = (this.metrics.averageRestoreTimeMs * (this.metrics.totalRestores - 1) + latencyMs) / this.metrics.totalRestores;
|
|
189
|
+
this.updatePercentileMetrics();
|
|
190
|
+
// Verify p99 latency requirement
|
|
191
|
+
if (latencyMs > 500 && this.metrics.totalRestores >= 100) {
|
|
192
|
+
console.warn(`⚠️ Checkpoint restore exceeded 500ms: ${latencyMs.toFixed(2)}ms (p99: ${this.metrics.p99RestoreTimeMs.toFixed(2)}ms)`);
|
|
193
|
+
}
|
|
194
|
+
console.log(`✅ Checkpoint restored: ${checkpointId} (${latencyMs.toFixed(2)}ms)`);
|
|
195
|
+
return {
|
|
196
|
+
success: true,
|
|
197
|
+
checkpointId,
|
|
198
|
+
restoredState,
|
|
199
|
+
latencyMs,
|
|
200
|
+
messageUUID: checkpoint.messageUUID
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Restore from message UUID (instant lookup)
|
|
205
|
+
*/ async restoreFromMessageUUID(messageUUID) {
|
|
206
|
+
const checkpointId = this.messageUUIDIndex.get(messageUUID);
|
|
207
|
+
if (!checkpointId) {
|
|
208
|
+
throw new Error(`No checkpoint found for message UUID: ${messageUUID}`);
|
|
209
|
+
}
|
|
210
|
+
return this.restoreCheckpoint(checkpointId);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Auto-checkpoint on state transition
|
|
214
|
+
*/ async autoCheckpointOnTransition(sessionId, agentId, messageUUID, fromState, toState, stateSnapshot) {
|
|
215
|
+
if (!this.autoCheckpointEnabled) {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
// Auto-checkpoint on critical state transitions
|
|
219
|
+
const criticalTransitions = [
|
|
220
|
+
`${AgentState.WORKING}->${AgentState.WAITING}`,
|
|
221
|
+
`${AgentState.WORKING}->${AgentState.BLOCKED}`,
|
|
222
|
+
`${AgentState.WORKING}->${AgentState.COMPLETED}`,
|
|
223
|
+
`${AgentState.WAITING}->${AgentState.WORKING}`,
|
|
224
|
+
`${AgentState.BLOCKED}->${AgentState.WORKING}`
|
|
225
|
+
];
|
|
226
|
+
const transition = `${fromState}->${toState}`;
|
|
227
|
+
if (!criticalTransitions.includes(transition)) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
const checkpointId = await this.createCheckpoint(sessionId, agentId, messageUUID, toState, stateSnapshot, {
|
|
231
|
+
reason: `State transition: ${transition}`,
|
|
232
|
+
autoCheckpoint: true
|
|
233
|
+
});
|
|
234
|
+
console.log(`🔄 Auto-checkpoint on transition: ${transition} -> ${checkpointId}`);
|
|
235
|
+
return checkpointId;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Get latest checkpoint for a session (internal use - returns CheckpointData)
|
|
239
|
+
*/ getLatestCheckpointData(sessionId) {
|
|
240
|
+
const checkpointIds = this.sessionCheckpoints.get(sessionId) || [];
|
|
241
|
+
const checkpoints = checkpointIds.map((id)=>this.checkpoints.get(id)).filter(Boolean).sort((a, b)=>b.timestamp - a.timestamp);
|
|
242
|
+
return checkpoints.length > 0 ? checkpoints[0] : null;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Delete checkpoint
|
|
246
|
+
*/ async deleteCheckpoint(checkpointId) {
|
|
247
|
+
const checkpoint = this.checkpoints.get(checkpointId);
|
|
248
|
+
if (!checkpoint) {
|
|
249
|
+
throw new Error(`Checkpoint ${checkpointId} not found`);
|
|
250
|
+
}
|
|
251
|
+
// Remove from memory
|
|
252
|
+
this.checkpoints.delete(checkpointId);
|
|
253
|
+
this.messageUUIDIndex.delete(checkpoint.messageUUID);
|
|
254
|
+
// Remove from session index
|
|
255
|
+
const sessionCheckpoints = this.sessionCheckpoints.get(checkpoint.sessionId);
|
|
256
|
+
if (sessionCheckpoints) {
|
|
257
|
+
const index = sessionCheckpoints.indexOf(checkpointId);
|
|
258
|
+
if (index > -1) {
|
|
259
|
+
sessionCheckpoints.splice(index, 1);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// Remove from disk
|
|
263
|
+
await this.deleteCheckpointFile(checkpointId);
|
|
264
|
+
console.log(`🗑️ Checkpoint deleted: ${checkpointId}`);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Delete checkpoint file from disk (helper for async cleanup)
|
|
268
|
+
*/ async deleteCheckpointFile(checkpointId) {
|
|
269
|
+
const filePath = this.getCheckpointFilePath(checkpointId);
|
|
270
|
+
try {
|
|
271
|
+
await fs.unlink(filePath);
|
|
272
|
+
} catch (error) {
|
|
273
|
+
console.warn(`Warning: Failed to delete checkpoint file: ${error}`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Cleanup old checkpoints (ISessionStore interface)
|
|
278
|
+
*/ async cleanup(retentionHours = 24, maxCheckpointsPerSession = 10) {
|
|
279
|
+
const cutoffTime = Date.now() - retentionHours * 60 * 60 * 1000;
|
|
280
|
+
let deletedCount = 0;
|
|
281
|
+
for (const [sessionId, checkpointIds] of Array.from(this.sessionCheckpoints.entries())){
|
|
282
|
+
const checkpoints = checkpointIds.map((id)=>this.checkpoints.get(id)).filter(Boolean).sort((a, b)=>b.timestamp - a.timestamp);
|
|
283
|
+
// Delete checkpoints older than retention period
|
|
284
|
+
for (const checkpoint of checkpoints){
|
|
285
|
+
if (checkpoint.timestamp < cutoffTime) {
|
|
286
|
+
await this.deleteCheckpoint(checkpoint.id);
|
|
287
|
+
deletedCount++;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// Keep only max checkpoints per session
|
|
291
|
+
if (checkpoints.length > maxCheckpointsPerSession) {
|
|
292
|
+
const toDelete = checkpoints.slice(maxCheckpointsPerSession);
|
|
293
|
+
for (const checkpoint of toDelete){
|
|
294
|
+
await this.deleteCheckpoint(checkpoint.id);
|
|
295
|
+
deletedCount++;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
console.log(`🧹 Cleaned up ${deletedCount} old checkpoints (retention: ${retentionHours}h, max/session: ${maxCheckpointsPerSession})`);
|
|
300
|
+
return deletedCount;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Automatic cleanup of expired checkpoints (internal)
|
|
304
|
+
*/ async cleanExpiredCheckpoints() {
|
|
305
|
+
const now = Date.now();
|
|
306
|
+
const retentionMs = this.retentionHours * 60 * 60 * 1000;
|
|
307
|
+
let deletedCount = 0;
|
|
308
|
+
const entries = Array.from(this.checkpoints.entries());
|
|
309
|
+
for (const [checkpointId, checkpoint] of entries){
|
|
310
|
+
const age = now - checkpoint.timestamp;
|
|
311
|
+
if (age > retentionMs) {
|
|
312
|
+
await this.deleteCheckpoint(checkpointId);
|
|
313
|
+
deletedCount++;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (deletedCount > 0) {
|
|
317
|
+
console.log(`🔄 Periodic cleanup: removed ${deletedCount} expired checkpoints (retention: ${this.retentionHours}h)`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Get checkpoint metrics (internal - CheckpointMetrics format)
|
|
322
|
+
*/ getCheckpointMetrics() {
|
|
323
|
+
return {
|
|
324
|
+
...this.metrics
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Get compression details for a specific checkpoint (for testing)
|
|
329
|
+
*/ getCheckpointCompressionDetails(checkpointId) {
|
|
330
|
+
const checkpoint = this.checkpoints.get(checkpointId);
|
|
331
|
+
if (!checkpoint) {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
compressedSize: checkpoint.compressedSize,
|
|
336
|
+
uncompressedSize: checkpoint.uncompressedSize,
|
|
337
|
+
compressionRatio: 1 - checkpoint.compressedSize / checkpoint.uncompressedSize
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Enable/disable auto-checkpointing
|
|
342
|
+
*/ setAutoCheckpointEnabled(enabled) {
|
|
343
|
+
this.autoCheckpointEnabled = enabled;
|
|
344
|
+
console.log(`Auto-checkpointing ${enabled ? 'enabled' : 'disabled'}`);
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Load checkpoints from disk on initialization
|
|
348
|
+
*/ async loadFromDisk() {
|
|
349
|
+
try {
|
|
350
|
+
const files = await fs.readdir(this.storagePath);
|
|
351
|
+
const checkpointFiles = files.filter((f)=>f.endsWith('.msgpack'));
|
|
352
|
+
let loadedCount = 0;
|
|
353
|
+
for (const file of checkpointFiles){
|
|
354
|
+
try {
|
|
355
|
+
const filePath = path.join(this.storagePath, file);
|
|
356
|
+
const buffer = await fs.readFile(filePath);
|
|
357
|
+
const checkpoint = msgpack.decode(buffer);
|
|
358
|
+
// Restore indexes
|
|
359
|
+
this.checkpoints.set(checkpoint.id, checkpoint);
|
|
360
|
+
this.messageUUIDIndex.set(checkpoint.messageUUID, checkpoint.id);
|
|
361
|
+
if (!this.sessionCheckpoints.has(checkpoint.sessionId)) {
|
|
362
|
+
this.sessionCheckpoints.set(checkpoint.sessionId, []);
|
|
363
|
+
}
|
|
364
|
+
this.sessionCheckpoints.get(checkpoint.sessionId).push(checkpoint.id);
|
|
365
|
+
loadedCount++;
|
|
366
|
+
} catch (error) {
|
|
367
|
+
console.warn(`Warning: Failed to load checkpoint from ${file}: ${error}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
console.log(`📁 Loaded ${loadedCount} checkpoints from disk`);
|
|
371
|
+
} catch (error) {
|
|
372
|
+
console.warn('Warning: Failed to load checkpoints from disk:', error);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Private: Persist checkpoint to disk with compression
|
|
377
|
+
*/ async persistCheckpoint(checkpoint, compressedBuffer) {
|
|
378
|
+
const filePath = this.getCheckpointFilePath(checkpoint.id);
|
|
379
|
+
// Encode entire checkpoint with MessagePack
|
|
380
|
+
const checkpointBuffer = msgpack.encode(checkpoint);
|
|
381
|
+
await fs.writeFile(filePath, checkpointBuffer);
|
|
382
|
+
// Update storage metrics
|
|
383
|
+
const stats = await fs.stat(filePath);
|
|
384
|
+
this.metrics.totalStorageMb += stats.size / (1024 * 1024);
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Private: Generate checkpoint ID
|
|
388
|
+
*/ generateCheckpointId(sessionId, messageUUID) {
|
|
389
|
+
const hash = createHash('sha256').update(`${sessionId}:${messageUUID}:${Date.now()}`).digest('hex').substring(0, 12);
|
|
390
|
+
return `cp_${hash}`;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Private: Calculate checksum for integrity verification
|
|
394
|
+
*/ calculateChecksum(data) {
|
|
395
|
+
const dataString = JSON.stringify(data, Object.keys(data).sort());
|
|
396
|
+
return createHash('sha256').update(dataString).digest('hex');
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Private: Get checkpoint file path
|
|
400
|
+
*/ getCheckpointFilePath(checkpointId) {
|
|
401
|
+
return path.join(this.storagePath, `${checkpointId}.msgpack`);
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Private: Update percentile metrics
|
|
405
|
+
*/ updatePercentileMetrics() {
|
|
406
|
+
if (this.restoreLatencies.length === 0) return;
|
|
407
|
+
const sorted = [
|
|
408
|
+
...this.restoreLatencies
|
|
409
|
+
].sort((a, b)=>a - b);
|
|
410
|
+
const p95Index = Math.floor(sorted.length * 0.95);
|
|
411
|
+
const p99Index = Math.floor(sorted.length * 0.99);
|
|
412
|
+
this.metrics.p95RestoreTimeMs = sorted[p95Index] || 0;
|
|
413
|
+
this.metrics.p99RestoreTimeMs = sorted[p99Index] || 0;
|
|
414
|
+
// Keep only last 1000 samples for percentile calculation
|
|
415
|
+
if (this.restoreLatencies.length > 1000) {
|
|
416
|
+
this.restoreLatencies = this.restoreLatencies.slice(-1000);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Private: Ensure storage directory exists
|
|
421
|
+
*/ async ensureStorageDirectory() {
|
|
422
|
+
try {
|
|
423
|
+
await fs.access(this.storagePath);
|
|
424
|
+
} catch {
|
|
425
|
+
await fs.mkdir(this.storagePath, {
|
|
426
|
+
recursive: true
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
// ===========================
|
|
431
|
+
// ISessionStore Implementation
|
|
432
|
+
// ===========================
|
|
433
|
+
/**
|
|
434
|
+
* Store agent session
|
|
435
|
+
*/ async storeSession(session) {
|
|
436
|
+
const startTime = performance.now();
|
|
437
|
+
this.sessions.set(session.sessionId, session);
|
|
438
|
+
this.agentSessions.set(session.agentId, session.sessionId);
|
|
439
|
+
// Persist to disk
|
|
440
|
+
const sessionPath = path.join(this.storagePath, 'sessions', `${session.sessionId}.msgpack`);
|
|
441
|
+
await fs.mkdir(path.dirname(sessionPath), {
|
|
442
|
+
recursive: true
|
|
443
|
+
});
|
|
444
|
+
const buffer = msgpack.encode(session);
|
|
445
|
+
await fs.writeFile(sessionPath, buffer);
|
|
446
|
+
// Update metrics
|
|
447
|
+
const writeTime = performance.now() - startTime;
|
|
448
|
+
this.storeMetrics.totalSessions = this.sessions.size;
|
|
449
|
+
this.storeMetrics.totalWrites++;
|
|
450
|
+
this.storeMetrics.averageWriteLatencyMs = (this.storeMetrics.averageWriteLatencyMs * (this.storeMetrics.totalWrites - 1) + writeTime) / this.storeMetrics.totalWrites;
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Retrieve agent session
|
|
454
|
+
*/ async getSession(sessionId) {
|
|
455
|
+
const startTime = performance.now();
|
|
456
|
+
let session = this.sessions.get(sessionId);
|
|
457
|
+
if (!session) {
|
|
458
|
+
// Try loading from disk
|
|
459
|
+
try {
|
|
460
|
+
const sessionPath = path.join(this.storagePath, 'sessions', `${sessionId}.msgpack`);
|
|
461
|
+
const buffer = await fs.readFile(sessionPath);
|
|
462
|
+
session = msgpack.decode(buffer);
|
|
463
|
+
if (session) {
|
|
464
|
+
this.sessions.set(sessionId, session);
|
|
465
|
+
this.agentSessions.set(session.agentId, sessionId);
|
|
466
|
+
}
|
|
467
|
+
} catch {
|
|
468
|
+
// Session not found
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
// Update metrics
|
|
472
|
+
const readTime = performance.now() - startTime;
|
|
473
|
+
this.storeMetrics.totalReads++;
|
|
474
|
+
this.storeMetrics.averageReadLatencyMs = (this.storeMetrics.averageReadLatencyMs * (this.storeMetrics.totalReads - 1) + readTime) / this.storeMetrics.totalReads;
|
|
475
|
+
return session || null;
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Retrieve session by agent ID
|
|
479
|
+
*/ async getSessionByAgentId(agentId) {
|
|
480
|
+
const sessionId = this.agentSessions.get(agentId);
|
|
481
|
+
if (!sessionId) {
|
|
482
|
+
return null;
|
|
483
|
+
}
|
|
484
|
+
return this.getSession(sessionId);
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Update existing session
|
|
488
|
+
*/ async updateSession(sessionId, updates) {
|
|
489
|
+
const session = await this.getSession(sessionId);
|
|
490
|
+
if (!session) {
|
|
491
|
+
throw new Error(`Session ${sessionId} not found`);
|
|
492
|
+
}
|
|
493
|
+
const updatedSession = {
|
|
494
|
+
...session,
|
|
495
|
+
...updates
|
|
496
|
+
};
|
|
497
|
+
await this.storeSession(updatedSession);
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Delete session and associated checkpoints
|
|
501
|
+
*/ async deleteSession(sessionId) {
|
|
502
|
+
const session = await this.getSession(sessionId);
|
|
503
|
+
if (session) {
|
|
504
|
+
this.agentSessions.delete(session.agentId);
|
|
505
|
+
}
|
|
506
|
+
this.sessions.delete(sessionId);
|
|
507
|
+
// Delete all associated checkpoints
|
|
508
|
+
const checkpointIds = this.sessionCheckpoints.get(sessionId) || [];
|
|
509
|
+
for (const checkpointId of checkpointIds){
|
|
510
|
+
await this.deleteCheckpoint(checkpointId);
|
|
511
|
+
}
|
|
512
|
+
this.sessionCheckpoints.delete(sessionId);
|
|
513
|
+
// Delete session file
|
|
514
|
+
try {
|
|
515
|
+
const sessionPath = path.join(this.storagePath, 'sessions', `${sessionId}.msgpack`);
|
|
516
|
+
await fs.unlink(sessionPath);
|
|
517
|
+
this.storeMetrics.totalDeletions++;
|
|
518
|
+
} catch {
|
|
519
|
+
// File may not exist
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Query sessions with filtering
|
|
524
|
+
*/ async querySessions(filter) {
|
|
525
|
+
let sessions = Array.from(this.sessions.values());
|
|
526
|
+
if (filter.agentId) {
|
|
527
|
+
sessions = sessions.filter((s)=>s.agentId === filter.agentId);
|
|
528
|
+
}
|
|
529
|
+
if (filter.sessionId) {
|
|
530
|
+
sessions = sessions.filter((s)=>s.sessionId === filter.sessionId);
|
|
531
|
+
}
|
|
532
|
+
if (filter.state && filter.state.length > 0) {
|
|
533
|
+
sessions = sessions.filter((s)=>filter.state.includes(s.state));
|
|
534
|
+
}
|
|
535
|
+
if (filter.isPaused !== undefined) {
|
|
536
|
+
sessions = sessions.filter((s)=>s.isPaused === filter.isPaused);
|
|
537
|
+
}
|
|
538
|
+
if (filter.priorityMin !== undefined) {
|
|
539
|
+
sessions = sessions.filter((s)=>s.priority >= filter.priorityMin);
|
|
540
|
+
}
|
|
541
|
+
if (filter.priorityMax !== undefined) {
|
|
542
|
+
sessions = sessions.filter((s)=>s.priority <= filter.priorityMax);
|
|
543
|
+
}
|
|
544
|
+
// Apply pagination
|
|
545
|
+
const offset = filter.offset || 0;
|
|
546
|
+
const limit = filter.limit || sessions.length;
|
|
547
|
+
return sessions.slice(offset, offset + limit);
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* List all sessions
|
|
551
|
+
*/ async listSessions(limit, offset) {
|
|
552
|
+
return this.querySessions({
|
|
553
|
+
limit,
|
|
554
|
+
offset
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Store checkpoint (ISessionStore interface)
|
|
559
|
+
*/ async storeCheckpoint(checkpoint) {
|
|
560
|
+
const startTime = performance.now();
|
|
561
|
+
// Convert Checkpoint to CheckpointData
|
|
562
|
+
const checkpointData = {
|
|
563
|
+
id: checkpoint.id,
|
|
564
|
+
sessionId: checkpoint.sessionId,
|
|
565
|
+
agentId: checkpoint.agentId,
|
|
566
|
+
messageUUID: checkpoint.messageUUID,
|
|
567
|
+
state: checkpoint.state,
|
|
568
|
+
timestamp: checkpoint.timestamp instanceof Date ? checkpoint.timestamp.getTime() : checkpoint.timestamp,
|
|
569
|
+
stateSnapshot: checkpoint.metadata?.context || {},
|
|
570
|
+
metadata: {
|
|
571
|
+
reason: checkpoint.metadata.reason,
|
|
572
|
+
autoCheckpoint: checkpoint.metadata.autoCheckpoint,
|
|
573
|
+
pendingDependencies: checkpoint.metadata.pendingDependencies,
|
|
574
|
+
context: checkpoint.metadata.context
|
|
575
|
+
},
|
|
576
|
+
checksum: this.calculateChecksum(checkpoint.metadata?.context || {}),
|
|
577
|
+
compressedSize: 0,
|
|
578
|
+
uncompressedSize: 0
|
|
579
|
+
};
|
|
580
|
+
// Calculate compression sizes
|
|
581
|
+
const uncompressedData = JSON.stringify(checkpointData.stateSnapshot);
|
|
582
|
+
checkpointData.uncompressedSize = Buffer.byteLength(uncompressedData, 'utf8');
|
|
583
|
+
const compressedBuffer = msgpack.encode(checkpointData.stateSnapshot);
|
|
584
|
+
checkpointData.compressedSize = compressedBuffer.length;
|
|
585
|
+
// Store in memory
|
|
586
|
+
this.checkpoints.set(checkpoint.id, checkpointData);
|
|
587
|
+
this.messageUUIDIndex.set(checkpoint.messageUUID, checkpoint.id);
|
|
588
|
+
if (!this.sessionCheckpoints.has(checkpoint.sessionId)) {
|
|
589
|
+
this.sessionCheckpoints.set(checkpoint.sessionId, []);
|
|
590
|
+
}
|
|
591
|
+
this.sessionCheckpoints.get(checkpoint.sessionId).push(checkpoint.id);
|
|
592
|
+
// Persist to disk
|
|
593
|
+
await this.persistCheckpoint(checkpointData, compressedBuffer);
|
|
594
|
+
// Update metrics
|
|
595
|
+
const writeTime = performance.now() - startTime;
|
|
596
|
+
this.storeMetrics.totalCheckpoints = this.checkpoints.size;
|
|
597
|
+
this.storeMetrics.totalWrites++;
|
|
598
|
+
this.storeMetrics.averageWriteLatencyMs = (this.storeMetrics.averageWriteLatencyMs * (this.storeMetrics.totalWrites - 1) + writeTime) / this.storeMetrics.totalWrites;
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Get checkpoint (ISessionStore interface)
|
|
602
|
+
*/ async getCheckpoint(checkpointId) {
|
|
603
|
+
const startTime = performance.now();
|
|
604
|
+
const checkpointData = this.checkpoints.get(checkpointId);
|
|
605
|
+
if (!checkpointData) {
|
|
606
|
+
return null;
|
|
607
|
+
}
|
|
608
|
+
// Convert CheckpointData to Checkpoint
|
|
609
|
+
const checkpoint = {
|
|
610
|
+
id: checkpointData.id,
|
|
611
|
+
sessionId: checkpointData.sessionId,
|
|
612
|
+
agentId: checkpointData.agentId,
|
|
613
|
+
messageUUID: checkpointData.messageUUID,
|
|
614
|
+
state: checkpointData.state,
|
|
615
|
+
timestamp: new Date(checkpointData.timestamp),
|
|
616
|
+
metadata: {
|
|
617
|
+
reason: checkpointData.metadata.reason,
|
|
618
|
+
autoCheckpoint: checkpointData.metadata.autoCheckpoint,
|
|
619
|
+
pendingDependencies: checkpointData.metadata.pendingDependencies,
|
|
620
|
+
context: checkpointData.metadata.context
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
// Update metrics
|
|
624
|
+
const readTime = performance.now() - startTime;
|
|
625
|
+
this.storeMetrics.totalReads++;
|
|
626
|
+
this.storeMetrics.averageReadLatencyMs = (this.storeMetrics.averageReadLatencyMs * (this.storeMetrics.totalReads - 1) + readTime) / this.storeMetrics.totalReads;
|
|
627
|
+
return checkpoint;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Get checkpoint by message UUID
|
|
631
|
+
*/ async getCheckpointByMessageUUID(messageUUID) {
|
|
632
|
+
const checkpointId = this.messageUUIDIndex.get(messageUUID);
|
|
633
|
+
if (!checkpointId) {
|
|
634
|
+
return null;
|
|
635
|
+
}
|
|
636
|
+
return this.getCheckpoint(checkpointId);
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Get latest checkpoint for session (ISessionStore interface)
|
|
640
|
+
*/ async getLatestCheckpoint(sessionId) {
|
|
641
|
+
const checkpointData = this.getLatestCheckpointData(sessionId);
|
|
642
|
+
if (!checkpointData) {
|
|
643
|
+
return null;
|
|
644
|
+
}
|
|
645
|
+
return this.getCheckpoint(checkpointData.id);
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* List checkpoints (ISessionStore interface)
|
|
649
|
+
*/ async listCheckpoints(sessionId, limit) {
|
|
650
|
+
const checkpointIds = this.sessionCheckpoints.get(sessionId) || [];
|
|
651
|
+
const checkpoints = [];
|
|
652
|
+
for (const id of checkpointIds){
|
|
653
|
+
const checkpoint = await this.getCheckpoint(id);
|
|
654
|
+
if (checkpoint) {
|
|
655
|
+
checkpoints.push(checkpoint);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
// Sort by timestamp (newest first)
|
|
659
|
+
checkpoints.sort((a, b)=>b.timestamp.getTime() - a.timestamp.getTime());
|
|
660
|
+
if (limit) {
|
|
661
|
+
return checkpoints.slice(0, limit);
|
|
662
|
+
}
|
|
663
|
+
return checkpoints;
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Query checkpoints with filtering
|
|
667
|
+
*/ async queryCheckpoints(filter) {
|
|
668
|
+
let checkpoints = [];
|
|
669
|
+
// Collect all checkpoints
|
|
670
|
+
for (const checkpointId of Array.from(this.checkpoints.keys())){
|
|
671
|
+
const checkpoint = await this.getCheckpoint(checkpointId);
|
|
672
|
+
if (checkpoint) {
|
|
673
|
+
checkpoints.push(checkpoint);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
// Apply filters
|
|
677
|
+
if (filter.checkpointId) {
|
|
678
|
+
checkpoints = checkpoints.filter((c)=>c.id === filter.checkpointId);
|
|
679
|
+
}
|
|
680
|
+
if (filter.sessionId) {
|
|
681
|
+
checkpoints = checkpoints.filter((c)=>c.sessionId === filter.sessionId);
|
|
682
|
+
}
|
|
683
|
+
if (filter.agentId) {
|
|
684
|
+
checkpoints = checkpoints.filter((c)=>c.agentId === filter.agentId);
|
|
685
|
+
}
|
|
686
|
+
if (filter.messageUUID) {
|
|
687
|
+
checkpoints = checkpoints.filter((c)=>c.messageUUID === filter.messageUUID);
|
|
688
|
+
}
|
|
689
|
+
if (filter.state && filter.state.length > 0) {
|
|
690
|
+
checkpoints = checkpoints.filter((c)=>filter.state.includes(c.state));
|
|
691
|
+
}
|
|
692
|
+
if (filter.autoCheckpoint !== undefined) {
|
|
693
|
+
checkpoints = checkpoints.filter((c)=>c.metadata.autoCheckpoint === filter.autoCheckpoint);
|
|
694
|
+
}
|
|
695
|
+
if (filter.createdAfter) {
|
|
696
|
+
checkpoints = checkpoints.filter((c)=>c.timestamp >= filter.createdAfter);
|
|
697
|
+
}
|
|
698
|
+
if (filter.createdBefore) {
|
|
699
|
+
checkpoints = checkpoints.filter((c)=>c.timestamp <= filter.createdBefore);
|
|
700
|
+
}
|
|
701
|
+
// Apply pagination
|
|
702
|
+
const offset = filter.offset || 0;
|
|
703
|
+
const limit = filter.limit || checkpoints.length;
|
|
704
|
+
return checkpoints.slice(offset, offset + limit);
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Verify checkpoint integrity (timing-safe comparison)
|
|
708
|
+
*/ async verifyCheckpointIntegrity(checkpointId) {
|
|
709
|
+
const checkpointData = this.checkpoints.get(checkpointId);
|
|
710
|
+
if (!checkpointData) {
|
|
711
|
+
return false;
|
|
712
|
+
}
|
|
713
|
+
const currentChecksum = this.calculateChecksum(checkpointData.stateSnapshot);
|
|
714
|
+
// Use timing-safe comparison to prevent timing attacks
|
|
715
|
+
const currentBuf = Buffer.from(currentChecksum, 'hex');
|
|
716
|
+
const storedBuf = Buffer.from(checkpointData.checksum, 'hex');
|
|
717
|
+
if (currentBuf.length !== storedBuf.length) {
|
|
718
|
+
return false;
|
|
719
|
+
}
|
|
720
|
+
return timingSafeEqual(currentBuf, storedBuf);
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Compact storage (ISessionStore interface)
|
|
724
|
+
*/ async compact() {
|
|
725
|
+
// Remove duplicate checkpoints and optimize storage
|
|
726
|
+
const sessionsToCompact = Array.from(this.sessionCheckpoints.keys());
|
|
727
|
+
let deletedCount = 0;
|
|
728
|
+
for (const sessionId of sessionsToCompact){
|
|
729
|
+
const checkpointIds = this.sessionCheckpoints.get(sessionId) || [];
|
|
730
|
+
const checkpoints = checkpointIds.map((id)=>this.checkpoints.get(id)).filter(Boolean).sort((a, b)=>b.timestamp - a.timestamp);
|
|
731
|
+
// Keep only the last 10 checkpoints per session
|
|
732
|
+
if (checkpoints.length > 10) {
|
|
733
|
+
const toDelete = checkpoints.slice(10);
|
|
734
|
+
for (const checkpoint of toDelete){
|
|
735
|
+
await this.deleteCheckpoint(checkpoint.id);
|
|
736
|
+
deletedCount++;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
console.log(`✅ Storage compaction complete (deleted ${deletedCount} checkpoints)`);
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Get storage metrics (ISessionStore interface)
|
|
744
|
+
*/ getMetrics() {
|
|
745
|
+
this.storeMetrics.uptimeMs = Date.now() - this.initTime;
|
|
746
|
+
this.storeMetrics.totalSessions = this.sessions.size;
|
|
747
|
+
this.storeMetrics.totalCheckpoints = this.checkpoints.size;
|
|
748
|
+
this.storeMetrics.totalStorageMb = this.metrics.totalStorageMb;
|
|
749
|
+
this.storeMetrics.averageCompressionRatio = this.metrics.compressionRatio;
|
|
750
|
+
return {
|
|
751
|
+
...this.storeMetrics,
|
|
752
|
+
totalRestores: this.metrics.totalRestores,
|
|
753
|
+
averageCreateTimeMs: this.metrics.averageCreateTimeMs,
|
|
754
|
+
averageRestoreTimeMs: this.metrics.averageRestoreTimeMs,
|
|
755
|
+
compressionRatio: this.metrics.compressionRatio,
|
|
756
|
+
p95RestoreTimeMs: this.metrics.p95RestoreTimeMs,
|
|
757
|
+
p99RestoreTimeMs: this.metrics.p99RestoreTimeMs
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Health check
|
|
762
|
+
*/ async healthCheck() {
|
|
763
|
+
const startTime = performance.now();
|
|
764
|
+
const errors = [];
|
|
765
|
+
try {
|
|
766
|
+
// Check storage directory accessibility
|
|
767
|
+
await fs.access(this.storagePath);
|
|
768
|
+
// Check if we can write to storage
|
|
769
|
+
const testPath = path.join(this.storagePath, '.health-check');
|
|
770
|
+
await fs.writeFile(testPath, 'test');
|
|
771
|
+
await fs.unlink(testPath);
|
|
772
|
+
const latencyMs = performance.now() - startTime;
|
|
773
|
+
return {
|
|
774
|
+
healthy: true,
|
|
775
|
+
latencyMs
|
|
776
|
+
};
|
|
777
|
+
} catch (error) {
|
|
778
|
+
errors.push(`Storage health check failed: ${error}`);
|
|
779
|
+
return {
|
|
780
|
+
healthy: false,
|
|
781
|
+
latencyMs: performance.now() - startTime,
|
|
782
|
+
errors
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Initialize storage backend
|
|
788
|
+
*/ async initialize() {
|
|
789
|
+
if (this.initialized) {
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
await this.ensureStorageDirectory();
|
|
793
|
+
await fs.mkdir(path.join(this.storagePath, 'sessions'), {
|
|
794
|
+
recursive: true
|
|
795
|
+
});
|
|
796
|
+
await this.loadFromDisk();
|
|
797
|
+
this.initialized = true;
|
|
798
|
+
console.log('✅ CheckpointManager initialized');
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* Close storage connections
|
|
802
|
+
*/ async close() {
|
|
803
|
+
// Stop automatic cleanup
|
|
804
|
+
if (this.cleanupInterval) {
|
|
805
|
+
clearInterval(this.cleanupInterval);
|
|
806
|
+
this.cleanupInterval = undefined;
|
|
807
|
+
}
|
|
808
|
+
// Persist any pending data
|
|
809
|
+
this.initialized = false;
|
|
810
|
+
console.log('✅ CheckpointManager closed');
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* Shutdown with cleanup
|
|
814
|
+
*/ async shutdown() {
|
|
815
|
+
if (this.cleanupInterval) {
|
|
816
|
+
clearInterval(this.cleanupInterval);
|
|
817
|
+
this.cleanupInterval = undefined;
|
|
818
|
+
}
|
|
819
|
+
// Final cleanup before shutdown
|
|
820
|
+
await this.cleanExpiredCheckpoints();
|
|
821
|
+
this.initialized = false;
|
|
822
|
+
console.log('✅ CheckpointManager shutdown complete');
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Get memory usage statistics
|
|
826
|
+
*/ getMemoryStats() {
|
|
827
|
+
const now = Date.now();
|
|
828
|
+
let oldestAge = 0;
|
|
829
|
+
const values = Array.from(this.checkpoints.values());
|
|
830
|
+
for (const checkpoint of values){
|
|
831
|
+
const age = now - checkpoint.timestamp;
|
|
832
|
+
oldestAge = Math.max(oldestAge, age);
|
|
833
|
+
}
|
|
834
|
+
return {
|
|
835
|
+
checkpointCount: this.checkpoints.size,
|
|
836
|
+
memoryUsageMB: process.memoryUsage().heapUsed / 1024 / 1024,
|
|
837
|
+
oldestCheckpointAge: oldestAge
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
* Check if storage is ready
|
|
842
|
+
*/ isReady() {
|
|
843
|
+
return this.initialized;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
//# sourceMappingURL=checkpoint-manager.js.map
|