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/lib/redis-queue-manager.ts"],"sourcesContent":["/**\r\n * Redis Queue Manager\r\n *\r\n * Provides reliable queue operations with idempotency, acknowledgment protocol,\r\n * and message visibility timeout for Docker agent ↔ Redis communication.\r\n * Part of Task 3.4: Redis Queue Consistency & Recovery (Integration Standardization Sprint 3)\r\n *\r\n * Features:\r\n * - Enqueue with idempotency (prevents duplicate messages)\r\n * - Dequeue with acknowledgment protocol\r\n * - Message visibility timeout\r\n * - Queue monitoring (depth, age, throughput)\r\n * - Multiple queue support (task, result, coordination)\r\n * - Performance: <100ms per operation\r\n *\r\n * Usage:\r\n * const queueManager = new RedisQueueManager(redisClient);\r\n *\r\n * // Producer\r\n * await queueManager.enqueue('task-queue', {\r\n * taskId: 'task-001',\r\n * agentType: 'backend-developer',\r\n * payload: { ... }\r\n * });\r\n *\r\n * // Consumer\r\n * const message = await queueManager.dequeue('task-queue', { timeout: 30000 });\r\n * try {\r\n * await processTask(message.payload);\r\n * await queueManager.acknowledge(message.id);\r\n * } catch (error) {\r\n * await queueManager.reject(message.id, { retry: true });\r\n * }\r\n */\r\n\r\nimport { RedisClientType } from 'redis';\r\nimport { v4 as uuidv4 } from 'uuid';\r\nimport { createLogger } from './logging.js';\r\nimport { createError, ErrorCode, isRetryableError } from './errors.js';\r\nimport { withRetry } from './retry.js';\r\nimport { MessageDeduplicator } from './message-deduplicator.js';\r\n\r\nconst logger = createLogger('redis-queue-manager');\r\n\r\n/**\r\n * Queue message\r\n */\r\nexport interface QueueMessage<T = any> {\r\n /** Unique message ID */\r\n id: string;\r\n /** Queue name */\r\n queue: string;\r\n /** Message payload */\r\n payload: T;\r\n /** Message creation timestamp */\r\n createdAt: Date;\r\n /** Message enqueue timestamp */\r\n enqueuedAt: Date;\r\n /** Message dequeue timestamp (if dequeued) */\r\n dequeuedAt?: Date;\r\n /** Number of delivery attempts */\r\n deliveryAttempts: number;\r\n /** Message visibility timeout (milliseconds) */\r\n visibilityTimeout?: number;\r\n /** Message metadata */\r\n metadata?: Record<string, any>;\r\n}\r\n\r\n/**\r\n * Enqueue options\r\n */\r\nexport interface EnqueueOptions {\r\n /** Enable deduplication (default: true) */\r\n deduplicate?: boolean;\r\n /** Message metadata */\r\n metadata?: Record<string, any>;\r\n /** Message visibility timeout in milliseconds (default: 30000) */\r\n visibilityTimeout?: number;\r\n}\r\n\r\n/**\r\n * Dequeue options\r\n */\r\nexport interface DequeueOptions {\r\n /** Maximum wait time in milliseconds (default: 0 - no wait) */\r\n timeout?: number;\r\n /** Message visibility timeout in milliseconds (default: 30000) */\r\n visibilityTimeout?: number;\r\n /** Number of messages to dequeue (default: 1) */\r\n count?: number;\r\n}\r\n\r\n/**\r\n * Reject options\r\n */\r\nexport interface RejectOptions {\r\n /** Retry message (re-enqueue) (default: false) */\r\n retry?: boolean;\r\n /** Error message */\r\n error?: string;\r\n /** Metadata to attach */\r\n metadata?: Record<string, any>;\r\n}\r\n\r\n/**\r\n * Queue statistics\r\n */\r\nexport interface QueueStats {\r\n /** Queue name */\r\n queue: string;\r\n /** Number of messages in queue */\r\n depth: number;\r\n /** Number of messages in processing (invisible) */\r\n inFlight: number;\r\n /** Age of oldest message in seconds */\r\n oldestMessageAge: number;\r\n /** Total messages enqueued */\r\n totalEnqueued: number;\r\n /** Total messages dequeued */\r\n totalDequeued: number;\r\n /** Total messages acknowledged */\r\n totalAcknowledged: number;\r\n /** Total messages rejected */\r\n totalRejected: number;\r\n /** Throughput (messages per second) */\r\n throughput: number;\r\n}\r\n\r\n/**\r\n * Default queue options\r\n */\r\nconst DEFAULT_ENQUEUE_OPTIONS: Required<EnqueueOptions> = {\r\n deduplicate: true,\r\n metadata: {},\r\n visibilityTimeout: 30000, // 30 seconds\r\n};\r\n\r\nconst DEFAULT_DEQUEUE_OPTIONS: Required<DequeueOptions> = {\r\n timeout: 0,\r\n visibilityTimeout: 30000, // 30 seconds\r\n count: 1,\r\n};\r\n\r\n/**\r\n * Redis Queue Manager\r\n *\r\n * Provides reliable queue operations with at-least-once delivery guarantees.\r\n */\r\nexport class RedisQueueManager {\r\n private redis: RedisClientType;\r\n private deduplicator: MessageDeduplicator;\r\n private stats: Map<string, {\r\n enqueued: number;\r\n dequeued: number;\r\n acknowledged: number;\r\n rejected: number;\r\n startTime: Date;\r\n }> = new Map();\r\n\r\n /**\r\n * Create a new RedisQueueManager instance\r\n *\r\n * @param redis - Redis client instance\r\n * @param deduplicator - Optional custom deduplicator instance\r\n */\r\n constructor(redis: RedisClientType, deduplicator?: MessageDeduplicator) {\r\n this.redis = redis;\r\n this.deduplicator = deduplicator || new MessageDeduplicator(redis);\r\n\r\n logger.info('RedisQueueManager initialized');\r\n }\r\n\r\n /**\r\n * Enqueue a message to a queue\r\n *\r\n * @param queue - Queue name\r\n * @param payload - Message payload\r\n * @param options - Enqueue options\r\n * @returns Message ID\r\n */\r\n public async enqueue<T = any>(\r\n queue: string,\r\n payload: T,\r\n options: EnqueueOptions = {}\r\n ): Promise<string> {\r\n const opts = { ...DEFAULT_ENQUEUE_OPTIONS, ...options };\r\n const startTime = Date.now();\r\n\r\n try {\r\n // Check for duplicates if enabled\r\n if (opts.deduplicate) {\r\n const isDuplicate = await this.deduplicator.isDuplicate(payload);\r\n\r\n if (isDuplicate) {\r\n logger.warn('Duplicate message detected, skipping enqueue', {\r\n queue,\r\n payloadHash: this.deduplicator.createFingerprint(payload).substring(0, 16) + '...',\r\n });\r\n\r\n throw createError(\r\n ErrorCode.DB_DUPLICATE_KEY,\r\n 'Duplicate message detected',\r\n { queue }\r\n );\r\n }\r\n }\r\n\r\n // Create message\r\n const message: QueueMessage<T> = {\r\n id: uuidv4(),\r\n queue,\r\n payload,\r\n createdAt: new Date(),\r\n enqueuedAt: new Date(),\r\n deliveryAttempts: 0,\r\n visibilityTimeout: opts.visibilityTimeout,\r\n metadata: opts.metadata,\r\n };\r\n\r\n // Push to queue (RPUSH for FIFO)\r\n await withRetry(\r\n async () => {\r\n const queueKey = this.getQueueKey(queue);\r\n await this.redis.rPush(queueKey, JSON.stringify(message));\r\n },\r\n { maxAttempts: 3, shouldRetry: isRetryableError }\r\n );\r\n\r\n // Mark as processed in deduplicator if enabled\r\n if (opts.deduplicate) {\r\n await this.deduplicator.markProcessed(payload, {\r\n messageId: message.id,\r\n queue,\r\n });\r\n }\r\n\r\n // Update stats\r\n this.updateStats(queue, 'enqueued');\r\n\r\n const duration = Date.now() - startTime;\r\n\r\n logger.debug('Message enqueued', {\r\n queue,\r\n messageId: message.id,\r\n durationMs: duration,\r\n });\r\n\r\n // Validate performance requirement (<100ms)\r\n if (duration > 100) {\r\n logger.warn('Enqueue operation exceeded 100ms target', {\r\n queue,\r\n durationMs: duration,\r\n });\r\n }\r\n\r\n return message.id;\r\n } catch (error) {\r\n logger.error('Failed to enqueue message', error instanceof Error ? error : new Error(String(error)), {\r\n queue,\r\n });\r\n\r\n throw createError(\r\n ErrorCode.DB_QUERY_FAILED,\r\n 'Failed to enqueue message',\r\n { queue },\r\n error instanceof Error ? error : undefined\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Dequeue a message from a queue\r\n *\r\n * @param queue - Queue name\r\n * @param options - Dequeue options\r\n * @returns Message or null if queue is empty\r\n */\r\n public async dequeue<T = any>(\r\n queue: string,\r\n options: DequeueOptions = {}\r\n ): Promise<QueueMessage<T> | null> {\r\n const opts = { ...DEFAULT_DEQUEUE_OPTIONS, ...options };\r\n const startTime = Date.now();\r\n\r\n try {\r\n const queueKey = this.getQueueKey(queue);\r\n const processingKey = this.getProcessingKey(queue);\r\n\r\n let messageData: string | null = null;\r\n\r\n // Use blocking pop if timeout specified\r\n if (opts.timeout > 0) {\r\n const result = await withRetry(\r\n async () => {\r\n // BLMOVE atomically moves from queue to processing set with timeout\r\n return await this.redis.blMove(\r\n queueKey,\r\n processingKey,\r\n 'LEFT',\r\n 'RIGHT',\r\n opts.timeout / 1000 // Convert to seconds\r\n );\r\n },\r\n { maxAttempts: 1 } // Don't retry blocking operations\r\n );\r\n\r\n messageData = result;\r\n } else {\r\n // Non-blocking pop\r\n messageData = await withRetry(\r\n async () => {\r\n return await this.redis.lMove(\r\n queueKey,\r\n processingKey,\r\n 'LEFT',\r\n 'RIGHT'\r\n );\r\n },\r\n { maxAttempts: 3, shouldRetry: isRetryableError }\r\n );\r\n }\r\n\r\n if (!messageData) {\r\n return null;\r\n }\r\n\r\n // Parse message\r\n const message = JSON.parse(messageData) as QueueMessage<T>;\r\n\r\n // Convert date strings back to Date objects\r\n message.createdAt = new Date(message.createdAt);\r\n message.enqueuedAt = new Date(message.enqueuedAt);\r\n message.dequeuedAt = new Date();\r\n message.deliveryAttempts++;\r\n\r\n // Store message with visibility timeout\r\n await this.storeInFlight(message, opts.visibilityTimeout);\r\n\r\n // Update stats\r\n this.updateStats(queue, 'dequeued');\r\n\r\n const duration = Date.now() - startTime;\r\n\r\n logger.debug('Message dequeued', {\r\n queue,\r\n messageId: message.id,\r\n deliveryAttempts: message.deliveryAttempts,\r\n durationMs: duration,\r\n });\r\n\r\n // Validate performance requirement (<100ms)\r\n if (duration > 100) {\r\n logger.warn('Dequeue operation exceeded 100ms target', {\r\n queue,\r\n durationMs: duration,\r\n });\r\n }\r\n\r\n return message;\r\n } catch (error) {\r\n logger.error('Failed to dequeue message', error instanceof Error ? error : new Error(String(error)), {\r\n queue,\r\n });\r\n\r\n throw createError(\r\n ErrorCode.DB_QUERY_FAILED,\r\n 'Failed to dequeue message',\r\n { queue },\r\n error instanceof Error ? error : undefined\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Acknowledge successful message processing\r\n *\r\n * @param messageId - Message ID to acknowledge\r\n */\r\n public async acknowledge(messageId: string): Promise<void> {\r\n const startTime = Date.now();\r\n\r\n try {\r\n // Remove from in-flight storage\r\n const message = await this.getInFlight(messageId);\r\n\r\n if (!message) {\r\n logger.warn('Message not found for acknowledgment', { messageId });\r\n return;\r\n }\r\n\r\n // Remove from processing set\r\n const processingKey = this.getProcessingKey(message.queue);\r\n await withRetry(\r\n async () => {\r\n await this.redis.lRem(processingKey, 1, JSON.stringify(message));\r\n },\r\n { maxAttempts: 3, shouldRetry: isRetryableError }\r\n );\r\n\r\n // Remove from in-flight storage\r\n await this.removeInFlight(messageId);\r\n\r\n // Update stats\r\n this.updateStats(message.queue, 'acknowledged');\r\n\r\n const duration = Date.now() - startTime;\r\n\r\n logger.debug('Message acknowledged', {\r\n queue: message.queue,\r\n messageId,\r\n durationMs: duration,\r\n });\r\n } catch (error) {\r\n logger.error('Failed to acknowledge message', error instanceof Error ? error : new Error(String(error)), {\r\n messageId,\r\n });\r\n\r\n throw createError(\r\n ErrorCode.DB_QUERY_FAILED,\r\n 'Failed to acknowledge message',\r\n { messageId },\r\n error instanceof Error ? error : undefined\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Reject message processing (with optional retry)\r\n *\r\n * @param messageId - Message ID to reject\r\n * @param options - Reject options\r\n */\r\n public async reject(messageId: string, options: RejectOptions = {}): Promise<void> {\r\n const startTime = Date.now();\r\n\r\n try {\r\n // Get message from in-flight storage\r\n const message = await this.getInFlight(messageId);\r\n\r\n if (!message) {\r\n logger.warn('Message not found for rejection', { messageId });\r\n return;\r\n }\r\n\r\n // Remove from processing set\r\n const processingKey = this.getProcessingKey(message.queue);\r\n await withRetry(\r\n async () => {\r\n await this.redis.lRem(processingKey, 1, JSON.stringify(message));\r\n },\r\n { maxAttempts: 3, shouldRetry: isRetryableError }\r\n );\r\n\r\n // Remove from in-flight storage\r\n await this.removeInFlight(messageId);\r\n\r\n if (options.retry) {\r\n // Re-enqueue message\r\n message.metadata = {\r\n ...message.metadata,\r\n ...options.metadata,\r\n rejectedAt: new Date().toISOString(),\r\n rejectionReason: options.error,\r\n };\r\n\r\n const queueKey = this.getQueueKey(message.queue);\r\n await withRetry(\r\n async () => {\r\n await this.redis.rPush(queueKey, JSON.stringify(message));\r\n },\r\n { maxAttempts: 3, shouldRetry: isRetryableError }\r\n );\r\n\r\n logger.debug('Message rejected and re-enqueued', {\r\n queue: message.queue,\r\n messageId,\r\n deliveryAttempts: message.deliveryAttempts,\r\n });\r\n } else {\r\n logger.debug('Message rejected without retry', {\r\n queue: message.queue,\r\n messageId,\r\n });\r\n }\r\n\r\n // Update stats\r\n this.updateStats(message.queue, 'rejected');\r\n\r\n const duration = Date.now() - startTime;\r\n\r\n logger.debug('Message rejected', {\r\n queue: message.queue,\r\n messageId,\r\n retry: options.retry,\r\n durationMs: duration,\r\n });\r\n } catch (error) {\r\n logger.error('Failed to reject message', error instanceof Error ? error : new Error(String(error)), {\r\n messageId,\r\n });\r\n\r\n throw createError(\r\n ErrorCode.DB_QUERY_FAILED,\r\n 'Failed to reject message',\r\n { messageId },\r\n error instanceof Error ? error : undefined\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Get queue statistics\r\n *\r\n * @param queue - Queue name\r\n * @returns Queue statistics\r\n */\r\n public async getStats(queue: string): Promise<QueueStats> {\r\n try {\r\n const queueKey = this.getQueueKey(queue);\r\n const processingKey = this.getProcessingKey(queue);\r\n\r\n // Get queue depth\r\n const depth = await this.redis.lLen(queueKey);\r\n\r\n // Get in-flight count\r\n const inFlight = await this.redis.lLen(processingKey);\r\n\r\n // Get oldest message age\r\n let oldestMessageAge = 0;\r\n const oldestMessage = await this.redis.lIndex(queueKey, 0);\r\n\r\n if (oldestMessage) {\r\n const message = JSON.parse(oldestMessage) as QueueMessage;\r\n const age = Date.now() - new Date(message.enqueuedAt).getTime();\r\n oldestMessageAge = Math.floor(age / 1000); // Convert to seconds\r\n }\r\n\r\n // Get stats from tracking\r\n const stats = this.stats.get(queue) || {\r\n enqueued: 0,\r\n dequeued: 0,\r\n acknowledged: 0,\r\n rejected: 0,\r\n startTime: new Date(),\r\n };\r\n\r\n // Calculate throughput (messages per second)\r\n const elapsed = (Date.now() - stats.startTime.getTime()) / 1000;\r\n const throughput = elapsed > 0 ? stats.dequeued / elapsed : 0;\r\n\r\n return {\r\n queue,\r\n depth,\r\n inFlight,\r\n oldestMessageAge,\r\n totalEnqueued: stats.enqueued,\r\n totalDequeued: stats.dequeued,\r\n totalAcknowledged: stats.acknowledged,\r\n totalRejected: stats.rejected,\r\n throughput,\r\n };\r\n } catch (error) {\r\n logger.error('Failed to get queue stats', error instanceof Error ? error : new Error(String(error)), {\r\n queue,\r\n });\r\n\r\n throw createError(\r\n ErrorCode.DB_QUERY_FAILED,\r\n 'Failed to get queue stats',\r\n { queue },\r\n error instanceof Error ? error : undefined\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Purge all messages from a queue\r\n *\r\n * @param queue - Queue name\r\n * @returns Number of messages purged\r\n */\r\n public async purge(queue: string): Promise<number> {\r\n try {\r\n const queueKey = this.getQueueKey(queue);\r\n\r\n const count = await withRetry(\r\n async () => {\r\n const len = await this.redis.lLen(queueKey);\r\n await this.redis.del(queueKey);\r\n return len;\r\n },\r\n { maxAttempts: 3, shouldRetry: isRetryableError }\r\n );\r\n\r\n logger.info('Queue purged', {\r\n queue,\r\n messagesPurged: count,\r\n });\r\n\r\n return count;\r\n } catch (error) {\r\n logger.error('Failed to purge queue', error instanceof Error ? error : new Error(String(error)), {\r\n queue,\r\n });\r\n\r\n throw createError(\r\n ErrorCode.DB_QUERY_FAILED,\r\n 'Failed to purge queue',\r\n { queue },\r\n error instanceof Error ? error : undefined\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Get all queue names\r\n *\r\n * @returns Array of queue names\r\n */\r\n public async getQueues(): Promise<string[]> {\r\n try {\r\n const pattern = 'queue:*';\r\n const keys = await this.redis.keys(pattern);\r\n\r\n const queues = keys\r\n .filter(key => !key.includes(':processing'))\r\n .map(key => key.replace('queue:', ''));\r\n\r\n return queues;\r\n } catch (error) {\r\n logger.error('Failed to get queues', error instanceof Error ? error : new Error(String(error)));\r\n\r\n throw createError(\r\n ErrorCode.DB_QUERY_FAILED,\r\n 'Failed to get queues',\r\n {},\r\n error instanceof Error ? error : undefined\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Shutdown queue manager (cleanup resources)\r\n */\r\n public shutdown(): void {\r\n this.deduplicator.shutdown();\r\n logger.info('RedisQueueManager shutdown');\r\n }\r\n\r\n /**\r\n * Get Redis key for queue\r\n */\r\n private getQueueKey(queue: string): string {\r\n return `queue:${queue}`;\r\n }\r\n\r\n /**\r\n * Get Redis key for processing set\r\n */\r\n private getProcessingKey(queue: string): string {\r\n return `queue:${queue}:processing`;\r\n }\r\n\r\n /**\r\n * Get Redis key for in-flight message storage\r\n */\r\n private getInFlightKey(messageId: string): string {\r\n return `inflight:${messageId}`;\r\n }\r\n\r\n /**\r\n * Store message in in-flight storage with TTL\r\n */\r\n private async storeInFlight<T = any>(\r\n message: QueueMessage<T>,\r\n visibilityTimeout: number\r\n ): Promise<void> {\r\n const key = this.getInFlightKey(message.id);\r\n\r\n await withRetry(\r\n async () => {\r\n await this.redis.set(\r\n key,\r\n JSON.stringify(message),\r\n { PX: visibilityTimeout }\r\n );\r\n },\r\n { maxAttempts: 3, shouldRetry: isRetryableError }\r\n );\r\n }\r\n\r\n /**\r\n * Get message from in-flight storage\r\n */\r\n private async getInFlight<T = any>(messageId: string): Promise<QueueMessage<T> | null> {\r\n const key = this.getInFlightKey(messageId);\r\n\r\n const data = await withRetry(\r\n async () => await this.redis.get(key),\r\n { maxAttempts: 3, shouldRetry: isRetryableError }\r\n );\r\n\r\n if (!data) {\r\n return null;\r\n }\r\n\r\n const message = JSON.parse(data) as QueueMessage<T>;\r\n\r\n // Convert date strings back to Date objects\r\n message.createdAt = new Date(message.createdAt);\r\n message.enqueuedAt = new Date(message.enqueuedAt);\r\n if (message.dequeuedAt) {\r\n message.dequeuedAt = new Date(message.dequeuedAt);\r\n }\r\n\r\n return message;\r\n }\r\n\r\n /**\r\n * Remove message from in-flight storage\r\n */\r\n private async removeInFlight(messageId: string): Promise<void> {\r\n const key = this.getInFlightKey(messageId);\r\n\r\n await withRetry(\r\n async () => await this.redis.del(key),\r\n { maxAttempts: 3, shouldRetry: isRetryableError }\r\n );\r\n }\r\n\r\n /**\r\n * Update queue statistics\r\n */\r\n private updateStats(queue: string, operation: 'enqueued' | 'dequeued' | 'acknowledged' | 'rejected'): void {\r\n let stats = this.stats.get(queue);\r\n\r\n if (!stats) {\r\n stats = {\r\n enqueued: 0,\r\n dequeued: 0,\r\n acknowledged: 0,\r\n rejected: 0,\r\n startTime: new Date(),\r\n };\r\n this.stats.set(queue, stats);\r\n }\r\n\r\n stats[operation]++;\r\n }\r\n}\r\n"],"names":["v4","uuidv4","createLogger","createError","ErrorCode","isRetryableError","withRetry","MessageDeduplicator","logger","DEFAULT_ENQUEUE_OPTIONS","deduplicate","metadata","visibilityTimeout","DEFAULT_DEQUEUE_OPTIONS","timeout","count","RedisQueueManager","redis","deduplicator","stats","Map","info","enqueue","queue","payload","options","opts","startTime","Date","now","isDuplicate","warn","payloadHash","createFingerprint","substring","DB_DUPLICATE_KEY","message","id","createdAt","enqueuedAt","deliveryAttempts","queueKey","getQueueKey","rPush","JSON","stringify","maxAttempts","shouldRetry","markProcessed","messageId","updateStats","duration","debug","durationMs","error","Error","String","DB_QUERY_FAILED","undefined","dequeue","processingKey","getProcessingKey","messageData","result","blMove","lMove","parse","dequeuedAt","storeInFlight","acknowledge","getInFlight","lRem","removeInFlight","reject","retry","rejectedAt","toISOString","rejectionReason","getStats","depth","lLen","inFlight","oldestMessageAge","oldestMessage","lIndex","age","getTime","Math","floor","get","enqueued","dequeued","acknowledged","rejected","elapsed","throughput","totalEnqueued","totalDequeued","totalAcknowledged","totalRejected","purge","len","del","messagesPurged","getQueues","pattern","keys","queues","filter","key","includes","map","replace","shutdown","getInFlightKey","set","PX","data","operation"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCC,GAGD,SAASA,MAAMC,MAAM,QAAQ,OAAO;AACpC,SAASC,YAAY,QAAQ,eAAe;AAC5C,SAASC,WAAW,EAAEC,SAAS,EAAEC,gBAAgB,QAAQ,cAAc;AACvE,SAASC,SAAS,QAAQ,aAAa;AACvC,SAASC,mBAAmB,QAAQ,4BAA4B;AAEhE,MAAMC,SAASN,aAAa;AAsF5B;;CAEC,GACD,MAAMO,0BAAoD;IACxDC,aAAa;IACbC,UAAU,CAAC;IACXC,mBAAmB;AACrB;AAEA,MAAMC,0BAAoD;IACxDC,SAAS;IACTF,mBAAmB;IACnBG,OAAO;AACT;AAEA;;;;CAIC,GACD,OAAO,MAAMC;IACHC,MAAuB;IACvBC,aAAkC;IAClCC,QAMH,IAAIC,MAAM;IAEf;;;;;GAKC,GACD,YAAYH,KAAsB,EAAEC,YAAkC,CAAE;QACtE,IAAI,CAACD,KAAK,GAAGA;QACb,IAAI,CAACC,YAAY,GAAGA,gBAAgB,IAAIX,oBAAoBU;QAE5DT,OAAOa,IAAI,CAAC;IACd;IAEA;;;;;;;GAOC,GACD,MAAaC,QACXC,KAAa,EACbC,OAAU,EACVC,UAA0B,CAAC,CAAC,EACX;QACjB,MAAMC,OAAO;YAAE,GAAGjB,uBAAuB;YAAE,GAAGgB,OAAO;QAAC;QACtD,MAAME,YAAYC,KAAKC,GAAG;QAE1B,IAAI;YACF,kCAAkC;YAClC,IAAIH,KAAKhB,WAAW,EAAE;gBACpB,MAAMoB,cAAc,MAAM,IAAI,CAACZ,YAAY,CAACY,WAAW,CAACN;gBAExD,IAAIM,aAAa;oBACftB,OAAOuB,IAAI,CAAC,gDAAgD;wBAC1DR;wBACAS,aAAa,IAAI,CAACd,YAAY,CAACe,iBAAiB,CAACT,SAASU,SAAS,CAAC,GAAG,MAAM;oBAC/E;oBAEA,MAAM/B,YACJC,UAAU+B,gBAAgB,EAC1B,8BACA;wBAAEZ;oBAAM;gBAEZ;YACF;YAEA,iBAAiB;YACjB,MAAMa,UAA2B;gBAC/BC,IAAIpC;gBACJsB;gBACAC;gBACAc,WAAW,IAAIV;gBACfW,YAAY,IAAIX;gBAChBY,kBAAkB;gBAClB5B,mBAAmBc,KAAKd,iBAAiB;gBACzCD,UAAUe,KAAKf,QAAQ;YACzB;YAEA,iCAAiC;YACjC,MAAML,UACJ;gBACE,MAAMmC,WAAW,IAAI,CAACC,WAAW,CAACnB;gBAClC,MAAM,IAAI,CAACN,KAAK,CAAC0B,KAAK,CAACF,UAAUG,KAAKC,SAAS,CAACT;YAClD,GACA;gBAAEU,aAAa;gBAAGC,aAAa1C;YAAiB;YAGlD,+CAA+C;YAC/C,IAAIqB,KAAKhB,WAAW,EAAE;gBACpB,MAAM,IAAI,CAACQ,YAAY,CAAC8B,aAAa,CAACxB,SAAS;oBAC7CyB,WAAWb,QAAQC,EAAE;oBACrBd;gBACF;YACF;YAEA,eAAe;YACf,IAAI,CAAC2B,WAAW,CAAC3B,OAAO;YAExB,MAAM4B,WAAWvB,KAAKC,GAAG,KAAKF;YAE9BnB,OAAO4C,KAAK,CAAC,oBAAoB;gBAC/B7B;gBACA0B,WAAWb,QAAQC,EAAE;gBACrBgB,YAAYF;YACd;YAEA,4CAA4C;YAC5C,IAAIA,WAAW,KAAK;gBAClB3C,OAAOuB,IAAI,CAAC,2CAA2C;oBACrDR;oBACA8B,YAAYF;gBACd;YACF;YAEA,OAAOf,QAAQC,EAAE;QACnB,EAAE,OAAOiB,OAAO;YACd9C,OAAO8C,KAAK,CAAC,6BAA6BA,iBAAiBC,QAAQD,QAAQ,IAAIC,MAAMC,OAAOF,SAAS;gBACnG/B;YACF;YAEA,MAAMpB,YACJC,UAAUqD,eAAe,EACzB,6BACA;gBAAElC;YAAM,GACR+B,iBAAiBC,QAAQD,QAAQI;QAErC;IACF;IAEA;;;;;;GAMC,GACD,MAAaC,QACXpC,KAAa,EACbE,UAA0B,CAAC,CAAC,EACK;QACjC,MAAMC,OAAO;YAAE,GAAGb,uBAAuB;YAAE,GAAGY,OAAO;QAAC;QACtD,MAAME,YAAYC,KAAKC,GAAG;QAE1B,IAAI;YACF,MAAMY,WAAW,IAAI,CAACC,WAAW,CAACnB;YAClC,MAAMqC,gBAAgB,IAAI,CAACC,gBAAgB,CAACtC;YAE5C,IAAIuC,cAA6B;YAEjC,wCAAwC;YACxC,IAAIpC,KAAKZ,OAAO,GAAG,GAAG;gBACpB,MAAMiD,SAAS,MAAMzD,UACnB;oBACE,oEAAoE;oBACpE,OAAO,MAAM,IAAI,CAACW,KAAK,CAAC+C,MAAM,CAC5BvB,UACAmB,eACA,QACA,SACAlC,KAAKZ,OAAO,GAAG,KAAK,qBAAqB;;gBAE7C,GACA;oBAAEgC,aAAa;gBAAE,EAAE,kCAAkC;;gBAGvDgB,cAAcC;YAChB,OAAO;gBACL,mBAAmB;gBACnBD,cAAc,MAAMxD,UAClB;oBACE,OAAO,MAAM,IAAI,CAACW,KAAK,CAACgD,KAAK,CAC3BxB,UACAmB,eACA,QACA;gBAEJ,GACA;oBAAEd,aAAa;oBAAGC,aAAa1C;gBAAiB;YAEpD;YAEA,IAAI,CAACyD,aAAa;gBAChB,OAAO;YACT;YAEA,gBAAgB;YAChB,MAAM1B,UAAUQ,KAAKsB,KAAK,CAACJ;YAE3B,4CAA4C;YAC5C1B,QAAQE,SAAS,GAAG,IAAIV,KAAKQ,QAAQE,SAAS;YAC9CF,QAAQG,UAAU,GAAG,IAAIX,KAAKQ,QAAQG,UAAU;YAChDH,QAAQ+B,UAAU,GAAG,IAAIvC;YACzBQ,QAAQI,gBAAgB;YAExB,wCAAwC;YACxC,MAAM,IAAI,CAAC4B,aAAa,CAAChC,SAASV,KAAKd,iBAAiB;YAExD,eAAe;YACf,IAAI,CAACsC,WAAW,CAAC3B,OAAO;YAExB,MAAM4B,WAAWvB,KAAKC,GAAG,KAAKF;YAE9BnB,OAAO4C,KAAK,CAAC,oBAAoB;gBAC/B7B;gBACA0B,WAAWb,QAAQC,EAAE;gBACrBG,kBAAkBJ,QAAQI,gBAAgB;gBAC1Ca,YAAYF;YACd;YAEA,4CAA4C;YAC5C,IAAIA,WAAW,KAAK;gBAClB3C,OAAOuB,IAAI,CAAC,2CAA2C;oBACrDR;oBACA8B,YAAYF;gBACd;YACF;YAEA,OAAOf;QACT,EAAE,OAAOkB,OAAO;YACd9C,OAAO8C,KAAK,CAAC,6BAA6BA,iBAAiBC,QAAQD,QAAQ,IAAIC,MAAMC,OAAOF,SAAS;gBACnG/B;YACF;YAEA,MAAMpB,YACJC,UAAUqD,eAAe,EACzB,6BACA;gBAAElC;YAAM,GACR+B,iBAAiBC,QAAQD,QAAQI;QAErC;IACF;IAEA;;;;GAIC,GACD,MAAaW,YAAYpB,SAAiB,EAAiB;QACzD,MAAMtB,YAAYC,KAAKC,GAAG;QAE1B,IAAI;YACF,gCAAgC;YAChC,MAAMO,UAAU,MAAM,IAAI,CAACkC,WAAW,CAACrB;YAEvC,IAAI,CAACb,SAAS;gBACZ5B,OAAOuB,IAAI,CAAC,wCAAwC;oBAAEkB;gBAAU;gBAChE;YACF;YAEA,6BAA6B;YAC7B,MAAMW,gBAAgB,IAAI,CAACC,gBAAgB,CAACzB,QAAQb,KAAK;YACzD,MAAMjB,UACJ;gBACE,MAAM,IAAI,CAACW,KAAK,CAACsD,IAAI,CAACX,eAAe,GAAGhB,KAAKC,SAAS,CAACT;YACzD,GACA;gBAAEU,aAAa;gBAAGC,aAAa1C;YAAiB;YAGlD,gCAAgC;YAChC,MAAM,IAAI,CAACmE,cAAc,CAACvB;YAE1B,eAAe;YACf,IAAI,CAACC,WAAW,CAACd,QAAQb,KAAK,EAAE;YAEhC,MAAM4B,WAAWvB,KAAKC,GAAG,KAAKF;YAE9BnB,OAAO4C,KAAK,CAAC,wBAAwB;gBACnC7B,OAAOa,QAAQb,KAAK;gBACpB0B;gBACAI,YAAYF;YACd;QACF,EAAE,OAAOG,OAAO;YACd9C,OAAO8C,KAAK,CAAC,iCAAiCA,iBAAiBC,QAAQD,QAAQ,IAAIC,MAAMC,OAAOF,SAAS;gBACvGL;YACF;YAEA,MAAM9C,YACJC,UAAUqD,eAAe,EACzB,iCACA;gBAAER;YAAU,GACZK,iBAAiBC,QAAQD,QAAQI;QAErC;IACF;IAEA;;;;;GAKC,GACD,MAAae,OAAOxB,SAAiB,EAAExB,UAAyB,CAAC,CAAC,EAAiB;QACjF,MAAME,YAAYC,KAAKC,GAAG;QAE1B,IAAI;YACF,qCAAqC;YACrC,MAAMO,UAAU,MAAM,IAAI,CAACkC,WAAW,CAACrB;YAEvC,IAAI,CAACb,SAAS;gBACZ5B,OAAOuB,IAAI,CAAC,mCAAmC;oBAAEkB;gBAAU;gBAC3D;YACF;YAEA,6BAA6B;YAC7B,MAAMW,gBAAgB,IAAI,CAACC,gBAAgB,CAACzB,QAAQb,KAAK;YACzD,MAAMjB,UACJ;gBACE,MAAM,IAAI,CAACW,KAAK,CAACsD,IAAI,CAACX,eAAe,GAAGhB,KAAKC,SAAS,CAACT;YACzD,GACA;gBAAEU,aAAa;gBAAGC,aAAa1C;YAAiB;YAGlD,gCAAgC;YAChC,MAAM,IAAI,CAACmE,cAAc,CAACvB;YAE1B,IAAIxB,QAAQiD,KAAK,EAAE;gBACjB,qBAAqB;gBACrBtC,QAAQzB,QAAQ,GAAG;oBACjB,GAAGyB,QAAQzB,QAAQ;oBACnB,GAAGc,QAAQd,QAAQ;oBACnBgE,YAAY,IAAI/C,OAAOgD,WAAW;oBAClCC,iBAAiBpD,QAAQ6B,KAAK;gBAChC;gBAEA,MAAMb,WAAW,IAAI,CAACC,WAAW,CAACN,QAAQb,KAAK;gBAC/C,MAAMjB,UACJ;oBACE,MAAM,IAAI,CAACW,KAAK,CAAC0B,KAAK,CAACF,UAAUG,KAAKC,SAAS,CAACT;gBAClD,GACA;oBAAEU,aAAa;oBAAGC,aAAa1C;gBAAiB;gBAGlDG,OAAO4C,KAAK,CAAC,oCAAoC;oBAC/C7B,OAAOa,QAAQb,KAAK;oBACpB0B;oBACAT,kBAAkBJ,QAAQI,gBAAgB;gBAC5C;YACF,OAAO;gBACLhC,OAAO4C,KAAK,CAAC,kCAAkC;oBAC7C7B,OAAOa,QAAQb,KAAK;oBACpB0B;gBACF;YACF;YAEA,eAAe;YACf,IAAI,CAACC,WAAW,CAACd,QAAQb,KAAK,EAAE;YAEhC,MAAM4B,WAAWvB,KAAKC,GAAG,KAAKF;YAE9BnB,OAAO4C,KAAK,CAAC,oBAAoB;gBAC/B7B,OAAOa,QAAQb,KAAK;gBACpB0B;gBACAyB,OAAOjD,QAAQiD,KAAK;gBACpBrB,YAAYF;YACd;QACF,EAAE,OAAOG,OAAO;YACd9C,OAAO8C,KAAK,CAAC,4BAA4BA,iBAAiBC,QAAQD,QAAQ,IAAIC,MAAMC,OAAOF,SAAS;gBAClGL;YACF;YAEA,MAAM9C,YACJC,UAAUqD,eAAe,EACzB,4BACA;gBAAER;YAAU,GACZK,iBAAiBC,QAAQD,QAAQI;QAErC;IACF;IAEA;;;;;GAKC,GACD,MAAaoB,SAASvD,KAAa,EAAuB;QACxD,IAAI;YACF,MAAMkB,WAAW,IAAI,CAACC,WAAW,CAACnB;YAClC,MAAMqC,gBAAgB,IAAI,CAACC,gBAAgB,CAACtC;YAE5C,kBAAkB;YAClB,MAAMwD,QAAQ,MAAM,IAAI,CAAC9D,KAAK,CAAC+D,IAAI,CAACvC;YAEpC,sBAAsB;YACtB,MAAMwC,WAAW,MAAM,IAAI,CAAChE,KAAK,CAAC+D,IAAI,CAACpB;YAEvC,yBAAyB;YACzB,IAAIsB,mBAAmB;YACvB,MAAMC,gBAAgB,MAAM,IAAI,CAAClE,KAAK,CAACmE,MAAM,CAAC3C,UAAU;YAExD,IAAI0C,eAAe;gBACjB,MAAM/C,UAAUQ,KAAKsB,KAAK,CAACiB;gBAC3B,MAAME,MAAMzD,KAAKC,GAAG,KAAK,IAAID,KAAKQ,QAAQG,UAAU,EAAE+C,OAAO;gBAC7DJ,mBAAmBK,KAAKC,KAAK,CAACH,MAAM,OAAO,qBAAqB;YAClE;YAEA,0BAA0B;YAC1B,MAAMlE,QAAQ,IAAI,CAACA,KAAK,CAACsE,GAAG,CAAClE,UAAU;gBACrCmE,UAAU;gBACVC,UAAU;gBACVC,cAAc;gBACdC,UAAU;gBACVlE,WAAW,IAAIC;YACjB;YAEA,6CAA6C;YAC7C,MAAMkE,UAAU,AAAClE,CAAAA,KAAKC,GAAG,KAAKV,MAAMQ,SAAS,CAAC2D,OAAO,EAAC,IAAK;YAC3D,MAAMS,aAAaD,UAAU,IAAI3E,MAAMwE,QAAQ,GAAGG,UAAU;YAE5D,OAAO;gBACLvE;gBACAwD;gBACAE;gBACAC;gBACAc,eAAe7E,MAAMuE,QAAQ;gBAC7BO,eAAe9E,MAAMwE,QAAQ;gBAC7BO,mBAAmB/E,MAAMyE,YAAY;gBACrCO,eAAehF,MAAM0E,QAAQ;gBAC7BE;YACF;QACF,EAAE,OAAOzC,OAAO;YACd9C,OAAO8C,KAAK,CAAC,6BAA6BA,iBAAiBC,QAAQD,QAAQ,IAAIC,MAAMC,OAAOF,SAAS;gBACnG/B;YACF;YAEA,MAAMpB,YACJC,UAAUqD,eAAe,EACzB,6BACA;gBAAElC;YAAM,GACR+B,iBAAiBC,QAAQD,QAAQI;QAErC;IACF;IAEA;;;;;GAKC,GACD,MAAa0C,MAAM7E,KAAa,EAAmB;QACjD,IAAI;YACF,MAAMkB,WAAW,IAAI,CAACC,WAAW,CAACnB;YAElC,MAAMR,QAAQ,MAAMT,UAClB;gBACE,MAAM+F,MAAM,MAAM,IAAI,CAACpF,KAAK,CAAC+D,IAAI,CAACvC;gBAClC,MAAM,IAAI,CAACxB,KAAK,CAACqF,GAAG,CAAC7D;gBACrB,OAAO4D;YACT,GACA;gBAAEvD,aAAa;gBAAGC,aAAa1C;YAAiB;YAGlDG,OAAOa,IAAI,CAAC,gBAAgB;gBAC1BE;gBACAgF,gBAAgBxF;YAClB;YAEA,OAAOA;QACT,EAAE,OAAOuC,OAAO;YACd9C,OAAO8C,KAAK,CAAC,yBAAyBA,iBAAiBC,QAAQD,QAAQ,IAAIC,MAAMC,OAAOF,SAAS;gBAC/F/B;YACF;YAEA,MAAMpB,YACJC,UAAUqD,eAAe,EACzB,yBACA;gBAAElC;YAAM,GACR+B,iBAAiBC,QAAQD,QAAQI;QAErC;IACF;IAEA;;;;GAIC,GACD,MAAa8C,YAA+B;QAC1C,IAAI;YACF,MAAMC,UAAU;YAChB,MAAMC,OAAO,MAAM,IAAI,CAACzF,KAAK,CAACyF,IAAI,CAACD;YAEnC,MAAME,SAASD,KACZE,MAAM,CAACC,CAAAA,MAAO,CAACA,IAAIC,QAAQ,CAAC,gBAC5BC,GAAG,CAACF,CAAAA,MAAOA,IAAIG,OAAO,CAAC,UAAU;YAEpC,OAAOL;QACT,EAAE,OAAOrD,OAAO;YACd9C,OAAO8C,KAAK,CAAC,wBAAwBA,iBAAiBC,QAAQD,QAAQ,IAAIC,MAAMC,OAAOF;YAEvF,MAAMnD,YACJC,UAAUqD,eAAe,EACzB,wBACA,CAAC,GACDH,iBAAiBC,QAAQD,QAAQI;QAErC;IACF;IAEA;;GAEC,GACD,AAAOuD,WAAiB;QACtB,IAAI,CAAC/F,YAAY,CAAC+F,QAAQ;QAC1BzG,OAAOa,IAAI,CAAC;IACd;IAEA;;GAEC,GACD,AAAQqB,YAAYnB,KAAa,EAAU;QACzC,OAAO,CAAC,MAAM,EAAEA,OAAO;IACzB;IAEA;;GAEC,GACD,AAAQsC,iBAAiBtC,KAAa,EAAU;QAC9C,OAAO,CAAC,MAAM,EAAEA,MAAM,WAAW,CAAC;IACpC;IAEA;;GAEC,GACD,AAAQ2F,eAAejE,SAAiB,EAAU;QAChD,OAAO,CAAC,SAAS,EAAEA,WAAW;IAChC;IAEA;;GAEC,GACD,MAAcmB,cACZhC,OAAwB,EACxBxB,iBAAyB,EACV;QACf,MAAMiG,MAAM,IAAI,CAACK,cAAc,CAAC9E,QAAQC,EAAE;QAE1C,MAAM/B,UACJ;YACE,MAAM,IAAI,CAACW,KAAK,CAACkG,GAAG,CAClBN,KACAjE,KAAKC,SAAS,CAACT,UACf;gBAAEgF,IAAIxG;YAAkB;QAE5B,GACA;YAAEkC,aAAa;YAAGC,aAAa1C;QAAiB;IAEpD;IAEA;;GAEC,GACD,MAAciE,YAAqBrB,SAAiB,EAAmC;QACrF,MAAM4D,MAAM,IAAI,CAACK,cAAc,CAACjE;QAEhC,MAAMoE,OAAO,MAAM/G,UACjB,UAAY,MAAM,IAAI,CAACW,KAAK,CAACwE,GAAG,CAACoB,MACjC;YAAE/D,aAAa;YAAGC,aAAa1C;QAAiB;QAGlD,IAAI,CAACgH,MAAM;YACT,OAAO;QACT;QAEA,MAAMjF,UAAUQ,KAAKsB,KAAK,CAACmD;QAE3B,4CAA4C;QAC5CjF,QAAQE,SAAS,GAAG,IAAIV,KAAKQ,QAAQE,SAAS;QAC9CF,QAAQG,UAAU,GAAG,IAAIX,KAAKQ,QAAQG,UAAU;QAChD,IAAIH,QAAQ+B,UAAU,EAAE;YACtB/B,QAAQ+B,UAAU,GAAG,IAAIvC,KAAKQ,QAAQ+B,UAAU;QAClD;QAEA,OAAO/B;IACT;IAEA;;GAEC,GACD,MAAcoC,eAAevB,SAAiB,EAAiB;QAC7D,MAAM4D,MAAM,IAAI,CAACK,cAAc,CAACjE;QAEhC,MAAM3C,UACJ,UAAY,MAAM,IAAI,CAACW,KAAK,CAACqF,GAAG,CAACO,MACjC;YAAE/D,aAAa;YAAGC,aAAa1C;QAAiB;IAEpD;IAEA;;GAEC,GACD,AAAQ6C,YAAY3B,KAAa,EAAE+F,SAAgE,EAAQ;QACzG,IAAInG,QAAQ,IAAI,CAACA,KAAK,CAACsE,GAAG,CAAClE;QAE3B,IAAI,CAACJ,OAAO;YACVA,QAAQ;gBACNuE,UAAU;gBACVC,UAAU;gBACVC,cAAc;gBACdC,UAAU;gBACVlE,WAAW,IAAIC;YACjB;YACA,IAAI,CAACT,KAAK,CAACgG,GAAG,CAAC5F,OAAOJ;QACxB;QAEAA,KAAK,CAACmG,UAAU;IAClB;AACF"}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reflection Archiver
|
|
3
|
+
* Task 5.3: ACE Reflection Persistence Standardization
|
|
4
|
+
*
|
|
5
|
+
* Manages automatic archival of Redis reflections to PostgreSQL
|
|
6
|
+
* based on TTL thresholds. Runs as background task.
|
|
7
|
+
*
|
|
8
|
+
* Performance target: <500ms per reflection archive
|
|
9
|
+
*/ import { StandardError, ErrorCode } from './errors.js';
|
|
10
|
+
import { logger } from './logging.js';
|
|
11
|
+
const DEFAULT_CONFIG = {
|
|
12
|
+
ttl_threshold_seconds: 3600,
|
|
13
|
+
scan_interval_ms: 300000,
|
|
14
|
+
max_per_scan: 100,
|
|
15
|
+
auto_archive: true
|
|
16
|
+
};
|
|
17
|
+
export class ReflectionArchiver {
|
|
18
|
+
dbService;
|
|
19
|
+
config;
|
|
20
|
+
scanInterval = null;
|
|
21
|
+
metrics = {
|
|
22
|
+
total_archived: 0,
|
|
23
|
+
last_scan_time: null,
|
|
24
|
+
last_scan_count: 0,
|
|
25
|
+
failed_archives: 0,
|
|
26
|
+
average_archive_time_ms: 0,
|
|
27
|
+
redis_unavailable_count: 0
|
|
28
|
+
};
|
|
29
|
+
archiveTimes = [];
|
|
30
|
+
isScanning = false;
|
|
31
|
+
constructor(dbService, config){
|
|
32
|
+
this.dbService = dbService;
|
|
33
|
+
this.config = {
|
|
34
|
+
...DEFAULT_CONFIG,
|
|
35
|
+
...config
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Start automatic archival process
|
|
40
|
+
*/ start() {
|
|
41
|
+
if (this.scanInterval) {
|
|
42
|
+
logger.warn('Archiver already running');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (!this.config.auto_archive) {
|
|
46
|
+
logger.info('Auto-archive disabled in config');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
logger.info('Starting reflection archiver', {
|
|
50
|
+
scan_interval_ms: this.config.scan_interval_ms,
|
|
51
|
+
ttl_threshold_seconds: this.config.ttl_threshold_seconds,
|
|
52
|
+
max_per_scan: this.config.max_per_scan
|
|
53
|
+
});
|
|
54
|
+
this.scanInterval = setInterval(()=>this.runArchiveScan(), this.config.scan_interval_ms);
|
|
55
|
+
// Run initial scan immediately
|
|
56
|
+
this.runArchiveScan();
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Stop automatic archival process
|
|
60
|
+
*/ stop() {
|
|
61
|
+
if (this.scanInterval) {
|
|
62
|
+
clearInterval(this.scanInterval);
|
|
63
|
+
this.scanInterval = null;
|
|
64
|
+
logger.info('Reflection archiver stopped');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Manually trigger an archive scan
|
|
69
|
+
*/ async manualScan() {
|
|
70
|
+
return await this.runArchiveScan();
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get current archiver metrics
|
|
74
|
+
*/ getMetrics() {
|
|
75
|
+
return {
|
|
76
|
+
...this.metrics
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Reset metrics (useful for testing)
|
|
81
|
+
*/ resetMetrics() {
|
|
82
|
+
this.metrics = {
|
|
83
|
+
total_archived: 0,
|
|
84
|
+
last_scan_time: null,
|
|
85
|
+
last_scan_count: 0,
|
|
86
|
+
failed_archives: 0,
|
|
87
|
+
average_archive_time_ms: 0,
|
|
88
|
+
redis_unavailable_count: 0
|
|
89
|
+
};
|
|
90
|
+
this.archiveTimes = [];
|
|
91
|
+
}
|
|
92
|
+
// ========== Private Methods ==========
|
|
93
|
+
async runArchiveScan() {
|
|
94
|
+
if (this.isScanning) {
|
|
95
|
+
logger.debug('Archive scan already in progress, skipping');
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
98
|
+
this.isScanning = true;
|
|
99
|
+
const scanStartTime = Date.now();
|
|
100
|
+
try {
|
|
101
|
+
const archivedCount = await this.scanAndArchive();
|
|
102
|
+
this.metrics.last_scan_time = new Date();
|
|
103
|
+
this.metrics.last_scan_count = archivedCount;
|
|
104
|
+
this.metrics.total_archived += archivedCount;
|
|
105
|
+
const scanDuration = Date.now() - scanStartTime;
|
|
106
|
+
logger.info('Archive scan complete', {
|
|
107
|
+
archived_count: archivedCount,
|
|
108
|
+
scan_duration_ms: scanDuration,
|
|
109
|
+
total_archived: this.metrics.total_archived
|
|
110
|
+
});
|
|
111
|
+
return archivedCount;
|
|
112
|
+
} catch (error) {
|
|
113
|
+
logger.error('Archive scan failed', {
|
|
114
|
+
error: error instanceof Error ? error.message : String(error)
|
|
115
|
+
});
|
|
116
|
+
return 0;
|
|
117
|
+
} finally{
|
|
118
|
+
this.isScanning = false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async scanAndArchive() {
|
|
122
|
+
let redisAdapter;
|
|
123
|
+
try {
|
|
124
|
+
redisAdapter = this.dbService.getAdapter('redis');
|
|
125
|
+
} catch (error) {
|
|
126
|
+
this.metrics.redis_unavailable_count++;
|
|
127
|
+
logger.warn('Redis adapter unavailable, skipping archive scan', {
|
|
128
|
+
redis_unavailable_count: this.metrics.redis_unavailable_count
|
|
129
|
+
});
|
|
130
|
+
return 0;
|
|
131
|
+
}
|
|
132
|
+
let archivedCount = 0;
|
|
133
|
+
try {
|
|
134
|
+
// Get all reflection keys
|
|
135
|
+
const keys = await redisAdapter.keys('reflection:*');
|
|
136
|
+
logger.debug('Scanning reflection keys', {
|
|
137
|
+
key_count: keys.length
|
|
138
|
+
});
|
|
139
|
+
// Process up to max_per_scan keys
|
|
140
|
+
const keysToProcess = keys.slice(0, this.config.max_per_scan);
|
|
141
|
+
for (const key of keysToProcess){
|
|
142
|
+
try {
|
|
143
|
+
const archived = await this.archiveIfExpiring(key);
|
|
144
|
+
if (archived) {
|
|
145
|
+
archivedCount++;
|
|
146
|
+
}
|
|
147
|
+
} catch (error) {
|
|
148
|
+
this.metrics.failed_archives++;
|
|
149
|
+
logger.error('Failed to archive reflection', {
|
|
150
|
+
key,
|
|
151
|
+
error: error instanceof Error ? error.message : String(error),
|
|
152
|
+
failed_archives: this.metrics.failed_archives
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Update average archive time
|
|
157
|
+
if (this.archiveTimes.length > 0) {
|
|
158
|
+
const sum = this.archiveTimes.reduce((a, b)=>a + b, 0);
|
|
159
|
+
this.metrics.average_archive_time_ms = sum / this.archiveTimes.length;
|
|
160
|
+
}
|
|
161
|
+
// Keep only last 100 archive times for moving average
|
|
162
|
+
if (this.archiveTimes.length > 100) {
|
|
163
|
+
this.archiveTimes = this.archiveTimes.slice(-100);
|
|
164
|
+
}
|
|
165
|
+
return archivedCount;
|
|
166
|
+
} catch (error) {
|
|
167
|
+
logger.error('Scan failed', {
|
|
168
|
+
error: error instanceof Error ? error.message : String(error)
|
|
169
|
+
});
|
|
170
|
+
return archivedCount;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
async archiveIfExpiring(key) {
|
|
174
|
+
const startTime = Date.now();
|
|
175
|
+
try {
|
|
176
|
+
const redisAdapter = this.dbService.getAdapter('redis');
|
|
177
|
+
// Check TTL
|
|
178
|
+
const ttl = await redisAdapter.ttl(key);
|
|
179
|
+
// Skip if key doesn't exist or has no expiration
|
|
180
|
+
if (ttl < 0) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
// Archive if TTL is below threshold
|
|
184
|
+
if (ttl < this.config.ttl_threshold_seconds) {
|
|
185
|
+
const data = await redisAdapter.get(key);
|
|
186
|
+
if (!data) {
|
|
187
|
+
logger.warn('Key exists but has no data', {
|
|
188
|
+
key,
|
|
189
|
+
ttl
|
|
190
|
+
});
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
let reflection;
|
|
194
|
+
try {
|
|
195
|
+
reflection = JSON.parse(data);
|
|
196
|
+
} catch (error) {
|
|
197
|
+
logger.error('Failed to parse reflection data', {
|
|
198
|
+
key,
|
|
199
|
+
error: error instanceof Error ? error.message : String(error)
|
|
200
|
+
});
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
// Write to PostgreSQL (idempotent)
|
|
204
|
+
await this.writeToPostgreSQL(reflection);
|
|
205
|
+
const archiveTime = Date.now() - startTime;
|
|
206
|
+
this.archiveTimes.push(archiveTime);
|
|
207
|
+
logger.debug('Reflection archived', {
|
|
208
|
+
key,
|
|
209
|
+
ttl,
|
|
210
|
+
archive_time_ms: archiveTime,
|
|
211
|
+
agent_id: reflection.agent_id,
|
|
212
|
+
task_id: reflection.task_id
|
|
213
|
+
});
|
|
214
|
+
// Warn if archive took too long
|
|
215
|
+
if (archiveTime >= 500) {
|
|
216
|
+
logger.warn('Archive exceeded performance target', {
|
|
217
|
+
key,
|
|
218
|
+
archive_time_ms: archiveTime,
|
|
219
|
+
target_ms: 500
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
return false;
|
|
225
|
+
} catch (error) {
|
|
226
|
+
throw new StandardError(ErrorCode.DATABASE_ERROR, 'Failed to archive reflection', {
|
|
227
|
+
key,
|
|
228
|
+
error: error instanceof Error ? error.message : String(error)
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
async writeToPostgreSQL(reflection) {
|
|
233
|
+
const pgAdapter = this.dbService.getAdapter('postgres');
|
|
234
|
+
const query = `
|
|
235
|
+
INSERT INTO reflections (
|
|
236
|
+
agent_id,
|
|
237
|
+
task_id,
|
|
238
|
+
reflection_type,
|
|
239
|
+
confidence,
|
|
240
|
+
payload,
|
|
241
|
+
timestamp,
|
|
242
|
+
archived_at
|
|
243
|
+
) VALUES ($1, $2, $3, $4, $5, $6, NOW())
|
|
244
|
+
ON CONFLICT (agent_id, task_id, timestamp)
|
|
245
|
+
DO UPDATE SET
|
|
246
|
+
reflection_type = EXCLUDED.reflection_type,
|
|
247
|
+
confidence = EXCLUDED.confidence,
|
|
248
|
+
payload = EXCLUDED.payload,
|
|
249
|
+
archived_at = NOW()
|
|
250
|
+
`;
|
|
251
|
+
const params = [
|
|
252
|
+
reflection.agent_id,
|
|
253
|
+
reflection.task_id,
|
|
254
|
+
reflection.reflection_type,
|
|
255
|
+
reflection.confidence,
|
|
256
|
+
typeof reflection.payload === 'string' ? reflection.payload : JSON.stringify(reflection.payload),
|
|
257
|
+
reflection.timestamp || new Date()
|
|
258
|
+
];
|
|
259
|
+
await pgAdapter.execute(query, params);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Factory function to create and start an archiver
|
|
264
|
+
*/ export function createArchiver(dbService, config) {
|
|
265
|
+
const archiver = new ReflectionArchiver(dbService, config);
|
|
266
|
+
if (config?.auto_archive !== false) {
|
|
267
|
+
archiver.start();
|
|
268
|
+
}
|
|
269
|
+
return archiver;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
//# sourceMappingURL=reflection-archiver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/reflection-archiver.ts"],"sourcesContent":["/**\r\n * Reflection Archiver\r\n * Task 5.3: ACE Reflection Persistence Standardization\r\n *\r\n * Manages automatic archival of Redis reflections to PostgreSQL\r\n * based on TTL thresholds. Runs as background task.\r\n *\r\n * Performance target: <500ms per reflection archive\r\n */\r\n\r\nimport { DatabaseService } from './database-service.js';\r\nimport { StandardError, ErrorCode } from './errors.js';\r\nimport { logger } from './logging.js';\r\n\r\nexport interface ArchiverConfig {\r\n /**\r\n * TTL threshold in seconds - archive when Redis key TTL falls below this\r\n * Default: 3600 (1 hour)\r\n */\r\n ttl_threshold_seconds?: number;\r\n\r\n /**\r\n * Interval between archive scans in milliseconds\r\n * Default: 300000 (5 minutes)\r\n */\r\n scan_interval_ms?: number;\r\n\r\n /**\r\n * Maximum reflections to archive per scan\r\n * Default: 100\r\n */\r\n max_per_scan?: number;\r\n\r\n /**\r\n * Enable automatic archival on interval\r\n * Default: true\r\n */\r\n auto_archive?: boolean;\r\n}\r\n\r\nexport interface ArchiveMetrics {\r\n total_archived: number;\r\n last_scan_time: Date | null;\r\n last_scan_count: number;\r\n failed_archives: number;\r\n average_archive_time_ms: number;\r\n redis_unavailable_count: number;\r\n}\r\n\r\nconst DEFAULT_CONFIG: Required<ArchiverConfig> = {\r\n ttl_threshold_seconds: 3600, // 1 hour\r\n scan_interval_ms: 300000, // 5 minutes\r\n max_per_scan: 100,\r\n auto_archive: true,\r\n};\r\n\r\nexport class ReflectionArchiver {\r\n private dbService: DatabaseService;\r\n private config: Required<ArchiverConfig>;\r\n private scanInterval: NodeJS.Timeout | null = null;\r\n private metrics: ArchiveMetrics = {\r\n total_archived: 0,\r\n last_scan_time: null,\r\n last_scan_count: 0,\r\n failed_archives: 0,\r\n average_archive_time_ms: 0,\r\n redis_unavailable_count: 0,\r\n };\r\n\r\n private archiveTimes: number[] = [];\r\n private isScanning: boolean = false;\r\n\r\n constructor(dbService: DatabaseService, config?: ArchiverConfig) {\r\n this.dbService = dbService;\r\n this.config = { ...DEFAULT_CONFIG, ...config };\r\n }\r\n\r\n /**\r\n * Start automatic archival process\r\n */\r\n start(): void {\r\n if (this.scanInterval) {\r\n logger.warn('Archiver already running');\r\n return;\r\n }\r\n\r\n if (!this.config.auto_archive) {\r\n logger.info('Auto-archive disabled in config');\r\n return;\r\n }\r\n\r\n logger.info('Starting reflection archiver', {\r\n scan_interval_ms: this.config.scan_interval_ms,\r\n ttl_threshold_seconds: this.config.ttl_threshold_seconds,\r\n max_per_scan: this.config.max_per_scan,\r\n });\r\n\r\n this.scanInterval = setInterval(\r\n () => this.runArchiveScan(),\r\n this.config.scan_interval_ms\r\n );\r\n\r\n // Run initial scan immediately\r\n this.runArchiveScan();\r\n }\r\n\r\n /**\r\n * Stop automatic archival process\r\n */\r\n stop(): void {\r\n if (this.scanInterval) {\r\n clearInterval(this.scanInterval);\r\n this.scanInterval = null;\r\n logger.info('Reflection archiver stopped');\r\n }\r\n }\r\n\r\n /**\r\n * Manually trigger an archive scan\r\n */\r\n async manualScan(): Promise<number> {\r\n return await this.runArchiveScan();\r\n }\r\n\r\n /**\r\n * Get current archiver metrics\r\n */\r\n getMetrics(): ArchiveMetrics {\r\n return { ...this.metrics };\r\n }\r\n\r\n /**\r\n * Reset metrics (useful for testing)\r\n */\r\n resetMetrics(): void {\r\n this.metrics = {\r\n total_archived: 0,\r\n last_scan_time: null,\r\n last_scan_count: 0,\r\n failed_archives: 0,\r\n average_archive_time_ms: 0,\r\n redis_unavailable_count: 0,\r\n };\r\n this.archiveTimes = [];\r\n }\r\n\r\n // ========== Private Methods ==========\r\n\r\n private async runArchiveScan(): Promise<number> {\r\n if (this.isScanning) {\r\n logger.debug('Archive scan already in progress, skipping');\r\n return 0;\r\n }\r\n\r\n this.isScanning = true;\r\n const scanStartTime = Date.now();\r\n\r\n try {\r\n const archivedCount = await this.scanAndArchive();\r\n\r\n this.metrics.last_scan_time = new Date();\r\n this.metrics.last_scan_count = archivedCount;\r\n this.metrics.total_archived += archivedCount;\r\n\r\n const scanDuration = Date.now() - scanStartTime;\r\n\r\n logger.info('Archive scan complete', {\r\n archived_count: archivedCount,\r\n scan_duration_ms: scanDuration,\r\n total_archived: this.metrics.total_archived,\r\n });\r\n\r\n return archivedCount;\r\n } catch (error) {\r\n logger.error('Archive scan failed', {\r\n error: error instanceof Error ? error.message : String(error),\r\n });\r\n return 0;\r\n } finally {\r\n this.isScanning = false;\r\n }\r\n }\r\n\r\n private async scanAndArchive(): Promise<number> {\r\n let redisAdapter;\r\n\r\n try {\r\n redisAdapter = this.dbService.getAdapter('redis');\r\n } catch (error) {\r\n this.metrics.redis_unavailable_count++;\r\n logger.warn('Redis adapter unavailable, skipping archive scan', {\r\n redis_unavailable_count: this.metrics.redis_unavailable_count,\r\n });\r\n return 0;\r\n }\r\n\r\n let archivedCount = 0;\r\n\r\n try {\r\n // Get all reflection keys\r\n const keys = await redisAdapter.keys('reflection:*');\r\n\r\n logger.debug('Scanning reflection keys', { key_count: keys.length });\r\n\r\n // Process up to max_per_scan keys\r\n const keysToProcess = keys.slice(0, this.config.max_per_scan);\r\n\r\n for (const key of keysToProcess) {\r\n try {\r\n const archived = await this.archiveIfExpiring(key);\r\n\r\n if (archived) {\r\n archivedCount++;\r\n }\r\n } catch (error) {\r\n this.metrics.failed_archives++;\r\n\r\n logger.error('Failed to archive reflection', {\r\n key,\r\n error: error instanceof Error ? error.message : String(error),\r\n failed_archives: this.metrics.failed_archives,\r\n });\r\n }\r\n }\r\n\r\n // Update average archive time\r\n if (this.archiveTimes.length > 0) {\r\n const sum = this.archiveTimes.reduce((a, b) => a + b, 0);\r\n this.metrics.average_archive_time_ms = sum / this.archiveTimes.length;\r\n }\r\n\r\n // Keep only last 100 archive times for moving average\r\n if (this.archiveTimes.length > 100) {\r\n this.archiveTimes = this.archiveTimes.slice(-100);\r\n }\r\n\r\n return archivedCount;\r\n } catch (error) {\r\n logger.error('Scan failed', {\r\n error: error instanceof Error ? error.message : String(error),\r\n });\r\n return archivedCount;\r\n }\r\n }\r\n\r\n private async archiveIfExpiring(key: string): Promise<boolean> {\r\n const startTime = Date.now();\r\n\r\n try {\r\n const redisAdapter = this.dbService.getAdapter('redis');\r\n\r\n // Check TTL\r\n const ttl = await redisAdapter.ttl(key);\r\n\r\n // Skip if key doesn't exist or has no expiration\r\n if (ttl < 0) {\r\n return false;\r\n }\r\n\r\n // Archive if TTL is below threshold\r\n if (ttl < this.config.ttl_threshold_seconds) {\r\n const data = await redisAdapter.get(key);\r\n\r\n if (!data) {\r\n logger.warn('Key exists but has no data', { key, ttl });\r\n return false;\r\n }\r\n\r\n let reflection;\r\n try {\r\n reflection = JSON.parse(data);\r\n } catch (error) {\r\n logger.error('Failed to parse reflection data', {\r\n key,\r\n error: error instanceof Error ? error.message : String(error),\r\n });\r\n return false;\r\n }\r\n\r\n // Write to PostgreSQL (idempotent)\r\n await this.writeToPostgreSQL(reflection);\r\n\r\n const archiveTime = Date.now() - startTime;\r\n this.archiveTimes.push(archiveTime);\r\n\r\n logger.debug('Reflection archived', {\r\n key,\r\n ttl,\r\n archive_time_ms: archiveTime,\r\n agent_id: reflection.agent_id,\r\n task_id: reflection.task_id,\r\n });\r\n\r\n // Warn if archive took too long\r\n if (archiveTime >= 500) {\r\n logger.warn('Archive exceeded performance target', {\r\n key,\r\n archive_time_ms: archiveTime,\r\n target_ms: 500,\r\n });\r\n }\r\n\r\n return true;\r\n }\r\n\r\n return false;\r\n } catch (error) {\r\n throw new StandardError(\r\n ErrorCode.DATABASE_ERROR,\r\n 'Failed to archive reflection',\r\n {\r\n key,\r\n error: error instanceof Error ? error.message : String(error),\r\n }\r\n );\r\n }\r\n }\r\n\r\n private async writeToPostgreSQL(reflection: any): Promise<void> {\r\n const pgAdapter = this.dbService.getAdapter('postgres');\r\n\r\n const query = `\r\n INSERT INTO reflections (\r\n agent_id,\r\n task_id,\r\n reflection_type,\r\n confidence,\r\n payload,\r\n timestamp,\r\n archived_at\r\n ) VALUES ($1, $2, $3, $4, $5, $6, NOW())\r\n ON CONFLICT (agent_id, task_id, timestamp)\r\n DO UPDATE SET\r\n reflection_type = EXCLUDED.reflection_type,\r\n confidence = EXCLUDED.confidence,\r\n payload = EXCLUDED.payload,\r\n archived_at = NOW()\r\n `;\r\n\r\n const params = [\r\n reflection.agent_id,\r\n reflection.task_id,\r\n reflection.reflection_type,\r\n reflection.confidence,\r\n typeof reflection.payload === 'string'\r\n ? reflection.payload\r\n : JSON.stringify(reflection.payload),\r\n reflection.timestamp || new Date(),\r\n ];\r\n\r\n await pgAdapter.execute(query, params);\r\n }\r\n}\r\n\r\n/**\r\n * Factory function to create and start an archiver\r\n */\r\nexport function createArchiver(\r\n dbService: DatabaseService,\r\n config?: ArchiverConfig\r\n): ReflectionArchiver {\r\n const archiver = new ReflectionArchiver(dbService, config);\r\n\r\n if (config?.auto_archive !== false) {\r\n archiver.start();\r\n }\r\n\r\n return archiver;\r\n}\r\n"],"names":["StandardError","ErrorCode","logger","DEFAULT_CONFIG","ttl_threshold_seconds","scan_interval_ms","max_per_scan","auto_archive","ReflectionArchiver","dbService","config","scanInterval","metrics","total_archived","last_scan_time","last_scan_count","failed_archives","average_archive_time_ms","redis_unavailable_count","archiveTimes","isScanning","start","warn","info","setInterval","runArchiveScan","stop","clearInterval","manualScan","getMetrics","resetMetrics","debug","scanStartTime","Date","now","archivedCount","scanAndArchive","scanDuration","archived_count","scan_duration_ms","error","Error","message","String","redisAdapter","getAdapter","keys","key_count","length","keysToProcess","slice","key","archived","archiveIfExpiring","sum","reduce","a","b","startTime","ttl","data","get","reflection","JSON","parse","writeToPostgreSQL","archiveTime","push","archive_time_ms","agent_id","task_id","target_ms","DATABASE_ERROR","pgAdapter","query","params","reflection_type","confidence","payload","stringify","timestamp","execute","createArchiver","archiver"],"mappings":"AAAA;;;;;;;;CAQC,GAGD,SAASA,aAAa,EAAEC,SAAS,QAAQ,cAAc;AACvD,SAASC,MAAM,QAAQ,eAAe;AAqCtC,MAAMC,iBAA2C;IAC/CC,uBAAuB;IACvBC,kBAAkB;IAClBC,cAAc;IACdC,cAAc;AAChB;AAEA,OAAO,MAAMC;IACHC,UAA2B;IAC3BC,OAAiC;IACjCC,eAAsC,KAAK;IAC3CC,UAA0B;QAChCC,gBAAgB;QAChBC,gBAAgB;QAChBC,iBAAiB;QACjBC,iBAAiB;QACjBC,yBAAyB;QACzBC,yBAAyB;IAC3B,EAAE;IAEMC,eAAyB,EAAE,CAAC;IAC5BC,aAAsB,MAAM;IAEpC,YAAYX,SAA0B,EAAEC,MAAuB,CAAE;QAC/D,IAAI,CAACD,SAAS,GAAGA;QACjB,IAAI,CAACC,MAAM,GAAG;YAAE,GAAGP,cAAc;YAAE,GAAGO,MAAM;QAAC;IAC/C;IAEA;;GAEC,GACDW,QAAc;QACZ,IAAI,IAAI,CAACV,YAAY,EAAE;YACrBT,OAAOoB,IAAI,CAAC;YACZ;QACF;QAEA,IAAI,CAAC,IAAI,CAACZ,MAAM,CAACH,YAAY,EAAE;YAC7BL,OAAOqB,IAAI,CAAC;YACZ;QACF;QAEArB,OAAOqB,IAAI,CAAC,gCAAgC;YAC1ClB,kBAAkB,IAAI,CAACK,MAAM,CAACL,gBAAgB;YAC9CD,uBAAuB,IAAI,CAACM,MAAM,CAACN,qBAAqB;YACxDE,cAAc,IAAI,CAACI,MAAM,CAACJ,YAAY;QACxC;QAEA,IAAI,CAACK,YAAY,GAAGa,YAClB,IAAM,IAAI,CAACC,cAAc,IACzB,IAAI,CAACf,MAAM,CAACL,gBAAgB;QAG9B,+BAA+B;QAC/B,IAAI,CAACoB,cAAc;IACrB;IAEA;;GAEC,GACDC,OAAa;QACX,IAAI,IAAI,CAACf,YAAY,EAAE;YACrBgB,cAAc,IAAI,CAAChB,YAAY;YAC/B,IAAI,CAACA,YAAY,GAAG;YACpBT,OAAOqB,IAAI,CAAC;QACd;IACF;IAEA;;GAEC,GACD,MAAMK,aAA8B;QAClC,OAAO,MAAM,IAAI,CAACH,cAAc;IAClC;IAEA;;GAEC,GACDI,aAA6B;QAC3B,OAAO;YAAE,GAAG,IAAI,CAACjB,OAAO;QAAC;IAC3B;IAEA;;GAEC,GACDkB,eAAqB;QACnB,IAAI,CAAClB,OAAO,GAAG;YACbC,gBAAgB;YAChBC,gBAAgB;YAChBC,iBAAiB;YACjBC,iBAAiB;YACjBC,yBAAyB;YACzBC,yBAAyB;QAC3B;QACA,IAAI,CAACC,YAAY,GAAG,EAAE;IACxB;IAEA,wCAAwC;IAExC,MAAcM,iBAAkC;QAC9C,IAAI,IAAI,CAACL,UAAU,EAAE;YACnBlB,OAAO6B,KAAK,CAAC;YACb,OAAO;QACT;QAEA,IAAI,CAACX,UAAU,GAAG;QAClB,MAAMY,gBAAgBC,KAAKC,GAAG;QAE9B,IAAI;YACF,MAAMC,gBAAgB,MAAM,IAAI,CAACC,cAAc;YAE/C,IAAI,CAACxB,OAAO,CAACE,cAAc,GAAG,IAAImB;YAClC,IAAI,CAACrB,OAAO,CAACG,eAAe,GAAGoB;YAC/B,IAAI,CAACvB,OAAO,CAACC,cAAc,IAAIsB;YAE/B,MAAME,eAAeJ,KAAKC,GAAG,KAAKF;YAElC9B,OAAOqB,IAAI,CAAC,yBAAyB;gBACnCe,gBAAgBH;gBAChBI,kBAAkBF;gBAClBxB,gBAAgB,IAAI,CAACD,OAAO,CAACC,cAAc;YAC7C;YAEA,OAAOsB;QACT,EAAE,OAAOK,OAAO;YACdtC,OAAOsC,KAAK,CAAC,uBAAuB;gBAClCA,OAAOA,iBAAiBC,QAAQD,MAAME,OAAO,GAAGC,OAAOH;YACzD;YACA,OAAO;QACT,SAAU;YACR,IAAI,CAACpB,UAAU,GAAG;QACpB;IACF;IAEA,MAAcgB,iBAAkC;QAC9C,IAAIQ;QAEJ,IAAI;YACFA,eAAe,IAAI,CAACnC,SAAS,CAACoC,UAAU,CAAC;QAC3C,EAAE,OAAOL,OAAO;YACd,IAAI,CAAC5B,OAAO,CAACM,uBAAuB;YACpChB,OAAOoB,IAAI,CAAC,oDAAoD;gBAC9DJ,yBAAyB,IAAI,CAACN,OAAO,CAACM,uBAAuB;YAC/D;YACA,OAAO;QACT;QAEA,IAAIiB,gBAAgB;QAEpB,IAAI;YACF,0BAA0B;YAC1B,MAAMW,OAAO,MAAMF,aAAaE,IAAI,CAAC;YAErC5C,OAAO6B,KAAK,CAAC,4BAA4B;gBAAEgB,WAAWD,KAAKE,MAAM;YAAC;YAElE,kCAAkC;YAClC,MAAMC,gBAAgBH,KAAKI,KAAK,CAAC,GAAG,IAAI,CAACxC,MAAM,CAACJ,YAAY;YAE5D,KAAK,MAAM6C,OAAOF,cAAe;gBAC/B,IAAI;oBACF,MAAMG,WAAW,MAAM,IAAI,CAACC,iBAAiB,CAACF;oBAE9C,IAAIC,UAAU;wBACZjB;oBACF;gBACF,EAAE,OAAOK,OAAO;oBACd,IAAI,CAAC5B,OAAO,CAACI,eAAe;oBAE5Bd,OAAOsC,KAAK,CAAC,gCAAgC;wBAC3CW;wBACAX,OAAOA,iBAAiBC,QAAQD,MAAME,OAAO,GAAGC,OAAOH;wBACvDxB,iBAAiB,IAAI,CAACJ,OAAO,CAACI,eAAe;oBAC/C;gBACF;YACF;YAEA,8BAA8B;YAC9B,IAAI,IAAI,CAACG,YAAY,CAAC6B,MAAM,GAAG,GAAG;gBAChC,MAAMM,MAAM,IAAI,CAACnC,YAAY,CAACoC,MAAM,CAAC,CAACC,GAAGC,IAAMD,IAAIC,GAAG;gBACtD,IAAI,CAAC7C,OAAO,CAACK,uBAAuB,GAAGqC,MAAM,IAAI,CAACnC,YAAY,CAAC6B,MAAM;YACvE;YAEA,sDAAsD;YACtD,IAAI,IAAI,CAAC7B,YAAY,CAAC6B,MAAM,GAAG,KAAK;gBAClC,IAAI,CAAC7B,YAAY,GAAG,IAAI,CAACA,YAAY,CAAC+B,KAAK,CAAC,CAAC;YAC/C;YAEA,OAAOf;QACT,EAAE,OAAOK,OAAO;YACdtC,OAAOsC,KAAK,CAAC,eAAe;gBAC1BA,OAAOA,iBAAiBC,QAAQD,MAAME,OAAO,GAAGC,OAAOH;YACzD;YACA,OAAOL;QACT;IACF;IAEA,MAAckB,kBAAkBF,GAAW,EAAoB;QAC7D,MAAMO,YAAYzB,KAAKC,GAAG;QAE1B,IAAI;YACF,MAAMU,eAAe,IAAI,CAACnC,SAAS,CAACoC,UAAU,CAAC;YAE/C,YAAY;YACZ,MAAMc,MAAM,MAAMf,aAAae,GAAG,CAACR;YAEnC,iDAAiD;YACjD,IAAIQ,MAAM,GAAG;gBACX,OAAO;YACT;YAEA,oCAAoC;YACpC,IAAIA,MAAM,IAAI,CAACjD,MAAM,CAACN,qBAAqB,EAAE;gBAC3C,MAAMwD,OAAO,MAAMhB,aAAaiB,GAAG,CAACV;gBAEpC,IAAI,CAACS,MAAM;oBACT1D,OAAOoB,IAAI,CAAC,8BAA8B;wBAAE6B;wBAAKQ;oBAAI;oBACrD,OAAO;gBACT;gBAEA,IAAIG;gBACJ,IAAI;oBACFA,aAAaC,KAAKC,KAAK,CAACJ;gBAC1B,EAAE,OAAOpB,OAAO;oBACdtC,OAAOsC,KAAK,CAAC,mCAAmC;wBAC9CW;wBACAX,OAAOA,iBAAiBC,QAAQD,MAAME,OAAO,GAAGC,OAAOH;oBACzD;oBACA,OAAO;gBACT;gBAEA,mCAAmC;gBACnC,MAAM,IAAI,CAACyB,iBAAiB,CAACH;gBAE7B,MAAMI,cAAcjC,KAAKC,GAAG,KAAKwB;gBACjC,IAAI,CAACvC,YAAY,CAACgD,IAAI,CAACD;gBAEvBhE,OAAO6B,KAAK,CAAC,uBAAuB;oBAClCoB;oBACAQ;oBACAS,iBAAiBF;oBACjBG,UAAUP,WAAWO,QAAQ;oBAC7BC,SAASR,WAAWQ,OAAO;gBAC7B;gBAEA,gCAAgC;gBAChC,IAAIJ,eAAe,KAAK;oBACtBhE,OAAOoB,IAAI,CAAC,uCAAuC;wBACjD6B;wBACAiB,iBAAiBF;wBACjBK,WAAW;oBACb;gBACF;gBAEA,OAAO;YACT;YAEA,OAAO;QACT,EAAE,OAAO/B,OAAO;YACd,MAAM,IAAIxC,cACRC,UAAUuE,cAAc,EACxB,gCACA;gBACErB;gBACAX,OAAOA,iBAAiBC,QAAQD,MAAME,OAAO,GAAGC,OAAOH;YACzD;QAEJ;IACF;IAEA,MAAcyB,kBAAkBH,UAAe,EAAiB;QAC9D,MAAMW,YAAY,IAAI,CAAChE,SAAS,CAACoC,UAAU,CAAC;QAE5C,MAAM6B,QAAQ,CAAC;;;;;;;;;;;;;;;;IAgBf,CAAC;QAED,MAAMC,SAAS;YACbb,WAAWO,QAAQ;YACnBP,WAAWQ,OAAO;YAClBR,WAAWc,eAAe;YAC1Bd,WAAWe,UAAU;YACrB,OAAOf,WAAWgB,OAAO,KAAK,WAC1BhB,WAAWgB,OAAO,GAClBf,KAAKgB,SAAS,CAACjB,WAAWgB,OAAO;YACrChB,WAAWkB,SAAS,IAAI,IAAI/C;SAC7B;QAED,MAAMwC,UAAUQ,OAAO,CAACP,OAAOC;IACjC;AACF;AAEA;;CAEC,GACD,OAAO,SAASO,eACdzE,SAA0B,EAC1BC,MAAuB;IAEvB,MAAMyE,WAAW,IAAI3E,mBAAmBC,WAAWC;IAEnD,IAAIA,QAAQH,iBAAiB,OAAO;QAClC4E,SAAS9D,KAAK;IAChB;IAEA,OAAO8D;AACT"}
|