claude-flow-novice 2.15.3 → 2.15.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (473) hide show
  1. package/.claude/cfn-extras/skills/advanced-features/cfn-agent-swap/recommend-swap.sh +59 -59
  2. package/.claude/cfn-extras/skills/analytics/cfn-improvement-recommender/recommend-improvements.sh +91 -91
  3. package/.claude/cfn-extras/skills/analytics/cfn-pattern-extraction/extract-patterns.sh +79 -79
  4. package/.claude/cfn-extras/skills/analytics/cfn-retrospective-report/generate-report.sh +100 -100
  5. package/.claude/cfn-extras/skills/analytics/cfn-telemetry/start-telemetry.sh +110 -110
  6. package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/add-bullet.sh +145 -145
  7. package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/log-merge.sh +67 -67
  8. package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/monitor-injection-performance.sh +137 -137
  9. package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/optimize-injection-pipeline.sh +168 -168
  10. package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/query-reflections.sh +35 -35
  11. package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/store-reflection.sh +45 -45
  12. package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/track-ab-test.sh +41 -41
  13. package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/update-reflection.sh +41 -41
  14. package/.claude/cfn-extras/skills/deprecated/cfn-cli-setup/validate-cli-environment.sh +191 -191
  15. package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/create-campaign.sh +231 -231
  16. package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/get-campaign-performance.sh +190 -190
  17. package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/pause-campaign.sh +142 -142
  18. package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/set-budget.sh +181 -181
  19. package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/update-bid-strategy.sh +133 -133
  20. package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/get-conversation-history.sh +121 -121
  21. package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/qualify-lead.sh +156 -156
  22. package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/schedule-demo.sh +181 -181
  23. package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/send-message.sh +137 -137
  24. package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/transfer-to-human.sh +179 -179
  25. package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/create-campaign.sh +183 -183
  26. package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/get-delivery-status.sh +139 -139
  27. package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/opt-out.sh +150 -150
  28. package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/schedule-campaign.sh +187 -187
  29. package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/send-sms.sh +181 -181
  30. package/.claude/cfn-extras/skills/ui-portal/cfn-web-portal/test-web-portal-skill.sh +50 -50
  31. package/.claude/cfn-extras/skills/ui-portal/cfn-web-portal/validate-deployment.sh +84 -84
  32. package/.claude/cfn-extras/skills/utility/cfn-environment-sanitization/sanitize-environment.sh +243 -243
  33. package/.claude/commands/cfn-loop-cli.md +29 -6
  34. package/.claude/commands/switch-api.md +31 -10
  35. package/.claude/hooks/cfn-lint-sql-injection.sh +61 -0
  36. package/.claude/hooks/cfn-post-edit-cfn-retrospective.sh +33 -2
  37. package/.claude/hooks/cfn-pre-edit-security-warning.sh +40 -0
  38. package/.claude/skills/cfn-agent-spawning/spawn-agent.sh +22 -24
  39. package/.claude/skills/cfn-docker-agent-spawning/SKILL.md +28 -4
  40. package/.claude/skills/cfn-docker-agent-spawning/spawn-agent.sh +3 -1
  41. package/.claude/skills/cfn-docker-loop-orchestration/orchestrate.sh +224 -20
  42. package/.claude/skills/cfn-loop-orchestration/helpers/gate-check.sh +550 -46
  43. package/.claude/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +277 -0
  44. package/.claude/skills/cfn-loop-orchestration/orchestrate.sh +238 -29
  45. package/.claude/skills/cfn-loop-orchestration/security_utils.sh +24 -0
  46. package/.claude/skills/cfn-loop-orchestration/test-iteration-context-injection.sh +366 -0
  47. package/.claude/skills/cfn-redis-coordination/CENTRALIZED_REDIS_WRAPPER.md +319 -0
  48. package/.claude/skills/cfn-redis-coordination/agent-log.sh +4 -0
  49. package/.claude/skills/cfn-redis-coordination/agent-log.sh.bak +124 -0
  50. package/.claude/skills/cfn-redis-coordination/agent-recovery.sh +2 -2
  51. package/.claude/skills/cfn-redis-coordination/collect-confidence-scores.sh +30 -0
  52. package/.claude/skills/cfn-redis-coordination/get-context.sh +33 -0
  53. package/.claude/skills/cfn-redis-coordination/get-success-criteria.sh +54 -0
  54. package/.claude/skills/cfn-redis-coordination/invoke-waiting-mode.sh +6 -2
  55. package/.claude/skills/cfn-redis-coordination/redis-cli-wrapper.sh +24 -3
  56. package/.claude/skills/cfn-redis-coordination/redis-functions.sh +34 -0
  57. package/.claude/skills/cfn-redis-coordination/report-completion.sh +24 -31
  58. package/.claude/skills/cfn-redis-coordination/store-context.sh +4 -0
  59. package/.claude/skills/cfn-redis-coordination/store-success-criteria.sh +85 -0
  60. package/.claude/skills/cfn-redis-coordination/update-all-scripts.sh +67 -0
  61. package/.claude/skills/cfn-sqlite-memory/ttl-cleanup.sh +17 -25
  62. package/.claude/skills/cfn-transparency-middleware/test-e2e.sh +15 -0
  63. package/.claude/skills/cfn-transparency-middleware/tests/input-validation.sh +15 -0
  64. package/README.md +116 -475
  65. package/claude-assets/agents/cfn-dev-team/README.md +103 -0
  66. package/claude-assets/agents/cfn-dev-team/architecture/goal-planner.md +1 -1
  67. package/claude-assets/agents/cfn-dev-team/coordinators/cfn-frontend-coordinator.md +77 -15
  68. package/claude-assets/agents/cfn-dev-team/coordinators/cfn-v3-coordinator.md +355 -6
  69. package/claude-assets/agents/cfn-dev-team/coordinators/consensus-builder.md +82 -1
  70. package/claude-assets/agents/cfn-dev-team/coordinators/handoff-coordinator.md +82 -1
  71. package/claude-assets/agents/cfn-dev-team/coordinators/multi-sprint-coordinator.md +77 -15
  72. package/claude-assets/agents/cfn-dev-team/dev-ops/docker-specialist.md +99 -12
  73. package/claude-assets/agents/cfn-dev-team/dev-ops/github-commit-agent.md +1 -1
  74. package/claude-assets/agents/cfn-dev-team/dev-ops/kubernetes-specialist.md +97 -0
  75. package/claude-assets/agents/cfn-dev-team/dev-ops/monitoring-specialist.md +20 -1
  76. package/claude-assets/agents/cfn-dev-team/developers/api-gateway-specialist.md +97 -0
  77. package/claude-assets/agents/cfn-dev-team/developers/backend-developer.md +110 -13
  78. package/claude-assets/agents/cfn-dev-team/developers/data/data-engineer.md +106 -15
  79. package/claude-assets/agents/cfn-dev-team/developers/database/database-architect.md +115 -11
  80. package/claude-assets/agents/cfn-dev-team/developers/frontend/mobile-dev.md +94 -7
  81. package/claude-assets/agents/cfn-dev-team/developers/frontend/react-frontend-engineer.md +87 -9
  82. package/claude-assets/agents/cfn-dev-team/developers/frontend/typescript-specialist.md +85 -7
  83. package/claude-assets/agents/cfn-dev-team/developers/frontend/ui-designer.md +160 -28
  84. package/claude-assets/agents/cfn-dev-team/developers/graphql-specialist.md +101 -19
  85. package/claude-assets/agents/cfn-dev-team/developers/rust-developer.md +108 -14
  86. package/claude-assets/agents/cfn-dev-team/reviewers/{reviewer.md → code-reviewer.md} +95 -8
  87. package/claude-assets/agents/cfn-dev-team/reviewers/quality/code-quality-validator.md +107 -7
  88. package/claude-assets/agents/cfn-dev-team/reviewers/quality/perf-analyzer.md +98 -7
  89. package/claude-assets/agents/cfn-dev-team/reviewers/quality/performance-benchmarker.md +95 -7
  90. package/claude-assets/agents/cfn-dev-team/reviewers/quality/security-specialist.md +136 -9
  91. package/claude-assets/agents/cfn-dev-team/testers/api-testing-specialist.md +108 -1
  92. package/claude-assets/agents/cfn-dev-team/testers/chaos-engineering-specialist.md +107 -13
  93. package/claude-assets/agents/cfn-dev-team/testers/contract-tester.md +737 -0
  94. package/claude-assets/agents/cfn-dev-team/testers/e2e/playwright-tester.md +1 -1
  95. package/claude-assets/agents/cfn-dev-team/testers/integration-tester.md +828 -0
  96. package/claude-assets/agents/cfn-dev-team/testers/interaction-tester.md +106 -7
  97. package/claude-assets/agents/cfn-dev-team/testers/load-testing-specialist.md +77 -0
  98. package/claude-assets/agents/cfn-dev-team/testers/mutation-testing-specialist.md +684 -0
  99. package/claude-assets/agents/cfn-dev-team/testers/playwright-tester.md +110 -1
  100. package/claude-assets/agents/cfn-dev-team/testers/tester.md +94 -7
  101. package/claude-assets/agents/cfn-dev-team/utility/code-booster.md +1 -3
  102. package/claude-assets/agents/cfn-dev-team/utility/epic-creator.md +87 -13
  103. package/claude-assets/agents/cfn-dev-team/utility/memory-leak-specialist.md +103 -7
  104. package/claude-assets/agents/cfn-dev-team/utility/researcher.md +1 -3
  105. package/claude-assets/agents/cfn-dev-team/utility/z-ai-specialist.md +94 -7
  106. package/claude-assets/agents/docker-coordinators/cfn-docker-v3-coordinator.md +46 -0
  107. package/claude-assets/agents/project-only-agents/npm-package-specialist.md +1 -1
  108. package/claude-assets/cfn-extras/skills/advanced-features/cfn-agent-swap/recommend-swap.sh +59 -59
  109. package/claude-assets/cfn-extras/skills/analytics/cfn-improvement-recommender/recommend-improvements.sh +91 -91
  110. package/claude-assets/cfn-extras/skills/analytics/cfn-pattern-extraction/extract-patterns.sh +79 -79
  111. package/claude-assets/cfn-extras/skills/analytics/cfn-retrospective-report/generate-report.sh +100 -100
  112. package/claude-assets/cfn-extras/skills/analytics/cfn-telemetry/start-telemetry.sh +110 -110
  113. package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/add-bullet.sh +145 -145
  114. package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/log-merge.sh +67 -67
  115. package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/monitor-injection-performance.sh +137 -137
  116. package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/optimize-injection-pipeline.sh +168 -168
  117. package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/query-reflections.sh +35 -35
  118. package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/store-reflection.sh +45 -45
  119. package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/track-ab-test.sh +41 -41
  120. package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/update-reflection.sh +41 -41
  121. package/claude-assets/cfn-extras/skills/deprecated/cfn-cli-setup/validate-cli-environment.sh +191 -191
  122. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/create-campaign.sh +231 -231
  123. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/get-campaign-performance.sh +190 -190
  124. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/pause-campaign.sh +142 -142
  125. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/set-budget.sh +181 -181
  126. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/update-bid-strategy.sh +133 -133
  127. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/get-conversation-history.sh +121 -121
  128. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/qualify-lead.sh +156 -156
  129. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/schedule-demo.sh +181 -181
  130. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/send-message.sh +137 -137
  131. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/transfer-to-human.sh +179 -179
  132. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/create-campaign.sh +183 -183
  133. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/get-delivery-status.sh +139 -139
  134. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/opt-out.sh +150 -150
  135. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/schedule-campaign.sh +187 -187
  136. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/send-sms.sh +181 -181
  137. package/claude-assets/cfn-extras/skills/ui-portal/cfn-web-portal/test-web-portal-skill.sh +50 -50
  138. package/claude-assets/cfn-extras/skills/ui-portal/cfn-web-portal/validate-deployment.sh +84 -84
  139. package/claude-assets/cfn-extras/skills/utility/cfn-environment-sanitization/sanitize-environment.sh +243 -243
  140. package/claude-assets/commands/cfn-loop-cli.md +29 -6
  141. package/claude-assets/commands/switch-api.md +31 -10
  142. package/claude-assets/hooks/cfn-lint-sql-injection.sh +61 -0
  143. package/claude-assets/hooks/cfn-post-edit-cfn-retrospective.sh +33 -2
  144. package/claude-assets/hooks/cfn-pre-edit-security-warning.sh +40 -0
  145. package/claude-assets/hooks/detect-hardcoded-credentials.sh +212 -0
  146. package/claude-assets/skills/SKILL_TEMPLATE.md +774 -0
  147. package/claude-assets/skills/agent-lifecycle/execute-lifecycle-hook.sh +84 -113
  148. package/claude-assets/skills/agent-lifecycle/simple-audit.sh +33 -6
  149. package/claude-assets/skills/agent-template-generator/SKILL.md +440 -0
  150. package/claude-assets/skills/agent-template-generator/generate-agent.sh +405 -0
  151. package/claude-assets/skills/agent-validation-linter/SKILL.md +589 -0
  152. package/claude-assets/skills/agent-validation-linter/lint-agents.sh +271 -0
  153. package/claude-assets/skills/bootstrap/bash-fundamentals.md +786 -0
  154. package/claude-assets/skills/bootstrap/database-connection.md +464 -0
  155. package/claude-assets/skills/bootstrap/error-handling.md +580 -0
  156. package/claude-assets/skills/bootstrap/file-operations.md +699 -0
  157. package/claude-assets/skills/bootstrap/skill-loader.md +616 -0
  158. package/claude-assets/skills/bootstrap/sqlite-params.sh +287 -0
  159. package/claude-assets/skills/cfn-agent-spawning/spawn-agent.sh +22 -24
  160. package/claude-assets/skills/cfn-automatic-memory-persistence/test-memory-persistence.sh +17 -16
  161. package/claude-assets/skills/cfn-deployment/SKILL.md +293 -0
  162. package/claude-assets/skills/cfn-deployment/execute.sh +21 -0
  163. package/claude-assets/skills/cfn-docker-agent-spawning/SKILL.md +28 -4
  164. package/claude-assets/skills/cfn-docker-agent-spawning/spawn-agent.sh +3 -1
  165. package/claude-assets/skills/cfn-docker-loop-orchestration/orchestrate.sh +224 -20
  166. package/claude-assets/skills/cfn-environment-sanitization/sanitize-environment.sh +38 -0
  167. package/claude-assets/skills/cfn-error-batching-strategy/lib/core-functions.sh +47 -47
  168. package/claude-assets/skills/cfn-file-operations/SKILL.md +290 -0
  169. package/claude-assets/skills/cfn-file-operations/execute.sh +129 -0
  170. package/claude-assets/skills/cfn-file-operations/lib/atomic-write.sh +294 -0
  171. package/claude-assets/skills/cfn-file-operations/lib/lock.sh +361 -0
  172. package/claude-assets/skills/cfn-file-operations/test.sh +369 -0
  173. package/claude-assets/skills/cfn-log-operations/SKILL.md +308 -0
  174. package/claude-assets/skills/cfn-log-operations/execute.sh +420 -0
  175. package/claude-assets/skills/cfn-log-operations/lib/rotate.sh +406 -0
  176. package/claude-assets/skills/cfn-log-operations/lib/search.sh +448 -0
  177. package/claude-assets/skills/cfn-log-operations/test.sh +394 -0
  178. package/claude-assets/skills/cfn-loop-orchestration/helpers/gate-check.sh +550 -46
  179. package/claude-assets/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +277 -0
  180. package/claude-assets/skills/cfn-loop-orchestration/orchestrate.sh +238 -29
  181. package/claude-assets/skills/cfn-loop-orchestration/security_utils.sh +24 -0
  182. package/claude-assets/skills/cfn-loop-orchestration/test-iteration-context-injection.sh +366 -0
  183. package/claude-assets/skills/cfn-parameterized-queries/SKILL.md +339 -0
  184. package/claude-assets/skills/cfn-playbook/query-playbook.sh +19 -15
  185. package/claude-assets/skills/cfn-playbook/update-playbook.sh +25 -14
  186. package/claude-assets/skills/cfn-process-instrumentation/instrument-process.sh +44 -0
  187. package/claude-assets/skills/cfn-promotion/SKILL.md +305 -0
  188. package/claude-assets/skills/cfn-redis-coordination/CENTRALIZED_REDIS_WRAPPER.md +319 -0
  189. package/claude-assets/skills/cfn-redis-coordination/agent-log.sh +4 -0
  190. package/claude-assets/skills/cfn-redis-coordination/agent-log.sh.bak +124 -0
  191. package/claude-assets/skills/cfn-redis-coordination/agent-recovery.sh +2 -2
  192. package/claude-assets/skills/cfn-redis-coordination/collect-confidence-scores.sh +30 -0
  193. package/claude-assets/skills/cfn-redis-coordination/get-context.sh +33 -0
  194. package/claude-assets/skills/cfn-redis-coordination/get-success-criteria.sh +54 -0
  195. package/claude-assets/skills/cfn-redis-coordination/invoke-waiting-mode.sh +6 -2
  196. package/claude-assets/skills/cfn-redis-coordination/redis-cli-wrapper.sh +24 -3
  197. package/claude-assets/skills/cfn-redis-coordination/redis-functions.sh +34 -0
  198. package/claude-assets/skills/cfn-redis-coordination/report-completion.sh +24 -31
  199. package/claude-assets/skills/cfn-redis-coordination/store-context.sh +4 -0
  200. package/claude-assets/skills/cfn-redis-coordination/store-success-criteria.sh +85 -0
  201. package/claude-assets/skills/cfn-redis-coordination/update-all-scripts.sh +67 -0
  202. package/claude-assets/skills/cfn-skill-loader/SKILL.md +466 -0
  203. package/claude-assets/skills/cfn-skill-loader/execute.sh +344 -0
  204. package/claude-assets/skills/cfn-sqlite-memory/ttl-cleanup.sh +17 -25
  205. package/claude-assets/skills/cfn-task-audit/get-audit-data.sh +42 -21
  206. package/claude-assets/skills/cfn-task-audit/store-task-audit.sh +17 -10
  207. package/claude-assets/skills/cfn-test-runner/detect-regressions.sh +17 -14
  208. package/claude-assets/skills/cfn-test-runner/detect-regressions.sh.backup-1763392821 +55 -0
  209. package/claude-assets/skills/cfn-test-runner/store-benchmarks.sh +17 -19
  210. package/claude-assets/skills/cfn-transparency-middleware/test-e2e.sh +15 -0
  211. package/claude-assets/skills/cfn-transparency-middleware/tests/input-validation.sh +15 -0
  212. package/claude-assets/skills/cfn-utilities/SKILL.md +237 -0
  213. package/claude-assets/skills/cfn-utilities/execute.sh +32 -0
  214. package/claude-assets/skills/cfn-utilities/lib/errors.sh +56 -0
  215. package/claude-assets/skills/cfn-utilities/lib/file-ops.sh +164 -0
  216. package/claude-assets/skills/cfn-utilities/lib/logging.sh +77 -0
  217. package/claude-assets/skills/cfn-utilities/lib/retry.sh +127 -0
  218. package/claude-assets/skills/cfn-utilities/test.sh +317 -0
  219. package/claude-assets/skills/integration/agent-handoff.sh +62 -64
  220. package/claude-assets/skills/json-validation/SKILL.md +431 -0
  221. package/claude-assets/skills/json-validation/test-validate-success-criteria.sh +421 -0
  222. package/claude-assets/skills/json-validation/validate-success-criteria.sh +197 -0
  223. package/claude-assets/skills/redis-coordination/validate-parameters.sh +34 -0
  224. package/claude-assets/skills/workflow-codification/DEPLOY_QUICK_REFERENCE.md +106 -0
  225. package/claude-assets/skills/workflow-codification/PROPAGATE_UPDATE_QUICK_REFERENCE.md +366 -0
  226. package/claude-assets/skills/workflow-codification/deploy-approved-skill.sh +481 -0
  227. package/claude-assets/skills/workflow-codification/deploy-approved-skill.sh.backup-1763392820 +512 -0
  228. package/claude-assets/skills/workflow-codification/lib/security-utils.sh +204 -0
  229. package/claude-assets/skills/workflow-codification/propagate-skill-update.sh +648 -0
  230. package/claude-assets/skills/workflow-codification/propagate-skill-update.sh.backup-1763392820 +664 -0
  231. package/claude-assets/skills/workflow-codification/test-integration.sh +15 -0
  232. package/claude-assets/skills/workflow-codification/test-metadata-update.sh +350 -0
  233. package/claude-assets/skills/workflow-codification/track-cost-savings.sh +55 -14
  234. package/claude-assets/skills/workflow-codification/track-cost-savings.sh.backup-1763392821 +445 -0
  235. package/claude-assets/skills/workflow-codification/track-edge-case.sh +27 -60
  236. package/claude-assets/skills/workflow-codification/workflow-codification.db +0 -0
  237. package/dist/ace/ace-curator.js +10 -2
  238. package/dist/ace/ace-curator.js.map +1 -1
  239. package/dist/ace/ace-generator.js +4 -0
  240. package/dist/ace/ace-generator.js.map +1 -1
  241. package/dist/ace/ace-reflector.js +1 -1
  242. package/dist/ace/ace-reflector.js.map +1 -1
  243. package/dist/ace/context-injection.js +24 -2
  244. package/dist/ace/context-injection.js.map +1 -1
  245. package/dist/agents/task-agent-integration.js +1 -1
  246. package/dist/agents/task-agent-integration.js.map +1 -1
  247. package/dist/api/health-endpoints.js +390 -0
  248. package/dist/api/health-endpoints.js.map +1 -0
  249. package/dist/cli/agent-executor.js +4 -1
  250. package/dist/cli/agent-executor.js.map +1 -1
  251. package/dist/cli/agent-prompt-builder.js +89 -1
  252. package/dist/cli/agent-prompt-builder.js.map +1 -1
  253. package/dist/cli/agent-spawn.js +130 -37
  254. package/dist/cli/agent-spawn.js.map +1 -1
  255. package/dist/cli/config-manager.js +109 -91
  256. package/dist/cli/config-manager.js.map +1 -1
  257. package/dist/cli/conversation-fork-cleanup.js +201 -0
  258. package/dist/cli/conversation-fork-cleanup.js.map +1 -0
  259. package/dist/cli/conversation-fork.js +16 -3
  260. package/dist/cli/conversation-fork.js.map +1 -1
  261. package/dist/cli/skill-cache-validator.js +412 -0
  262. package/dist/cli/skill-cache-validator.js.map +1 -0
  263. package/dist/cli/skill-cli.js +991 -0
  264. package/dist/cli/skill-cli.js.map +1 -0
  265. package/dist/cli/skill-execution-logger.js +284 -0
  266. package/dist/cli/skill-execution-logger.js.map +1 -0
  267. package/dist/cli/skill-loader.js +457 -0
  268. package/dist/cli/skill-loader.js.map +1 -0
  269. package/dist/coordination/event-bus.js +2 -2
  270. package/dist/coordination/event-bus.js.map +1 -1
  271. package/dist/coordination/fleet-manager.js +1 -1
  272. package/dist/coordination/fleet-manager.js.map +1 -1
  273. package/dist/coordination/index.js +23 -9
  274. package/dist/coordination/index.js.map +1 -1
  275. package/dist/coordination/types/fleet-manager.types.js.map +1 -1
  276. package/dist/db/migration-manager.js +483 -0
  277. package/dist/db/migration-manager.js.map +1 -0
  278. package/dist/db/skills-query.js +535 -0
  279. package/dist/db/skills-query.js.map +1 -0
  280. package/dist/integration/DatabaseHandoff.js +1 -1
  281. package/dist/integration/DatabaseHandoff.js.map +1 -1
  282. package/dist/jobs/edge-case-analyzer.js +367 -0
  283. package/dist/jobs/edge-case-analyzer.js.map +1 -0
  284. package/dist/jobs/promotion-sla-enforcer.js +288 -0
  285. package/dist/jobs/promotion-sla-enforcer.js.map +1 -0
  286. package/dist/lib/agent-output-parser.js.map +1 -1
  287. package/dist/lib/agent-output-validator.js.map +1 -1
  288. package/dist/lib/agent-workspace.js +281 -0
  289. package/dist/lib/agent-workspace.js.map +1 -0
  290. package/dist/lib/atomic-file-writer.js +377 -0
  291. package/dist/lib/atomic-file-writer.js.map +1 -0
  292. package/dist/lib/backup-manager.js +779 -0
  293. package/dist/lib/backup-manager.js.map +1 -0
  294. package/dist/lib/checkpoint-manager.js +837 -0
  295. package/dist/lib/checkpoint-manager.js.map +1 -0
  296. package/dist/lib/circuit-breaker.js +340 -0
  297. package/dist/lib/circuit-breaker.js.map +1 -0
  298. package/dist/lib/completion-signal-handler.js +243 -0
  299. package/dist/lib/completion-signal-handler.js.map +1 -0
  300. package/dist/lib/config-manager.js +312 -0
  301. package/dist/lib/config-manager.js.map +1 -0
  302. package/dist/lib/config-migrator.js +386 -0
  303. package/dist/lib/config-migrator.js.map +1 -0
  304. package/dist/lib/config-validator.js.map +1 -1
  305. package/dist/lib/correlation-cache.js +311 -0
  306. package/dist/lib/correlation-cache.js.map +1 -0
  307. package/dist/lib/correlation.js +263 -0
  308. package/dist/lib/correlation.js.map +1 -0
  309. package/dist/lib/database-service/connection-pool-manager.js +520 -0
  310. package/dist/lib/database-service/connection-pool-manager.js.map +1 -0
  311. package/dist/lib/database-service/correlation.js +329 -0
  312. package/dist/lib/database-service/correlation.js.map +1 -0
  313. package/dist/lib/database-service/errors.js +120 -0
  314. package/dist/lib/database-service/errors.js.map +1 -0
  315. package/dist/lib/database-service/index.js +168 -0
  316. package/dist/lib/database-service/index.js.map +1 -0
  317. package/dist/lib/database-service/postgres-adapter.js +526 -0
  318. package/dist/lib/database-service/postgres-adapter.js.map +1 -0
  319. package/dist/lib/database-service/redis-adapter.js +360 -0
  320. package/dist/lib/database-service/redis-adapter.js.map +1 -0
  321. package/dist/lib/database-service/sqlite-adapter.js +544 -0
  322. package/dist/lib/database-service/sqlite-adapter.js.map +1 -0
  323. package/dist/lib/database-service/transaction-manager.js +773 -0
  324. package/dist/lib/database-service/transaction-manager.js.map +1 -0
  325. package/dist/lib/database-service/types.js +23 -0
  326. package/dist/lib/database-service/types.js.map +1 -0
  327. package/dist/lib/deadlock-resolver.js +292 -0
  328. package/dist/lib/deadlock-resolver.js.map +1 -0
  329. package/dist/lib/distributed-lock.js +451 -0
  330. package/dist/lib/distributed-lock.js.map +1 -0
  331. package/dist/lib/edge-case-deduplicator.js +227 -0
  332. package/dist/lib/edge-case-deduplicator.js.map +1 -0
  333. package/dist/lib/encryption-manager.js +322 -0
  334. package/dist/lib/encryption-manager.js.map +1 -0
  335. package/dist/lib/error-aggregator.js +234 -0
  336. package/dist/lib/error-aggregator.js.map +1 -0
  337. package/dist/lib/errors.js +287 -0
  338. package/dist/lib/errors.js.map +1 -0
  339. package/dist/lib/file-lock-manager.js +578 -0
  340. package/dist/lib/file-lock-manager.js.map +1 -0
  341. package/dist/lib/file-operations.js +367 -0
  342. package/dist/lib/file-operations.js.map +1 -0
  343. package/dist/lib/idempotent-write.js +237 -0
  344. package/dist/lib/idempotent-write.js.map +1 -0
  345. package/dist/lib/integration-schema-validator.js +522 -0
  346. package/dist/lib/integration-schema-validator.js.map +1 -0
  347. package/dist/lib/lock-health-monitor.js +298 -0
  348. package/dist/lib/lock-health-monitor.js.map +1 -0
  349. package/dist/lib/log-shipper.js +422 -0
  350. package/dist/lib/log-shipper.js.map +1 -0
  351. package/dist/lib/logging.js +146 -0
  352. package/dist/lib/logging.js.map +1 -0
  353. package/dist/lib/message-deduplicator.js +439 -0
  354. package/dist/lib/message-deduplicator.js.map +1 -0
  355. package/dist/lib/multi-system-query.js +604 -0
  356. package/dist/lib/multi-system-query.js.map +1 -0
  357. package/dist/lib/orphan-detector.js +332 -0
  358. package/dist/lib/orphan-detector.js.map +1 -0
  359. package/dist/lib/password-generator.js +166 -0
  360. package/dist/lib/password-generator.js.map +1 -0
  361. package/dist/lib/path-validator.js +429 -0
  362. package/dist/lib/path-validator.js.map +1 -0
  363. package/dist/lib/query-translator.js +905 -0
  364. package/dist/lib/query-translator.js.map +1 -0
  365. package/dist/lib/queue-recovery.js +469 -0
  366. package/dist/lib/queue-recovery.js.map +1 -0
  367. package/dist/lib/redis-queue-manager.js +512 -0
  368. package/dist/lib/redis-queue-manager.js.map +1 -0
  369. package/dist/lib/reflection-archiver.js +272 -0
  370. package/dist/lib/reflection-archiver.js.map +1 -0
  371. package/dist/lib/retry-manager.js +453 -0
  372. package/dist/lib/retry-manager.js.map +1 -0
  373. package/dist/lib/retry.js +262 -0
  374. package/dist/lib/retry.js.map +1 -0
  375. package/dist/lib/schema-transform.js +695 -0
  376. package/dist/lib/schema-transform.js.map +1 -0
  377. package/dist/lib/schema-validator.js +491 -0
  378. package/dist/lib/schema-validator.js.map +1 -0
  379. package/dist/lib/skill-cache.js +297 -0
  380. package/dist/lib/skill-cache.js.map +1 -0
  381. package/dist/lib/skill-content-manager.js +337 -0
  382. package/dist/lib/skill-content-manager.js.map +1 -0
  383. package/dist/lib/skill-frontmatter-parser.js +237 -0
  384. package/dist/lib/skill-frontmatter-parser.js.map +1 -0
  385. package/dist/lib/skill-git-integration.js +275 -0
  386. package/dist/lib/skill-git-integration.js.map +1 -0
  387. package/dist/lib/skill-markdown-validator.js +396 -0
  388. package/dist/lib/skill-markdown-validator.js.map +1 -0
  389. package/dist/lib/skill-output-parser.js +312 -0
  390. package/dist/lib/skill-output-parser.js.map +1 -0
  391. package/dist/lib/unified-query-api.js +467 -0
  392. package/dist/lib/unified-query-api.js.map +1 -0
  393. package/dist/middleware/auth-middleware.js +350 -0
  394. package/dist/middleware/auth-middleware.js.map +1 -0
  395. package/dist/middleware/schema-validation.js +347 -0
  396. package/dist/middleware/schema-validation.js.map +1 -0
  397. package/dist/providers/anthropic-provider.js +1 -1
  398. package/dist/providers/anthropic-provider.js.map +1 -1
  399. package/dist/providers/provider-factory.js +2 -2
  400. package/dist/providers/provider-factory.js.map +1 -1
  401. package/dist/services/edge-case-analyzer.js +321 -0
  402. package/dist/services/edge-case-analyzer.js.map +1 -0
  403. package/dist/services/edge-case-deduplicator.js +266 -0
  404. package/dist/services/edge-case-deduplicator.js.map +1 -0
  405. package/dist/services/edge-case-detector.js +337 -0
  406. package/dist/services/edge-case-detector.js.map +1 -0
  407. package/dist/services/edge-case-tracker.js +547 -0
  408. package/dist/services/edge-case-tracker.js.map +1 -0
  409. package/dist/services/health-check-system.js +586 -0
  410. package/dist/services/health-check-system.js.map +1 -0
  411. package/dist/services/metrics-logger.js +412 -0
  412. package/dist/services/metrics-logger.js.map +1 -0
  413. package/dist/services/patch-generator.js +378 -0
  414. package/dist/services/patch-generator.js.map +1 -0
  415. package/dist/services/patch-validator.js +337 -0
  416. package/dist/services/patch-validator.js.map +1 -0
  417. package/dist/services/performance-monitor.js +811 -0
  418. package/dist/services/performance-monitor.js.map +1 -0
  419. package/dist/services/promotion-pipeline.js +918 -0
  420. package/dist/services/promotion-pipeline.js.map +1 -0
  421. package/dist/services/promotion-validator.js +394 -0
  422. package/dist/services/promotion-validator.js.map +1 -0
  423. package/dist/services/reflection-logger.js +388 -0
  424. package/dist/services/reflection-logger.js.map +1 -0
  425. package/dist/services/skill-deployment.js +472 -0
  426. package/dist/services/skill-deployment.js.map +1 -0
  427. package/dist/services/skill-loader.js +427 -0
  428. package/dist/services/skill-loader.js.map +1 -0
  429. package/dist/services/skill-promotion.js +372 -0
  430. package/dist/services/skill-promotion.js.map +1 -0
  431. package/dist/services/skill-validator.js +454 -0
  432. package/dist/services/skill-validator.js.map +1 -0
  433. package/dist/services/skill-versioning.js +244 -0
  434. package/dist/services/skill-versioning.js.map +1 -0
  435. package/dist/services/workspace-supervisor.js +597 -0
  436. package/dist/services/workspace-supervisor.js.map +1 -0
  437. package/dist/types/edge-case.js +45 -0
  438. package/dist/types/edge-case.js.map +1 -0
  439. package/docs/BUG_19_MEMORY_LEAK_TASK_MODE.md +405 -0
  440. package/docs/MEMORY_CLEANUP_GUIDE.md +358 -0
  441. package/docs/MEMORY_LEAK_FIX_SUMMARY.md +322 -0
  442. package/docs/REDIS_CLEANUP_EXECUTIVE_SUMMARY.md +319 -0
  443. package/docs/REDIS_CLEANUP_VERIFICATION_REPORT.md +574 -0
  444. package/package.json +35 -4
  445. package/readme/README.md +53 -5
  446. package/scripts/backup-cleanup.sh +627 -0
  447. package/scripts/cleanup-workspaces.sh +412 -0
  448. package/scripts/cleanup-yaml-configs.sh +141 -0
  449. package/scripts/deploy-approved-skills.sh +263 -0
  450. package/scripts/health-check.sh +447 -0
  451. package/scripts/log-aggregator.sh +554 -0
  452. package/scripts/log-monitor.sh +629 -0
  453. package/scripts/manage-agent-workspaces.sh +434 -0
  454. package/scripts/migrate-schema.sh +533 -0
  455. package/scripts/promote-staged-skills.sh +423 -0
  456. package/scripts/verify-no-secrets.sh +88 -35
  457. package/scripts/verify-redis-cleanup.sh +173 -0
  458. package/tests/README.md +84 -0
  459. package/tests/test-memory-leak-task-mode.sh +435 -0
  460. package/.claude/cfn-extras/agents/deprecated-coordinators/adaptive-coordinator.md.backup +0 -161
  461. package/.claude/cfn-extras/agents/deprecated-coordinators/blocking-coordinator-example.md.backup +0 -728
  462. package/.claude/cfn-extras/agents/deprecated-coordinators/mesh-coordinator.md.backup +0 -131
  463. package/.claude/skills/agent-lifecycle/SKILL.md +0 -60
  464. package/.claude/skills/agent-lifecycle/execute-lifecycle-hook.sh +0 -573
  465. package/.claude/skills/agent-lifecycle/simple-audit.sh +0 -31
  466. package/.claude/skills/cfn-agent-spawning/spawn-agent.sh.backup +0 -273
  467. package/.claude/skills/cfn-loop-orchestration/orchestrate.sh.backup +0 -949
  468. package/README.md.backup_before_replace +0 -781
  469. package/claude-assets/cfn-extras/agents/deprecated-coordinators/adaptive-coordinator.md.backup +0 -161
  470. package/claude-assets/cfn-extras/agents/deprecated-coordinators/blocking-coordinator-example.md.backup +0 -728
  471. package/claude-assets/cfn-extras/agents/deprecated-coordinators/mesh-coordinator.md.backup +0 -131
  472. package/claude-assets/skills/cfn-agent-spawning/spawn-agent.sh.backup +0 -273
  473. package/claude-assets/skills/cfn-loop-orchestration/orchestrate.sh.backup +0 -949
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/queue-recovery.ts"],"sourcesContent":["/**\r\n * Queue Recovery System\r\n *\r\n * Provides failure detection, dead letter queue management, and automatic recovery\r\n * for Redis queue operations. Handles coordinator crashes and message reprocessing.\r\n * Part of Task 3.4: Redis Queue Consistency & Recovery (Integration Standardization Sprint 3)\r\n *\r\n * Features:\r\n * - Dead letter queue for failed messages\r\n * - Automatic retry with exponential backoff\r\n * - Failure detection (tasks not processed within timeout)\r\n * - Coordinator crash recovery procedure\r\n * - Message reprocessing safeguards\r\n *\r\n * Usage:\r\n * const recovery = new QueueRecovery(queueManager);\r\n *\r\n * // Start monitoring for stuck messages\r\n * recovery.startMonitoring();\r\n *\r\n * // Recover from coordinator crash\r\n * await recovery.recoverFromCrash();\r\n *\r\n * // Process dead letter queue\r\n * const reprocessed = await recovery.reprocessDeadLetters();\r\n */\r\n\r\nimport { RedisClientType } from 'redis';\r\nimport { createLogger } from './logging.js';\r\nimport { createError, ErrorCode, isRetryableError } from './errors.js';\r\nimport { withRetry, sleep } from './retry.js';\r\nimport { RedisQueueManager, QueueMessage } from './redis-queue-manager.js';\r\n\r\nconst logger = createLogger('queue-recovery');\r\n\r\n/**\r\n * Recovery options\r\n */\r\nexport interface RecoveryOptions {\r\n /** Maximum number of retry attempts (default: 3) */\r\n maxRetries?: number;\r\n /** Base retry delay in milliseconds (default: 1000) */\r\n baseRetryDelayMs?: number;\r\n /** Maximum retry delay in milliseconds (default: 60000) */\r\n maxRetryDelayMs?: number;\r\n /** Message processing timeout in milliseconds (default: 300000 - 5 minutes) */\r\n processingTimeoutMs?: number;\r\n /** Monitoring interval in milliseconds (default: 60000 - 1 minute) */\r\n monitoringIntervalMs?: number;\r\n /** Dead letter queue name (default: 'dlq') */\r\n deadLetterQueue?: string;\r\n /** Enable automatic reprocessing of dead letters (default: false) */\r\n autoReprocess?: boolean;\r\n}\r\n\r\n/**\r\n * Recovery statistics\r\n */\r\nexport interface RecoveryStats {\r\n /** Total messages recovered */\r\n totalRecovered: number;\r\n /** Total messages sent to DLQ */\r\n totalDeadLettered: number;\r\n /** Total messages reprocessed from DLQ */\r\n totalReprocessed: number;\r\n /** Total stuck messages detected */\r\n totalStuckDetected: number;\r\n /** Last recovery timestamp */\r\n lastRecoveryAt?: Date;\r\n}\r\n\r\n/**\r\n * Dead letter metadata\r\n */\r\nexport interface DeadLetterMetadata {\r\n /** Original queue */\r\n originalQueue: string;\r\n /** Failure reason */\r\n failureReason: string;\r\n /** Number of retry attempts made */\r\n retryAttempts: number;\r\n /** Dead lettered timestamp */\r\n deadLetteredAt: Date;\r\n /** Original message metadata */\r\n originalMetadata?: Record<string, any>;\r\n}\r\n\r\n/**\r\n * Default recovery options\r\n */\r\nconst DEFAULT_RECOVERY_OPTIONS: Required<RecoveryOptions> = {\r\n maxRetries: 3,\r\n baseRetryDelayMs: 1000,\r\n maxRetryDelayMs: 60000,\r\n processingTimeoutMs: 300000, // 5 minutes\r\n monitoringIntervalMs: 60000, // 1 minute\r\n deadLetterQueue: 'dlq',\r\n autoReprocess: false,\r\n};\r\n\r\n/**\r\n * Queue Recovery System\r\n *\r\n * Handles failure detection, retry, and recovery for queue operations.\r\n */\r\nexport class QueueRecovery {\r\n private queueManager: RedisQueueManager;\r\n private redis: RedisClientType;\r\n private options: Required<RecoveryOptions>;\r\n private monitoringTimer: NodeJS.Timeout | null = null;\r\n private stats: RecoveryStats = {\r\n totalRecovered: 0,\r\n totalDeadLettered: 0,\r\n totalReprocessed: 0,\r\n totalStuckDetected: 0,\r\n };\r\n\r\n /**\r\n * Create a new QueueRecovery instance\r\n *\r\n * @param queueManager - Queue manager instance\r\n * @param redis - Redis client instance\r\n * @param options - Recovery options\r\n */\r\n constructor(\r\n queueManager: RedisQueueManager,\r\n redis: RedisClientType,\r\n options: RecoveryOptions = {}\r\n ) {\r\n this.queueManager = queueManager;\r\n this.redis = redis;\r\n this.options = { ...DEFAULT_RECOVERY_OPTIONS, ...options };\r\n\r\n logger.info('QueueRecovery initialized', {\r\n maxRetries: this.options.maxRetries,\r\n processingTimeoutMs: this.options.processingTimeoutMs,\r\n deadLetterQueue: this.options.deadLetterQueue,\r\n });\r\n }\r\n\r\n /**\r\n * Retry message processing with exponential backoff\r\n *\r\n * @param message - Message to retry\r\n * @param processFn - Function to process message\r\n * @returns Processing result\r\n */\r\n public async retryWithBackoff<T = any, R = any>(\r\n message: QueueMessage<T>,\r\n processFn: (payload: T) => Promise<R>\r\n ): Promise<R> {\r\n const maxAttempts = this.options.maxRetries;\r\n let attempt = message.deliveryAttempts;\r\n\r\n while (attempt <= maxAttempts) {\r\n try {\r\n logger.debug('Processing message with retry', {\r\n messageId: message.id,\r\n attempt,\r\n maxAttempts,\r\n });\r\n\r\n const result = await processFn(message.payload);\r\n\r\n if (attempt > 1) {\r\n this.stats.totalRecovered++;\r\n logger.info('Message processed successfully after retry', {\r\n messageId: message.id,\r\n attempt,\r\n });\r\n }\r\n\r\n return result;\r\n } catch (error) {\r\n const err = error instanceof Error ? error : new Error(String(error));\r\n\r\n logger.warn('Message processing failed', {\r\n messageId: message.id,\r\n attempt,\r\n error: err.message,\r\n });\r\n\r\n // Check if we should retry\r\n if (!isRetryableError(err) || attempt >= maxAttempts) {\r\n logger.error('Message processing failed permanently', err, {\r\n messageId: message.id,\r\n attempt,\r\n retryable: isRetryableError(err),\r\n });\r\n\r\n // Send to dead letter queue\r\n await this.sendToDeadLetter(message, err.message);\r\n\r\n throw err;\r\n }\r\n\r\n // Calculate backoff delay\r\n const delay = this.calculateBackoffDelay(attempt);\r\n\r\n logger.debug('Retrying message after delay', {\r\n messageId: message.id,\r\n attempt,\r\n delayMs: delay,\r\n });\r\n\r\n // Wait before retry\r\n await sleep(delay);\r\n\r\n attempt++;\r\n }\r\n }\r\n\r\n // Should never reach here, but TypeScript needs it\r\n throw createError(\r\n ErrorCode.RETRY_EXHAUSTED,\r\n 'Message processing retry exhausted',\r\n { messageId: message.id, attempts: attempt }\r\n );\r\n }\r\n\r\n /**\r\n * Send message to dead letter queue\r\n *\r\n * @param message - Message to dead letter\r\n * @param reason - Failure reason\r\n */\r\n public async sendToDeadLetter<T = any>(\r\n message: QueueMessage<T>,\r\n reason: string\r\n ): Promise<void> {\r\n try {\r\n const metadata: DeadLetterMetadata = {\r\n originalQueue: message.queue,\r\n failureReason: reason,\r\n retryAttempts: message.deliveryAttempts,\r\n deadLetteredAt: new Date(),\r\n originalMetadata: message.metadata,\r\n };\r\n\r\n // Enqueue to dead letter queue\r\n await this.queueManager.enqueue(\r\n this.options.deadLetterQueue,\r\n message.payload,\r\n {\r\n deduplicate: false, // Allow duplicate dead letters\r\n metadata,\r\n }\r\n );\r\n\r\n this.stats.totalDeadLettered++;\r\n\r\n logger.info('Message sent to dead letter queue', {\r\n messageId: message.id,\r\n originalQueue: message.queue,\r\n reason,\r\n retryAttempts: message.deliveryAttempts,\r\n });\r\n } catch (error) {\r\n logger.error('Failed to send message to DLQ', error instanceof Error ? error : new Error(String(error)), {\r\n messageId: message.id,\r\n });\r\n\r\n throw createError(\r\n ErrorCode.DB_QUERY_FAILED,\r\n 'Failed to send message to dead letter queue',\r\n { messageId: message.id },\r\n error instanceof Error ? error : undefined\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Reprocess messages from dead letter queue\r\n *\r\n * @param processFn - Function to process dead letter messages\r\n * @param maxMessages - Maximum number of messages to reprocess (default: 100)\r\n * @returns Number of messages reprocessed\r\n */\r\n public async reprocessDeadLetters<T = any>(\r\n processFn: (payload: T, metadata: DeadLetterMetadata) => Promise<void>,\r\n maxMessages: number = 100\r\n ): Promise<number> {\r\n let reprocessedCount = 0;\r\n\r\n try {\r\n logger.info('Starting dead letter reprocessing', {\r\n maxMessages,\r\n });\r\n\r\n for (let i = 0; i < maxMessages; i++) {\r\n // Dequeue from dead letter queue\r\n const message = await this.queueManager.dequeue<T>(\r\n this.options.deadLetterQueue,\r\n { timeout: 0 }\r\n );\r\n\r\n if (!message) {\r\n // No more messages\r\n break;\r\n }\r\n\r\n try {\r\n const metadata = message.metadata as DeadLetterMetadata;\r\n\r\n // Process message\r\n await processFn(message.payload, metadata);\r\n\r\n // Acknowledge successful processing\r\n await this.queueManager.acknowledge(message.id);\r\n\r\n reprocessedCount++;\r\n this.stats.totalReprocessed++;\r\n\r\n logger.debug('Dead letter message reprocessed', {\r\n messageId: message.id,\r\n originalQueue: metadata.originalQueue,\r\n });\r\n } catch (error) {\r\n // Reject message (will stay in DLQ or be retried based on options)\r\n await this.queueManager.reject(message.id, {\r\n retry: false,\r\n error: error instanceof Error ? error.message : String(error),\r\n });\r\n\r\n logger.error('Failed to reprocess dead letter', error instanceof Error ? error : new Error(String(error)), {\r\n messageId: message.id,\r\n });\r\n }\r\n }\r\n\r\n logger.info('Dead letter reprocessing complete', {\r\n reprocessedCount,\r\n });\r\n\r\n return reprocessedCount;\r\n } catch (error) {\r\n logger.error('Dead letter reprocessing failed', error instanceof Error ? error : new Error(String(error)));\r\n\r\n throw createError(\r\n ErrorCode.DB_QUERY_FAILED,\r\n 'Failed to reprocess dead letters',\r\n { reprocessedCount },\r\n error instanceof Error ? error : undefined\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Detect and recover stuck messages (messages in processing longer than timeout)\r\n *\r\n * @param queue - Queue name to check\r\n * @returns Number of stuck messages recovered\r\n */\r\n public async recoverStuckMessages(queue: string): Promise<number> {\r\n try {\r\n const processingKey = `queue:${queue}:processing`;\r\n const queueKey = `queue:${queue}`;\r\n\r\n // Get all messages in processing\r\n const processingMessages = await this.redis.lRange(processingKey, 0, -1);\r\n\r\n let recoveredCount = 0;\r\n const now = Date.now();\r\n\r\n for (const messageData of processingMessages) {\r\n const message = JSON.parse(messageData) as QueueMessage;\r\n\r\n // Check if message is stuck (processing longer than timeout)\r\n if (message.dequeuedAt) {\r\n const processingTime = now - new Date(message.dequeuedAt).getTime();\r\n\r\n if (processingTime > this.options.processingTimeoutMs) {\r\n logger.warn('Stuck message detected', {\r\n messageId: message.id,\r\n queue,\r\n processingTimeMs: processingTime,\r\n timeoutMs: this.options.processingTimeoutMs,\r\n });\r\n\r\n this.stats.totalStuckDetected++;\r\n\r\n // Check if message has exceeded max retries\r\n if (message.deliveryAttempts >= this.options.maxRetries) {\r\n // Send to dead letter queue\r\n await this.sendToDeadLetter(\r\n message,\r\n `Message stuck in processing for ${processingTime}ms (exceeded max retries)`\r\n );\r\n\r\n // Remove from processing\r\n await this.redis.lRem(processingKey, 1, messageData);\r\n\r\n logger.info('Stuck message sent to DLQ', {\r\n messageId: message.id,\r\n deliveryAttempts: message.deliveryAttempts,\r\n });\r\n } else {\r\n // Re-enqueue for retry\r\n message.deliveryAttempts++;\r\n message.metadata = {\r\n ...message.metadata,\r\n recoveredAt: new Date().toISOString(),\r\n recoveryReason: 'Stuck in processing',\r\n };\r\n\r\n // Remove from processing and add back to queue\r\n await this.redis.lRem(processingKey, 1, messageData);\r\n await this.redis.rPush(queueKey, JSON.stringify(message));\r\n\r\n recoveredCount++;\r\n this.stats.totalRecovered++;\r\n\r\n logger.info('Stuck message re-enqueued', {\r\n messageId: message.id,\r\n deliveryAttempts: message.deliveryAttempts,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (recoveredCount > 0) {\r\n this.stats.lastRecoveryAt = new Date();\r\n }\r\n\r\n logger.debug('Stuck message recovery complete', {\r\n queue,\r\n recoveredCount,\r\n totalStuckDetected: this.stats.totalStuckDetected,\r\n });\r\n\r\n return recoveredCount;\r\n } catch (error) {\r\n logger.error('Failed to recover stuck messages', 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 recover stuck messages',\r\n { queue },\r\n error instanceof Error ? error : undefined\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Recover from coordinator crash\r\n *\r\n * Scans all queues for stuck messages and recovers them.\r\n *\r\n * @returns Object with recovery results per queue\r\n */\r\n public async recoverFromCrash(): Promise<Record<string, number>> {\r\n try {\r\n logger.info('Starting coordinator crash recovery');\r\n\r\n // Get all queues\r\n const queues = await this.queueManager.getQueues();\r\n\r\n const results: Record<string, number> = {};\r\n\r\n // Recover stuck messages from each queue\r\n for (const queue of queues) {\r\n // Skip dead letter queue\r\n if (queue === this.options.deadLetterQueue) {\r\n continue;\r\n }\r\n\r\n const recovered = await this.recoverStuckMessages(queue);\r\n results[queue] = recovered;\r\n }\r\n\r\n const totalRecovered = Object.values(results).reduce((sum, count) => sum + count, 0);\r\n\r\n logger.info('Coordinator crash recovery complete', {\r\n queuesProcessed: queues.length,\r\n totalRecovered,\r\n results,\r\n });\r\n\r\n return results;\r\n } catch (error) {\r\n logger.error('Coordinator crash recovery failed', error instanceof Error ? error : new Error(String(error)));\r\n\r\n throw createError(\r\n ErrorCode.DB_QUERY_FAILED,\r\n 'Failed to recover from coordinator crash',\r\n {},\r\n error instanceof Error ? error : undefined\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Start automatic monitoring for stuck messages\r\n */\r\n public startMonitoring(): void {\r\n if (this.monitoringTimer) {\r\n logger.warn('Monitoring already started');\r\n return;\r\n }\r\n\r\n this.monitoringTimer = setInterval(\r\n async () => {\r\n try {\r\n await this.recoverFromCrash();\r\n\r\n // Auto-reprocess dead letters if enabled\r\n if (this.options.autoReprocess) {\r\n await this.reprocessDeadLetters(async (payload, metadata) => {\r\n logger.debug('Auto-reprocessing dead letter', {\r\n originalQueue: metadata.originalQueue,\r\n });\r\n\r\n // Re-enqueue to original queue\r\n await this.queueManager.enqueue(metadata.originalQueue, payload, {\r\n deduplicate: false,\r\n metadata: {\r\n ...metadata,\r\n reprocessedAt: new Date().toISOString(),\r\n },\r\n });\r\n });\r\n }\r\n } catch (error) {\r\n logger.error('Monitoring cycle failed', error instanceof Error ? error : new Error(String(error)));\r\n }\r\n },\r\n this.options.monitoringIntervalMs\r\n );\r\n\r\n logger.info('Monitoring started', {\r\n intervalMs: this.options.monitoringIntervalMs,\r\n autoReprocess: this.options.autoReprocess,\r\n });\r\n }\r\n\r\n /**\r\n * Stop automatic monitoring\r\n */\r\n public stopMonitoring(): void {\r\n if (this.monitoringTimer) {\r\n clearInterval(this.monitoringTimer);\r\n this.monitoringTimer = null;\r\n logger.info('Monitoring stopped');\r\n }\r\n }\r\n\r\n /**\r\n * Get recovery statistics\r\n *\r\n * @returns Current statistics\r\n */\r\n public getStats(): RecoveryStats {\r\n return { ...this.stats };\r\n }\r\n\r\n /**\r\n * Reset statistics\r\n */\r\n public resetStats(): void {\r\n this.stats = {\r\n totalRecovered: 0,\r\n totalDeadLettered: 0,\r\n totalReprocessed: 0,\r\n totalStuckDetected: 0,\r\n };\r\n\r\n logger.debug('Statistics reset');\r\n }\r\n\r\n /**\r\n * Shutdown recovery system (stop monitoring)\r\n */\r\n public shutdown(): void {\r\n this.stopMonitoring();\r\n logger.info('QueueRecovery shutdown');\r\n }\r\n\r\n /**\r\n * Calculate exponential backoff delay\r\n *\r\n * @param attempt - Current attempt number (1-based)\r\n * @returns Delay in milliseconds\r\n */\r\n private calculateBackoffDelay(attempt: number): number {\r\n // Exponential backoff: baseDelay * 2^(attempt - 1)\r\n const delay = this.options.baseRetryDelayMs * Math.pow(2, attempt - 1);\r\n\r\n // Cap at max delay\r\n const cappedDelay = Math.min(delay, this.options.maxRetryDelayMs);\r\n\r\n // Add jitter (+/- 10%) to prevent thundering herd\r\n const jitterFactor = 0.1;\r\n const jitterRange = cappedDelay * jitterFactor;\r\n const jitter = (Math.random() * 2 - 1) * jitterRange;\r\n\r\n return Math.max(0, Math.floor(cappedDelay + jitter));\r\n }\r\n}\r\n\r\n/**\r\n * Message reprocessing safeguards\r\n */\r\nexport class ReprocessingSafeguards {\r\n private processedMessages: Set<string> = new Set();\r\n private maxProcessedTracking: number;\r\n\r\n /**\r\n * Create a new ReprocessingSafeguards instance\r\n *\r\n * @param maxProcessedTracking - Maximum number of processed message IDs to track (default: 10000)\r\n */\r\n constructor(maxProcessedTracking: number = 10000) {\r\n this.maxProcessedTracking = maxProcessedTracking;\r\n\r\n logger.debug('ReprocessingSafeguards initialized', {\r\n maxProcessedTracking,\r\n });\r\n }\r\n\r\n /**\r\n * Check if message has already been processed\r\n *\r\n * @param messageId - Message ID to check\r\n * @returns True if already processed\r\n */\r\n public hasBeenProcessed(messageId: string): boolean {\r\n return this.processedMessages.has(messageId);\r\n }\r\n\r\n /**\r\n * Mark message as processed\r\n *\r\n * @param messageId - Message ID to mark\r\n */\r\n public markProcessed(messageId: string): void {\r\n // Implement simple LRU-like behavior\r\n if (this.processedMessages.size >= this.maxProcessedTracking) {\r\n // Remove oldest entry (first in Set)\r\n const firstId = this.processedMessages.values().next().value;\r\n this.processedMessages.delete(firstId);\r\n }\r\n\r\n this.processedMessages.add(messageId);\r\n }\r\n\r\n /**\r\n * Clear all processed message tracking\r\n */\r\n public clear(): void {\r\n this.processedMessages.clear();\r\n logger.debug('Processed messages cleared');\r\n }\r\n\r\n /**\r\n * Get number of tracked processed messages\r\n */\r\n public getTrackedCount(): number {\r\n return this.processedMessages.size;\r\n }\r\n}\r\n"],"names":["createLogger","createError","ErrorCode","isRetryableError","sleep","logger","DEFAULT_RECOVERY_OPTIONS","maxRetries","baseRetryDelayMs","maxRetryDelayMs","processingTimeoutMs","monitoringIntervalMs","deadLetterQueue","autoReprocess","QueueRecovery","queueManager","redis","options","monitoringTimer","stats","totalRecovered","totalDeadLettered","totalReprocessed","totalStuckDetected","info","retryWithBackoff","message","processFn","maxAttempts","attempt","deliveryAttempts","debug","messageId","id","result","payload","error","err","Error","String","warn","retryable","sendToDeadLetter","delay","calculateBackoffDelay","delayMs","RETRY_EXHAUSTED","attempts","reason","metadata","originalQueue","queue","failureReason","retryAttempts","deadLetteredAt","Date","originalMetadata","enqueue","deduplicate","DB_QUERY_FAILED","undefined","reprocessDeadLetters","maxMessages","reprocessedCount","i","dequeue","timeout","acknowledge","reject","retry","recoverStuckMessages","processingKey","queueKey","processingMessages","lRange","recoveredCount","now","messageData","JSON","parse","dequeuedAt","processingTime","getTime","processingTimeMs","timeoutMs","lRem","recoveredAt","toISOString","recoveryReason","rPush","stringify","lastRecoveryAt","recoverFromCrash","queues","getQueues","results","recovered","Object","values","reduce","sum","count","queuesProcessed","length","startMonitoring","setInterval","reprocessedAt","intervalMs","stopMonitoring","clearInterval","getStats","resetStats","shutdown","Math","pow","cappedDelay","min","jitterFactor","jitterRange","jitter","random","max","floor","ReprocessingSafeguards","processedMessages","Set","maxProcessedTracking","hasBeenProcessed","has","markProcessed","size","firstId","next","value","delete","add","clear","getTrackedCount"],"mappings":"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"}
@@ -0,0 +1,512 @@
1
+ /**
2
+ * Redis Queue Manager
3
+ *
4
+ * Provides reliable queue operations with idempotency, acknowledgment protocol,
5
+ * and message visibility timeout for Docker agent ↔ Redis communication.
6
+ * Part of Task 3.4: Redis Queue Consistency & Recovery (Integration Standardization Sprint 3)
7
+ *
8
+ * Features:
9
+ * - Enqueue with idempotency (prevents duplicate messages)
10
+ * - Dequeue with acknowledgment protocol
11
+ * - Message visibility timeout
12
+ * - Queue monitoring (depth, age, throughput)
13
+ * - Multiple queue support (task, result, coordination)
14
+ * - Performance: <100ms per operation
15
+ *
16
+ * Usage:
17
+ * const queueManager = new RedisQueueManager(redisClient);
18
+ *
19
+ * // Producer
20
+ * await queueManager.enqueue('task-queue', {
21
+ * taskId: 'task-001',
22
+ * agentType: 'backend-developer',
23
+ * payload: { ... }
24
+ * });
25
+ *
26
+ * // Consumer
27
+ * const message = await queueManager.dequeue('task-queue', { timeout: 30000 });
28
+ * try {
29
+ * await processTask(message.payload);
30
+ * await queueManager.acknowledge(message.id);
31
+ * } catch (error) {
32
+ * await queueManager.reject(message.id, { retry: true });
33
+ * }
34
+ */ import { v4 as uuidv4 } from 'uuid';
35
+ import { createLogger } from './logging.js';
36
+ import { createError, ErrorCode, isRetryableError } from './errors.js';
37
+ import { withRetry } from './retry.js';
38
+ import { MessageDeduplicator } from './message-deduplicator.js';
39
+ const logger = createLogger('redis-queue-manager');
40
+ /**
41
+ * Default queue options
42
+ */ const DEFAULT_ENQUEUE_OPTIONS = {
43
+ deduplicate: true,
44
+ metadata: {},
45
+ visibilityTimeout: 30000
46
+ };
47
+ const DEFAULT_DEQUEUE_OPTIONS = {
48
+ timeout: 0,
49
+ visibilityTimeout: 30000,
50
+ count: 1
51
+ };
52
+ /**
53
+ * Redis Queue Manager
54
+ *
55
+ * Provides reliable queue operations with at-least-once delivery guarantees.
56
+ */ export class RedisQueueManager {
57
+ redis;
58
+ deduplicator;
59
+ stats = new Map();
60
+ /**
61
+ * Create a new RedisQueueManager instance
62
+ *
63
+ * @param redis - Redis client instance
64
+ * @param deduplicator - Optional custom deduplicator instance
65
+ */ constructor(redis, deduplicator){
66
+ this.redis = redis;
67
+ this.deduplicator = deduplicator || new MessageDeduplicator(redis);
68
+ logger.info('RedisQueueManager initialized');
69
+ }
70
+ /**
71
+ * Enqueue a message to a queue
72
+ *
73
+ * @param queue - Queue name
74
+ * @param payload - Message payload
75
+ * @param options - Enqueue options
76
+ * @returns Message ID
77
+ */ async enqueue(queue, payload, options = {}) {
78
+ const opts = {
79
+ ...DEFAULT_ENQUEUE_OPTIONS,
80
+ ...options
81
+ };
82
+ const startTime = Date.now();
83
+ try {
84
+ // Check for duplicates if enabled
85
+ if (opts.deduplicate) {
86
+ const isDuplicate = await this.deduplicator.isDuplicate(payload);
87
+ if (isDuplicate) {
88
+ logger.warn('Duplicate message detected, skipping enqueue', {
89
+ queue,
90
+ payloadHash: this.deduplicator.createFingerprint(payload).substring(0, 16) + '...'
91
+ });
92
+ throw createError(ErrorCode.DB_DUPLICATE_KEY, 'Duplicate message detected', {
93
+ queue
94
+ });
95
+ }
96
+ }
97
+ // Create message
98
+ const message = {
99
+ id: uuidv4(),
100
+ queue,
101
+ payload,
102
+ createdAt: new Date(),
103
+ enqueuedAt: new Date(),
104
+ deliveryAttempts: 0,
105
+ visibilityTimeout: opts.visibilityTimeout,
106
+ metadata: opts.metadata
107
+ };
108
+ // Push to queue (RPUSH for FIFO)
109
+ await withRetry(async ()=>{
110
+ const queueKey = this.getQueueKey(queue);
111
+ await this.redis.rPush(queueKey, JSON.stringify(message));
112
+ }, {
113
+ maxAttempts: 3,
114
+ shouldRetry: isRetryableError
115
+ });
116
+ // Mark as processed in deduplicator if enabled
117
+ if (opts.deduplicate) {
118
+ await this.deduplicator.markProcessed(payload, {
119
+ messageId: message.id,
120
+ queue
121
+ });
122
+ }
123
+ // Update stats
124
+ this.updateStats(queue, 'enqueued');
125
+ const duration = Date.now() - startTime;
126
+ logger.debug('Message enqueued', {
127
+ queue,
128
+ messageId: message.id,
129
+ durationMs: duration
130
+ });
131
+ // Validate performance requirement (<100ms)
132
+ if (duration > 100) {
133
+ logger.warn('Enqueue operation exceeded 100ms target', {
134
+ queue,
135
+ durationMs: duration
136
+ });
137
+ }
138
+ return message.id;
139
+ } catch (error) {
140
+ logger.error('Failed to enqueue message', error instanceof Error ? error : new Error(String(error)), {
141
+ queue
142
+ });
143
+ throw createError(ErrorCode.DB_QUERY_FAILED, 'Failed to enqueue message', {
144
+ queue
145
+ }, error instanceof Error ? error : undefined);
146
+ }
147
+ }
148
+ /**
149
+ * Dequeue a message from a queue
150
+ *
151
+ * @param queue - Queue name
152
+ * @param options - Dequeue options
153
+ * @returns Message or null if queue is empty
154
+ */ async dequeue(queue, options = {}) {
155
+ const opts = {
156
+ ...DEFAULT_DEQUEUE_OPTIONS,
157
+ ...options
158
+ };
159
+ const startTime = Date.now();
160
+ try {
161
+ const queueKey = this.getQueueKey(queue);
162
+ const processingKey = this.getProcessingKey(queue);
163
+ let messageData = null;
164
+ // Use blocking pop if timeout specified
165
+ if (opts.timeout > 0) {
166
+ const result = await withRetry(async ()=>{
167
+ // BLMOVE atomically moves from queue to processing set with timeout
168
+ return await this.redis.blMove(queueKey, processingKey, 'LEFT', 'RIGHT', opts.timeout / 1000 // Convert to seconds
169
+ );
170
+ }, {
171
+ maxAttempts: 1
172
+ } // Don't retry blocking operations
173
+ );
174
+ messageData = result;
175
+ } else {
176
+ // Non-blocking pop
177
+ messageData = await withRetry(async ()=>{
178
+ return await this.redis.lMove(queueKey, processingKey, 'LEFT', 'RIGHT');
179
+ }, {
180
+ maxAttempts: 3,
181
+ shouldRetry: isRetryableError
182
+ });
183
+ }
184
+ if (!messageData) {
185
+ return null;
186
+ }
187
+ // Parse message
188
+ const message = JSON.parse(messageData);
189
+ // Convert date strings back to Date objects
190
+ message.createdAt = new Date(message.createdAt);
191
+ message.enqueuedAt = new Date(message.enqueuedAt);
192
+ message.dequeuedAt = new Date();
193
+ message.deliveryAttempts++;
194
+ // Store message with visibility timeout
195
+ await this.storeInFlight(message, opts.visibilityTimeout);
196
+ // Update stats
197
+ this.updateStats(queue, 'dequeued');
198
+ const duration = Date.now() - startTime;
199
+ logger.debug('Message dequeued', {
200
+ queue,
201
+ messageId: message.id,
202
+ deliveryAttempts: message.deliveryAttempts,
203
+ durationMs: duration
204
+ });
205
+ // Validate performance requirement (<100ms)
206
+ if (duration > 100) {
207
+ logger.warn('Dequeue operation exceeded 100ms target', {
208
+ queue,
209
+ durationMs: duration
210
+ });
211
+ }
212
+ return message;
213
+ } catch (error) {
214
+ logger.error('Failed to dequeue message', error instanceof Error ? error : new Error(String(error)), {
215
+ queue
216
+ });
217
+ throw createError(ErrorCode.DB_QUERY_FAILED, 'Failed to dequeue message', {
218
+ queue
219
+ }, error instanceof Error ? error : undefined);
220
+ }
221
+ }
222
+ /**
223
+ * Acknowledge successful message processing
224
+ *
225
+ * @param messageId - Message ID to acknowledge
226
+ */ async acknowledge(messageId) {
227
+ const startTime = Date.now();
228
+ try {
229
+ // Remove from in-flight storage
230
+ const message = await this.getInFlight(messageId);
231
+ if (!message) {
232
+ logger.warn('Message not found for acknowledgment', {
233
+ messageId
234
+ });
235
+ return;
236
+ }
237
+ // Remove from processing set
238
+ const processingKey = this.getProcessingKey(message.queue);
239
+ await withRetry(async ()=>{
240
+ await this.redis.lRem(processingKey, 1, JSON.stringify(message));
241
+ }, {
242
+ maxAttempts: 3,
243
+ shouldRetry: isRetryableError
244
+ });
245
+ // Remove from in-flight storage
246
+ await this.removeInFlight(messageId);
247
+ // Update stats
248
+ this.updateStats(message.queue, 'acknowledged');
249
+ const duration = Date.now() - startTime;
250
+ logger.debug('Message acknowledged', {
251
+ queue: message.queue,
252
+ messageId,
253
+ durationMs: duration
254
+ });
255
+ } catch (error) {
256
+ logger.error('Failed to acknowledge message', error instanceof Error ? error : new Error(String(error)), {
257
+ messageId
258
+ });
259
+ throw createError(ErrorCode.DB_QUERY_FAILED, 'Failed to acknowledge message', {
260
+ messageId
261
+ }, error instanceof Error ? error : undefined);
262
+ }
263
+ }
264
+ /**
265
+ * Reject message processing (with optional retry)
266
+ *
267
+ * @param messageId - Message ID to reject
268
+ * @param options - Reject options
269
+ */ async reject(messageId, options = {}) {
270
+ const startTime = Date.now();
271
+ try {
272
+ // Get message from in-flight storage
273
+ const message = await this.getInFlight(messageId);
274
+ if (!message) {
275
+ logger.warn('Message not found for rejection', {
276
+ messageId
277
+ });
278
+ return;
279
+ }
280
+ // Remove from processing set
281
+ const processingKey = this.getProcessingKey(message.queue);
282
+ await withRetry(async ()=>{
283
+ await this.redis.lRem(processingKey, 1, JSON.stringify(message));
284
+ }, {
285
+ maxAttempts: 3,
286
+ shouldRetry: isRetryableError
287
+ });
288
+ // Remove from in-flight storage
289
+ await this.removeInFlight(messageId);
290
+ if (options.retry) {
291
+ // Re-enqueue message
292
+ message.metadata = {
293
+ ...message.metadata,
294
+ ...options.metadata,
295
+ rejectedAt: new Date().toISOString(),
296
+ rejectionReason: options.error
297
+ };
298
+ const queueKey = this.getQueueKey(message.queue);
299
+ await withRetry(async ()=>{
300
+ await this.redis.rPush(queueKey, JSON.stringify(message));
301
+ }, {
302
+ maxAttempts: 3,
303
+ shouldRetry: isRetryableError
304
+ });
305
+ logger.debug('Message rejected and re-enqueued', {
306
+ queue: message.queue,
307
+ messageId,
308
+ deliveryAttempts: message.deliveryAttempts
309
+ });
310
+ } else {
311
+ logger.debug('Message rejected without retry', {
312
+ queue: message.queue,
313
+ messageId
314
+ });
315
+ }
316
+ // Update stats
317
+ this.updateStats(message.queue, 'rejected');
318
+ const duration = Date.now() - startTime;
319
+ logger.debug('Message rejected', {
320
+ queue: message.queue,
321
+ messageId,
322
+ retry: options.retry,
323
+ durationMs: duration
324
+ });
325
+ } catch (error) {
326
+ logger.error('Failed to reject message', error instanceof Error ? error : new Error(String(error)), {
327
+ messageId
328
+ });
329
+ throw createError(ErrorCode.DB_QUERY_FAILED, 'Failed to reject message', {
330
+ messageId
331
+ }, error instanceof Error ? error : undefined);
332
+ }
333
+ }
334
+ /**
335
+ * Get queue statistics
336
+ *
337
+ * @param queue - Queue name
338
+ * @returns Queue statistics
339
+ */ async getStats(queue) {
340
+ try {
341
+ const queueKey = this.getQueueKey(queue);
342
+ const processingKey = this.getProcessingKey(queue);
343
+ // Get queue depth
344
+ const depth = await this.redis.lLen(queueKey);
345
+ // Get in-flight count
346
+ const inFlight = await this.redis.lLen(processingKey);
347
+ // Get oldest message age
348
+ let oldestMessageAge = 0;
349
+ const oldestMessage = await this.redis.lIndex(queueKey, 0);
350
+ if (oldestMessage) {
351
+ const message = JSON.parse(oldestMessage);
352
+ const age = Date.now() - new Date(message.enqueuedAt).getTime();
353
+ oldestMessageAge = Math.floor(age / 1000); // Convert to seconds
354
+ }
355
+ // Get stats from tracking
356
+ const stats = this.stats.get(queue) || {
357
+ enqueued: 0,
358
+ dequeued: 0,
359
+ acknowledged: 0,
360
+ rejected: 0,
361
+ startTime: new Date()
362
+ };
363
+ // Calculate throughput (messages per second)
364
+ const elapsed = (Date.now() - stats.startTime.getTime()) / 1000;
365
+ const throughput = elapsed > 0 ? stats.dequeued / elapsed : 0;
366
+ return {
367
+ queue,
368
+ depth,
369
+ inFlight,
370
+ oldestMessageAge,
371
+ totalEnqueued: stats.enqueued,
372
+ totalDequeued: stats.dequeued,
373
+ totalAcknowledged: stats.acknowledged,
374
+ totalRejected: stats.rejected,
375
+ throughput
376
+ };
377
+ } catch (error) {
378
+ logger.error('Failed to get queue stats', error instanceof Error ? error : new Error(String(error)), {
379
+ queue
380
+ });
381
+ throw createError(ErrorCode.DB_QUERY_FAILED, 'Failed to get queue stats', {
382
+ queue
383
+ }, error instanceof Error ? error : undefined);
384
+ }
385
+ }
386
+ /**
387
+ * Purge all messages from a queue
388
+ *
389
+ * @param queue - Queue name
390
+ * @returns Number of messages purged
391
+ */ async purge(queue) {
392
+ try {
393
+ const queueKey = this.getQueueKey(queue);
394
+ const count = await withRetry(async ()=>{
395
+ const len = await this.redis.lLen(queueKey);
396
+ await this.redis.del(queueKey);
397
+ return len;
398
+ }, {
399
+ maxAttempts: 3,
400
+ shouldRetry: isRetryableError
401
+ });
402
+ logger.info('Queue purged', {
403
+ queue,
404
+ messagesPurged: count
405
+ });
406
+ return count;
407
+ } catch (error) {
408
+ logger.error('Failed to purge queue', error instanceof Error ? error : new Error(String(error)), {
409
+ queue
410
+ });
411
+ throw createError(ErrorCode.DB_QUERY_FAILED, 'Failed to purge queue', {
412
+ queue
413
+ }, error instanceof Error ? error : undefined);
414
+ }
415
+ }
416
+ /**
417
+ * Get all queue names
418
+ *
419
+ * @returns Array of queue names
420
+ */ async getQueues() {
421
+ try {
422
+ const pattern = 'queue:*';
423
+ const keys = await this.redis.keys(pattern);
424
+ const queues = keys.filter((key)=>!key.includes(':processing')).map((key)=>key.replace('queue:', ''));
425
+ return queues;
426
+ } catch (error) {
427
+ logger.error('Failed to get queues', error instanceof Error ? error : new Error(String(error)));
428
+ throw createError(ErrorCode.DB_QUERY_FAILED, 'Failed to get queues', {}, error instanceof Error ? error : undefined);
429
+ }
430
+ }
431
+ /**
432
+ * Shutdown queue manager (cleanup resources)
433
+ */ shutdown() {
434
+ this.deduplicator.shutdown();
435
+ logger.info('RedisQueueManager shutdown');
436
+ }
437
+ /**
438
+ * Get Redis key for queue
439
+ */ getQueueKey(queue) {
440
+ return `queue:${queue}`;
441
+ }
442
+ /**
443
+ * Get Redis key for processing set
444
+ */ getProcessingKey(queue) {
445
+ return `queue:${queue}:processing`;
446
+ }
447
+ /**
448
+ * Get Redis key for in-flight message storage
449
+ */ getInFlightKey(messageId) {
450
+ return `inflight:${messageId}`;
451
+ }
452
+ /**
453
+ * Store message in in-flight storage with TTL
454
+ */ async storeInFlight(message, visibilityTimeout) {
455
+ const key = this.getInFlightKey(message.id);
456
+ await withRetry(async ()=>{
457
+ await this.redis.set(key, JSON.stringify(message), {
458
+ PX: visibilityTimeout
459
+ });
460
+ }, {
461
+ maxAttempts: 3,
462
+ shouldRetry: isRetryableError
463
+ });
464
+ }
465
+ /**
466
+ * Get message from in-flight storage
467
+ */ async getInFlight(messageId) {
468
+ const key = this.getInFlightKey(messageId);
469
+ const data = await withRetry(async ()=>await this.redis.get(key), {
470
+ maxAttempts: 3,
471
+ shouldRetry: isRetryableError
472
+ });
473
+ if (!data) {
474
+ return null;
475
+ }
476
+ const message = JSON.parse(data);
477
+ // Convert date strings back to Date objects
478
+ message.createdAt = new Date(message.createdAt);
479
+ message.enqueuedAt = new Date(message.enqueuedAt);
480
+ if (message.dequeuedAt) {
481
+ message.dequeuedAt = new Date(message.dequeuedAt);
482
+ }
483
+ return message;
484
+ }
485
+ /**
486
+ * Remove message from in-flight storage
487
+ */ async removeInFlight(messageId) {
488
+ const key = this.getInFlightKey(messageId);
489
+ await withRetry(async ()=>await this.redis.del(key), {
490
+ maxAttempts: 3,
491
+ shouldRetry: isRetryableError
492
+ });
493
+ }
494
+ /**
495
+ * Update queue statistics
496
+ */ updateStats(queue, operation) {
497
+ let stats = this.stats.get(queue);
498
+ if (!stats) {
499
+ stats = {
500
+ enqueued: 0,
501
+ dequeued: 0,
502
+ acknowledged: 0,
503
+ rejected: 0,
504
+ startTime: new Date()
505
+ };
506
+ this.stats.set(queue, stats);
507
+ }
508
+ stats[operation]++;
509
+ }
510
+ }
511
+
512
+ //# sourceMappingURL=redis-queue-manager.js.map