claude-flow-novice 2.15.3 → 2.15.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/cfn-extras/skills/advanced-features/cfn-agent-swap/recommend-swap.sh +59 -59
- package/.claude/cfn-extras/skills/analytics/cfn-improvement-recommender/recommend-improvements.sh +91 -91
- package/.claude/cfn-extras/skills/analytics/cfn-pattern-extraction/extract-patterns.sh +79 -79
- package/.claude/cfn-extras/skills/analytics/cfn-retrospective-report/generate-report.sh +100 -100
- package/.claude/cfn-extras/skills/analytics/cfn-telemetry/start-telemetry.sh +110 -110
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/add-bullet.sh +145 -145
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/log-merge.sh +67 -67
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/monitor-injection-performance.sh +137 -137
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/optimize-injection-pipeline.sh +168 -168
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/query-reflections.sh +35 -35
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/store-reflection.sh +45 -45
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/track-ab-test.sh +41 -41
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/update-reflection.sh +41 -41
- package/.claude/cfn-extras/skills/deprecated/cfn-cli-setup/validate-cli-environment.sh +191 -191
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/create-campaign.sh +231 -231
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/get-campaign-performance.sh +190 -190
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/pause-campaign.sh +142 -142
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/set-budget.sh +181 -181
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/update-bid-strategy.sh +133 -133
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/get-conversation-history.sh +121 -121
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/qualify-lead.sh +156 -156
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/schedule-demo.sh +181 -181
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/send-message.sh +137 -137
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/transfer-to-human.sh +179 -179
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/create-campaign.sh +183 -183
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/get-delivery-status.sh +139 -139
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/opt-out.sh +150 -150
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/schedule-campaign.sh +187 -187
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/send-sms.sh +181 -181
- package/.claude/cfn-extras/skills/ui-portal/cfn-web-portal/test-web-portal-skill.sh +50 -50
- package/.claude/cfn-extras/skills/ui-portal/cfn-web-portal/validate-deployment.sh +84 -84
- package/.claude/cfn-extras/skills/utility/cfn-environment-sanitization/sanitize-environment.sh +243 -243
- package/.claude/commands/cfn-loop-cli.md +16 -2
- package/.claude/commands/switch-api.md +31 -10
- package/.claude/hooks/cfn-lint-sql-injection.sh +61 -0
- package/.claude/hooks/cfn-post-edit-cfn-retrospective.sh +33 -2
- package/.claude/hooks/cfn-pre-edit-security-warning.sh +40 -0
- package/.claude/skills/cfn-agent-spawning/spawn-agent.sh +22 -24
- package/.claude/skills/cfn-docker-agent-spawning/SKILL.md +28 -4
- package/.claude/skills/cfn-docker-agent-spawning/spawn-agent.sh +3 -1
- package/.claude/skills/cfn-docker-loop-orchestration/orchestrate.sh +224 -20
- package/.claude/skills/cfn-loop-orchestration/helpers/gate-check.sh +550 -46
- package/.claude/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +277 -0
- package/.claude/skills/cfn-loop-orchestration/orchestrate.sh +184 -23
- package/.claude/skills/cfn-loop-orchestration/security_utils.sh +24 -0
- package/.claude/skills/cfn-loop-orchestration/test-iteration-context-injection.sh +366 -0
- package/.claude/skills/cfn-redis-coordination/CENTRALIZED_REDIS_WRAPPER.md +319 -0
- package/.claude/skills/cfn-redis-coordination/agent-log.sh +4 -0
- package/.claude/skills/cfn-redis-coordination/agent-log.sh.bak +124 -0
- package/.claude/skills/cfn-redis-coordination/agent-recovery.sh +2 -2
- package/.claude/skills/cfn-redis-coordination/collect-confidence-scores.sh +30 -0
- package/.claude/skills/cfn-redis-coordination/get-context.sh +33 -0
- package/.claude/skills/cfn-redis-coordination/get-success-criteria.sh +54 -0
- package/.claude/skills/cfn-redis-coordination/invoke-waiting-mode.sh +3 -0
- package/.claude/skills/cfn-redis-coordination/redis-cli-wrapper.sh +24 -3
- package/.claude/skills/cfn-redis-coordination/redis-functions.sh +33 -0
- package/.claude/skills/cfn-redis-coordination/report-completion.sh +24 -31
- package/.claude/skills/cfn-redis-coordination/store-context.sh +4 -0
- package/.claude/skills/cfn-redis-coordination/store-success-criteria.sh +85 -0
- package/.claude/skills/cfn-redis-coordination/update-all-scripts.sh +67 -0
- package/.claude/skills/cfn-sqlite-memory/ttl-cleanup.sh +17 -25
- package/.claude/skills/cfn-transparency-middleware/test-e2e.sh +15 -0
- package/.claude/skills/cfn-transparency-middleware/tests/input-validation.sh +15 -0
- package/README.md +116 -475
- package/claude-assets/agents/cfn-dev-team/README.md +103 -0
- package/claude-assets/agents/cfn-dev-team/architecture/goal-planner.md +1 -1
- package/claude-assets/agents/cfn-dev-team/coordinators/cfn-frontend-coordinator.md +77 -15
- package/claude-assets/agents/cfn-dev-team/coordinators/cfn-v3-coordinator.md +355 -6
- package/claude-assets/agents/cfn-dev-team/coordinators/consensus-builder.md +82 -1
- package/claude-assets/agents/cfn-dev-team/coordinators/handoff-coordinator.md +82 -1
- package/claude-assets/agents/cfn-dev-team/coordinators/multi-sprint-coordinator.md +77 -15
- package/claude-assets/agents/cfn-dev-team/dev-ops/docker-specialist.md +99 -12
- package/claude-assets/agents/cfn-dev-team/dev-ops/github-commit-agent.md +1 -1
- package/claude-assets/agents/cfn-dev-team/dev-ops/kubernetes-specialist.md +97 -0
- package/claude-assets/agents/cfn-dev-team/dev-ops/monitoring-specialist.md +20 -1
- package/claude-assets/agents/cfn-dev-team/developers/api-gateway-specialist.md +97 -0
- package/claude-assets/agents/cfn-dev-team/developers/backend-developer.md +110 -13
- package/claude-assets/agents/cfn-dev-team/developers/data/data-engineer.md +106 -15
- package/claude-assets/agents/cfn-dev-team/developers/database/database-architect.md +115 -11
- package/claude-assets/agents/cfn-dev-team/developers/frontend/mobile-dev.md +94 -7
- package/claude-assets/agents/cfn-dev-team/developers/frontend/react-frontend-engineer.md +87 -9
- package/claude-assets/agents/cfn-dev-team/developers/frontend/typescript-specialist.md +85 -7
- package/claude-assets/agents/cfn-dev-team/developers/frontend/ui-designer.md +160 -28
- package/claude-assets/agents/cfn-dev-team/developers/graphql-specialist.md +101 -19
- package/claude-assets/agents/cfn-dev-team/developers/rust-developer.md +108 -14
- package/claude-assets/agents/cfn-dev-team/reviewers/{reviewer.md → code-reviewer.md} +95 -8
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/code-quality-validator.md +107 -7
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/perf-analyzer.md +98 -7
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/performance-benchmarker.md +95 -7
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/security-specialist.md +136 -9
- package/claude-assets/agents/cfn-dev-team/testers/api-testing-specialist.md +108 -1
- package/claude-assets/agents/cfn-dev-team/testers/chaos-engineering-specialist.md +107 -13
- package/claude-assets/agents/cfn-dev-team/testers/contract-tester.md +737 -0
- package/claude-assets/agents/cfn-dev-team/testers/e2e/playwright-tester.md +1 -1
- package/claude-assets/agents/cfn-dev-team/testers/integration-tester.md +828 -0
- package/claude-assets/agents/cfn-dev-team/testers/interaction-tester.md +106 -7
- package/claude-assets/agents/cfn-dev-team/testers/load-testing-specialist.md +77 -0
- package/claude-assets/agents/cfn-dev-team/testers/mutation-testing-specialist.md +684 -0
- package/claude-assets/agents/cfn-dev-team/testers/playwright-tester.md +110 -1
- package/claude-assets/agents/cfn-dev-team/testers/tester.md +94 -7
- package/claude-assets/agents/cfn-dev-team/utility/code-booster.md +1 -3
- package/claude-assets/agents/cfn-dev-team/utility/epic-creator.md +87 -13
- package/claude-assets/agents/cfn-dev-team/utility/memory-leak-specialist.md +103 -7
- package/claude-assets/agents/cfn-dev-team/utility/researcher.md +1 -3
- package/claude-assets/agents/cfn-dev-team/utility/z-ai-specialist.md +94 -7
- package/claude-assets/agents/docker-coordinators/cfn-docker-v3-coordinator.md +46 -0
- package/claude-assets/agents/project-only-agents/npm-package-specialist.md +1 -1
- package/claude-assets/cfn-extras/skills/advanced-features/cfn-agent-swap/recommend-swap.sh +59 -59
- package/claude-assets/cfn-extras/skills/analytics/cfn-improvement-recommender/recommend-improvements.sh +91 -91
- package/claude-assets/cfn-extras/skills/analytics/cfn-pattern-extraction/extract-patterns.sh +79 -79
- package/claude-assets/cfn-extras/skills/analytics/cfn-retrospective-report/generate-report.sh +100 -100
- package/claude-assets/cfn-extras/skills/analytics/cfn-telemetry/start-telemetry.sh +110 -110
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/add-bullet.sh +145 -145
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/log-merge.sh +67 -67
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/monitor-injection-performance.sh +137 -137
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/optimize-injection-pipeline.sh +168 -168
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/query-reflections.sh +35 -35
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/store-reflection.sh +45 -45
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/track-ab-test.sh +41 -41
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/update-reflection.sh +41 -41
- package/claude-assets/cfn-extras/skills/deprecated/cfn-cli-setup/validate-cli-environment.sh +191 -191
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/create-campaign.sh +231 -231
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/get-campaign-performance.sh +190 -190
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/pause-campaign.sh +142 -142
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/set-budget.sh +181 -181
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/update-bid-strategy.sh +133 -133
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/get-conversation-history.sh +121 -121
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/qualify-lead.sh +156 -156
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/schedule-demo.sh +181 -181
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/send-message.sh +137 -137
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/transfer-to-human.sh +179 -179
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/create-campaign.sh +183 -183
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/get-delivery-status.sh +139 -139
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/opt-out.sh +150 -150
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/schedule-campaign.sh +187 -187
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/send-sms.sh +181 -181
- package/claude-assets/cfn-extras/skills/ui-portal/cfn-web-portal/test-web-portal-skill.sh +50 -50
- package/claude-assets/cfn-extras/skills/ui-portal/cfn-web-portal/validate-deployment.sh +84 -84
- package/claude-assets/cfn-extras/skills/utility/cfn-environment-sanitization/sanitize-environment.sh +243 -243
- package/claude-assets/commands/cfn-loop-cli.md +16 -2
- package/claude-assets/commands/switch-api.md +31 -10
- package/claude-assets/hooks/cfn-lint-sql-injection.sh +61 -0
- package/claude-assets/hooks/cfn-post-edit-cfn-retrospective.sh +33 -2
- package/claude-assets/hooks/cfn-pre-edit-security-warning.sh +40 -0
- package/claude-assets/hooks/detect-hardcoded-credentials.sh +212 -0
- package/claude-assets/skills/SKILL_TEMPLATE.md +774 -0
- package/claude-assets/skills/agent-lifecycle/execute-lifecycle-hook.sh +84 -113
- package/claude-assets/skills/agent-lifecycle/simple-audit.sh +33 -6
- package/claude-assets/skills/agent-template-generator/SKILL.md +440 -0
- package/claude-assets/skills/agent-template-generator/generate-agent.sh +405 -0
- package/claude-assets/skills/agent-validation-linter/SKILL.md +589 -0
- package/claude-assets/skills/agent-validation-linter/lint-agents.sh +271 -0
- package/claude-assets/skills/bootstrap/bash-fundamentals.md +786 -0
- package/claude-assets/skills/bootstrap/database-connection.md +464 -0
- package/claude-assets/skills/bootstrap/error-handling.md +580 -0
- package/claude-assets/skills/bootstrap/file-operations.md +699 -0
- package/claude-assets/skills/bootstrap/skill-loader.md +616 -0
- package/claude-assets/skills/bootstrap/sqlite-params.sh +287 -0
- package/claude-assets/skills/cfn-agent-spawning/spawn-agent.sh +22 -24
- package/claude-assets/skills/cfn-automatic-memory-persistence/test-memory-persistence.sh +17 -16
- package/claude-assets/skills/cfn-deployment/SKILL.md +293 -0
- package/claude-assets/skills/cfn-deployment/execute.sh +21 -0
- package/claude-assets/skills/cfn-docker-agent-spawning/SKILL.md +28 -4
- package/claude-assets/skills/cfn-docker-agent-spawning/spawn-agent.sh +3 -1
- package/claude-assets/skills/cfn-docker-loop-orchestration/orchestrate.sh +224 -20
- package/claude-assets/skills/cfn-environment-sanitization/sanitize-environment.sh +38 -0
- package/claude-assets/skills/cfn-error-batching-strategy/lib/core-functions.sh +47 -47
- package/claude-assets/skills/cfn-file-operations/SKILL.md +290 -0
- package/claude-assets/skills/cfn-file-operations/execute.sh +129 -0
- package/claude-assets/skills/cfn-file-operations/lib/atomic-write.sh +294 -0
- package/claude-assets/skills/cfn-file-operations/lib/lock.sh +361 -0
- package/claude-assets/skills/cfn-file-operations/test.sh +369 -0
- package/claude-assets/skills/cfn-log-operations/SKILL.md +308 -0
- package/claude-assets/skills/cfn-log-operations/execute.sh +420 -0
- package/claude-assets/skills/cfn-log-operations/lib/rotate.sh +406 -0
- package/claude-assets/skills/cfn-log-operations/lib/search.sh +448 -0
- package/claude-assets/skills/cfn-log-operations/test.sh +394 -0
- package/claude-assets/skills/cfn-loop-orchestration/helpers/gate-check.sh +550 -46
- package/claude-assets/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +277 -0
- package/claude-assets/skills/cfn-loop-orchestration/orchestrate.sh +184 -23
- package/claude-assets/skills/cfn-loop-orchestration/security_utils.sh +24 -0
- package/claude-assets/skills/cfn-loop-orchestration/test-iteration-context-injection.sh +366 -0
- package/claude-assets/skills/cfn-parameterized-queries/SKILL.md +339 -0
- package/claude-assets/skills/cfn-playbook/query-playbook.sh +19 -15
- package/claude-assets/skills/cfn-playbook/update-playbook.sh +25 -14
- package/claude-assets/skills/cfn-process-instrumentation/instrument-process.sh +44 -0
- package/claude-assets/skills/cfn-promotion/SKILL.md +305 -0
- package/claude-assets/skills/cfn-redis-coordination/CENTRALIZED_REDIS_WRAPPER.md +319 -0
- package/claude-assets/skills/cfn-redis-coordination/agent-log.sh +4 -0
- package/claude-assets/skills/cfn-redis-coordination/agent-log.sh.bak +124 -0
- package/claude-assets/skills/cfn-redis-coordination/agent-recovery.sh +2 -2
- package/claude-assets/skills/cfn-redis-coordination/collect-confidence-scores.sh +30 -0
- package/claude-assets/skills/cfn-redis-coordination/get-context.sh +33 -0
- package/claude-assets/skills/cfn-redis-coordination/get-success-criteria.sh +54 -0
- package/claude-assets/skills/cfn-redis-coordination/invoke-waiting-mode.sh +3 -0
- package/claude-assets/skills/cfn-redis-coordination/redis-cli-wrapper.sh +24 -3
- package/claude-assets/skills/cfn-redis-coordination/redis-functions.sh +33 -0
- package/claude-assets/skills/cfn-redis-coordination/report-completion.sh +24 -31
- package/claude-assets/skills/cfn-redis-coordination/store-context.sh +4 -0
- package/claude-assets/skills/cfn-redis-coordination/store-success-criteria.sh +85 -0
- package/claude-assets/skills/cfn-redis-coordination/update-all-scripts.sh +67 -0
- package/claude-assets/skills/cfn-skill-loader/SKILL.md +466 -0
- package/claude-assets/skills/cfn-skill-loader/execute.sh +344 -0
- package/claude-assets/skills/cfn-sqlite-memory/ttl-cleanup.sh +17 -25
- package/claude-assets/skills/cfn-task-audit/get-audit-data.sh +42 -21
- package/claude-assets/skills/cfn-task-audit/store-task-audit.sh +17 -10
- package/claude-assets/skills/cfn-test-runner/detect-regressions.sh +17 -14
- package/claude-assets/skills/cfn-test-runner/detect-regressions.sh.backup-1763392821 +55 -0
- package/claude-assets/skills/cfn-test-runner/store-benchmarks.sh +17 -19
- package/claude-assets/skills/cfn-transparency-middleware/test-e2e.sh +15 -0
- package/claude-assets/skills/cfn-transparency-middleware/tests/input-validation.sh +15 -0
- package/claude-assets/skills/cfn-utilities/SKILL.md +237 -0
- package/claude-assets/skills/cfn-utilities/execute.sh +32 -0
- package/claude-assets/skills/cfn-utilities/lib/errors.sh +56 -0
- package/claude-assets/skills/cfn-utilities/lib/file-ops.sh +164 -0
- package/claude-assets/skills/cfn-utilities/lib/logging.sh +77 -0
- package/claude-assets/skills/cfn-utilities/lib/retry.sh +127 -0
- package/claude-assets/skills/cfn-utilities/test.sh +317 -0
- package/claude-assets/skills/integration/agent-handoff.sh +62 -64
- package/claude-assets/skills/json-validation/SKILL.md +431 -0
- package/claude-assets/skills/json-validation/test-validate-success-criteria.sh +421 -0
- package/claude-assets/skills/json-validation/validate-success-criteria.sh +197 -0
- package/claude-assets/skills/redis-coordination/validate-parameters.sh +34 -0
- package/claude-assets/skills/workflow-codification/DEPLOY_QUICK_REFERENCE.md +106 -0
- package/claude-assets/skills/workflow-codification/PROPAGATE_UPDATE_QUICK_REFERENCE.md +366 -0
- package/claude-assets/skills/workflow-codification/deploy-approved-skill.sh +481 -0
- package/claude-assets/skills/workflow-codification/deploy-approved-skill.sh.backup-1763392820 +512 -0
- package/claude-assets/skills/workflow-codification/lib/security-utils.sh +204 -0
- package/claude-assets/skills/workflow-codification/propagate-skill-update.sh +648 -0
- package/claude-assets/skills/workflow-codification/propagate-skill-update.sh.backup-1763392820 +664 -0
- package/claude-assets/skills/workflow-codification/test-integration.sh +15 -0
- package/claude-assets/skills/workflow-codification/test-metadata-update.sh +350 -0
- package/claude-assets/skills/workflow-codification/track-cost-savings.sh +55 -14
- package/claude-assets/skills/workflow-codification/track-cost-savings.sh.backup-1763392821 +445 -0
- package/claude-assets/skills/workflow-codification/track-edge-case.sh +27 -60
- package/claude-assets/skills/workflow-codification/workflow-codification.db +0 -0
- package/dist/ace/ace-curator.js +10 -2
- package/dist/ace/ace-curator.js.map +1 -1
- package/dist/ace/ace-generator.js +4 -0
- package/dist/ace/ace-generator.js.map +1 -1
- package/dist/ace/ace-reflector.js +1 -1
- package/dist/ace/ace-reflector.js.map +1 -1
- package/dist/ace/context-injection.js +24 -2
- package/dist/ace/context-injection.js.map +1 -1
- package/dist/agents/agent-loader.js +146 -165
- package/dist/agents/agent-loader.js.map +1 -1
- package/dist/agents/task-agent-integration.js +1 -1
- package/dist/agents/task-agent-integration.js.map +1 -1
- package/dist/api/health-endpoints.js +390 -0
- package/dist/api/health-endpoints.js.map +1 -0
- package/dist/cli/agent-executor.js +4 -1
- package/dist/cli/agent-executor.js.map +1 -1
- package/dist/cli/agent-prompt-builder.js +89 -1
- package/dist/cli/agent-prompt-builder.js.map +1 -1
- package/dist/cli/agent-spawn.js +130 -37
- package/dist/cli/agent-spawn.js.map +1 -1
- package/dist/cli/skill-cache-validator.js +412 -0
- package/dist/cli/skill-cache-validator.js.map +1 -0
- package/dist/cli/skill-cli.js +991 -0
- package/dist/cli/skill-cli.js.map +1 -0
- package/dist/cli/skill-execution-logger.js +284 -0
- package/dist/cli/skill-execution-logger.js.map +1 -0
- package/dist/cli/skill-loader.js +457 -0
- package/dist/cli/skill-loader.js.map +1 -0
- package/dist/coordination/event-bus.js +2 -2
- package/dist/coordination/event-bus.js.map +1 -1
- package/dist/coordination/fleet-manager.js +1 -1
- package/dist/coordination/fleet-manager.js.map +1 -1
- package/dist/coordination/index.js +23 -9
- package/dist/coordination/index.js.map +1 -1
- package/dist/coordination/types/fleet-manager.types.js.map +1 -1
- package/dist/db/migration-manager.js +483 -0
- package/dist/db/migration-manager.js.map +1 -0
- package/dist/db/skills-query.js +535 -0
- package/dist/db/skills-query.js.map +1 -0
- package/dist/integration/DatabaseHandoff.js +1 -1
- package/dist/integration/DatabaseHandoff.js.map +1 -1
- package/dist/jobs/edge-case-analyzer.js +367 -0
- package/dist/jobs/edge-case-analyzer.js.map +1 -0
- package/dist/jobs/promotion-sla-enforcer.js +288 -0
- package/dist/jobs/promotion-sla-enforcer.js.map +1 -0
- package/dist/lib/agent-output-parser.js.map +1 -1
- package/dist/lib/agent-output-validator.js.map +1 -1
- package/dist/lib/agent-workspace.js +281 -0
- package/dist/lib/agent-workspace.js.map +1 -0
- package/dist/lib/atomic-file-writer.js +377 -0
- package/dist/lib/atomic-file-writer.js.map +1 -0
- package/dist/lib/backup-manager.js +779 -0
- package/dist/lib/backup-manager.js.map +1 -0
- package/dist/lib/checkpoint-manager.js +837 -0
- package/dist/lib/checkpoint-manager.js.map +1 -0
- package/dist/lib/circuit-breaker.js +340 -0
- package/dist/lib/circuit-breaker.js.map +1 -0
- package/dist/lib/completion-signal-handler.js +243 -0
- package/dist/lib/completion-signal-handler.js.map +1 -0
- package/dist/lib/config-manager.js +312 -0
- package/dist/lib/config-manager.js.map +1 -0
- package/dist/lib/config-migrator.js +386 -0
- package/dist/lib/config-migrator.js.map +1 -0
- package/dist/lib/config-validator.js.map +1 -1
- package/dist/lib/correlation-cache.js +311 -0
- package/dist/lib/correlation-cache.js.map +1 -0
- package/dist/lib/correlation.js +263 -0
- package/dist/lib/correlation.js.map +1 -0
- package/dist/lib/database-service/connection-pool-manager.js +520 -0
- package/dist/lib/database-service/connection-pool-manager.js.map +1 -0
- package/dist/lib/database-service/correlation.js +329 -0
- package/dist/lib/database-service/correlation.js.map +1 -0
- package/dist/lib/database-service/errors.js +120 -0
- package/dist/lib/database-service/errors.js.map +1 -0
- package/dist/lib/database-service/index.js +168 -0
- package/dist/lib/database-service/index.js.map +1 -0
- package/dist/lib/database-service/postgres-adapter.js +526 -0
- package/dist/lib/database-service/postgres-adapter.js.map +1 -0
- package/dist/lib/database-service/redis-adapter.js +360 -0
- package/dist/lib/database-service/redis-adapter.js.map +1 -0
- package/dist/lib/database-service/sqlite-adapter.js +544 -0
- package/dist/lib/database-service/sqlite-adapter.js.map +1 -0
- package/dist/lib/database-service/transaction-manager.js +773 -0
- package/dist/lib/database-service/transaction-manager.js.map +1 -0
- package/dist/lib/database-service/types.js +23 -0
- package/dist/lib/database-service/types.js.map +1 -0
- package/dist/lib/deadlock-resolver.js +292 -0
- package/dist/lib/deadlock-resolver.js.map +1 -0
- package/dist/lib/distributed-lock.js +451 -0
- package/dist/lib/distributed-lock.js.map +1 -0
- package/dist/lib/edge-case-deduplicator.js +227 -0
- package/dist/lib/edge-case-deduplicator.js.map +1 -0
- package/dist/lib/encryption-manager.js +322 -0
- package/dist/lib/encryption-manager.js.map +1 -0
- package/dist/lib/error-aggregator.js +234 -0
- package/dist/lib/error-aggregator.js.map +1 -0
- package/dist/lib/errors.js +287 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/file-lock-manager.js +578 -0
- package/dist/lib/file-lock-manager.js.map +1 -0
- package/dist/lib/file-operations.js +367 -0
- package/dist/lib/file-operations.js.map +1 -0
- package/dist/lib/idempotent-write.js +237 -0
- package/dist/lib/idempotent-write.js.map +1 -0
- package/dist/lib/integration-schema-validator.js +522 -0
- package/dist/lib/integration-schema-validator.js.map +1 -0
- package/dist/lib/lock-health-monitor.js +298 -0
- package/dist/lib/lock-health-monitor.js.map +1 -0
- package/dist/lib/log-shipper.js +422 -0
- package/dist/lib/log-shipper.js.map +1 -0
- package/dist/lib/logging.js +146 -0
- package/dist/lib/logging.js.map +1 -0
- package/dist/lib/message-deduplicator.js +439 -0
- package/dist/lib/message-deduplicator.js.map +1 -0
- package/dist/lib/multi-system-query.js +604 -0
- package/dist/lib/multi-system-query.js.map +1 -0
- package/dist/lib/orphan-detector.js +332 -0
- package/dist/lib/orphan-detector.js.map +1 -0
- package/dist/lib/password-generator.js +166 -0
- package/dist/lib/password-generator.js.map +1 -0
- package/dist/lib/path-validator.js +429 -0
- package/dist/lib/path-validator.js.map +1 -0
- package/dist/lib/query-translator.js +905 -0
- package/dist/lib/query-translator.js.map +1 -0
- package/dist/lib/queue-recovery.js +469 -0
- package/dist/lib/queue-recovery.js.map +1 -0
- package/dist/lib/redis-queue-manager.js +512 -0
- package/dist/lib/redis-queue-manager.js.map +1 -0
- package/dist/lib/reflection-archiver.js +272 -0
- package/dist/lib/reflection-archiver.js.map +1 -0
- package/dist/lib/retry-manager.js +453 -0
- package/dist/lib/retry-manager.js.map +1 -0
- package/dist/lib/retry.js +262 -0
- package/dist/lib/retry.js.map +1 -0
- package/dist/lib/schema-transform.js +695 -0
- package/dist/lib/schema-transform.js.map +1 -0
- package/dist/lib/schema-validator.js +491 -0
- package/dist/lib/schema-validator.js.map +1 -0
- package/dist/lib/skill-cache.js +297 -0
- package/dist/lib/skill-cache.js.map +1 -0
- package/dist/lib/skill-content-manager.js +337 -0
- package/dist/lib/skill-content-manager.js.map +1 -0
- package/dist/lib/skill-frontmatter-parser.js +237 -0
- package/dist/lib/skill-frontmatter-parser.js.map +1 -0
- package/dist/lib/skill-git-integration.js +275 -0
- package/dist/lib/skill-git-integration.js.map +1 -0
- package/dist/lib/skill-markdown-validator.js +396 -0
- package/dist/lib/skill-markdown-validator.js.map +1 -0
- package/dist/lib/skill-output-parser.js +312 -0
- package/dist/lib/skill-output-parser.js.map +1 -0
- package/dist/lib/unified-query-api.js +467 -0
- package/dist/lib/unified-query-api.js.map +1 -0
- package/dist/middleware/auth-middleware.js +350 -0
- package/dist/middleware/auth-middleware.js.map +1 -0
- package/dist/middleware/schema-validation.js +347 -0
- package/dist/middleware/schema-validation.js.map +1 -0
- package/dist/providers/anthropic-provider.js +1 -1
- package/dist/providers/anthropic-provider.js.map +1 -1
- package/dist/providers/provider-factory.js +2 -2
- package/dist/providers/provider-factory.js.map +1 -1
- package/dist/services/edge-case-analyzer.js +321 -0
- package/dist/services/edge-case-analyzer.js.map +1 -0
- package/dist/services/edge-case-deduplicator.js +266 -0
- package/dist/services/edge-case-deduplicator.js.map +1 -0
- package/dist/services/edge-case-detector.js +337 -0
- package/dist/services/edge-case-detector.js.map +1 -0
- package/dist/services/edge-case-tracker.js +547 -0
- package/dist/services/edge-case-tracker.js.map +1 -0
- package/dist/services/health-check-system.js +586 -0
- package/dist/services/health-check-system.js.map +1 -0
- package/dist/services/metrics-logger.js +412 -0
- package/dist/services/metrics-logger.js.map +1 -0
- package/dist/services/patch-generator.js +378 -0
- package/dist/services/patch-generator.js.map +1 -0
- package/dist/services/patch-validator.js +337 -0
- package/dist/services/patch-validator.js.map +1 -0
- package/dist/services/performance-monitor.js +811 -0
- package/dist/services/performance-monitor.js.map +1 -0
- package/dist/services/promotion-pipeline.js +918 -0
- package/dist/services/promotion-pipeline.js.map +1 -0
- package/dist/services/promotion-validator.js +394 -0
- package/dist/services/promotion-validator.js.map +1 -0
- package/dist/services/reflection-logger.js +388 -0
- package/dist/services/reflection-logger.js.map +1 -0
- package/dist/services/skill-deployment.js +472 -0
- package/dist/services/skill-deployment.js.map +1 -0
- package/dist/services/skill-loader.js +427 -0
- package/dist/services/skill-loader.js.map +1 -0
- package/dist/services/skill-promotion.js +372 -0
- package/dist/services/skill-promotion.js.map +1 -0
- package/dist/services/skill-validator.js +454 -0
- package/dist/services/skill-validator.js.map +1 -0
- package/dist/services/skill-versioning.js +244 -0
- package/dist/services/skill-versioning.js.map +1 -0
- package/dist/services/workspace-supervisor.js +597 -0
- package/dist/services/workspace-supervisor.js.map +1 -0
- package/dist/types/edge-case.js +45 -0
- package/dist/types/edge-case.js.map +1 -0
- package/package.json +201 -177
- package/readme/README.md +19 -4
- package/scripts/backup-cleanup.sh +627 -0
- package/scripts/cleanup-workspaces.sh +412 -0
- package/scripts/cleanup-yaml-configs.sh +141 -0
- package/scripts/deploy-approved-skills.sh +263 -0
- package/scripts/health-check.sh +447 -0
- package/scripts/log-aggregator.sh +554 -0
- package/scripts/log-monitor.sh +629 -0
- package/scripts/manage-agent-workspaces.sh +434 -0
- package/scripts/migrate-schema.sh +533 -0
- package/scripts/promote-staged-skills.sh +423 -0
- package/scripts/verify-no-secrets.sh +88 -35
- package/.claude/cfn-extras/agents/deprecated-coordinators/adaptive-coordinator.md.backup +0 -161
- package/.claude/cfn-extras/agents/deprecated-coordinators/blocking-coordinator-example.md.backup +0 -728
- package/.claude/cfn-extras/agents/deprecated-coordinators/mesh-coordinator.md.backup +0 -131
- package/.claude/skills/agent-lifecycle/SKILL.md +0 -60
- package/.claude/skills/agent-lifecycle/execute-lifecycle-hook.sh +0 -573
- package/.claude/skills/agent-lifecycle/simple-audit.sh +0 -31
- package/.claude/skills/cfn-agent-spawning/spawn-agent.sh.backup +0 -273
- package/.claude/skills/cfn-loop-orchestration/orchestrate.sh.backup +0 -949
- package/README.md.backup_before_replace +0 -781
- package/claude-assets/cfn-extras/agents/deprecated-coordinators/adaptive-coordinator.md.backup +0 -161
- package/claude-assets/cfn-extras/agents/deprecated-coordinators/blocking-coordinator-example.md.backup +0 -728
- package/claude-assets/cfn-extras/agents/deprecated-coordinators/mesh-coordinator.md.backup +0 -131
- package/claude-assets/skills/cfn-agent-spawning/spawn-agent.sh.backup +0 -273
- package/claude-assets/skills/cfn-loop-orchestration/orchestrate.sh.backup +0 -949
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/health-check-system.ts"],"sourcesContent":["/**\r\n * Health Check System\r\n *\r\n * Comprehensive health monitoring for all critical services:\r\n * - Database connectivity and latency\r\n * - Redis connectivity and performance\r\n * - File system availability and disk space\r\n * - Active agent count and queue depth\r\n *\r\n * Provides sub-second health detection (<1s overall response time)\r\n * and detailed health reports for monitoring integration.\r\n *\r\n * Part of Task P2-4.1: Comprehensive Health Checks\r\n */\r\n\r\nimport { getDatabaseService } from '../lib/database-service.js';\r\nimport { RedisQueueManager } from '../lib/redis-queue-manager.js';\r\nimport { StandardError, ErrorCode } from '../lib/errors.js';\r\nimport fs from 'fs';\r\nimport os from 'os';\r\nimport util from 'util';\r\n\r\n/**\r\n * Health status enumeration\r\n */\r\nexport enum HealthStatus {\r\n HEALTHY = 'healthy',\r\n DEGRADED = 'degraded',\r\n UNHEALTHY = 'unhealthy',\r\n}\r\n\r\n/**\r\n * Individual health check result\r\n */\r\nexport interface HealthCheck {\r\n /**\r\n * Service name\r\n */\r\n name: string;\r\n\r\n /**\r\n * Current health status\r\n */\r\n status: 'healthy' | 'degraded' | 'unhealthy';\r\n\r\n /**\r\n * Response latency in milliseconds\r\n */\r\n latency: number;\r\n\r\n /**\r\n * Human-readable health status message\r\n */\r\n message?: string;\r\n\r\n /**\r\n * Timestamp of the health check\r\n */\r\n timestamp: Date;\r\n\r\n /**\r\n * Dependent service health checks (if aggregated)\r\n */\r\n dependencies?: HealthCheck[];\r\n\r\n /**\r\n * Additional metadata about the service\r\n */\r\n metadata?: Record<string, any>;\r\n}\r\n\r\n/**\r\n * Detailed health report including all services and metrics\r\n */\r\nexport interface DetailedHealthReport {\r\n /**\r\n * Report generation timestamp\r\n */\r\n timestamp: Date;\r\n\r\n /**\r\n * Overall system health status\r\n */\r\n overallStatus: 'healthy' | 'degraded' | 'unhealthy';\r\n\r\n /**\r\n * Total health check latency in milliseconds\r\n */\r\n latency: number;\r\n\r\n /**\r\n * Individual service health checks\r\n */\r\n services: {\r\n database: HealthCheck;\r\n redis: HealthCheck;\r\n filesystem: HealthCheck;\r\n agents: HealthCheck;\r\n };\r\n\r\n /**\r\n * Alerts or warnings\r\n */\r\n alerts?: string[];\r\n}\r\n\r\n/**\r\n * Aggregated health statistics from all services\r\n */\r\nexport interface AggregatedHealthStats {\r\n /**\r\n * Timestamp when stats were collected\r\n */\r\n timestamp: Date;\r\n\r\n /**\r\n * Overall system health status\r\n */\r\n overallStatus: 'healthy' | 'degraded' | 'unhealthy';\r\n\r\n /**\r\n * Total aggregation latency in milliseconds\r\n */\r\n latency: number;\r\n\r\n /**\r\n * Average latency across all services in milliseconds\r\n */\r\n averageServiceLatency: number;\r\n\r\n /**\r\n * Service count summary\r\n */\r\n serviceCount: {\r\n total: number;\r\n healthy: number;\r\n degraded: number;\r\n unhealthy: number;\r\n };\r\n\r\n /**\r\n * Individual service summaries\r\n */\r\n services: {\r\n database: {\r\n status: 'healthy' | 'degraded' | 'unhealthy';\r\n latency: number;\r\n message?: string;\r\n };\r\n redis: {\r\n status: 'healthy' | 'degraded' | 'unhealthy';\r\n latency: number;\r\n message?: string;\r\n };\r\n filesystem: {\r\n status: 'healthy' | 'degraded' | 'unhealthy';\r\n latency: number;\r\n message?: string;\r\n };\r\n agents: {\r\n status: 'healthy' | 'degraded' | 'unhealthy';\r\n latency: number;\r\n message?: string;\r\n };\r\n };\r\n\r\n /**\r\n * Detailed metadata from all services\r\n */\r\n metadata: Record<string, any>;\r\n\r\n /**\r\n * Warning messages from degraded services\r\n */\r\n warnings: string[];\r\n\r\n /**\r\n * Error messages from unhealthy services\r\n */\r\n errors: string[];\r\n}\r\n\r\n/**\r\n * Health check system configuration\r\n */\r\nexport interface HealthCheckConfig {\r\n /**\r\n * Database check timeout in milliseconds (default: 500)\r\n */\r\n databaseTimeout?: number;\r\n\r\n /**\r\n * Redis check timeout in milliseconds (default: 500)\r\n */\r\n redisTimeout?: number;\r\n\r\n /**\r\n * File system check timeout in milliseconds (default: 500)\r\n */\r\n filesystemTimeout?: number;\r\n\r\n /**\r\n * Agents check timeout in milliseconds (default: 500)\r\n */\r\n agentsTimeout?: number;\r\n\r\n /**\r\n * Disk usage warning threshold percentage (default: 80)\r\n */\r\n diskUsageWarnThreshold?: number;\r\n\r\n /**\r\n * Disk usage critical threshold percentage (default: 95)\r\n */\r\n diskUsageCriticalThreshold?: number;\r\n\r\n /**\r\n * Queue depth warning threshold (default: 100)\r\n */\r\n queueDepthWarnThreshold?: number;\r\n\r\n /**\r\n * Queue depth critical threshold (default: 500)\r\n */\r\n queueDepthCriticalThreshold?: number;\r\n}\r\n\r\n/**\r\n * Comprehensive health check system\r\n */\r\nexport class HealthCheckSystem {\r\n private config: Required<HealthCheckConfig>;\r\n private redisManager: RedisQueueManager | null = null;\r\n\r\n constructor(config?: HealthCheckConfig) {\r\n this.config = {\r\n databaseTimeout: config?.databaseTimeout ?? 500,\r\n redisTimeout: config?.redisTimeout ?? 500,\r\n filesystemTimeout: config?.filesystemTimeout ?? 500,\r\n agentsTimeout: config?.agentsTimeout ?? 500,\r\n diskUsageWarnThreshold: config?.diskUsageWarnThreshold ?? 80,\r\n diskUsageCriticalThreshold: config?.diskUsageCriticalThreshold ?? 95,\r\n queueDepthWarnThreshold: config?.queueDepthWarnThreshold ?? 100,\r\n queueDepthCriticalThreshold: config?.queueDepthCriticalThreshold ?? 500,\r\n };\r\n\r\n try {\r\n this.redisManager = new RedisQueueManager();\r\n } catch (error) {\r\n // Redis initialization may fail in test environments\r\n // Will be handled gracefully in checkRedis()\r\n }\r\n }\r\n\r\n /**\r\n * Check database health\r\n * Verifies connectivity and measures response latency\r\n */\r\n async checkDatabase(): Promise<HealthCheck> {\r\n const startTime = Date.now();\r\n\r\n try {\r\n const db = getDatabaseService();\r\n\r\n // Create a timeout promise\r\n const timeoutPromise = new Promise((_, reject) =>\r\n setTimeout(() => reject(new Error('Database check timeout')), this.config.databaseTimeout)\r\n );\r\n\r\n // Race between actual check and timeout\r\n await Promise.race([\r\n (async () => {\r\n // Simple connectivity check using a lightweight query\r\n await db.query('SELECT 1');\r\n })(),\r\n timeoutPromise,\r\n ]);\r\n\r\n const latency = Date.now() - startTime;\r\n\r\n return {\r\n name: 'database',\r\n status: HealthStatus.HEALTHY,\r\n latency,\r\n message: 'Database connected and responding',\r\n timestamp: new Date(),\r\n metadata: {\r\n responseTime: latency,\r\n type: 'postgresql',\r\n },\r\n };\r\n } catch (error) {\r\n const latency = Date.now() - startTime;\r\n const message =\r\n error instanceof Error\r\n ? error.message\r\n : 'Unknown database error';\r\n\r\n return {\r\n name: 'database',\r\n status: latency > this.config.databaseTimeout\r\n ? HealthStatus.UNHEALTHY\r\n : HealthStatus.UNHEALTHY,\r\n latency,\r\n message: `Database check failed: ${message}`,\r\n timestamp: new Date(),\r\n metadata: {\r\n error: message,\r\n timeout: latency > this.config.databaseTimeout,\r\n },\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Check Redis health\r\n * Verifies connectivity and measures ping response time\r\n */\r\n async checkRedis(): Promise<HealthCheck> {\r\n const startTime = Date.now();\r\n\r\n try {\r\n if (!this.redisManager) {\r\n throw new Error('Redis manager not initialized');\r\n }\r\n\r\n // Create a timeout promise\r\n const timeoutPromise = new Promise((_, reject) =>\r\n setTimeout(() => reject(new Error('Redis check timeout')), this.config.redisTimeout)\r\n );\r\n\r\n // Race between actual check and timeout\r\n await Promise.race([\r\n this.redisManager.ping(),\r\n timeoutPromise,\r\n ]);\r\n\r\n const latency = Date.now() - startTime;\r\n\r\n // Get additional metrics\r\n let metadata: Record<string, any> = { responseTime: latency };\r\n\r\n try {\r\n const stats = await this.redisManager.getStats();\r\n metadata = { ...metadata, ...stats };\r\n } catch {\r\n // If stats fail, just continue with basic response time\r\n }\r\n\r\n return {\r\n name: 'redis',\r\n status: HealthStatus.HEALTHY,\r\n latency,\r\n message: 'Redis responding to PING',\r\n timestamp: new Date(),\r\n metadata,\r\n };\r\n } catch (error) {\r\n const latency = Date.now() - startTime;\r\n const message =\r\n error instanceof Error\r\n ? error.message\r\n : 'Unknown Redis error';\r\n\r\n const status =\r\n message.includes('timeout') || latency > this.config.redisTimeout\r\n ? HealthStatus.UNHEALTHY\r\n : HealthStatus.UNHEALTHY;\r\n\r\n return {\r\n name: 'redis',\r\n status,\r\n latency,\r\n message: `Redis check failed: ${message}`,\r\n timestamp: new Date(),\r\n metadata: {\r\n error: message,\r\n timeout: latency > this.config.redisTimeout,\r\n },\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Check file system health\r\n * Verifies disk space and write permissions\r\n */\r\n async checkFileSystem(): Promise<HealthCheck> {\r\n const startTime = Date.now();\r\n\r\n try {\r\n // Create a timeout promise\r\n const timeoutPromise = new Promise((_, reject) =>\r\n setTimeout(() => reject(new Error('Filesystem check timeout')), this.config.filesystemTimeout)\r\n );\r\n\r\n // Race between actual check and timeout\r\n const result = await Promise.race([\r\n this.getFileSystemMetrics(),\r\n timeoutPromise,\r\n ]) as FileSystemMetrics;\r\n\r\n const latency = Date.now() - startTime;\r\n\r\n // Determine health status based on disk usage\r\n let status: 'healthy' | 'degraded' | 'unhealthy' = HealthStatus.HEALTHY;\r\n let message = 'File system healthy';\r\n\r\n if (result.diskUsagePercent > this.config.diskUsageCriticalThreshold) {\r\n status = HealthStatus.UNHEALTHY;\r\n message = `Critical disk usage: ${result.diskUsagePercent.toFixed(1)}%`;\r\n } else if (result.diskUsagePercent > this.config.diskUsageWarnThreshold) {\r\n status = HealthStatus.DEGRADED;\r\n message = `Degraded disk usage: ${result.diskUsagePercent.toFixed(1)}%`;\r\n }\r\n\r\n if (!result.writePermission) {\r\n status = HealthStatus.UNHEALTHY;\r\n message = 'Write permission denied on temp directory';\r\n }\r\n\r\n return {\r\n name: 'filesystem',\r\n status,\r\n latency,\r\n message,\r\n timestamp: new Date(),\r\n metadata: {\r\n diskUsagePercent: result.diskUsagePercent,\r\n writePermission: result.writePermission,\r\n freeSpaceMB: result.freeSpaceMB,\r\n totalSpaceMB: result.totalSpaceMB,\r\n },\r\n };\r\n } catch (error) {\r\n const latency = Date.now() - startTime;\r\n const message =\r\n error instanceof Error\r\n ? error.message\r\n : 'Unknown filesystem error';\r\n\r\n return {\r\n name: 'filesystem',\r\n status: HealthStatus.UNHEALTHY,\r\n latency,\r\n message: `File system check failed: ${message}`,\r\n timestamp: new Date(),\r\n metadata: {\r\n error: message,\r\n },\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Check agent health\r\n * Verifies active agent count and queue depth\r\n */\r\n async checkAgents(): Promise<HealthCheck> {\r\n const startTime = Date.now();\r\n\r\n try {\r\n // Create a timeout promise\r\n const timeoutPromise = new Promise((_, reject) =>\r\n setTimeout(() => reject(new Error('Agents check timeout')), this.config.agentsTimeout)\r\n );\r\n\r\n // Race between actual check and timeout\r\n const metrics = await Promise.race([\r\n this.getAgentMetrics(),\r\n timeoutPromise,\r\n ]) as AgentMetrics;\r\n\r\n const latency = Date.now() - startTime;\r\n\r\n // Determine health status based on queue depth\r\n let status: 'healthy' | 'degraded' | 'unhealthy' = HealthStatus.HEALTHY;\r\n let message = `${metrics.activeAgentCount} agents active`;\r\n\r\n if (metrics.queueDepth > this.config.queueDepthCriticalThreshold) {\r\n status = HealthStatus.UNHEALTHY;\r\n message = `Critical queue depth: ${metrics.queueDepth} tasks`;\r\n } else if (metrics.queueDepth > this.config.queueDepthWarnThreshold) {\r\n status = HealthStatus.DEGRADED;\r\n message = `High queue depth: ${metrics.queueDepth} tasks`;\r\n }\r\n\r\n return {\r\n name: 'agents',\r\n status,\r\n latency,\r\n message,\r\n timestamp: new Date(),\r\n metadata: {\r\n activeAgentCount: metrics.activeAgentCount,\r\n queueDepth: metrics.queueDepth,\r\n },\r\n };\r\n } catch (error) {\r\n const latency = Date.now() - startTime;\r\n const message =\r\n error instanceof Error\r\n ? error.message\r\n : 'Unknown agent error';\r\n\r\n return {\r\n name: 'agents',\r\n status: HealthStatus.UNHEALTHY,\r\n latency,\r\n message: `Agent check failed: ${message}`,\r\n timestamp: new Date(),\r\n metadata: {\r\n error: message,\r\n },\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Get overall system health\r\n * Aggregates all service health checks\r\n */\r\n async getOverallHealth(): Promise<HealthCheck> {\r\n const overallStartTime = Date.now();\r\n\r\n const [database, redis, filesystem, agents] = await Promise.all([\r\n this.checkDatabase(),\r\n this.checkRedis(),\r\n this.checkFileSystem(),\r\n this.checkAgents(),\r\n ]);\r\n\r\n const dependencies = [database, redis, filesystem, agents];\r\n\r\n // Determine overall status\r\n // UNHEALTHY if any service is unhealthy\r\n // DEGRADED if any service is degraded\r\n // HEALTHY if all services are healthy\r\n let overallStatus: 'healthy' | 'degraded' | 'unhealthy' = HealthStatus.HEALTHY;\r\n const unhealthyServices = dependencies.filter((d) => d.status === HealthStatus.UNHEALTHY);\r\n const degradedServices = dependencies.filter((d) => d.status === HealthStatus.DEGRADED);\r\n\r\n if (unhealthyServices.length > 0) {\r\n overallStatus = HealthStatus.UNHEALTHY;\r\n } else if (degradedServices.length > 0) {\r\n overallStatus = HealthStatus.DEGRADED;\r\n }\r\n\r\n const latency = Date.now() - overallStartTime;\r\n const statusMessage =\r\n unhealthyServices.length > 0\r\n ? `${unhealthyServices.length} service(s) unhealthy`\r\n : degradedServices.length > 0\r\n ? `${degradedServices.length} service(s) degraded`\r\n : 'All services healthy';\r\n\r\n return {\r\n name: 'overall',\r\n status: overallStatus,\r\n latency,\r\n message: statusMessage,\r\n timestamp: new Date(),\r\n dependencies,\r\n };\r\n }\r\n\r\n /**\r\n * Get detailed health report\r\n * Includes all services and aggregated metrics\r\n */\r\n async getDetailedHealthReport(): Promise<DetailedHealthReport> {\r\n const reportStartTime = Date.now();\r\n\r\n const overall = await this.getOverallHealth();\r\n\r\n const report: DetailedHealthReport = {\r\n timestamp: new Date(),\r\n overallStatus: overall.status as 'healthy' | 'degraded' | 'unhealthy',\r\n latency: Date.now() - reportStartTime,\r\n services: {\r\n database: overall.dependencies![0],\r\n redis: overall.dependencies![1],\r\n filesystem: overall.dependencies![2],\r\n agents: overall.dependencies![3],\r\n },\r\n alerts: [],\r\n };\r\n\r\n // Build alerts\r\n if (report.overallStatus === HealthStatus.UNHEALTHY) {\r\n const unhealthy = overall.dependencies!.filter((d) => d.status === HealthStatus.UNHEALTHY);\r\n report.alerts = unhealthy.map((s) => `${s.name}: ${s.message}`);\r\n }\r\n\r\n if (report.overallStatus === HealthStatus.DEGRADED) {\r\n const degraded = overall.dependencies!.filter((d) => d.status === HealthStatus.DEGRADED);\r\n report.alerts = degraded.map((s) => `${s.name}: ${s.message}`);\r\n }\r\n\r\n return report;\r\n }\r\n\r\n /**\r\n * Fast ping check for basic connectivity\r\n * Returns in <100ms for Kubernetes probes and dashboards\r\n *\r\n * This is a lightweight check that verifies the system is responsive\r\n * without performing expensive operations like database queries.\r\n *\r\n * @param timeout - Optional timeout in milliseconds (default: 100ms)\r\n * @returns HealthCheck with basic connectivity status\r\n * @throws StandardError if ping fails or timeout exceeded\r\n */\r\n async ping(timeout: number = 100): Promise<HealthCheck> {\r\n const startTime = Date.now();\r\n\r\n try {\r\n // Create a timeout promise\r\n const timeoutPromise = new Promise<never>((_, reject) =>\r\n setTimeout(() => reject(\r\n new StandardError(\r\n ErrorCode.OPERATION_TIMEOUT,\r\n `Ping timeout after ${timeout}ms`,\r\n { timeout }\r\n )\r\n ), timeout)\r\n );\r\n\r\n // Race between basic checks and timeout\r\n await Promise.race([\r\n // Minimal checks - just verify system is responsive\r\n (async () => {\r\n // Check if we can access Date (basic runtime check)\r\n const now = Date.now();\r\n\r\n // Verify process is alive\r\n if (typeof process === 'undefined') {\r\n throw new StandardError(\r\n ErrorCode.UNKNOWN_ERROR,\r\n 'Process runtime not available',\r\n { check: 'ping' }\r\n );\r\n }\r\n\r\n // Verify we have memory available\r\n const memUsage = process.memoryUsage();\r\n if (memUsage.heapUsed > memUsage.heapTotal * 0.95) {\r\n throw new StandardError(\r\n ErrorCode.UNKNOWN_ERROR,\r\n 'Memory critically low',\r\n {\r\n heapUsed: memUsage.heapUsed,\r\n heapTotal: memUsage.heapTotal,\r\n percentUsed: (memUsage.heapUsed / memUsage.heapTotal) * 100\r\n }\r\n );\r\n }\r\n })(),\r\n timeoutPromise,\r\n ]);\r\n\r\n const latency = Date.now() - startTime;\r\n\r\n // Ensure we're under the target response time\r\n if (latency >= timeout) {\r\n throw new StandardError(\r\n ErrorCode.OPERATION_TIMEOUT,\r\n `Ping exceeded target response time: ${latency}ms >= ${timeout}ms`,\r\n { latency, timeout }\r\n );\r\n }\r\n\r\n return {\r\n name: 'ping',\r\n status: HealthStatus.HEALTHY,\r\n latency,\r\n message: 'System responsive',\r\n timestamp: new Date(),\r\n metadata: {\r\n responseTime: latency,\r\n memoryUsage: process.memoryUsage(),\r\n uptime: process.uptime(),\r\n },\r\n };\r\n } catch (error) {\r\n const latency = Date.now() - startTime;\r\n\r\n if (error instanceof StandardError) {\r\n throw error;\r\n }\r\n\r\n const message = error instanceof Error ? error.message : 'Unknown ping error';\r\n\r\n throw new StandardError(\r\n ErrorCode.UNKNOWN_ERROR,\r\n `Ping failed: ${message}`,\r\n { latency, timeout },\r\n error instanceof Error ? error : undefined\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Get aggregated health statistics from all endpoints\r\n * Provides a comprehensive view of system health metrics\r\n *\r\n * @param timeout - Optional timeout in milliseconds (default: 5000ms)\r\n * @returns AggregatedHealthStats with metrics from all services\r\n */\r\n async getAggregateStats(timeout: number = 5000): Promise<AggregatedHealthStats> {\r\n const startTime = Date.now();\r\n\r\n try {\r\n // Create a timeout promise\r\n const timeoutPromise = new Promise<never>((_, reject) =>\r\n setTimeout(() => reject(\r\n new StandardError(\r\n ErrorCode.OPERATION_TIMEOUT,\r\n `Aggregate stats timeout after ${timeout}ms`,\r\n { timeout }\r\n )\r\n ), timeout)\r\n );\r\n\r\n // Race between collecting all stats and timeout\r\n const result = await Promise.race([\r\n (async () => {\r\n // Collect all health checks in parallel\r\n const [database, redis, filesystem, agents] = await Promise.all([\r\n this.checkDatabase(),\r\n this.checkRedis(),\r\n this.checkFileSystem(),\r\n this.checkAgents(),\r\n ]);\r\n\r\n return { database, redis, filesystem, agents };\r\n })(),\r\n timeoutPromise,\r\n ]);\r\n\r\n const latency = Date.now() - startTime;\r\n\r\n // Calculate aggregate metrics\r\n const services = [result.database, result.redis, result.filesystem, result.agents];\r\n const healthyCount = services.filter((s) => s.status === HealthStatus.HEALTHY).length;\r\n const degradedCount = services.filter((s) => s.status === HealthStatus.DEGRADED).length;\r\n const unhealthyCount = services.filter((s) => s.status === HealthStatus.UNHEALTHY).length;\r\n\r\n // Determine overall status\r\n let overallStatus: 'healthy' | 'degraded' | 'unhealthy' = HealthStatus.HEALTHY;\r\n if (unhealthyCount > 0) {\r\n overallStatus = HealthStatus.UNHEALTHY;\r\n } else if (degradedCount > 0) {\r\n overallStatus = HealthStatus.DEGRADED;\r\n }\r\n\r\n // Calculate average latency\r\n const totalLatency = services.reduce((sum, s) => sum + s.latency, 0);\r\n const averageLatency = totalLatency / services.length;\r\n\r\n // Collect metadata from all services\r\n const metadata: Record<string, any> = {\r\n database: result.database.metadata,\r\n redis: result.redis.metadata,\r\n filesystem: result.filesystem.metadata,\r\n agents: result.agents.metadata,\r\n };\r\n\r\n // Build warnings list\r\n const warnings: string[] = [];\r\n if (degradedCount > 0) {\r\n const degradedServices = services.filter((s) => s.status === HealthStatus.DEGRADED);\r\n warnings.push(...degradedServices.map((s) => `${s.name}: ${s.message}`));\r\n }\r\n\r\n // Build errors list\r\n const errors: string[] = [];\r\n if (unhealthyCount > 0) {\r\n const unhealthyServices = services.filter((s) => s.status === HealthStatus.UNHEALTHY);\r\n errors.push(...unhealthyServices.map((s) => `${s.name}: ${s.message}`));\r\n }\r\n\r\n return {\r\n timestamp: new Date(),\r\n overallStatus,\r\n latency,\r\n averageServiceLatency: averageLatency,\r\n serviceCount: {\r\n total: services.length,\r\n healthy: healthyCount,\r\n degraded: degradedCount,\r\n unhealthy: unhealthyCount,\r\n },\r\n services: {\r\n database: {\r\n status: result.database.status,\r\n latency: result.database.latency,\r\n message: result.database.message,\r\n },\r\n redis: {\r\n status: result.redis.status,\r\n latency: result.redis.latency,\r\n message: result.redis.message,\r\n },\r\n filesystem: {\r\n status: result.filesystem.status,\r\n latency: result.filesystem.latency,\r\n message: result.filesystem.message,\r\n },\r\n agents: {\r\n status: result.agents.status,\r\n latency: result.agents.latency,\r\n message: result.agents.message,\r\n },\r\n },\r\n metadata,\r\n warnings,\r\n errors,\r\n };\r\n } catch (error) {\r\n const latency = Date.now() - startTime;\r\n\r\n if (error instanceof StandardError) {\r\n throw error;\r\n }\r\n\r\n const message = error instanceof Error ? error.message : 'Unknown aggregation error';\r\n\r\n throw new StandardError(\r\n ErrorCode.UNKNOWN_ERROR,\r\n `Failed to aggregate health stats: ${message}`,\r\n { latency, timeout },\r\n error instanceof Error ? error : undefined\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Get file system metrics\r\n * Private helper for filesystem check\r\n */\r\n private async getFileSystemMetrics(): Promise<FileSystemMetrics> {\r\n return new Promise((resolve, reject) => {\r\n // Get disk usage statistics\r\n const tempDir = os.tmpdir();\r\n const stat = fs.statSync(tempDir);\r\n\r\n // Use statvfs to get disk space information\r\n fs.statfs(tempDir, (err, stats) => {\r\n if (err) {\r\n reject(err);\r\n return;\r\n }\r\n\r\n const totalBlocks = stats.blocks;\r\n const availableBlocks = stats.bavail;\r\n const blockSize = stats.bsize;\r\n\r\n const totalSpaceMB = (totalBlocks * blockSize) / (1024 * 1024);\r\n const availableSpaceMB = (availableBlocks * blockSize) / (1024 * 1024);\r\n const usedSpaceMB = totalSpaceMB - availableSpaceMB;\r\n const diskUsagePercent = (usedSpaceMB / totalSpaceMB) * 100;\r\n\r\n // Check write permission by attempting to create a temp file\r\n const testFile = `${tempDir}/.health-check-test-${Date.now()}`;\r\n let writePermission = false;\r\n\r\n try {\r\n fs.writeFileSync(testFile, 'health-check-test');\r\n fs.unlinkSync(testFile);\r\n writePermission = true;\r\n } catch {\r\n writePermission = false;\r\n }\r\n\r\n resolve({\r\n totalSpaceMB,\r\n availableSpaceMB,\r\n usedSpaceMB,\r\n diskUsagePercent,\r\n writePermission,\r\n freeSpaceMB: availableSpaceMB,\r\n });\r\n });\r\n });\r\n }\r\n\r\n /**\r\n * Get agent metrics\r\n * Private helper for agent check\r\n */\r\n private async getAgentMetrics(): Promise<AgentMetrics> {\r\n // Get active agent count from Redis queue\r\n let activeAgentCount = 0;\r\n let queueDepth = 0;\r\n\r\n try {\r\n if (this.redisManager) {\r\n const stats = await this.redisManager.getStats();\r\n activeAgentCount = stats.activeCount || 0;\r\n queueDepth = stats.pendingCount || 0;\r\n }\r\n } catch {\r\n // If Redis is unavailable, return default metrics\r\n activeAgentCount = 0;\r\n queueDepth = 0;\r\n }\r\n\r\n return {\r\n activeAgentCount,\r\n queueDepth,\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * File system metrics helper interface\r\n */\r\ninterface FileSystemMetrics {\r\n totalSpaceMB: number;\r\n availableSpaceMB: number;\r\n usedSpaceMB: number;\r\n diskUsagePercent: number;\r\n writePermission: boolean;\r\n freeSpaceMB: number;\r\n}\r\n\r\n/**\r\n * Agent metrics helper interface\r\n */\r\ninterface AgentMetrics {\r\n activeAgentCount: number;\r\n queueDepth: number;\r\n}\r\n\r\n// Export types for use in API endpoints\r\nexport { FileSystemMetrics, AgentMetrics };\r\n"],"names":["getDatabaseService","RedisQueueManager","StandardError","ErrorCode","fs","os","HealthStatus","HealthCheckSystem","config","redisManager","databaseTimeout","redisTimeout","filesystemTimeout","agentsTimeout","diskUsageWarnThreshold","diskUsageCriticalThreshold","queueDepthWarnThreshold","queueDepthCriticalThreshold","error","checkDatabase","startTime","Date","now","db","timeoutPromise","Promise","_","reject","setTimeout","Error","race","query","latency","name","status","message","timestamp","metadata","responseTime","type","timeout","checkRedis","ping","stats","getStats","includes","checkFileSystem","result","getFileSystemMetrics","diskUsagePercent","toFixed","writePermission","freeSpaceMB","totalSpaceMB","checkAgents","metrics","getAgentMetrics","activeAgentCount","queueDepth","getOverallHealth","overallStartTime","database","redis","filesystem","agents","all","dependencies","overallStatus","unhealthyServices","filter","d","degradedServices","length","statusMessage","getDetailedHealthReport","reportStartTime","overall","report","services","alerts","unhealthy","map","s","degraded","OPERATION_TIMEOUT","process","UNKNOWN_ERROR","check","memUsage","memoryUsage","heapUsed","heapTotal","percentUsed","uptime","undefined","getAggregateStats","healthyCount","degradedCount","unhealthyCount","totalLatency","reduce","sum","averageLatency","warnings","push","errors","averageServiceLatency","serviceCount","total","healthy","resolve","tempDir","tmpdir","stat","statSync","statfs","err","totalBlocks","blocks","availableBlocks","bavail","blockSize","bsize","availableSpaceMB","usedSpaceMB","testFile","writeFileSync","unlinkSync","activeCount","pendingCount"],"mappings":"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"}
|
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metrics Logger Service
|
|
3
|
+
*
|
|
4
|
+
* Unified metrics and execution logging with dual database writes (PostgreSQL + SQLite).
|
|
5
|
+
* Part of Task 2.3: Unified Metrics and Execution Logging
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Atomic dual writes to PostgreSQL and SQLite
|
|
9
|
+
* - Idempotent writes (retries don't create duplicates)
|
|
10
|
+
* - Batch logging support
|
|
11
|
+
* - Cost tracking ($0.001 precision)
|
|
12
|
+
* - Token counting
|
|
13
|
+
* - Query interface for metrics analysis
|
|
14
|
+
*/ import { v4 as uuidv4 } from 'uuid';
|
|
15
|
+
import { createIdempotentKey, hasBeenWritten, markWritten, batchCheckWritten, batchMarkWritten, validateCostAccuracy, roundCost } from '../lib/idempotent-write.js';
|
|
16
|
+
import { createLogger } from '../lib/logging.js';
|
|
17
|
+
import { StandardError, ErrorCode } from '../lib/errors.js';
|
|
18
|
+
const logger = createLogger('metrics-logger');
|
|
19
|
+
/**
|
|
20
|
+
* Metrics Logger Service
|
|
21
|
+
*/ export class MetricsLogger {
|
|
22
|
+
dbService;
|
|
23
|
+
sqliteAdapter;
|
|
24
|
+
postgresAdapter;
|
|
25
|
+
batchSize;
|
|
26
|
+
flushIntervalMs;
|
|
27
|
+
batchQueue = [];
|
|
28
|
+
flushTimer = null;
|
|
29
|
+
constructor(config){
|
|
30
|
+
this.dbService = config.dbService;
|
|
31
|
+
this.batchSize = config.batchSize || 100;
|
|
32
|
+
this.flushIntervalMs = config.flushIntervalMs || 5000; // 5 seconds
|
|
33
|
+
// Get database adapters
|
|
34
|
+
this.sqliteAdapter = this.dbService.getAdapter('sqlite');
|
|
35
|
+
this.postgresAdapter = this.dbService.getAdapter('postgres');
|
|
36
|
+
logger.info('MetricsLogger initialized', {
|
|
37
|
+
batch_size: this.batchSize,
|
|
38
|
+
flush_interval_ms: this.flushIntervalMs
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Log single execution metrics
|
|
43
|
+
*
|
|
44
|
+
* @param metrics - Execution metrics to log
|
|
45
|
+
*/ async logExecution(metrics) {
|
|
46
|
+
try {
|
|
47
|
+
// Validate and normalize metrics
|
|
48
|
+
const normalizedMetrics = this.normalizeMetrics(metrics);
|
|
49
|
+
// Generate idempotency key
|
|
50
|
+
const idempotencyKey = createIdempotentKey(normalizedMetrics);
|
|
51
|
+
// Check if already written to both databases
|
|
52
|
+
const [sqliteWritten, postgresWritten] = await Promise.all([
|
|
53
|
+
hasBeenWritten(idempotencyKey, this.sqliteAdapter),
|
|
54
|
+
hasBeenWritten(idempotencyKey, this.postgresAdapter)
|
|
55
|
+
]);
|
|
56
|
+
if (sqliteWritten && postgresWritten) {
|
|
57
|
+
logger.debug('Metrics already logged (idempotent)', {
|
|
58
|
+
idempotency_key: idempotencyKey.substring(0, 16) + '...',
|
|
59
|
+
metrics_id: normalizedMetrics.id
|
|
60
|
+
});
|
|
61
|
+
return; // Already written, skip
|
|
62
|
+
}
|
|
63
|
+
// Atomic write to both databases
|
|
64
|
+
await this.dbService.executeTransaction([
|
|
65
|
+
{
|
|
66
|
+
database: 'sqlite',
|
|
67
|
+
operation: async (adapter)=>{
|
|
68
|
+
if (!sqliteWritten) {
|
|
69
|
+
await adapter.insert('execution_metrics', normalizedMetrics);
|
|
70
|
+
await markWritten(idempotencyKey, adapter, normalizedMetrics.id);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
database: 'postgres',
|
|
76
|
+
operation: async (adapter)=>{
|
|
77
|
+
if (!postgresWritten) {
|
|
78
|
+
await adapter.insert('execution_metrics', normalizedMetrics);
|
|
79
|
+
await markWritten(idempotencyKey, adapter, normalizedMetrics.id);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
]);
|
|
84
|
+
logger.info('Metrics logged successfully', {
|
|
85
|
+
metrics_id: normalizedMetrics.id,
|
|
86
|
+
agent_id: normalizedMetrics.agent_id,
|
|
87
|
+
task_id: normalizedMetrics.task_id,
|
|
88
|
+
duration_ms: normalizedMetrics.duration_ms,
|
|
89
|
+
cost_usd: normalizedMetrics.cost_usd,
|
|
90
|
+
status: normalizedMetrics.status
|
|
91
|
+
});
|
|
92
|
+
} catch (error) {
|
|
93
|
+
logger.error('Failed to log metrics', {
|
|
94
|
+
agent_id: metrics.agent_id,
|
|
95
|
+
task_id: metrics.task_id,
|
|
96
|
+
error: error instanceof Error ? error.message : String(error)
|
|
97
|
+
});
|
|
98
|
+
throw new StandardError(ErrorCode.DATABASE_ERROR, 'Failed to log execution metrics', {
|
|
99
|
+
metrics,
|
|
100
|
+
error
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Log batch of execution metrics (atomic)
|
|
106
|
+
*
|
|
107
|
+
* @param metricsList - Array of execution metrics
|
|
108
|
+
*/ async logBatch(metricsList) {
|
|
109
|
+
try {
|
|
110
|
+
if (metricsList.length === 0) {
|
|
111
|
+
logger.debug('Empty batch, skipping');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
// Normalize all metrics
|
|
115
|
+
const normalizedMetrics = metricsList.map((m)=>this.normalizeMetrics(m));
|
|
116
|
+
// Deduplicate based on idempotency keys
|
|
117
|
+
const uniqueMetrics = await this.deduplicateBatch(normalizedMetrics);
|
|
118
|
+
if (uniqueMetrics.length === 0) {
|
|
119
|
+
logger.debug('All metrics already logged (idempotent)', {
|
|
120
|
+
original_count: metricsList.length
|
|
121
|
+
});
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
// Prepare idempotency key entries
|
|
125
|
+
const idempotencyEntries = uniqueMetrics.map((m)=>({
|
|
126
|
+
key: createIdempotentKey(m),
|
|
127
|
+
metricsId: m.id
|
|
128
|
+
}));
|
|
129
|
+
// Batch write to both databases (atomic)
|
|
130
|
+
await this.dbService.executeTransaction([
|
|
131
|
+
{
|
|
132
|
+
database: 'sqlite',
|
|
133
|
+
operation: async (adapter)=>{
|
|
134
|
+
await adapter.insertMany('execution_metrics', uniqueMetrics);
|
|
135
|
+
await batchMarkWritten(idempotencyEntries, adapter);
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
database: 'postgres',
|
|
140
|
+
operation: async (adapter)=>{
|
|
141
|
+
await adapter.insertMany('execution_metrics', uniqueMetrics);
|
|
142
|
+
await batchMarkWritten(idempotencyEntries, adapter);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
]);
|
|
146
|
+
logger.info('Batch metrics logged successfully', {
|
|
147
|
+
total_metrics: metricsList.length,
|
|
148
|
+
unique_metrics: uniqueMetrics.length,
|
|
149
|
+
duplicates_skipped: metricsList.length - uniqueMetrics.length
|
|
150
|
+
});
|
|
151
|
+
} catch (error) {
|
|
152
|
+
logger.error('Failed to log batch metrics', {
|
|
153
|
+
batch_size: metricsList.length,
|
|
154
|
+
error: error instanceof Error ? error.message : String(error)
|
|
155
|
+
});
|
|
156
|
+
throw new StandardError(ErrorCode.DATABASE_ERROR, 'Failed to log batch metrics', {
|
|
157
|
+
metricsList,
|
|
158
|
+
error
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Query metrics with filters
|
|
164
|
+
*
|
|
165
|
+
* @param filters - Query filters
|
|
166
|
+
* @returns Array of execution metrics
|
|
167
|
+
*/ async queryMetrics(filters) {
|
|
168
|
+
try {
|
|
169
|
+
const queryOptions = {
|
|
170
|
+
filters: [],
|
|
171
|
+
limit: filters.limit || 100,
|
|
172
|
+
offset: filters.offset || 0,
|
|
173
|
+
orderBy: 'timestamp',
|
|
174
|
+
order: 'desc'
|
|
175
|
+
};
|
|
176
|
+
// Add filters
|
|
177
|
+
if (filters.agent_id) {
|
|
178
|
+
queryOptions.filters?.push({
|
|
179
|
+
field: 'agent_id',
|
|
180
|
+
operator: 'eq',
|
|
181
|
+
value: filters.agent_id
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
if (filters.skill_id) {
|
|
185
|
+
queryOptions.filters?.push({
|
|
186
|
+
field: 'skill_id',
|
|
187
|
+
operator: 'eq',
|
|
188
|
+
value: filters.skill_id
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
if (filters.task_id) {
|
|
192
|
+
queryOptions.filters?.push({
|
|
193
|
+
field: 'task_id',
|
|
194
|
+
operator: 'eq',
|
|
195
|
+
value: filters.task_id
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
if (filters.status) {
|
|
199
|
+
queryOptions.filters?.push({
|
|
200
|
+
field: 'status',
|
|
201
|
+
operator: 'eq',
|
|
202
|
+
value: filters.status
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
if (filters.start_date && filters.end_date) {
|
|
206
|
+
queryOptions.filters?.push({
|
|
207
|
+
field: 'timestamp',
|
|
208
|
+
operator: 'between',
|
|
209
|
+
value: [
|
|
210
|
+
filters.start_date,
|
|
211
|
+
filters.end_date
|
|
212
|
+
]
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
// Query from SQLite (primary source for metrics)
|
|
216
|
+
const results = await this.sqliteAdapter.list('execution_metrics', queryOptions);
|
|
217
|
+
logger.debug('Metrics query executed', {
|
|
218
|
+
filters,
|
|
219
|
+
result_count: results.length
|
|
220
|
+
});
|
|
221
|
+
return results;
|
|
222
|
+
} catch (error) {
|
|
223
|
+
logger.error('Failed to query metrics', {
|
|
224
|
+
filters,
|
|
225
|
+
error: error instanceof Error ? error.message : String(error)
|
|
226
|
+
});
|
|
227
|
+
throw new StandardError(ErrorCode.DATABASE_ERROR, 'Failed to query metrics', {
|
|
228
|
+
filters,
|
|
229
|
+
error
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Get aggregated metrics by agent
|
|
235
|
+
*
|
|
236
|
+
* @param startDate - Start date for aggregation
|
|
237
|
+
* @param endDate - End date for aggregation
|
|
238
|
+
* @returns Array of aggregated metrics
|
|
239
|
+
*/ async getAggregatedMetrics(startDate, endDate) {
|
|
240
|
+
try {
|
|
241
|
+
// Query all metrics in date range
|
|
242
|
+
const filters = {
|
|
243
|
+
start_date: startDate,
|
|
244
|
+
end_date: endDate,
|
|
245
|
+
limit: 10000
|
|
246
|
+
};
|
|
247
|
+
const metrics = await this.queryMetrics(filters);
|
|
248
|
+
// Aggregate by agent_id
|
|
249
|
+
const aggregationMap = new Map();
|
|
250
|
+
metrics.forEach((m)=>{
|
|
251
|
+
if (!aggregationMap.has(m.agent_id)) {
|
|
252
|
+
aggregationMap.set(m.agent_id, {
|
|
253
|
+
agent_id: m.agent_id,
|
|
254
|
+
total_executions: 0,
|
|
255
|
+
total_cost_usd: 0,
|
|
256
|
+
total_tokens: 0,
|
|
257
|
+
avg_duration_ms: 0,
|
|
258
|
+
success_count: 0,
|
|
259
|
+
failure_count: 0,
|
|
260
|
+
success_rate: 0
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
const agg = aggregationMap.get(m.agent_id);
|
|
264
|
+
agg.total_executions++;
|
|
265
|
+
agg.total_cost_usd += m.cost_usd;
|
|
266
|
+
agg.total_tokens += m.tokens_used;
|
|
267
|
+
agg.avg_duration_ms += m.duration_ms;
|
|
268
|
+
if (m.status === 'success') {
|
|
269
|
+
agg.success_count++;
|
|
270
|
+
} else {
|
|
271
|
+
agg.failure_count++;
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
// Calculate averages and success rates
|
|
275
|
+
const results = [];
|
|
276
|
+
aggregationMap.forEach((agg)=>{
|
|
277
|
+
agg.avg_duration_ms = Math.round(agg.avg_duration_ms / agg.total_executions);
|
|
278
|
+
agg.success_rate = agg.success_count / agg.total_executions * 100;
|
|
279
|
+
agg.total_cost_usd = roundCost(agg.total_cost_usd);
|
|
280
|
+
results.push(agg);
|
|
281
|
+
});
|
|
282
|
+
// Sort by total cost (descending)
|
|
283
|
+
results.sort((a, b)=>b.total_cost_usd - a.total_cost_usd);
|
|
284
|
+
logger.info('Aggregated metrics calculated', {
|
|
285
|
+
agent_count: results.length,
|
|
286
|
+
total_executions: results.reduce((sum, agg)=>sum + agg.total_executions, 0)
|
|
287
|
+
});
|
|
288
|
+
return results;
|
|
289
|
+
} catch (error) {
|
|
290
|
+
logger.error('Failed to get aggregated metrics', {
|
|
291
|
+
error: error instanceof Error ? error.message : String(error)
|
|
292
|
+
});
|
|
293
|
+
throw new StandardError(ErrorCode.DATABASE_ERROR, 'Failed to get aggregated metrics', {
|
|
294
|
+
error
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Add metrics to batch queue
|
|
300
|
+
*
|
|
301
|
+
* @param metrics - Execution metrics to queue
|
|
302
|
+
*/ async queueMetrics(metrics) {
|
|
303
|
+
this.batchQueue.push(metrics);
|
|
304
|
+
// Flush if batch size reached
|
|
305
|
+
if (this.batchQueue.length >= this.batchSize) {
|
|
306
|
+
await this.flush();
|
|
307
|
+
} else {
|
|
308
|
+
// Start flush timer if not already running
|
|
309
|
+
if (!this.flushTimer) {
|
|
310
|
+
this.flushTimer = setTimeout(()=>this.flush(), this.flushIntervalMs);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Flush batch queue
|
|
316
|
+
*/ async flush() {
|
|
317
|
+
if (this.batchQueue.length === 0) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
// Clear timer
|
|
321
|
+
if (this.flushTimer) {
|
|
322
|
+
clearTimeout(this.flushTimer);
|
|
323
|
+
this.flushTimer = null;
|
|
324
|
+
}
|
|
325
|
+
// Copy and clear queue
|
|
326
|
+
const batch = [
|
|
327
|
+
...this.batchQueue
|
|
328
|
+
];
|
|
329
|
+
this.batchQueue = [];
|
|
330
|
+
// Log batch
|
|
331
|
+
await this.logBatch(batch);
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Normalize metrics (add ID, validate, round cost)
|
|
335
|
+
*
|
|
336
|
+
* @param metrics - Raw metrics
|
|
337
|
+
* @returns Normalized metrics
|
|
338
|
+
*/ normalizeMetrics(metrics) {
|
|
339
|
+
// Generate ID if not provided
|
|
340
|
+
const id = metrics.id || uuidv4();
|
|
341
|
+
// Validate cost accuracy
|
|
342
|
+
const cost = roundCost(metrics.cost_usd);
|
|
343
|
+
if (!validateCostAccuracy(cost)) {
|
|
344
|
+
logger.warn('Cost accuracy validation failed', {
|
|
345
|
+
original_cost: metrics.cost_usd,
|
|
346
|
+
rounded_cost: cost
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
// Ensure timestamp is Date object
|
|
350
|
+
const timestamp = metrics.timestamp instanceof Date ? metrics.timestamp : new Date(metrics.timestamp);
|
|
351
|
+
return {
|
|
352
|
+
...metrics,
|
|
353
|
+
id,
|
|
354
|
+
timestamp,
|
|
355
|
+
cost_usd: cost,
|
|
356
|
+
metadata: metrics.metadata ? JSON.stringify(metrics.metadata) : undefined
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Deduplicate batch based on idempotency keys
|
|
361
|
+
*
|
|
362
|
+
* @param metricsList - Batch of metrics
|
|
363
|
+
* @returns Unique metrics (not already written)
|
|
364
|
+
*/ async deduplicateBatch(metricsList) {
|
|
365
|
+
// Generate idempotency keys
|
|
366
|
+
const keys = metricsList.map((m)=>createIdempotentKey(m));
|
|
367
|
+
// Check which keys already exist in SQLite
|
|
368
|
+
const sqliteStatus = await batchCheckWritten(keys, this.sqliteAdapter);
|
|
369
|
+
// Check which keys already exist in PostgreSQL
|
|
370
|
+
const postgresStatus = await batchCheckWritten(keys, this.postgresAdapter);
|
|
371
|
+
// Filter out metrics that are already written to BOTH databases
|
|
372
|
+
const uniqueMetrics = [];
|
|
373
|
+
metricsList.forEach((metrics, index)=>{
|
|
374
|
+
const key = keys[index];
|
|
375
|
+
const inSQLite = sqliteStatus.get(key) || false;
|
|
376
|
+
const inPostgres = postgresStatus.get(key) || false;
|
|
377
|
+
if (!inSQLite || !inPostgres) {
|
|
378
|
+
// At least one database doesn't have this metric, include it
|
|
379
|
+
uniqueMetrics.push(metrics);
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
return uniqueMetrics;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Close and cleanup resources
|
|
386
|
+
*/ async close() {
|
|
387
|
+
// Flush remaining metrics
|
|
388
|
+
await this.flush();
|
|
389
|
+
// Clear timer
|
|
390
|
+
if (this.flushTimer) {
|
|
391
|
+
clearTimeout(this.flushTimer);
|
|
392
|
+
this.flushTimer = null;
|
|
393
|
+
}
|
|
394
|
+
logger.info('MetricsLogger closed');
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Create MetricsLogger instance
|
|
399
|
+
*
|
|
400
|
+
* @param dbService - Database service
|
|
401
|
+
* @param batchSize - Optional batch size
|
|
402
|
+
* @param flushIntervalMs - Optional flush interval
|
|
403
|
+
* @returns MetricsLogger instance
|
|
404
|
+
*/ export function createMetricsLogger(dbService, batchSize, flushIntervalMs) {
|
|
405
|
+
return new MetricsLogger({
|
|
406
|
+
dbService,
|
|
407
|
+
batchSize,
|
|
408
|
+
flushIntervalMs
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
//# sourceMappingURL=metrics-logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/metrics-logger.ts"],"sourcesContent":["/**\r\n * Metrics Logger Service\r\n *\r\n * Unified metrics and execution logging with dual database writes (PostgreSQL + SQLite).\r\n * Part of Task 2.3: Unified Metrics and Execution Logging\r\n *\r\n * Features:\r\n * - Atomic dual writes to PostgreSQL and SQLite\r\n * - Idempotent writes (retries don't create duplicates)\r\n * - Batch logging support\r\n * - Cost tracking ($0.001 precision)\r\n * - Token counting\r\n * - Query interface for metrics analysis\r\n */\r\n\r\nimport { v4 as uuidv4 } from 'uuid';\r\nimport { DatabaseService } from '../lib/database-service.js';\r\nimport { IDatabaseAdapter, QueryOptions, OperationResult } from '../lib/database-service/types.js';\r\nimport {\r\n createIdempotentKey,\r\n hasBeenWritten,\r\n markWritten,\r\n batchCheckWritten,\r\n batchMarkWritten,\r\n validateCostAccuracy,\r\n roundCost,\r\n} from '../lib/idempotent-write.js';\r\nimport { createLogger } from '../lib/logging.js';\r\nimport { StandardError, ErrorCode } from '../lib/errors.js';\r\n\r\nconst logger = createLogger('metrics-logger');\r\n\r\n/**\r\n * Execution metrics interface\r\n */\r\nexport interface ExecutionMetrics {\r\n id?: string;\r\n timestamp: Date;\r\n agent_id: string;\r\n skill_id?: string;\r\n task_id: string;\r\n duration_ms: number;\r\n tokens_used: number;\r\n cost_usd: number;\r\n status: 'success' | 'failure' | 'timeout' | 'cancelled';\r\n error_message?: string;\r\n metadata?: Record<string, any>;\r\n}\r\n\r\n/**\r\n * Metrics filter for querying\r\n */\r\nexport interface MetricsFilter {\r\n agent_id?: string;\r\n skill_id?: string;\r\n task_id?: string;\r\n status?: 'success' | 'failure' | 'timeout' | 'cancelled';\r\n start_date?: Date;\r\n end_date?: Date;\r\n limit?: number;\r\n offset?: number;\r\n}\r\n\r\n/**\r\n * Metrics aggregation result\r\n */\r\nexport interface MetricsAggregation {\r\n agent_id: string;\r\n total_executions: number;\r\n total_cost_usd: number;\r\n total_tokens: number;\r\n avg_duration_ms: number;\r\n success_count: number;\r\n failure_count: number;\r\n success_rate: number;\r\n}\r\n\r\n/**\r\n * Metrics Logger Service Configuration\r\n */\r\nexport interface MetricsLoggerConfig {\r\n dbService: DatabaseService;\r\n batchSize?: number;\r\n flushIntervalMs?: number;\r\n}\r\n\r\n/**\r\n * Metrics Logger Service\r\n */\r\nexport class MetricsLogger {\r\n private dbService: DatabaseService;\r\n private sqliteAdapter: IDatabaseAdapter;\r\n private postgresAdapter: IDatabaseAdapter;\r\n private batchSize: number;\r\n private flushIntervalMs: number;\r\n private batchQueue: ExecutionMetrics[] = [];\r\n private flushTimer: NodeJS.Timeout | null = null;\r\n\r\n constructor(config: MetricsLoggerConfig) {\r\n this.dbService = config.dbService;\r\n this.batchSize = config.batchSize || 100;\r\n this.flushIntervalMs = config.flushIntervalMs || 5000; // 5 seconds\r\n\r\n // Get database adapters\r\n this.sqliteAdapter = this.dbService.getAdapter('sqlite');\r\n this.postgresAdapter = this.dbService.getAdapter('postgres');\r\n\r\n logger.info('MetricsLogger initialized', {\r\n batch_size: this.batchSize,\r\n flush_interval_ms: this.flushIntervalMs,\r\n });\r\n }\r\n\r\n /**\r\n * Log single execution metrics\r\n *\r\n * @param metrics - Execution metrics to log\r\n */\r\n async logExecution(metrics: ExecutionMetrics): Promise<void> {\r\n try {\r\n // Validate and normalize metrics\r\n const normalizedMetrics = this.normalizeMetrics(metrics);\r\n\r\n // Generate idempotency key\r\n const idempotencyKey = createIdempotentKey(normalizedMetrics);\r\n\r\n // Check if already written to both databases\r\n const [sqliteWritten, postgresWritten] = await Promise.all([\r\n hasBeenWritten(idempotencyKey, this.sqliteAdapter),\r\n hasBeenWritten(idempotencyKey, this.postgresAdapter),\r\n ]);\r\n\r\n if (sqliteWritten && postgresWritten) {\r\n logger.debug('Metrics already logged (idempotent)', {\r\n idempotency_key: idempotencyKey.substring(0, 16) + '...',\r\n metrics_id: normalizedMetrics.id,\r\n });\r\n return; // Already written, skip\r\n }\r\n\r\n // Atomic write to both databases\r\n await this.dbService.executeTransaction([\r\n {\r\n database: 'sqlite',\r\n operation: async (adapter) => {\r\n if (!sqliteWritten) {\r\n await adapter.insert('execution_metrics', normalizedMetrics);\r\n await markWritten(idempotencyKey, adapter, normalizedMetrics.id);\r\n }\r\n },\r\n },\r\n {\r\n database: 'postgres',\r\n operation: async (adapter) => {\r\n if (!postgresWritten) {\r\n await adapter.insert('execution_metrics', normalizedMetrics);\r\n await markWritten(idempotencyKey, adapter, normalizedMetrics.id);\r\n }\r\n },\r\n },\r\n ]);\r\n\r\n logger.info('Metrics logged successfully', {\r\n metrics_id: normalizedMetrics.id,\r\n agent_id: normalizedMetrics.agent_id,\r\n task_id: normalizedMetrics.task_id,\r\n duration_ms: normalizedMetrics.duration_ms,\r\n cost_usd: normalizedMetrics.cost_usd,\r\n status: normalizedMetrics.status,\r\n });\r\n } catch (error) {\r\n logger.error('Failed to log metrics', {\r\n agent_id: metrics.agent_id,\r\n task_id: metrics.task_id,\r\n error: error instanceof Error ? error.message : String(error),\r\n });\r\n\r\n throw new StandardError(\r\n ErrorCode.DATABASE_ERROR,\r\n 'Failed to log execution metrics',\r\n { metrics, error }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Log batch of execution metrics (atomic)\r\n *\r\n * @param metricsList - Array of execution metrics\r\n */\r\n async logBatch(metricsList: ExecutionMetrics[]): Promise<void> {\r\n try {\r\n if (metricsList.length === 0) {\r\n logger.debug('Empty batch, skipping');\r\n return;\r\n }\r\n\r\n // Normalize all metrics\r\n const normalizedMetrics = metricsList.map(m => this.normalizeMetrics(m));\r\n\r\n // Deduplicate based on idempotency keys\r\n const uniqueMetrics = await this.deduplicateBatch(normalizedMetrics);\r\n\r\n if (uniqueMetrics.length === 0) {\r\n logger.debug('All metrics already logged (idempotent)', {\r\n original_count: metricsList.length,\r\n });\r\n return;\r\n }\r\n\r\n // Prepare idempotency key entries\r\n const idempotencyEntries = uniqueMetrics.map(m => ({\r\n key: createIdempotentKey(m),\r\n metricsId: m.id,\r\n }));\r\n\r\n // Batch write to both databases (atomic)\r\n await this.dbService.executeTransaction([\r\n {\r\n database: 'sqlite',\r\n operation: async (adapter) => {\r\n await adapter.insertMany('execution_metrics', uniqueMetrics);\r\n await batchMarkWritten(idempotencyEntries, adapter);\r\n },\r\n },\r\n {\r\n database: 'postgres',\r\n operation: async (adapter) => {\r\n await adapter.insertMany('execution_metrics', uniqueMetrics);\r\n await batchMarkWritten(idempotencyEntries, adapter);\r\n },\r\n },\r\n ]);\r\n\r\n logger.info('Batch metrics logged successfully', {\r\n total_metrics: metricsList.length,\r\n unique_metrics: uniqueMetrics.length,\r\n duplicates_skipped: metricsList.length - uniqueMetrics.length,\r\n });\r\n } catch (error) {\r\n logger.error('Failed to log batch metrics', {\r\n batch_size: metricsList.length,\r\n error: error instanceof Error ? error.message : String(error),\r\n });\r\n\r\n throw new StandardError(\r\n ErrorCode.DATABASE_ERROR,\r\n 'Failed to log batch metrics',\r\n { metricsList, error }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Query metrics with filters\r\n *\r\n * @param filters - Query filters\r\n * @returns Array of execution metrics\r\n */\r\n async queryMetrics(filters: MetricsFilter): Promise<ExecutionMetrics[]> {\r\n try {\r\n const queryOptions: QueryOptions<ExecutionMetrics> = {\r\n filters: [],\r\n limit: filters.limit || 100,\r\n offset: filters.offset || 0,\r\n orderBy: 'timestamp' as keyof ExecutionMetrics,\r\n order: 'desc',\r\n };\r\n\r\n // Add filters\r\n if (filters.agent_id) {\r\n queryOptions.filters?.push({\r\n field: 'agent_id' as keyof ExecutionMetrics,\r\n operator: 'eq',\r\n value: filters.agent_id,\r\n });\r\n }\r\n\r\n if (filters.skill_id) {\r\n queryOptions.filters?.push({\r\n field: 'skill_id' as keyof ExecutionMetrics,\r\n operator: 'eq',\r\n value: filters.skill_id,\r\n });\r\n }\r\n\r\n if (filters.task_id) {\r\n queryOptions.filters?.push({\r\n field: 'task_id' as keyof ExecutionMetrics,\r\n operator: 'eq',\r\n value: filters.task_id,\r\n });\r\n }\r\n\r\n if (filters.status) {\r\n queryOptions.filters?.push({\r\n field: 'status' as keyof ExecutionMetrics,\r\n operator: 'eq',\r\n value: filters.status,\r\n });\r\n }\r\n\r\n if (filters.start_date && filters.end_date) {\r\n queryOptions.filters?.push({\r\n field: 'timestamp' as keyof ExecutionMetrics,\r\n operator: 'between',\r\n value: [filters.start_date, filters.end_date],\r\n });\r\n }\r\n\r\n // Query from SQLite (primary source for metrics)\r\n const results = await this.sqliteAdapter.list<ExecutionMetrics>(\r\n 'execution_metrics',\r\n queryOptions\r\n );\r\n\r\n logger.debug('Metrics query executed', {\r\n filters,\r\n result_count: results.length,\r\n });\r\n\r\n return results;\r\n } catch (error) {\r\n logger.error('Failed to query metrics', {\r\n filters,\r\n error: error instanceof Error ? error.message : String(error),\r\n });\r\n\r\n throw new StandardError(\r\n ErrorCode.DATABASE_ERROR,\r\n 'Failed to query metrics',\r\n { filters, error }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Get aggregated metrics by agent\r\n *\r\n * @param startDate - Start date for aggregation\r\n * @param endDate - End date for aggregation\r\n * @returns Array of aggregated metrics\r\n */\r\n async getAggregatedMetrics(\r\n startDate?: Date,\r\n endDate?: Date\r\n ): Promise<MetricsAggregation[]> {\r\n try {\r\n // Query all metrics in date range\r\n const filters: MetricsFilter = {\r\n start_date: startDate,\r\n end_date: endDate,\r\n limit: 10000, // Large limit for aggregation\r\n };\r\n\r\n const metrics = await this.queryMetrics(filters);\r\n\r\n // Aggregate by agent_id\r\n const aggregationMap = new Map<string, MetricsAggregation>();\r\n\r\n metrics.forEach(m => {\r\n if (!aggregationMap.has(m.agent_id)) {\r\n aggregationMap.set(m.agent_id, {\r\n agent_id: m.agent_id,\r\n total_executions: 0,\r\n total_cost_usd: 0,\r\n total_tokens: 0,\r\n avg_duration_ms: 0,\r\n success_count: 0,\r\n failure_count: 0,\r\n success_rate: 0,\r\n });\r\n }\r\n\r\n const agg = aggregationMap.get(m.agent_id)!;\r\n agg.total_executions++;\r\n agg.total_cost_usd += m.cost_usd;\r\n agg.total_tokens += m.tokens_used;\r\n agg.avg_duration_ms += m.duration_ms;\r\n\r\n if (m.status === 'success') {\r\n agg.success_count++;\r\n } else {\r\n agg.failure_count++;\r\n }\r\n });\r\n\r\n // Calculate averages and success rates\r\n const results: MetricsAggregation[] = [];\r\n aggregationMap.forEach(agg => {\r\n agg.avg_duration_ms = Math.round(agg.avg_duration_ms / agg.total_executions);\r\n agg.success_rate = (agg.success_count / agg.total_executions) * 100;\r\n agg.total_cost_usd = roundCost(agg.total_cost_usd);\r\n results.push(agg);\r\n });\r\n\r\n // Sort by total cost (descending)\r\n results.sort((a, b) => b.total_cost_usd - a.total_cost_usd);\r\n\r\n logger.info('Aggregated metrics calculated', {\r\n agent_count: results.length,\r\n total_executions: results.reduce((sum, agg) => sum + agg.total_executions, 0),\r\n });\r\n\r\n return results;\r\n } catch (error) {\r\n logger.error('Failed to get aggregated metrics', {\r\n error: error instanceof Error ? error.message : String(error),\r\n });\r\n\r\n throw new StandardError(\r\n ErrorCode.DATABASE_ERROR,\r\n 'Failed to get aggregated metrics',\r\n { error }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Add metrics to batch queue\r\n *\r\n * @param metrics - Execution metrics to queue\r\n */\r\n async queueMetrics(metrics: ExecutionMetrics): Promise<void> {\r\n this.batchQueue.push(metrics);\r\n\r\n // Flush if batch size reached\r\n if (this.batchQueue.length >= this.batchSize) {\r\n await this.flush();\r\n } else {\r\n // Start flush timer if not already running\r\n if (!this.flushTimer) {\r\n this.flushTimer = setTimeout(() => this.flush(), this.flushIntervalMs);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Flush batch queue\r\n */\r\n async flush(): Promise<void> {\r\n if (this.batchQueue.length === 0) {\r\n return;\r\n }\r\n\r\n // Clear timer\r\n if (this.flushTimer) {\r\n clearTimeout(this.flushTimer);\r\n this.flushTimer = null;\r\n }\r\n\r\n // Copy and clear queue\r\n const batch = [...this.batchQueue];\r\n this.batchQueue = [];\r\n\r\n // Log batch\r\n await this.logBatch(batch);\r\n }\r\n\r\n /**\r\n * Normalize metrics (add ID, validate, round cost)\r\n *\r\n * @param metrics - Raw metrics\r\n * @returns Normalized metrics\r\n */\r\n private normalizeMetrics(metrics: ExecutionMetrics): ExecutionMetrics {\r\n // Generate ID if not provided\r\n const id = metrics.id || uuidv4();\r\n\r\n // Validate cost accuracy\r\n const cost = roundCost(metrics.cost_usd);\r\n if (!validateCostAccuracy(cost)) {\r\n logger.warn('Cost accuracy validation failed', {\r\n original_cost: metrics.cost_usd,\r\n rounded_cost: cost,\r\n });\r\n }\r\n\r\n // Ensure timestamp is Date object\r\n const timestamp = metrics.timestamp instanceof Date\r\n ? metrics.timestamp\r\n : new Date(metrics.timestamp);\r\n\r\n return {\r\n ...metrics,\r\n id,\r\n timestamp,\r\n cost_usd: cost,\r\n metadata: metrics.metadata ? JSON.stringify(metrics.metadata) : undefined,\r\n } as ExecutionMetrics;\r\n }\r\n\r\n /**\r\n * Deduplicate batch based on idempotency keys\r\n *\r\n * @param metricsList - Batch of metrics\r\n * @returns Unique metrics (not already written)\r\n */\r\n private async deduplicateBatch(\r\n metricsList: ExecutionMetrics[]\r\n ): Promise<ExecutionMetrics[]> {\r\n // Generate idempotency keys\r\n const keys = metricsList.map(m => createIdempotentKey(m));\r\n\r\n // Check which keys already exist in SQLite\r\n const sqliteStatus = await batchCheckWritten(keys, this.sqliteAdapter);\r\n\r\n // Check which keys already exist in PostgreSQL\r\n const postgresStatus = await batchCheckWritten(keys, this.postgresAdapter);\r\n\r\n // Filter out metrics that are already written to BOTH databases\r\n const uniqueMetrics: ExecutionMetrics[] = [];\r\n metricsList.forEach((metrics, index) => {\r\n const key = keys[index];\r\n const inSQLite = sqliteStatus.get(key) || false;\r\n const inPostgres = postgresStatus.get(key) || false;\r\n\r\n if (!inSQLite || !inPostgres) {\r\n // At least one database doesn't have this metric, include it\r\n uniqueMetrics.push(metrics);\r\n }\r\n });\r\n\r\n return uniqueMetrics;\r\n }\r\n\r\n /**\r\n * Close and cleanup resources\r\n */\r\n async close(): Promise<void> {\r\n // Flush remaining metrics\r\n await this.flush();\r\n\r\n // Clear timer\r\n if (this.flushTimer) {\r\n clearTimeout(this.flushTimer);\r\n this.flushTimer = null;\r\n }\r\n\r\n logger.info('MetricsLogger closed');\r\n }\r\n}\r\n\r\n/**\r\n * Create MetricsLogger instance\r\n *\r\n * @param dbService - Database service\r\n * @param batchSize - Optional batch size\r\n * @param flushIntervalMs - Optional flush interval\r\n * @returns MetricsLogger instance\r\n */\r\nexport function createMetricsLogger(\r\n dbService: DatabaseService,\r\n batchSize?: number,\r\n flushIntervalMs?: number\r\n): MetricsLogger {\r\n return new MetricsLogger({\r\n dbService,\r\n batchSize,\r\n flushIntervalMs,\r\n });\r\n}\r\n"],"names":["v4","uuidv4","createIdempotentKey","hasBeenWritten","markWritten","batchCheckWritten","batchMarkWritten","validateCostAccuracy","roundCost","createLogger","StandardError","ErrorCode","logger","MetricsLogger","dbService","sqliteAdapter","postgresAdapter","batchSize","flushIntervalMs","batchQueue","flushTimer","config","getAdapter","info","batch_size","flush_interval_ms","logExecution","metrics","normalizedMetrics","normalizeMetrics","idempotencyKey","sqliteWritten","postgresWritten","Promise","all","debug","idempotency_key","substring","metrics_id","id","executeTransaction","database","operation","adapter","insert","agent_id","task_id","duration_ms","cost_usd","status","error","Error","message","String","DATABASE_ERROR","logBatch","metricsList","length","map","m","uniqueMetrics","deduplicateBatch","original_count","idempotencyEntries","key","metricsId","insertMany","total_metrics","unique_metrics","duplicates_skipped","queryMetrics","filters","queryOptions","limit","offset","orderBy","order","push","field","operator","value","skill_id","start_date","end_date","results","list","result_count","getAggregatedMetrics","startDate","endDate","aggregationMap","Map","forEach","has","set","total_executions","total_cost_usd","total_tokens","avg_duration_ms","success_count","failure_count","success_rate","agg","get","tokens_used","Math","round","sort","a","b","agent_count","reduce","sum","queueMetrics","flush","setTimeout","clearTimeout","batch","cost","warn","original_cost","rounded_cost","timestamp","Date","metadata","JSON","stringify","undefined","keys","sqliteStatus","postgresStatus","index","inSQLite","inPostgres","close","createMetricsLogger"],"mappings":"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"}
|