claude-flow-novice 1.6.1 → 1.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/cfn-loop/product-owner.md +54 -4
- package/.claude/commands/cfn-claude-sync.md +303 -0
- package/.claude/commands/cfn-loop-epic.md +290 -0
- package/.claude/commands/cfn-loop-single.md +168 -0
- package/.claude/commands/cfn-loop-sprints.md +384 -0
- package/.claude/commands/cfn-loop.md +180 -0
- package/.claude/commands/metrics-summary.md +58 -0
- package/.claude/commands/parse-epic.md +357 -0
- package/.claude/settings.json +5 -4
- package/.claude/settings.local.json +9 -2
- package/.claude-flow-novice/.claude/agents/cfn-loop/product-owner.md +792 -0
- package/.claude-flow-novice/dist/mcp/server.js +21 -2
- package/.claude-flow-novice/dist/src/api/auth-service.js +84 -38
- package/.claude-flow-novice/dist/src/api/auth-service.js.map +1 -1
- package/.claude-flow-novice/dist/src/api/claude-client.js +138 -3
- package/.claude-flow-novice/dist/src/api/claude-client.js.map +1 -1
- package/.claude-flow-novice/dist/src/cfn-loop/phase-orchestrator-example.js +1 -1
- package/.claude-flow-novice/dist/src/cfn-loop/scope-control.js +247 -0
- package/.claude-flow-novice/dist/src/cfn-loop/scope-control.js.map +1 -0
- package/.claude-flow-novice/dist/src/cli/commands/swarm.js +32 -15
- package/.claude-flow-novice/dist/src/cli/commands/swarm.js.map +1 -1
- package/.claude-flow-novice/dist/src/cli/commands/transparency.js +455 -0
- package/.claude-flow-novice/dist/src/cli/commands/transparency.js.map +1 -0
- package/.claude-flow-novice/dist/src/cli/simple-commands/init/templates/CLAUDE.md +129 -13
- package/.claude-flow-novice/dist/src/components/visualizations/index.js +9 -0
- package/.claude-flow-novice/dist/src/components/visualizations/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/adapters/v1-coordinator-adapter.js +462 -0
- package/.claude-flow-novice/dist/src/coordination/adapters/v1-coordinator-adapter.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/config-translator.js +248 -0
- package/.claude-flow-novice/dist/src/coordination/config-translator.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/coordination-toggle.js +287 -0
- package/.claude-flow-novice/dist/src/coordination/coordination-toggle.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/distributed-consensus.js +68 -9
- package/.claude-flow-novice/dist/src/coordination/distributed-consensus.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/feature-flags.js +166 -0
- package/.claude-flow-novice/dist/src/coordination/feature-flags.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/queen-agent.js +18 -4
- package/.claude-flow-novice/dist/src/coordination/queen-agent.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/role-assignment.js +6 -110
- package/.claude-flow-novice/dist/src/coordination/role-assignment.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/v2/cache/artifact-cache-optimizer.js +632 -0
- package/.claude-flow-novice/dist/src/coordination/v2/cache/artifact-cache-optimizer.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/cache/index.js +11 -0
- package/.claude-flow-novice/dist/src/coordination/v2/cache/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/checkpoints/checkpoint-compressor.js +318 -0
- package/.claude-flow-novice/dist/src/coordination/v2/checkpoints/checkpoint-compressor.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/cascading-shutdown.example.js +364 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/cascading-shutdown.example.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/cascading-shutdown.js +492 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/cascading-shutdown.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/hierarchical-coordinator.js +786 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/hierarchical-coordinator.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/index.js +16 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/parent-child-manager.js +342 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/parent-child-manager.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/swarm-coordinator-v2.js +601 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/swarm-coordinator-v2.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/core/help-request-metrics.js +211 -0
- package/.claude-flow-novice/dist/src/coordination/v2/core/help-request-metrics.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/core/index.js +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/core/index.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/v2/core/message-broker.js +365 -6
- package/.claude-flow-novice/dist/src/coordination/v2/core/message-broker.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/v2/core/resource-manager-safe.js +478 -0
- package/.claude-flow-novice/dist/src/coordination/v2/core/resource-manager-safe.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/core/state-machine-config.js +5 -2
- package/.claude-flow-novice/dist/src/coordination/v2/core/state-machine-config.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/v2/core/state-machine.js +189 -0
- package/.claude-flow-novice/dist/src/coordination/v2/core/state-machine.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/v2/deadlock/deadlock-detector.js +424 -0
- package/.claude-flow-novice/dist/src/coordination/v2/deadlock/deadlock-detector.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/deadlock/index.js +9 -0
- package/.claude-flow-novice/dist/src/coordination/v2/deadlock/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/deadlock/resource-manager.js +669 -0
- package/.claude-flow-novice/dist/src/coordination/v2/deadlock/resource-manager.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/artifact-storage.js +451 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/artifact-storage.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/cycle-detector.js +271 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/cycle-detector.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/dependency-graph.js +335 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/dependency-graph.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/dependency-manager.js +439 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/dependency-manager.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/dependency-request.js +92 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/dependency-request.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/index.js +21 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/topological-sort.js +223 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/topological-sort.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/help-coordinator.js +436 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/help-coordinator.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/help-matcher.js +278 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/help-matcher.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/help-request-handler.js +317 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/help-request-handler.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/help-request.js +273 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/help-request.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/index.js +15 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/waiting-agent-pool.js +512 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/waiting-agent-pool.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/index.js +6 -0
- package/.claude-flow-novice/dist/src/coordination/v2/index.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/v2/integration/help-deadlock-integration.js +557 -0
- package/.claude-flow-novice/dist/src/coordination/v2/integration/help-deadlock-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/integration/index.js +14 -0
- package/.claude-flow-novice/dist/src/coordination/v2/integration/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/integration/message-bus-completion-integration.example.js +212 -0
- package/.claude-flow-novice/dist/src/coordination/v2/integration/message-bus-completion-integration.example.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/integration/message-bus-completion-integration.js +552 -0
- package/.claude-flow-novice/dist/src/coordination/v2/integration/message-bus-completion-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/memory/dependency-storage.js +367 -0
- package/.claude-flow-novice/dist/src/coordination/v2/memory/dependency-storage.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/memory/index.js +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/memory/index.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channel.js +371 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channel.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channels/dependency-channel.js +355 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channels/dependency-channel.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channels/help-channel.js +424 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channels/help-channel.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channels/index.js +16 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channels/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channels/state-channel.js +295 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channels/state-channel.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channels/task-channel.js +411 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channels/task-channel.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/index.js +14 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/message-bus.js +387 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/message-bus.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/message-persistence.js +589 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/message-persistence.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/message-router.js +444 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/message-router.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/checkpoint-manager.js +29 -8
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/checkpoint-manager.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/help-coordinator.js +470 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/help-coordinator.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/hierarchical-background-integration.js +450 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/hierarchical-background-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/index.js +5 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/index.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/multi-level-control.js +545 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/multi-level-control.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/query-controller.js +44 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/query-controller.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/query-message-integration.js +415 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/query-message-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/session-pool-optimizer.js +615 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/session-pool-optimizer.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/security/payload-validator.js +259 -0
- package/.claude-flow-novice/dist/src/coordination/v2/security/payload-validator.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/transparency/index.js +17 -0
- package/.claude-flow-novice/dist/src/coordination/v2/transparency/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/transparency/transparency-integration.js +357 -0
- package/.claude-flow-novice/dist/src/coordination/v2/transparency/transparency-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/transparency/transparency-system.js +679 -0
- package/.claude-flow-novice/dist/src/coordination/v2/transparency/transparency-system.js.map +1 -0
- package/.claude-flow-novice/dist/src/core/agent-manager.js +30 -0
- package/.claude-flow-novice/dist/src/core/agent-manager.js.map +1 -1
- package/.claude-flow-novice/dist/src/mcp/server.js +21 -2
- package/.claude-flow-novice/dist/src/mcp/server.js.map +1 -1
- package/.claude-flow-novice/dist/src/monitoring/apm/apm-integration.js +719 -0
- package/.claude-flow-novice/dist/src/monitoring/apm/apm-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/monitoring/apm/datadog-collector.js +363 -0
- package/.claude-flow-novice/dist/src/monitoring/apm/datadog-collector.js.map +1 -0
- package/.claude-flow-novice/dist/src/monitoring/apm/index.js +97 -0
- package/.claude-flow-novice/dist/src/monitoring/apm/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/monitoring/apm/newrelic-collector.js +384 -0
- package/.claude-flow-novice/dist/src/monitoring/apm/newrelic-collector.js.map +1 -0
- package/.claude-flow-novice/dist/src/monitoring/apm/performance-optimizer.js +612 -0
- package/.claude-flow-novice/dist/src/monitoring/apm/performance-optimizer.js.map +1 -0
- package/.claude-flow-novice/dist/src/monitoring/metrics-collector.js +282 -0
- package/.claude-flow-novice/dist/src/monitoring/metrics-collector.js.map +1 -0
- package/.claude-flow-novice/dist/src/observability/metrics-counter.js +268 -0
- package/.claude-flow-novice/dist/src/observability/metrics-counter.js.map +1 -0
- package/.claude-flow-novice/dist/src/observability/metrics-storage.js +265 -0
- package/.claude-flow-novice/dist/src/observability/metrics-storage.js.map +1 -0
- package/.claude-flow-novice/dist/src/observability/telemetry.js +26 -0
- package/.claude-flow-novice/dist/src/observability/telemetry.js.map +1 -1
- package/.claude-flow-novice/dist/src/providers/tiered-router.js +64 -10
- package/.claude-flow-novice/dist/src/providers/tiered-router.js.map +1 -1
- package/.claude-flow-novice/dist/src/providers/zai-provider.js +196 -97
- package/.claude-flow-novice/dist/src/providers/zai-provider.js.map +1 -1
- package/.claude-flow-novice/dist/src/slash-commands/cfn-claude-sync.js +533 -0
- package/.claude-flow-novice/dist/src/slash-commands/index.js +5 -0
- package/.claude-flow-novice/dist/src/slash-commands/metrics-summary-class.js +74 -0
- package/.claude-flow-novice/dist/src/slash-commands/metrics-summary.js +335 -0
- package/.claude-flow-novice/dist/src/slash-commands/register-all-commands.js +12 -0
- package/.claude-flow-novice/dist/src/verification/checkpoint-compression-demo.js +96 -0
- package/.claude-flow-novice/dist/src/verification/checkpoint-compression-demo.js.map +1 -0
- package/.claude-flow-novice/dist/src/verification/checkpoint-compression.js +406 -0
- package/.claude-flow-novice/dist/src/verification/checkpoint-compression.js.map +1 -0
- package/.claude-flow-novice/dist/src/verification/checkpoint-manager.js +35 -5
- package/.claude-flow-novice/dist/src/verification/checkpoint-manager.js.map +1 -1
- package/.claude-flow-novice/dist/src/web/api/apm-routes.js +355 -0
- package/.claude-flow-novice/dist/src/web/api/apm-routes.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/config/api-config.js +186 -0
- package/.claude-flow-novice/dist/src/web/api/config/api-config.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/middleware/auth.js +205 -0
- package/.claude-flow-novice/dist/src/web/api/middleware/auth.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/middleware/cache.js +262 -0
- package/.claude-flow-novice/dist/src/web/api/middleware/cache.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/middleware/error-handler.js +250 -0
- package/.claude-flow-novice/dist/src/web/api/middleware/error-handler.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/middleware/request-logger.js +217 -0
- package/.claude-flow-novice/dist/src/web/api/middleware/request-logger.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/middleware/validation.js +325 -0
- package/.claude-flow-novice/dist/src/web/api/middleware/validation.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/routes/events.js +465 -0
- package/.claude-flow-novice/dist/src/web/api/routes/events.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/routes/hierarchy.js +302 -0
- package/.claude-flow-novice/dist/src/web/api/routes/hierarchy.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/routes/index.js +14 -0
- package/.claude-flow-novice/dist/src/web/api/routes/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/routes/metrics.js +561 -0
- package/.claude-flow-novice/dist/src/web/api/routes/metrics.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/routes/status.js +450 -0
- package/.claude-flow-novice/dist/src/web/api/routes/status.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/server.js +451 -0
- package/.claude-flow-novice/dist/src/web/api/server.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/dashboard/hooks/useWebSocket.js +385 -0
- package/.claude-flow-novice/dist/src/web/dashboard/hooks/useWebSocket.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/dashboard/index.js +87 -0
- package/.claude-flow-novice/dist/src/web/dashboard/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/dashboard/types.js +6 -0
- package/.claude-flow-novice/dist/src/web/dashboard/types.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/frontend/src/utils/security.js +425 -0
- package/.claude-flow-novice/dist/src/web/frontend/src/utils/security.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/security/security-middleware.js +379 -0
- package/.claude-flow-novice/dist/src/web/security/security-middleware.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/websocket/apm-websocket-handler.js +441 -0
- package/.claude-flow-novice/dist/src/web/websocket/apm-websocket-handler.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/websocket/websocket-manager.js +255 -1
- package/.claude-flow-novice/dist/src/web/websocket/websocket-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/AGENT_PERFORMANCE_GUIDELINES.md +88 -0
- package/CLAUDE.md +60 -3
- package/MEMORY_LEAK_ROOT_CAUSE.md +149 -0
- package/README.md +27 -0
- package/config/hooks/post-edit-pipeline.js +36 -2
- package/examples/metrics-counter-demo.ts +106 -0
- package/examples/persistent-metrics-demo.ts +83 -0
- package/examples/phase-5-multi-level-control.ts +282 -0
- package/examples/session-pool-optimizer-example.ts +311 -0
- package/package.json +18 -4
- package/scripts/check-routing-stats.cjs +122 -0
- package/scripts/monitor-loop.sh +65 -0
- package/scripts/monitor-memory.sh +47 -0
- package/scripts/monitor.py +43 -0
- package/scripts/pre-publish-validation.cjs +212 -0
- package/scripts/test-provider-routing.cjs +228 -0
- package/scripts/test-routing-telemetry.cjs +147 -0
- package/scripts/test-zai-10k.cjs +81 -0
- package/scripts/test-zai-api.cjs +191 -0
- package/scripts/test-zai-diagnostic.cjs +151 -0
- package/scripts/test-zai-final.cjs +128 -0
- package/scripts/test-zai-with-env.cjs +85 -0
- package/scripts/validate-coordination-cli.js +69 -0
- package/scripts/validate-coordination-toggle-integration.cjs +501 -0
- package/src/cli/simple-commands/init/templates/CLAUDE.md +29 -0
- package/src/observability/metrics-counter.ts +347 -0
- package/src/observability/metrics-storage.ts +356 -0
- package/src/observability/telemetry.ts +658 -0
- package/src/slash-commands/cfn-claude-sync.js +533 -0
- package/src/slash-commands/index.js +5 -0
- package/src/slash-commands/metrics-summary-class.js +74 -0
- package/src/slash-commands/metrics-summary.js +335 -0
- package/src/slash-commands/register-all-commands.js +12 -0
|
@@ -0,0 +1,669 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Coordination V2 - Resource Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages resource allocation and deadlock prevention through:
|
|
5
|
+
* - Resource allocation tracking with <5% overhead
|
|
6
|
+
* - Circular wait detection (Wait-For-Graph)
|
|
7
|
+
* - Resource ordering enforcement
|
|
8
|
+
* - Timeout-based automatic resource release
|
|
9
|
+
* - Thread-safe concurrent access control
|
|
10
|
+
*
|
|
11
|
+
* Performance targets:
|
|
12
|
+
* - Allocation tracking overhead: <5%
|
|
13
|
+
* - Lock acquisition: <10ms (p95)
|
|
14
|
+
* - Deadlock detection: <500ms (p95)
|
|
15
|
+
*
|
|
16
|
+
* @module coordination/v2/deadlock/resource-manager
|
|
17
|
+
*/ import { EventEmitter } from 'node:events';
|
|
18
|
+
import { Logger } from '../../../core/logger.js';
|
|
19
|
+
/**
|
|
20
|
+
* Resource types that can be allocated to agents
|
|
21
|
+
*/ export var ResourceType = /*#__PURE__*/ function(ResourceType) {
|
|
22
|
+
/** Memory/token budget allocation */ ResourceType["MEMORY"] = "memory";
|
|
23
|
+
/** File access lock */ ResourceType["FILE"] = "file";
|
|
24
|
+
/** API rate limit quota */ ResourceType["API_QUOTA"] = "api_quota";
|
|
25
|
+
/** Database connection */ ResourceType["DATABASE"] = "database";
|
|
26
|
+
/** Network socket */ ResourceType["NETWORK"] = "network";
|
|
27
|
+
/** Compute/CPU allocation */ ResourceType["COMPUTE"] = "compute";
|
|
28
|
+
/** Generic custom resource */ ResourceType["CUSTOM"] = "custom";
|
|
29
|
+
return ResourceType;
|
|
30
|
+
}({});
|
|
31
|
+
/**
|
|
32
|
+
* Resource Manager
|
|
33
|
+
*
|
|
34
|
+
* Thread-safe resource allocation manager with deadlock prevention.
|
|
35
|
+
* Tracks resource ownership, detects circular waits, and enforces
|
|
36
|
+
* resource ordering to prevent deadlocks.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const manager = new ResourceManager(config);
|
|
41
|
+
* await manager.initialize();
|
|
42
|
+
*
|
|
43
|
+
* // Acquire resource
|
|
44
|
+
* await manager.acquire('agent-1', 'file:/path/to/file', 10);
|
|
45
|
+
*
|
|
46
|
+
* // Release resource
|
|
47
|
+
* await manager.release('agent-1', 'file:/path/to/file');
|
|
48
|
+
*
|
|
49
|
+
* // Check for deadlocks
|
|
50
|
+
* const cycles = manager.detectCircularWaits();
|
|
51
|
+
* if (cycles.length > 0) {
|
|
52
|
+
* console.log('Deadlock detected:', cycles);
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
55
|
+
*/ export class ResourceManager extends EventEmitter {
|
|
56
|
+
logger;
|
|
57
|
+
config;
|
|
58
|
+
/** Active resource allocations: resourceId -> allocation */ allocations = new Map();
|
|
59
|
+
/** Resource wait queues: resourceId -> queue of requests */ waitQueues = new Map();
|
|
60
|
+
/** Agent resource holdings: agentId -> Set of resourceIds */ agentResources = new Map();
|
|
61
|
+
/** Wait-for graph edges for deadlock detection */ waitForGraph = new Map();
|
|
62
|
+
/** Resource ordering for deadlock prevention (resourceId -> order number) */ resourceOrder = new Map();
|
|
63
|
+
nextOrderNumber = 0;
|
|
64
|
+
/** Performance tracking */ overheadSamples = [];
|
|
65
|
+
MAX_SAMPLES = 1000;
|
|
66
|
+
/** Metrics */ metrics = {
|
|
67
|
+
totalAllocations: 0,
|
|
68
|
+
activeAllocations: 0,
|
|
69
|
+
totalRequests: 0,
|
|
70
|
+
pendingRequests: 0,
|
|
71
|
+
deadlocksDetected: 0,
|
|
72
|
+
timeoutReleases: 0,
|
|
73
|
+
averageOverheadMs: 0,
|
|
74
|
+
p95OverheadMs: 0,
|
|
75
|
+
allocationsByType: new Map()
|
|
76
|
+
};
|
|
77
|
+
/** Cleanup timers */ cleanupTimer;
|
|
78
|
+
deadlockCheckTimer;
|
|
79
|
+
/** Thread safety lock (using mutex pattern) */ operationLock = Promise.resolve();
|
|
80
|
+
constructor(config = {}){
|
|
81
|
+
super();
|
|
82
|
+
this.logger = new Logger({
|
|
83
|
+
level: 'info',
|
|
84
|
+
format: 'text',
|
|
85
|
+
destination: 'console'
|
|
86
|
+
}, {
|
|
87
|
+
namespace: 'ResourceManager'
|
|
88
|
+
});
|
|
89
|
+
this.config = {
|
|
90
|
+
defaultTimeoutMs: config.defaultTimeoutMs ?? 30000,
|
|
91
|
+
maxResourcesPerAgent: config.maxResourcesPerAgent ?? 100,
|
|
92
|
+
enableDeadlockDetection: config.enableDeadlockDetection ?? true,
|
|
93
|
+
deadlockCheckIntervalMs: config.deadlockCheckIntervalMs ?? 5000,
|
|
94
|
+
enableResourceOrdering: config.enableResourceOrdering ?? true,
|
|
95
|
+
cleanupIntervalMs: config.cleanupIntervalMs ?? 10000
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Initialize the resource manager
|
|
100
|
+
*/ async initialize() {
|
|
101
|
+
this.logger.info('Initializing ResourceManager', {
|
|
102
|
+
config: this.config
|
|
103
|
+
});
|
|
104
|
+
// Start cleanup timer
|
|
105
|
+
this.cleanupTimer = setInterval(()=>{
|
|
106
|
+
this.cleanup().catch((err)=>{
|
|
107
|
+
this.logger.error('Cleanup failed', err);
|
|
108
|
+
});
|
|
109
|
+
}, this.config.cleanupIntervalMs);
|
|
110
|
+
// Start deadlock detection timer
|
|
111
|
+
if (this.config.enableDeadlockDetection) {
|
|
112
|
+
this.deadlockCheckTimer = setInterval(()=>{
|
|
113
|
+
this.checkForDeadlocks().catch((err)=>{
|
|
114
|
+
this.logger.error('Deadlock check failed', err);
|
|
115
|
+
});
|
|
116
|
+
}, this.config.deadlockCheckIntervalMs);
|
|
117
|
+
}
|
|
118
|
+
this.emit('initialized');
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Shutdown the resource manager
|
|
122
|
+
*/ async shutdown() {
|
|
123
|
+
this.logger.info('Shutting down ResourceManager');
|
|
124
|
+
// Clear timers
|
|
125
|
+
if (this.cleanupTimer) {
|
|
126
|
+
clearInterval(this.cleanupTimer);
|
|
127
|
+
this.cleanupTimer = undefined;
|
|
128
|
+
}
|
|
129
|
+
if (this.deadlockCheckTimer) {
|
|
130
|
+
clearInterval(this.deadlockCheckTimer);
|
|
131
|
+
this.deadlockCheckTimer = undefined;
|
|
132
|
+
}
|
|
133
|
+
// Release all resources
|
|
134
|
+
await this.withLock(async ()=>{
|
|
135
|
+
const allAllocations = Array.from(this.allocations.entries());
|
|
136
|
+
for (const [resourceId, allocation] of allAllocations){
|
|
137
|
+
await this.releaseInternal(allocation.holderId, resourceId);
|
|
138
|
+
}
|
|
139
|
+
this.allocations.clear();
|
|
140
|
+
this.waitQueues.clear();
|
|
141
|
+
this.agentResources.clear();
|
|
142
|
+
this.waitForGraph.clear();
|
|
143
|
+
});
|
|
144
|
+
this.emit('shutdown');
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Acquire a resource for an agent
|
|
148
|
+
*
|
|
149
|
+
* @param agentId - Agent requesting the resource
|
|
150
|
+
* @param resourceId - Resource to acquire
|
|
151
|
+
* @param priority - Request priority (higher = more urgent)
|
|
152
|
+
* @param timeoutMs - Acquisition timeout (optional, uses default if not specified)
|
|
153
|
+
* @returns Promise that resolves when resource is acquired
|
|
154
|
+
* @throws Error if acquisition times out or fails
|
|
155
|
+
*/ async acquire(agentId, resourceId, priority = 0, timeoutMs) {
|
|
156
|
+
const startTime = performance.now();
|
|
157
|
+
const timeout = timeoutMs ?? this.config.defaultTimeoutMs;
|
|
158
|
+
const request = {
|
|
159
|
+
agentId,
|
|
160
|
+
resourceId,
|
|
161
|
+
priority,
|
|
162
|
+
timestamp: new Date(),
|
|
163
|
+
enqueuedAt: Date.now(),
|
|
164
|
+
timeoutMs: timeout
|
|
165
|
+
};
|
|
166
|
+
this.metrics.totalRequests++;
|
|
167
|
+
try {
|
|
168
|
+
await this.withLock(async ()=>{
|
|
169
|
+
// Check if agent already holds this resource
|
|
170
|
+
if (this.allocations.get(resourceId)?.holderId === agentId) {
|
|
171
|
+
this.logger.debug('Agent already holds resource', {
|
|
172
|
+
agentId,
|
|
173
|
+
resourceId
|
|
174
|
+
});
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
// Check resource limits
|
|
178
|
+
const agentResourceCount = this.agentResources.get(agentId)?.size ?? 0;
|
|
179
|
+
if (agentResourceCount >= this.config.maxResourcesPerAgent) {
|
|
180
|
+
throw new Error(`Agent ${agentId} has reached maximum resource limit (${this.config.maxResourcesPerAgent})`);
|
|
181
|
+
}
|
|
182
|
+
// Check resource ordering if enabled
|
|
183
|
+
if (this.config.enableResourceOrdering) {
|
|
184
|
+
this.enforceResourceOrdering(agentId, resourceId);
|
|
185
|
+
}
|
|
186
|
+
// Try to allocate immediately
|
|
187
|
+
if (!this.allocations.has(resourceId)) {
|
|
188
|
+
await this.allocateResource(agentId, resourceId, priority);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
// Resource is held, add to wait queue
|
|
192
|
+
await this.addToWaitQueue(request);
|
|
193
|
+
});
|
|
194
|
+
// Wait for resource to become available
|
|
195
|
+
await this.waitForResource(agentId, resourceId, timeout);
|
|
196
|
+
// Track overhead
|
|
197
|
+
const overhead = performance.now() - startTime;
|
|
198
|
+
this.trackOverhead(overhead);
|
|
199
|
+
this.logger.debug('Resource acquired', {
|
|
200
|
+
agentId,
|
|
201
|
+
resourceId,
|
|
202
|
+
overheadMs: overhead.toFixed(2)
|
|
203
|
+
});
|
|
204
|
+
} catch (error) {
|
|
205
|
+
this.logger.error('Resource acquisition failed', {
|
|
206
|
+
agentId,
|
|
207
|
+
resourceId,
|
|
208
|
+
error: error instanceof Error ? error.message : String(error)
|
|
209
|
+
});
|
|
210
|
+
throw error;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Release a resource held by an agent
|
|
215
|
+
*
|
|
216
|
+
* @param agentId - Agent releasing the resource
|
|
217
|
+
* @param resourceId - Resource to release
|
|
218
|
+
*/ async release(agentId, resourceId) {
|
|
219
|
+
await this.withLock(async ()=>{
|
|
220
|
+
await this.releaseInternal(agentId, resourceId);
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Release all resources held by an agent
|
|
225
|
+
*
|
|
226
|
+
* @param agentId - Agent ID
|
|
227
|
+
*/ async releaseAll(agentId) {
|
|
228
|
+
await this.withLock(async ()=>{
|
|
229
|
+
const resources = this.agentResources.get(agentId);
|
|
230
|
+
if (!resources) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
this.logger.info('Releasing all resources for agent', {
|
|
234
|
+
agentId,
|
|
235
|
+
resourceCount: resources.size
|
|
236
|
+
});
|
|
237
|
+
// Release all resources
|
|
238
|
+
const releases = Array.from(resources).map((resourceId)=>this.releaseInternal(agentId, resourceId));
|
|
239
|
+
await Promise.all(releases);
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Get current resource allocations
|
|
244
|
+
*
|
|
245
|
+
* @returns Map of resourceId -> allocation
|
|
246
|
+
*/ getAllocations() {
|
|
247
|
+
return new Map(this.allocations);
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Get resources held by a specific agent
|
|
251
|
+
*
|
|
252
|
+
* @param agentId - Agent ID
|
|
253
|
+
* @returns Set of resource IDs held by the agent
|
|
254
|
+
*/ getAgentResources(agentId) {
|
|
255
|
+
return new Set(this.agentResources.get(agentId) ?? []);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Detect circular waits in the wait-for graph
|
|
259
|
+
*
|
|
260
|
+
* @returns Array of detected circular wait cycles
|
|
261
|
+
*/ detectCircularWaits() {
|
|
262
|
+
const cycles = [];
|
|
263
|
+
const visited = new Set();
|
|
264
|
+
const recursionStack = new Set();
|
|
265
|
+
const path = [];
|
|
266
|
+
// DFS-based cycle detection
|
|
267
|
+
const detectCycle = (agentId)=>{
|
|
268
|
+
visited.add(agentId);
|
|
269
|
+
recursionStack.add(agentId);
|
|
270
|
+
path.push(agentId);
|
|
271
|
+
const edges = this.waitForGraph.get(agentId) ?? [];
|
|
272
|
+
for (const edge of edges){
|
|
273
|
+
if (!visited.has(edge.holder)) {
|
|
274
|
+
if (detectCycle(edge.holder)) {
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
} else if (recursionStack.has(edge.holder)) {
|
|
278
|
+
// Cycle detected
|
|
279
|
+
const cycleStartIndex = path.indexOf(edge.holder);
|
|
280
|
+
const cycleAgents = path.slice(cycleStartIndex);
|
|
281
|
+
const cycleResources = edges.filter((e)=>cycleAgents.includes(e.holder)).map((e)=>e.resourceId);
|
|
282
|
+
cycles.push({
|
|
283
|
+
agents: cycleAgents,
|
|
284
|
+
resources: Array.from(new Set(cycleResources)),
|
|
285
|
+
detectedAt: new Date(),
|
|
286
|
+
path: [
|
|
287
|
+
...path,
|
|
288
|
+
edge.holder
|
|
289
|
+
]
|
|
290
|
+
});
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
path.pop();
|
|
295
|
+
recursionStack.delete(agentId);
|
|
296
|
+
return false;
|
|
297
|
+
};
|
|
298
|
+
// Check all agents
|
|
299
|
+
const allAgents = Array.from(this.waitForGraph.keys());
|
|
300
|
+
for (const agentId of allAgents){
|
|
301
|
+
if (!visited.has(agentId)) {
|
|
302
|
+
detectCycle(agentId);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (cycles.length > 0) {
|
|
306
|
+
this.metrics.deadlocksDetected += cycles.length;
|
|
307
|
+
this.logger.warn('Circular wait cycles detected', {
|
|
308
|
+
count: cycles.length,
|
|
309
|
+
cycles: cycles.map((c)=>({
|
|
310
|
+
agents: c.agents,
|
|
311
|
+
resources: c.resources
|
|
312
|
+
}))
|
|
313
|
+
});
|
|
314
|
+
this.emit('deadlock-detected', {
|
|
315
|
+
cycles
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
return cycles;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Get current metrics
|
|
322
|
+
*
|
|
323
|
+
* @returns Current resource manager metrics
|
|
324
|
+
*/ getMetrics() {
|
|
325
|
+
// Update current state
|
|
326
|
+
this.metrics.activeAllocations = this.allocations.size;
|
|
327
|
+
this.metrics.pendingRequests = Array.from(this.waitQueues.values()).reduce((sum, queue)=>sum + queue.length, 0);
|
|
328
|
+
// Calculate overhead percentiles
|
|
329
|
+
if (this.overheadSamples.length > 0) {
|
|
330
|
+
const sorted = [
|
|
331
|
+
...this.overheadSamples
|
|
332
|
+
].sort((a, b)=>a - b);
|
|
333
|
+
this.metrics.averageOverheadMs = sorted.reduce((sum, val)=>sum + val, 0) / sorted.length;
|
|
334
|
+
const p95Index = Math.floor(sorted.length * 0.95);
|
|
335
|
+
this.metrics.p95OverheadMs = sorted[p95Index] ?? 0;
|
|
336
|
+
}
|
|
337
|
+
return {
|
|
338
|
+
...this.metrics
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Get health status
|
|
343
|
+
*/ async getHealthStatus() {
|
|
344
|
+
const metrics = this.getMetrics();
|
|
345
|
+
const cycles = this.detectCircularWaits();
|
|
346
|
+
return {
|
|
347
|
+
healthy: cycles.length === 0,
|
|
348
|
+
error: cycles.length > 0 ? `${cycles.length} deadlocks detected` : undefined,
|
|
349
|
+
metrics: {
|
|
350
|
+
activeAllocations: metrics.activeAllocations,
|
|
351
|
+
pendingRequests: metrics.pendingRequests,
|
|
352
|
+
deadlocksDetected: metrics.deadlocksDetected,
|
|
353
|
+
overheadPercentage: (metrics.averageOverheadMs / 100 * 100).toFixed(2)
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
// ==================== PRIVATE METHODS ====================
|
|
358
|
+
/**
|
|
359
|
+
* Thread-safe operation wrapper
|
|
360
|
+
*/ async withLock(operation) {
|
|
361
|
+
// Chain operations to ensure serial execution
|
|
362
|
+
const currentLock = this.operationLock;
|
|
363
|
+
let resolveLock;
|
|
364
|
+
this.operationLock = new Promise((resolve)=>{
|
|
365
|
+
resolveLock = resolve;
|
|
366
|
+
});
|
|
367
|
+
try {
|
|
368
|
+
await currentLock;
|
|
369
|
+
return await operation();
|
|
370
|
+
} finally{
|
|
371
|
+
resolveLock();
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Internal resource allocation (assumes lock is held)
|
|
376
|
+
*/ async allocateResource(agentId, resourceId, priority) {
|
|
377
|
+
// Get or assign resource order
|
|
378
|
+
if (!this.resourceOrder.has(resourceId)) {
|
|
379
|
+
this.resourceOrder.set(resourceId, this.nextOrderNumber++);
|
|
380
|
+
}
|
|
381
|
+
// Determine resource type from ID
|
|
382
|
+
const type = this.getResourceType(resourceId);
|
|
383
|
+
// Create allocation
|
|
384
|
+
const allocation = {
|
|
385
|
+
resourceId,
|
|
386
|
+
type,
|
|
387
|
+
holderId: agentId,
|
|
388
|
+
allocatedAt: new Date(),
|
|
389
|
+
priority
|
|
390
|
+
};
|
|
391
|
+
this.allocations.set(resourceId, allocation);
|
|
392
|
+
// Track agent resources
|
|
393
|
+
if (!this.agentResources.has(agentId)) {
|
|
394
|
+
this.agentResources.set(agentId, new Set());
|
|
395
|
+
}
|
|
396
|
+
this.agentResources.get(agentId).add(resourceId);
|
|
397
|
+
// Update metrics
|
|
398
|
+
this.metrics.totalAllocations++;
|
|
399
|
+
const currentCount = this.metrics.allocationsByType.get(type) ?? 0;
|
|
400
|
+
this.metrics.allocationsByType.set(type, currentCount + 1);
|
|
401
|
+
// Remove from wait graph
|
|
402
|
+
this.removeFromWaitGraph(agentId, resourceId);
|
|
403
|
+
this.logger.debug('Resource allocated', {
|
|
404
|
+
agentId,
|
|
405
|
+
resourceId,
|
|
406
|
+
type,
|
|
407
|
+
priority
|
|
408
|
+
});
|
|
409
|
+
this.emit('resource-acquired', {
|
|
410
|
+
agentId,
|
|
411
|
+
resourceId,
|
|
412
|
+
allocation
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Internal resource release (assumes lock is held)
|
|
417
|
+
*/ async releaseInternal(agentId, resourceId) {
|
|
418
|
+
const allocation = this.allocations.get(resourceId);
|
|
419
|
+
if (!allocation) {
|
|
420
|
+
this.logger.debug('Resource not allocated', {
|
|
421
|
+
agentId,
|
|
422
|
+
resourceId
|
|
423
|
+
});
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
if (allocation.holderId !== agentId) {
|
|
427
|
+
this.logger.warn('Agent does not hold resource', {
|
|
428
|
+
agentId,
|
|
429
|
+
resourceId,
|
|
430
|
+
actualHolder: allocation.holderId
|
|
431
|
+
});
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
// Remove allocation
|
|
435
|
+
this.allocations.delete(resourceId);
|
|
436
|
+
this.agentResources.get(agentId)?.delete(resourceId);
|
|
437
|
+
// Update metrics
|
|
438
|
+
const type = allocation.type;
|
|
439
|
+
const currentCount = this.metrics.allocationsByType.get(type) ?? 0;
|
|
440
|
+
this.metrics.allocationsByType.set(type, Math.max(0, currentCount - 1));
|
|
441
|
+
this.logger.debug('Resource released', {
|
|
442
|
+
agentId,
|
|
443
|
+
resourceId
|
|
444
|
+
});
|
|
445
|
+
this.emit('resource-released', {
|
|
446
|
+
agentId,
|
|
447
|
+
resourceId
|
|
448
|
+
});
|
|
449
|
+
// Process wait queue
|
|
450
|
+
await this.processWaitQueue(resourceId);
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Add request to wait queue
|
|
454
|
+
*/ async addToWaitQueue(request) {
|
|
455
|
+
if (!this.waitQueues.has(request.resourceId)) {
|
|
456
|
+
this.waitQueues.set(request.resourceId, []);
|
|
457
|
+
}
|
|
458
|
+
const queue = this.waitQueues.get(request.resourceId);
|
|
459
|
+
queue.push(request);
|
|
460
|
+
// Sort by effective priority (base priority + age-based boost)
|
|
461
|
+
// This prevents starvation: low-priority agents eventually get highest priority
|
|
462
|
+
this.sortWaitQueue(queue);
|
|
463
|
+
// Update wait-for graph
|
|
464
|
+
const allocation = this.allocations.get(request.resourceId);
|
|
465
|
+
if (allocation) {
|
|
466
|
+
this.addToWaitGraph(request.agentId, allocation.holderId, request.resourceId);
|
|
467
|
+
}
|
|
468
|
+
this.logger.debug('Added to wait queue', {
|
|
469
|
+
agentId: request.agentId,
|
|
470
|
+
resourceId: request.resourceId,
|
|
471
|
+
queueLength: queue.length
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Sort wait queue by effective priority with age-based boost
|
|
476
|
+
* Prevents starvation by increasing priority as requests age
|
|
477
|
+
*/ sortWaitQueue(queue) {
|
|
478
|
+
const now = Date.now();
|
|
479
|
+
queue.sort((a, b)=>{
|
|
480
|
+
// Calculate age in seconds
|
|
481
|
+
const ageA = (now - a.enqueuedAt) / 1000;
|
|
482
|
+
const ageB = (now - b.enqueuedAt) / 1000;
|
|
483
|
+
// Effective priority = base priority + age boost
|
|
484
|
+
// Age boost: 1 priority point per second of waiting
|
|
485
|
+
// This ensures even low-priority agents eventually get served
|
|
486
|
+
const effectivePriorityA = a.priority + ageA;
|
|
487
|
+
const effectivePriorityB = b.priority + ageB;
|
|
488
|
+
return effectivePriorityB - effectivePriorityA;
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Wait for resource to become available
|
|
493
|
+
*/ async waitForResource(agentId, resourceId, timeoutMs) {
|
|
494
|
+
const startTime = Date.now();
|
|
495
|
+
return new Promise((resolve, reject)=>{
|
|
496
|
+
const checkInterval = setInterval(()=>{
|
|
497
|
+
const allocation = this.allocations.get(resourceId);
|
|
498
|
+
// Check if we now hold the resource
|
|
499
|
+
if (allocation?.holderId === agentId) {
|
|
500
|
+
clearInterval(checkInterval);
|
|
501
|
+
resolve();
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
// Check if we're still in the queue
|
|
505
|
+
const queue = this.waitQueues.get(resourceId);
|
|
506
|
+
const stillWaiting = queue?.some((req)=>req.agentId === agentId);
|
|
507
|
+
if (!stillWaiting) {
|
|
508
|
+
clearInterval(checkInterval);
|
|
509
|
+
reject(new Error('Request removed from wait queue'));
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
// Check timeout
|
|
513
|
+
if (Date.now() - startTime > timeoutMs) {
|
|
514
|
+
clearInterval(checkInterval);
|
|
515
|
+
// Remove from queue
|
|
516
|
+
const index = queue?.findIndex((req)=>req.agentId === agentId) ?? -1;
|
|
517
|
+
if (index !== -1) {
|
|
518
|
+
queue?.splice(index, 1);
|
|
519
|
+
}
|
|
520
|
+
this.removeFromWaitGraph(agentId, resourceId);
|
|
521
|
+
reject(new Error(`Resource acquisition timeout: ${resourceId}`));
|
|
522
|
+
}
|
|
523
|
+
}, 100);
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Process wait queue for a resource
|
|
528
|
+
*/ async processWaitQueue(resourceId) {
|
|
529
|
+
const queue = this.waitQueues.get(resourceId);
|
|
530
|
+
if (!queue || queue.length === 0) {
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
// Re-sort queue to account for aging changes since last sort
|
|
534
|
+
// This ensures the most deserving agent (considering age) gets the resource
|
|
535
|
+
this.sortWaitQueue(queue);
|
|
536
|
+
// Allocate to next in queue (highest effective priority)
|
|
537
|
+
const nextRequest = queue.shift();
|
|
538
|
+
await this.allocateResource(nextRequest.agentId, resourceId, nextRequest.priority);
|
|
539
|
+
// Clean up empty queue
|
|
540
|
+
if (queue.length === 0) {
|
|
541
|
+
this.waitQueues.delete(resourceId);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Enforce resource ordering to prevent circular waits
|
|
546
|
+
*/ enforceResourceOrdering(agentId, resourceId) {
|
|
547
|
+
const agentResources = this.agentResources.get(agentId);
|
|
548
|
+
if (!agentResources || agentResources.size === 0) {
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
const requestedOrder = this.resourceOrder.get(resourceId) ?? this.nextOrderNumber;
|
|
552
|
+
// Check if agent holds any resource with higher order
|
|
553
|
+
const heldResources = Array.from(agentResources);
|
|
554
|
+
for (const heldResourceId of heldResources){
|
|
555
|
+
const heldOrder = this.resourceOrder.get(heldResourceId) ?? 0;
|
|
556
|
+
if (heldOrder > requestedOrder) {
|
|
557
|
+
throw new Error(`Resource ordering violation: agent ${agentId} holds ${heldResourceId} (order ${heldOrder}) but requests ${resourceId} (order ${requestedOrder})`);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Add edge to wait-for graph
|
|
563
|
+
*/ addToWaitGraph(waiter, holder, resourceId) {
|
|
564
|
+
if (!this.waitForGraph.has(waiter)) {
|
|
565
|
+
this.waitForGraph.set(waiter, []);
|
|
566
|
+
}
|
|
567
|
+
const edges = this.waitForGraph.get(waiter);
|
|
568
|
+
const existing = edges.find((e)=>e.holder === holder && e.resourceId === resourceId);
|
|
569
|
+
if (!existing) {
|
|
570
|
+
edges.push({
|
|
571
|
+
waiter,
|
|
572
|
+
holder,
|
|
573
|
+
resourceId,
|
|
574
|
+
waitingSince: new Date()
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Remove edge from wait-for graph
|
|
580
|
+
*/ removeFromWaitGraph(waiter, resourceId) {
|
|
581
|
+
const edges = this.waitForGraph.get(waiter);
|
|
582
|
+
if (!edges) {
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
const filtered = edges.filter((e)=>e.resourceId !== resourceId);
|
|
586
|
+
if (filtered.length === 0) {
|
|
587
|
+
this.waitForGraph.delete(waiter);
|
|
588
|
+
} else {
|
|
589
|
+
this.waitForGraph.set(waiter, filtered);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Cleanup expired requests and stale locks
|
|
594
|
+
*/ async cleanup() {
|
|
595
|
+
await this.withLock(async ()=>{
|
|
596
|
+
const now = Date.now();
|
|
597
|
+
// Clean up expired wait queue requests
|
|
598
|
+
const allQueues = Array.from(this.waitQueues.entries());
|
|
599
|
+
for (const [resourceId, queue] of allQueues){
|
|
600
|
+
const filtered = queue.filter((req)=>{
|
|
601
|
+
const age = now - req.timestamp.getTime();
|
|
602
|
+
if (age > req.timeoutMs) {
|
|
603
|
+
this.logger.warn('Removing expired wait request', {
|
|
604
|
+
agentId: req.agentId,
|
|
605
|
+
resourceId,
|
|
606
|
+
ageMs: age
|
|
607
|
+
});
|
|
608
|
+
this.removeFromWaitGraph(req.agentId, resourceId);
|
|
609
|
+
return false;
|
|
610
|
+
}
|
|
611
|
+
return true;
|
|
612
|
+
});
|
|
613
|
+
if (filtered.length === 0) {
|
|
614
|
+
this.waitQueues.delete(resourceId);
|
|
615
|
+
} else {
|
|
616
|
+
this.waitQueues.set(resourceId, filtered);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
// Clean up stale locks (held longer than 2x default timeout)
|
|
620
|
+
const staleThreshold = this.config.defaultTimeoutMs * 2;
|
|
621
|
+
const allAllocations = Array.from(this.allocations.entries());
|
|
622
|
+
for (const [resourceId, allocation] of allAllocations){
|
|
623
|
+
const age = now - allocation.allocatedAt.getTime();
|
|
624
|
+
if (age > staleThreshold) {
|
|
625
|
+
this.logger.warn('Force releasing stale lock', {
|
|
626
|
+
resourceId,
|
|
627
|
+
agentId: allocation.holderId,
|
|
628
|
+
ageMs: age
|
|
629
|
+
});
|
|
630
|
+
this.metrics.timeoutReleases++;
|
|
631
|
+
await this.releaseInternal(allocation.holderId, resourceId);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Periodic deadlock detection
|
|
638
|
+
*/ async checkForDeadlocks() {
|
|
639
|
+
const cycles = this.detectCircularWaits();
|
|
640
|
+
if (cycles.length > 0) {
|
|
641
|
+
this.logger.error('Deadlocks detected during periodic check', {
|
|
642
|
+
count: cycles.length,
|
|
643
|
+
cycles
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Determine resource type from resource ID
|
|
649
|
+
*/ getResourceType(resourceId) {
|
|
650
|
+
if (resourceId.startsWith('file:')) return "file";
|
|
651
|
+
if (resourceId.startsWith('memory:')) return "memory";
|
|
652
|
+
if (resourceId.startsWith('api:')) return "api_quota";
|
|
653
|
+
if (resourceId.startsWith('db:')) return "database";
|
|
654
|
+
if (resourceId.startsWith('net:')) return "network";
|
|
655
|
+
if (resourceId.startsWith('cpu:')) return "compute";
|
|
656
|
+
return "custom";
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Track overhead sample for metrics
|
|
660
|
+
*/ trackOverhead(overheadMs) {
|
|
661
|
+
this.overheadSamples.push(overheadMs);
|
|
662
|
+
// Keep only last N samples
|
|
663
|
+
if (this.overheadSamples.length > this.MAX_SAMPLES) {
|
|
664
|
+
this.overheadSamples.shift();
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
//# sourceMappingURL=resource-manager.js.map
|