crewly 1.20.203 → 1.20.205

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 (316) hide show
  1. package/config/skills/_common/lib.sh +4 -3
  2. package/config/skills/agent/core/project-tickets/SKILL.md +13 -1
  3. package/config/skills/agent/core/project-tickets/execute.sh +20 -6
  4. package/config/skills/agent/core/project-tickets/execute.test.sh +8 -2
  5. package/config/skills/agent/core/reply/SKILL.md +1 -0
  6. package/config/skills/agent/core/reply/execute.sh +7 -1
  7. package/config/skills/agent/core/reply-channel/SKILL.md +4 -0
  8. package/config/skills/agent/core/reply-channel/execute.sh +12 -1
  9. package/dist/backend/backend/src/constants.d.ts +124 -0
  10. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  11. package/dist/backend/backend/src/constants.js +130 -0
  12. package/dist/backend/backend/src/constants.js.map +1 -1
  13. package/dist/backend/backend/src/controllers/chat/agent-reply.controller.d.ts.map +1 -1
  14. package/dist/backend/backend/src/controllers/chat/agent-reply.controller.js +1 -0
  15. package/dist/backend/backend/src/controllers/chat/agent-reply.controller.js.map +1 -1
  16. package/dist/backend/backend/src/controllers/chat/chat.controller.d.ts.map +1 -1
  17. package/dist/backend/backend/src/controllers/chat/chat.controller.js +1 -0
  18. package/dist/backend/backend/src/controllers/chat/chat.controller.js.map +1 -1
  19. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.d.ts.map +1 -1
  20. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.js +50 -0
  21. package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.js.map +1 -1
  22. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.d.ts +6 -0
  23. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.d.ts.map +1 -1
  24. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js +2 -1
  25. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js.map +1 -1
  26. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.d.ts +1 -1
  27. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.d.ts.map +1 -1
  28. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.js +9 -1
  29. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.js.map +1 -1
  30. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.routes.d.ts +1 -1
  31. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.routes.js +1 -1
  32. package/dist/backend/backend/src/controllers/settings/role.controller.d.ts.map +1 -1
  33. package/dist/backend/backend/src/controllers/settings/role.controller.js +21 -7
  34. package/dist/backend/backend/src/controllers/settings/role.controller.js.map +1 -1
  35. package/dist/backend/backend/src/controllers/settings/settings.controller.d.ts.map +1 -1
  36. package/dist/backend/backend/src/controllers/settings/settings.controller.js +26 -20
  37. package/dist/backend/backend/src/controllers/settings/settings.controller.js.map +1 -1
  38. package/dist/backend/backend/src/controllers/skill/skill.controller.d.ts +13 -0
  39. package/dist/backend/backend/src/controllers/skill/skill.controller.d.ts.map +1 -1
  40. package/dist/backend/backend/src/controllers/skill/skill.controller.js +21 -0
  41. package/dist/backend/backend/src/controllers/skill/skill.controller.js.map +1 -1
  42. package/dist/backend/backend/src/controllers/system/system-control.controller.d.ts +11 -1
  43. package/dist/backend/backend/src/controllers/system/system-control.controller.d.ts.map +1 -1
  44. package/dist/backend/backend/src/controllers/system/system-control.controller.js +36 -2
  45. package/dist/backend/backend/src/controllers/system/system-control.controller.js.map +1 -1
  46. package/dist/backend/backend/src/index.d.ts +9 -0
  47. package/dist/backend/backend/src/index.d.ts.map +1 -1
  48. package/dist/backend/backend/src/index.js +37 -1
  49. package/dist/backend/backend/src/index.js.map +1 -1
  50. package/dist/backend/backend/src/middleware/agent-origin.middleware.d.ts +3 -2
  51. package/dist/backend/backend/src/middleware/agent-origin.middleware.d.ts.map +1 -1
  52. package/dist/backend/backend/src/middleware/agent-origin.middleware.js +5 -3
  53. package/dist/backend/backend/src/middleware/agent-origin.middleware.js.map +1 -1
  54. package/dist/backend/backend/src/middleware/caller-identity.middleware.d.ts +36 -0
  55. package/dist/backend/backend/src/middleware/caller-identity.middleware.d.ts.map +1 -1
  56. package/dist/backend/backend/src/middleware/caller-identity.middleware.js +97 -1
  57. package/dist/backend/backend/src/middleware/caller-identity.middleware.js.map +1 -1
  58. package/dist/backend/backend/src/routes/modules/terminal.routes.d.ts +4 -0
  59. package/dist/backend/backend/src/routes/modules/terminal.routes.d.ts.map +1 -1
  60. package/dist/backend/backend/src/routes/modules/terminal.routes.js +19 -5
  61. package/dist/backend/backend/src/routes/modules/terminal.routes.js.map +1 -1
  62. package/dist/backend/backend/src/scripts/input-guard-classify.d.ts +44 -0
  63. package/dist/backend/backend/src/scripts/input-guard-classify.d.ts.map +1 -0
  64. package/dist/backend/backend/src/scripts/input-guard-classify.js +116 -0
  65. package/dist/backend/backend/src/scripts/input-guard-classify.js.map +1 -0
  66. package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.d.ts +8 -0
  67. package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.d.ts.map +1 -1
  68. package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js +10 -0
  69. package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js.map +1 -1
  70. package/dist/backend/backend/src/services/agent/crewly-agent/in-process-runtime-registry.d.ts +8 -0
  71. package/dist/backend/backend/src/services/agent/crewly-agent/in-process-runtime-registry.d.ts.map +1 -1
  72. package/dist/backend/backend/src/services/agent/crewly-agent/in-process-runtime-registry.js +21 -0
  73. package/dist/backend/backend/src/services/agent/crewly-agent/in-process-runtime-registry.js.map +1 -1
  74. package/dist/backend/backend/src/services/agent/runtime-exit-monitor.service.d.ts.map +1 -1
  75. package/dist/backend/backend/src/services/agent/runtime-exit-monitor.service.js +13 -27
  76. package/dist/backend/backend/src/services/agent/runtime-exit-monitor.service.js.map +1 -1
  77. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts +117 -0
  78. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts.map +1 -1
  79. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js +165 -9
  80. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js.map +1 -1
  81. package/dist/backend/backend/src/services/chat-v2/room-context-backlog.d.ts +93 -0
  82. package/dist/backend/backend/src/services/chat-v2/room-context-backlog.d.ts.map +1 -0
  83. package/dist/backend/backend/src/services/chat-v2/room-context-backlog.js +138 -0
  84. package/dist/backend/backend/src/services/chat-v2/room-context-backlog.js.map +1 -0
  85. package/dist/backend/backend/src/services/decisions/decision-card.d.ts.map +1 -1
  86. package/dist/backend/backend/src/services/decisions/decision-card.js +5 -1
  87. package/dist/backend/backend/src/services/decisions/decision-card.js.map +1 -1
  88. package/dist/backend/backend/src/services/decisions/decision.service.d.ts +26 -1
  89. package/dist/backend/backend/src/services/decisions/decision.service.d.ts.map +1 -1
  90. package/dist/backend/backend/src/services/decisions/decision.service.js +83 -5
  91. package/dist/backend/backend/src/services/decisions/decision.service.js.map +1 -1
  92. package/dist/backend/backend/src/services/messaging/forced-delivery.d.ts +75 -0
  93. package/dist/backend/backend/src/services/messaging/forced-delivery.d.ts.map +1 -0
  94. package/dist/backend/backend/src/services/messaging/forced-delivery.js +87 -0
  95. package/dist/backend/backend/src/services/messaging/forced-delivery.js.map +1 -0
  96. package/dist/backend/backend/src/services/messaging/sub-agent-message-queue.service.d.ts +21 -0
  97. package/dist/backend/backend/src/services/messaging/sub-agent-message-queue.service.d.ts.map +1 -1
  98. package/dist/backend/backend/src/services/messaging/sub-agent-message-queue.service.js +48 -3
  99. package/dist/backend/backend/src/services/messaging/sub-agent-message-queue.service.js.map +1 -1
  100. package/dist/backend/backend/src/services/monitoring/liveness-monitor.service.d.ts +27 -1
  101. package/dist/backend/backend/src/services/monitoring/liveness-monitor.service.d.ts.map +1 -1
  102. package/dist/backend/backend/src/services/monitoring/liveness-monitor.service.js +51 -2
  103. package/dist/backend/backend/src/services/monitoring/liveness-monitor.service.js.map +1 -1
  104. package/dist/backend/backend/src/services/orc/orc-chat-owner-mirror.d.ts +51 -4
  105. package/dist/backend/backend/src/services/orc/orc-chat-owner-mirror.d.ts.map +1 -1
  106. package/dist/backend/backend/src/services/orc/orc-chat-owner-mirror.js +83 -6
  107. package/dist/backend/backend/src/services/orc/orc-chat-owner-mirror.js.map +1 -1
  108. package/dist/backend/backend/src/services/orc/reply-destination.wiring.d.ts +16 -0
  109. package/dist/backend/backend/src/services/orc/reply-destination.wiring.d.ts.map +1 -1
  110. package/dist/backend/backend/src/services/orc/reply-destination.wiring.js +22 -2
  111. package/dist/backend/backend/src/services/orc/reply-destination.wiring.js.map +1 -1
  112. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.d.ts +130 -4
  113. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.d.ts.map +1 -1
  114. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.js +115 -3
  115. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.js.map +1 -1
  116. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-goal.d.ts +122 -0
  117. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-goal.d.ts.map +1 -0
  118. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-goal.js +274 -0
  119. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-goal.js.map +1 -0
  120. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.d.ts +34 -2
  121. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.d.ts.map +1 -1
  122. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.js +59 -2
  123. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.js.map +1 -1
  124. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-retro.js +1 -1
  125. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-retro.js.map +1 -1
  126. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-stats.d.ts +6 -1
  127. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-stats.d.ts.map +1 -1
  128. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-stats.js +9 -1
  129. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-stats.js.map +1 -1
  130. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-trace.d.ts +13 -3
  131. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-trace.d.ts.map +1 -1
  132. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-trace.js +28 -2
  133. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-trace.js.map +1 -1
  134. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.d.ts +92 -7
  135. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.d.ts.map +1 -1
  136. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.js +316 -41
  137. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.js.map +1 -1
  138. package/dist/backend/backend/src/services/restart/in-flight-turn-tracker.service.d.ts +9 -0
  139. package/dist/backend/backend/src/services/restart/in-flight-turn-tracker.service.d.ts.map +1 -1
  140. package/dist/backend/backend/src/services/restart/in-flight-turn-tracker.service.js +11 -0
  141. package/dist/backend/backend/src/services/restart/in-flight-turn-tracker.service.js.map +1 -1
  142. package/dist/backend/backend/src/services/settings/api-key-access.service.d.ts +126 -0
  143. package/dist/backend/backend/src/services/settings/api-key-access.service.d.ts.map +1 -0
  144. package/dist/backend/backend/src/services/settings/api-key-access.service.js +223 -0
  145. package/dist/backend/backend/src/services/settings/api-key-access.service.js.map +1 -0
  146. package/dist/backend/backend/src/services/skill/skill-executor.service.d.ts.map +1 -1
  147. package/dist/backend/backend/src/services/skill/skill-executor.service.js +17 -0
  148. package/dist/backend/backend/src/services/skill/skill-executor.service.js.map +1 -1
  149. package/dist/backend/backend/src/services/slack/room-responder.d.ts +144 -0
  150. package/dist/backend/backend/src/services/slack/room-responder.d.ts.map +1 -0
  151. package/dist/backend/backend/src/services/slack/room-responder.js +236 -0
  152. package/dist/backend/backend/src/services/slack/room-responder.js.map +1 -0
  153. package/dist/backend/backend/src/services/slack/slack-initializer.d.ts.map +1 -1
  154. package/dist/backend/backend/src/services/slack/slack-initializer.js +36 -2
  155. package/dist/backend/backend/src/services/slack/slack-initializer.js.map +1 -1
  156. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  157. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +29 -8
  158. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  159. package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts +107 -2
  160. package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
  161. package/dist/backend/backend/src/services/slack/slack-team-channel.service.js +403 -13
  162. package/dist/backend/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
  163. package/dist/backend/backend/src/services/slack/slack-thread-context.service.d.ts +27 -0
  164. package/dist/backend/backend/src/services/slack/slack-thread-context.service.d.ts.map +1 -1
  165. package/dist/backend/backend/src/services/slack/slack-thread-context.service.js +52 -0
  166. package/dist/backend/backend/src/services/slack/slack-thread-context.service.js.map +1 -1
  167. package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.d.ts +9 -0
  168. package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.d.ts.map +1 -1
  169. package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.js +20 -0
  170. package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.js.map +1 -1
  171. package/dist/backend/backend/src/services/system/auto-update.service.d.ts +24 -0
  172. package/dist/backend/backend/src/services/system/auto-update.service.d.ts.map +1 -1
  173. package/dist/backend/backend/src/services/system/auto-update.service.js +78 -8
  174. package/dist/backend/backend/src/services/system/auto-update.service.js.map +1 -1
  175. package/dist/backend/backend/src/services/system/auto-update.utils.d.ts +1 -1
  176. package/dist/backend/backend/src/services/system/auto-update.utils.d.ts.map +1 -1
  177. package/dist/backend/backend/src/services/system/auto-update.utils.js.map +1 -1
  178. package/dist/backend/backend/src/services/system/input-guard-release-check.d.ts +162 -0
  179. package/dist/backend/backend/src/services/system/input-guard-release-check.d.ts.map +1 -0
  180. package/dist/backend/backend/src/services/system/input-guard-release-check.js +264 -0
  181. package/dist/backend/backend/src/services/system/input-guard-release-check.js.map +1 -0
  182. package/dist/backend/backend/src/services/system/system-control.service.d.ts +36 -0
  183. package/dist/backend/backend/src/services/system/system-control.service.d.ts.map +1 -1
  184. package/dist/backend/backend/src/services/system/system-control.service.js +109 -10
  185. package/dist/backend/backend/src/services/system/system-control.service.js.map +1 -1
  186. package/dist/backend/backend/src/services/v3/ticket-channel-hooks.d.ts +13 -1
  187. package/dist/backend/backend/src/services/v3/ticket-channel-hooks.d.ts.map +1 -1
  188. package/dist/backend/backend/src/services/v3/ticket-channel-hooks.js +36 -3
  189. package/dist/backend/backend/src/services/v3/ticket-channel-hooks.js.map +1 -1
  190. package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.d.ts +54 -3
  191. package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.d.ts.map +1 -1
  192. package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.js +117 -9
  193. package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.js.map +1 -1
  194. package/dist/backend/backend/src/types/decision.types.d.ts +7 -0
  195. package/dist/backend/backend/src/types/decision.types.d.ts.map +1 -1
  196. package/dist/backend/backend/src/types/skill.types.d.ts +16 -0
  197. package/dist/backend/backend/src/types/skill.types.d.ts.map +1 -1
  198. package/dist/backend/backend/src/types/skill.types.js.map +1 -1
  199. package/dist/backend/backend/src/types/ticket-autopilot.types.d.ts +21 -0
  200. package/dist/backend/backend/src/types/ticket-autopilot.types.d.ts.map +1 -1
  201. package/dist/backend/backend/src/types/ticket-autopilot.types.js +48 -0
  202. package/dist/backend/backend/src/types/ticket-autopilot.types.js.map +1 -1
  203. package/dist/backend/backend/src/types/v2/work-item.types.d.ts +1 -1
  204. package/dist/backend/backend/src/types/v2/work-item.types.d.ts.map +1 -1
  205. package/dist/backend/backend/src/types/v2/work-item.types.js +1 -0
  206. package/dist/backend/backend/src/types/v2/work-item.types.js.map +1 -1
  207. package/dist/backend/build-info.json +2 -2
  208. package/dist/cli/backend/src/constants.d.ts +124 -0
  209. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  210. package/dist/cli/backend/src/constants.js +130 -0
  211. package/dist/cli/backend/src/constants.js.map +1 -1
  212. package/dist/cli/backend/src/scripts/input-guard-classify.d.ts +44 -0
  213. package/dist/cli/backend/src/scripts/input-guard-classify.d.ts.map +1 -0
  214. package/dist/cli/backend/src/scripts/input-guard-classify.js +116 -0
  215. package/dist/cli/backend/src/scripts/input-guard-classify.js.map +1 -0
  216. package/dist/cli/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.d.ts +8 -0
  217. package/dist/cli/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.d.ts.map +1 -1
  218. package/dist/cli/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js +10 -0
  219. package/dist/cli/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js.map +1 -1
  220. package/dist/cli/backend/src/services/agent/crewly-agent/in-process-runtime-registry.d.ts +8 -0
  221. package/dist/cli/backend/src/services/agent/crewly-agent/in-process-runtime-registry.d.ts.map +1 -1
  222. package/dist/cli/backend/src/services/agent/crewly-agent/in-process-runtime-registry.js +21 -0
  223. package/dist/cli/backend/src/services/agent/crewly-agent/in-process-runtime-registry.js.map +1 -1
  224. package/dist/cli/backend/src/services/agent/runtime-exit-monitor.service.d.ts.map +1 -1
  225. package/dist/cli/backend/src/services/agent/runtime-exit-monitor.service.js +13 -27
  226. package/dist/cli/backend/src/services/agent/runtime-exit-monitor.service.js.map +1 -1
  227. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts +117 -0
  228. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts.map +1 -1
  229. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.js +165 -9
  230. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.js.map +1 -1
  231. package/dist/cli/backend/src/services/chat-v2/room-context-backlog.d.ts +93 -0
  232. package/dist/cli/backend/src/services/chat-v2/room-context-backlog.d.ts.map +1 -0
  233. package/dist/cli/backend/src/services/chat-v2/room-context-backlog.js +138 -0
  234. package/dist/cli/backend/src/services/chat-v2/room-context-backlog.js.map +1 -0
  235. package/dist/cli/backend/src/services/decisions/decision-card.d.ts.map +1 -1
  236. package/dist/cli/backend/src/services/decisions/decision-card.js +5 -1
  237. package/dist/cli/backend/src/services/decisions/decision-card.js.map +1 -1
  238. package/dist/cli/backend/src/services/decisions/decision.service.d.ts +26 -1
  239. package/dist/cli/backend/src/services/decisions/decision.service.d.ts.map +1 -1
  240. package/dist/cli/backend/src/services/decisions/decision.service.js +83 -5
  241. package/dist/cli/backend/src/services/decisions/decision.service.js.map +1 -1
  242. package/dist/cli/backend/src/services/messaging/drain-queued-delivery.d.ts +59 -0
  243. package/dist/cli/backend/src/services/messaging/drain-queued-delivery.d.ts.map +1 -0
  244. package/dist/cli/backend/src/services/messaging/drain-queued-delivery.js +70 -0
  245. package/dist/cli/backend/src/services/messaging/drain-queued-delivery.js.map +1 -0
  246. package/dist/cli/backend/src/services/messaging/forced-delivery.d.ts +75 -0
  247. package/dist/cli/backend/src/services/messaging/forced-delivery.d.ts.map +1 -0
  248. package/dist/cli/backend/src/services/messaging/forced-delivery.js +87 -0
  249. package/dist/cli/backend/src/services/messaging/forced-delivery.js.map +1 -0
  250. package/dist/cli/backend/src/services/messaging/spend-capped-delivery.d.ts +54 -0
  251. package/dist/cli/backend/src/services/messaging/spend-capped-delivery.d.ts.map +1 -0
  252. package/dist/cli/backend/src/services/messaging/spend-capped-delivery.js +65 -0
  253. package/dist/cli/backend/src/services/messaging/spend-capped-delivery.js.map +1 -0
  254. package/dist/cli/backend/src/services/messaging/sub-agent-message-queue.service.d.ts +21 -0
  255. package/dist/cli/backend/src/services/messaging/sub-agent-message-queue.service.d.ts.map +1 -1
  256. package/dist/cli/backend/src/services/messaging/sub-agent-message-queue.service.js +48 -3
  257. package/dist/cli/backend/src/services/messaging/sub-agent-message-queue.service.js.map +1 -1
  258. package/dist/cli/backend/src/services/restart/in-flight-turn-tracker.service.d.ts +9 -0
  259. package/dist/cli/backend/src/services/restart/in-flight-turn-tracker.service.d.ts.map +1 -1
  260. package/dist/cli/backend/src/services/restart/in-flight-turn-tracker.service.js +11 -0
  261. package/dist/cli/backend/src/services/restart/in-flight-turn-tracker.service.js.map +1 -1
  262. package/dist/cli/backend/src/services/skill/skill-executor.service.d.ts.map +1 -1
  263. package/dist/cli/backend/src/services/skill/skill-executor.service.js +17 -0
  264. package/dist/cli/backend/src/services/skill/skill-executor.service.js.map +1 -1
  265. package/dist/cli/backend/src/services/slack/room-responder.d.ts +144 -0
  266. package/dist/cli/backend/src/services/slack/room-responder.d.ts.map +1 -0
  267. package/dist/cli/backend/src/services/slack/room-responder.js +236 -0
  268. package/dist/cli/backend/src/services/slack/room-responder.js.map +1 -0
  269. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  270. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js +29 -8
  271. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  272. package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts +107 -2
  273. package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
  274. package/dist/cli/backend/src/services/slack/slack-team-channel.service.js +403 -13
  275. package/dist/cli/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
  276. package/dist/cli/backend/src/services/slack/slack-thread-context.service.d.ts +27 -0
  277. package/dist/cli/backend/src/services/slack/slack-thread-context.service.d.ts.map +1 -1
  278. package/dist/cli/backend/src/services/slack/slack-thread-context.service.js +52 -0
  279. package/dist/cli/backend/src/services/slack/slack-thread-context.service.js.map +1 -1
  280. package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.d.ts +9 -0
  281. package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.d.ts.map +1 -1
  282. package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.js +20 -0
  283. package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.js.map +1 -1
  284. package/dist/cli/backend/src/services/system/auto-update.utils.d.ts +1 -1
  285. package/dist/cli/backend/src/services/system/auto-update.utils.d.ts.map +1 -1
  286. package/dist/cli/backend/src/services/system/auto-update.utils.js.map +1 -1
  287. package/dist/cli/backend/src/services/system/input-guard-release-check.d.ts +162 -0
  288. package/dist/cli/backend/src/services/system/input-guard-release-check.d.ts.map +1 -0
  289. package/dist/cli/backend/src/services/system/input-guard-release-check.js +264 -0
  290. package/dist/cli/backend/src/services/system/input-guard-release-check.js.map +1 -0
  291. package/dist/cli/backend/src/services/v3/ticket-channel-hooks.d.ts +13 -1
  292. package/dist/cli/backend/src/services/v3/ticket-channel-hooks.d.ts.map +1 -1
  293. package/dist/cli/backend/src/services/v3/ticket-channel-hooks.js +36 -3
  294. package/dist/cli/backend/src/services/v3/ticket-channel-hooks.js.map +1 -1
  295. package/dist/cli/backend/src/types/decision.types.d.ts +7 -0
  296. package/dist/cli/backend/src/types/decision.types.d.ts.map +1 -1
  297. package/dist/cli/backend/src/types/skill.types.d.ts +16 -0
  298. package/dist/cli/backend/src/types/skill.types.d.ts.map +1 -1
  299. package/dist/cli/backend/src/types/skill.types.js.map +1 -1
  300. package/dist/cli/backend/src/types/ticket-autopilot.types.d.ts +21 -0
  301. package/dist/cli/backend/src/types/ticket-autopilot.types.d.ts.map +1 -1
  302. package/dist/cli/backend/src/types/ticket-autopilot.types.js +48 -0
  303. package/dist/cli/backend/src/types/ticket-autopilot.types.js.map +1 -1
  304. package/dist/cli/backend/src/types/v2/work-item.types.d.ts +1 -1
  305. package/dist/cli/backend/src/types/v2/work-item.types.d.ts.map +1 -1
  306. package/dist/cli/backend/src/types/v2/work-item.types.js +1 -0
  307. package/dist/cli/backend/src/types/v2/work-item.types.js.map +1 -1
  308. package/dist/cli/cli/src/commands/doctor.d.ts +20 -0
  309. package/dist/cli/cli/src/commands/doctor.d.ts.map +1 -1
  310. package/dist/cli/cli/src/commands/doctor.js +49 -0
  311. package/dist/cli/cli/src/commands/doctor.js.map +1 -1
  312. package/dist/cli/cli/src/index.js +9 -2
  313. package/dist/cli/cli/src/index.js.map +1 -1
  314. package/frontend/dist/assets/{index-63a33dbf.js → index-f64b333a.js} +258 -258
  315. package/frontend/dist/index.html +1 -1
  316. package/package.json +2 -1
@@ -10,6 +10,10 @@
10
10
  * - brakes hold: a daily USD budget over the team's agents (pauses the
11
11
  * autopilot and ticket auto-claim for the day, owner told once) and an
12
12
  * in-progress cap per member ({@link ProjectTicketAutopilotPolicy});
13
+ * - when the project has a goal but nothing is left to triage and someone is
14
+ * idle, the driver is woken with ONE `goal_replan` WorkItem to open the
15
+ * next tickets toward the goal (at most `replansPerDay` a day, same budget
16
+ * and in-progress brakes; specs/2026-10-04-autopilot-goal-replan.md);
13
17
  * - owner questions are decision cards (specs/2026-10-01-decision-cards.md),
14
18
  * posted by the asking agent in the ticket's thread; the owner gets one
15
19
  * evening digest (skipped when nothing changed) that links to open cards.
@@ -28,9 +32,9 @@ import { applyTicketAutopilotInput, legacyBudgetTokens, resolveTicketAutopilotSe
28
32
  import { ProjectTicketError } from './project-ticket.service.js';
29
33
  import { getTeamLeads } from '../../utils/team.utils.js';
30
34
  import { isTeamLead, } from './project-ticket-workflow.service.js';
31
- import { OPEN_TICKET_STATUSES, decideDigest, decideTriage, hasNeedsOwnerLabel, inFlightByAssignee, isMemberIdle, localDateKey, memberAvailability, memberResponsibility, localMidnight, selectTriageCandidates, } from './ticket-autopilot-decision.js';
32
- import { buildBudgetPausedMessage, buildDigestMessage, buildTriageBrief, } from './ticket-autopilot-messages.js';
33
- import { autopilotRunTrace, startTriageTrace, traceAutopilotAction, createAutopilotTicketListener } from './ticket-autopilot-trace.js';
35
+ import { OPEN_TICKET_STATUSES, closedTicketsSince, decideDigest, decideReplan, decideTriage, nextReplanBackoff, replanBackoffState, hasNeedsOwnerLabel, inFlightByAssignee, isMemberIdle, localDateKey, memberAvailability, memberResponsibility, localMidnight, selectTriageCandidates, } from './ticket-autopilot-decision.js';
36
+ import { buildBudgetPausedMessage, buildDigestMessage, buildReplanBrief, buildTriageBrief, REPLAN_ASK, } from './ticket-autopilot-messages.js';
37
+ import { autopilotRunTrace, startReplanTrace, startTriageTrace, traceAutopilotAction, createAutopilotTicketListener } from './ticket-autopilot-trace.js';
34
38
  import { computeAutopilotStats, dayEndMs, daysBetween, dayStartMs, rangeDays, } from './ticket-autopilot-stats.js';
35
39
  import { buildRetroBrief, duplicateOf, renderRetroMarkdown, RetroInputError, validateRetroInput, } from './ticket-autopilot-retro.js';
36
40
  import { getTraceStore } from '../trace/trace-store.js';
@@ -40,6 +44,7 @@ const SKIP_WORDS = {
40
44
  no_driver: 'no team lead to drive it',
41
45
  budget_reached: 'daily budget reached',
42
46
  triage_in_flight: 'a triage is still open',
47
+ replan_in_flight: 'a goal replan is still open',
43
48
  nothing_to_triage: 'nothing to triage',
44
49
  nobody_idle: 'nobody on the team is idle',
45
50
  too_soon: 'too soon after the last triage',
@@ -283,7 +288,8 @@ export class TicketAutopilotService {
283
288
  await this.noticeBudgetPaused(project, ps, spent, budget, now, teams[0]?.name);
284
289
  this.traceBudgetState(project, ps, settings.enabled && spent >= budget, spent, budget, now);
285
290
  const { tickets } = await this.deps.tickets.list(project.path);
286
- const live = await this.liveTriageItem(project, nowMs);
291
+ const live = await this.liveItem(project, nowMs, TICKET_AUTOPILOT_CONSTANTS.TRIAGE_METADATA_KIND);
292
+ const liveReplan = await this.liveItem(project, nowMs, TICKET_AUTOPILOT_CONSTANTS.REPLAN_METADATA_KIND, settings.replanTtlHours);
287
293
  const selection = selectTriageCandidates({ tickets, teams, now: nowMs, listed: ps.listed });
288
294
  const members = teams.flatMap((t) => t.members ?? []);
289
295
  const anyoneIdle = (!!idleSession && members.some((m) => sessionOf(m) === idleSession || m.sessionName === idleSession)) || members.some((m) => isMemberIdle(m));
@@ -294,45 +300,46 @@ export class TicketAutopilotService {
294
300
  now: nowMs,
295
301
  candidateCount: selection.candidates.length,
296
302
  liveTriage: !!live,
303
+ ...(liveReplan ? { liveReplanAt: Date.parse(liveReplan.createdAt) || nowMs } : {}),
297
304
  lastTriageAt: ps.lastTriageAt,
298
305
  anyoneIdle,
299
306
  usedTodayTokens: spent,
300
307
  dailyBudgetTokens: budget,
301
308
  });
302
309
  if (decision.action === 'skip' || !driver) {
310
+ // Nothing to triage: a project with a goal may wake its driver to plan
311
+ // the next tickets instead (specs/2026-10-04-autopilot-goal-replan.md).
312
+ if (decision.action === 'skip' && decision.reason === 'nothing_to_triage' && driver) {
313
+ const replan = await this.evaluateReplan(project, {
314
+ settings,
315
+ ps,
316
+ driver,
317
+ trigger,
318
+ now,
319
+ tickets,
320
+ teams,
321
+ idleSession,
322
+ spent,
323
+ budget,
324
+ liveTriage: !!live,
325
+ liveReplanId: liveReplan?.id ?? null,
326
+ candidateCount: selection.candidates.length,
327
+ anyoneIdle,
328
+ });
329
+ if (replan?.workItem)
330
+ return { projectId: project.id, decision, replan: replan.decision, workItem: replan.workItem };
331
+ this.logger.debug('Ticket autopilot: no triage', { projectId: project.id, trigger, decision, replan: replan?.decision });
332
+ this.traceSkip(project, ps, decision.reason, trigger, selection.candidates.length, now);
333
+ await this.saveState();
334
+ return { projectId: project.id, decision, ...(replan ? { replan: replan.decision } : {}) };
335
+ }
303
336
  this.logger.debug('Ticket autopilot: no triage', { projectId: project.id, trigger, decision });
304
337
  if (decision.action === 'skip')
305
338
  this.traceSkip(project, ps, decision.reason, trigger, selection.candidates.length, now);
306
339
  await this.saveState();
307
340
  return { projectId: project.id, decision };
308
341
  }
309
- const inFlight = inFlightByAssignee(tickets);
310
- const seen = new Set();
311
- const roster = teams.flatMap((t) => (t.members ?? [])
312
- .filter((m) => {
313
- const s = sessionOf(m);
314
- if (!s || seen.has(s))
315
- return false;
316
- seen.add(s);
317
- return true;
318
- })
319
- .map((m) => ({ team: t, m })));
320
- const roleLines = await this.roleDescriptions(roster.map(({ m }) => m.role));
321
- const briefMembers = roster.map(({ team: t, m }) => {
322
- const session = sessionOf(m);
323
- // Stopped (idle-stopped, suspended) is "available — started when
324
- // assigned", never "busy"; its in-progress count is its real one.
325
- const availability = session === idleSession && memberAvailability(m) !== 'stopped' ? 'idle' : memberAvailability(m);
326
- return {
327
- session,
328
- name: m.name,
329
- role: m.role,
330
- lead: isTeamLead(t, m),
331
- availability,
332
- responsibility: memberResponsibility(m, roleLines.get(String(m.role ?? ''))),
333
- inFlight: inFlight.get(session) ?? 0,
334
- };
335
- });
342
+ const briefMembers = await this.briefMembers(teams, tickets, idleSession);
336
343
  const brief = buildTriageBrief({
337
344
  project: { id: project.id, name: project.name },
338
345
  candidates: selection.candidates,
@@ -390,22 +397,278 @@ export class TicketAutopilotService {
390
397
  return { projectId: project.id, decision, workItem };
391
398
  }
392
399
  /**
393
- * The project's live triage item, if any. A triage item still queued
394
- * (never picked up) after {@link TICKET_AUTOPILOT_CONSTANTS.TRIAGE_STALE_QUEUED_MS}
395
- * is cancelled so a fresh one can replace it.
400
+ * Evaluate a goal replan for a project with nothing to triage and, when
401
+ * {@link decideReplan} says so, wake the driver with ONE `goal_replan`
402
+ * WorkItem: the goal, the tickets closed lately, open experiment cards and
403
+ * the ask. The driver opens the tickets; the autopilot never makes them
404
+ * ready or starts work.
405
+ *
406
+ * @param project - Project
407
+ * @param c - The evaluation's state
408
+ * @returns The decision and the WorkItem created, or null when the project has no goal (nothing changes)
409
+ */
410
+ async evaluateReplan(project, c) {
411
+ const C = TICKET_AUTOPILOT_CONSTANTS;
412
+ // No goal reader: never replans; the evaluation is as before.
413
+ if (!this.deps.goalOf)
414
+ return null;
415
+ const nowMs = c.now.getTime();
416
+ const today = localDateKey(c.now);
417
+ const inFlight = inFlightByAssignee(c.tickets);
418
+ const members = c.teams.flatMap((t) => t.members ?? []);
419
+ const isIdle = (m) => isMemberIdle(m) || (!!c.idleSession && (sessionOf(m) === c.idleSession || m.sessionName === c.idleSession));
420
+ const held = (m) => Math.max(inFlight.get(sessionOf(m)) ?? 0, m.sessionName ? inFlight.get(m.sessionName) ?? 0 : 0, m.agentId ? inFlight.get(m.agentId) ?? 0 : 0);
421
+ const idleWithRoom = members.some((m) => isIdle(m) && held(m) < c.settings.maxInFlightPerMember);
422
+ const replansToday = c.ps.replans?.day === today ? c.ps.replans.count : 0;
423
+ // The last replan is over: did it open tickets? None → back off longer.
424
+ this.assessLastReplan(project, c.ps, c.tickets, c.liveReplanId, nowMs);
425
+ // Cheap gates first (in-memory state only); the goal is read last.
426
+ const gates = {
427
+ enabled: c.settings.enabled,
428
+ driver: c.driver.session,
429
+ maxReplansPerDay: c.settings.replansPerDay,
430
+ replansToday,
431
+ usedTodayTokens: c.spent,
432
+ dailyBudgetTokens: c.budget,
433
+ liveTriage: c.liveTriage,
434
+ liveReplan: !!c.liveReplanId,
435
+ candidateCount: c.candidateCount,
436
+ anyoneIdle: c.anyoneIdle,
437
+ idleWithRoom,
438
+ };
439
+ const skip = (d) => {
440
+ this.logger.debug('Ticket autopilot: no goal replan', { projectId: project.id, trigger: c.trigger, decision: d });
441
+ return { decision: d };
442
+ };
443
+ const pre = decideReplan({ ...gates, backedOff: false, hasGoal: true });
444
+ if (pre.action === 'skip')
445
+ return skip(pre);
446
+ // Backoff after empty replans: lifted by a new ticket or a goal / OKR
447
+ // change (file times only).
448
+ let backedOff = false;
449
+ if (c.ps.replanBackoff) {
450
+ const changedAt = this.deps.goalChangedAt ? await this.deps.goalChangedAt(project).catch(() => null) : null;
451
+ const state = replanBackoffState(c.ps.replanBackoff, { today, tickets: c.tickets, goalChangedAt: changedAt });
452
+ if (state === 'lifted') {
453
+ this.logger.info('Goal replan backoff lifted (new ticket or goal change)', { projectId: project.id, streak: c.ps.replanBackoff.streak });
454
+ delete c.ps.replanBackoff;
455
+ await this.saveState();
456
+ }
457
+ backedOff = state === 'holds';
458
+ }
459
+ if (backedOff)
460
+ return skip(decideReplan({ ...gates, backedOff, hasGoal: true }));
461
+ const goal = await this.deps.goalOf(project, c.now).catch((err) => {
462
+ this.logger.warn('Could not read the project goal (no replan)', { projectId: project.id, error: err instanceof Error ? err.message : String(err) });
463
+ return null;
464
+ });
465
+ // No goal: the evaluation is what it was before goal replans.
466
+ if (!goal || !goal.text.trim())
467
+ return null;
468
+ const decision = decideReplan({ ...gates, backedOff, hasGoal: true });
469
+ if (decision.action === 'skip')
470
+ return skip(decision);
471
+ const closed = closedTicketsSince(c.tickets, nowMs - C.REPLAN_CLOSED_LOOKBACK_DAYS * 24 * 60 * 60 * 1000, C.REPLAN_MAX_CLOSED_TICKETS);
472
+ const experiments = this.deps.openExperiments ? await this.deps.openExperiments(project).catch(() => []) : [];
473
+ const brief = buildReplanBrief({
474
+ project: { id: project.id, name: project.name },
475
+ goal: goal.text,
476
+ closed,
477
+ lookbackDays: C.REPLAN_CLOSED_LOOKBACK_DAYS,
478
+ experiments,
479
+ members: await this.briefMembers(c.teams, c.tickets, c.idleSession),
480
+ maxInFlightPerMember: c.settings.maxInFlightPerMember,
481
+ now: nowMs,
482
+ });
483
+ const workItem = createWorkItem({
484
+ type: C.REPLAN_WORK_ITEM_TYPE,
485
+ owner: 'team_lead',
486
+ target: c.driver.session,
487
+ title: `Plan next tickets toward the goal: ${project.name}`,
488
+ description: `Nothing is left to triage on ${project.name}. ${REPLAN_ASK}`,
489
+ briefMarkdown: capBrief(brief),
490
+ metadata: {
491
+ kind: C.REPLAN_METADATA_KIND,
492
+ projectId: project.id,
493
+ projectPath: project.path,
494
+ teamId: c.driver.teamId,
495
+ requiresVerification: false,
496
+ trigger: c.trigger,
497
+ goalSources: goal.sources,
498
+ closedTicketIds: closed.map((t) => t.id),
499
+ experimentIds: experiments.map((e) => e.id),
500
+ },
501
+ });
502
+ workItem.createdAt = c.now.toISOString();
503
+ workItem.targetSource = 'assigned';
504
+ const replanTrace = startReplanTrace(project, c.now);
505
+ if (replanTrace)
506
+ workItem.traceId = replanTrace;
507
+ await this.deps.pool.addToPool(workItem);
508
+ const count = replansToday + 1;
509
+ c.ps.replans = { day: today, count, lastAt: nowMs, lastWorkItemId: workItem.id, assessed: false };
510
+ // A replan is autopilot work: it starts the day's run trace. Not an owner touch.
511
+ traceAutopilotAction(project, 'replan', {
512
+ summary: `${project.name}: ${c.driver.session} woken to plan the next tickets toward the goal (nothing left to triage)`,
513
+ outcome: 'queued',
514
+ workItemId: workItem.id,
515
+ session: c.driver.session,
516
+ data: {
517
+ trigger: c.trigger,
518
+ goalSources: goal.sources.join(','),
519
+ closed: closed.length,
520
+ experiments: experiments.length,
521
+ replansToday: count,
522
+ replansPerDay: c.settings.replansPerDay,
523
+ },
524
+ alsoTraceId: workItem.traceId ?? null,
525
+ now: c.now,
526
+ });
527
+ await this.saveState();
528
+ this.logger.info('Ticket autopilot woke the driver to plan toward the goal', {
529
+ projectId: project.id,
530
+ driver: c.driver.session,
531
+ trigger: c.trigger,
532
+ workItemId: workItem.id,
533
+ replansToday: count,
534
+ });
535
+ return { decision, workItem };
536
+ }
537
+ /**
538
+ * The team section of a brief: every member once, with availability,
539
+ * role line and in-progress count.
540
+ *
541
+ * @param teams - Project teams
542
+ * @param tickets - Project tickets (in-progress counts)
543
+ * @param idleSession - The member that just went idle
544
+ * @returns Brief members
545
+ */
546
+ async briefMembers(teams, tickets, idleSession) {
547
+ const inFlight = inFlightByAssignee(tickets);
548
+ const seen = new Set();
549
+ const roster = teams.flatMap((t) => (t.members ?? [])
550
+ .filter((m) => {
551
+ const s = sessionOf(m);
552
+ if (!s || seen.has(s))
553
+ return false;
554
+ seen.add(s);
555
+ return true;
556
+ })
557
+ .map((m) => ({ team: t, m })));
558
+ const roleLines = await this.roleDescriptions(roster.map(({ m }) => m.role));
559
+ return roster.map(({ team: t, m }) => {
560
+ const session = sessionOf(m);
561
+ // Stopped (idle-stopped, suspended) is "available — started when
562
+ // assigned", never "busy"; its in-progress count is its real one.
563
+ const availability = session === idleSession && memberAvailability(m) !== 'stopped' ? 'idle' : memberAvailability(m);
564
+ return {
565
+ session,
566
+ name: m.name,
567
+ role: m.role,
568
+ lead: isTeamLead(t, m),
569
+ availability,
570
+ responsibility: memberResponsibility(m, roleLines.get(String(m.role ?? ''))),
571
+ inFlight: inFlight.get(session) ?? 0,
572
+ };
573
+ });
574
+ }
575
+ /**
576
+ * Record the outcome of the last goal replan once it is no longer live:
577
+ * a ticket created since it was queued clears the backoff; none ("there
578
+ * are none") backs off one step longer (2, 4, 7 days).
579
+ *
580
+ * @param project - Project
581
+ * @param ps - Its bookkeeping
582
+ * @param tickets - Its tickets
583
+ * @param liveReplanId - The live replan, if any
584
+ * @param nowMs - Clock
585
+ */
586
+ assessLastReplan(project, ps, tickets, liveReplanId, nowMs) {
587
+ const last = ps.replans;
588
+ if (!last?.lastWorkItemId || last.lastAt === undefined || last.assessed !== false)
589
+ return;
590
+ if (liveReplanId === last.lastWorkItemId)
591
+ return;
592
+ const next = nextReplanBackoff({ replanAt: last.lastAt, replanDay: last.day, tickets, previous: ps.replanBackoff, now: nowMs });
593
+ if (next)
594
+ ps.replanBackoff = next;
595
+ else
596
+ delete ps.replanBackoff;
597
+ last.assessed = true;
598
+ this.logger.info(next ? 'Goal replan opened no tickets: backing off' : 'Goal replan opened tickets', {
599
+ projectId: project.id,
600
+ workItemId: last.lastWorkItemId,
601
+ ...(next ? { streak: next.streak, resumeDay: next.resumeDay } : {}),
602
+ });
603
+ }
604
+ /**
605
+ * Expire a goal replan past its TTL so it cannot hold triage forever:
606
+ * cancelled where the pool allows it (queued / blocked / scheduled,
607
+ * running, proposed / accepted / escalated). One the driver already
608
+ * finished (`done_by_worker`) waits on a verdict the autopilot cannot
609
+ * give; it is only no longer counted as live.
610
+ *
611
+ * @param project - Project
612
+ * @param wi - The replan
613
+ * @param ttlHours - Its TTL
614
+ */
615
+ async expireReplan(project, wi, ttlHours) {
616
+ const reason = `goal replan still open after ${ttlHours}h; expired by the ticket autopilot`;
617
+ const pool = this.deps.pool;
618
+ try {
619
+ if (wi.status === 'queued' || wi.status === 'blocked' || wi.status === 'scheduled') {
620
+ await pool.cancelQueued(wi.id, reason);
621
+ }
622
+ else if (wi.status === 'running') {
623
+ await pool.releaseClaim?.(wi.id, reason);
624
+ await pool.transitionStatus?.(wi.id, 'cancelled', 'system', undefined, reason);
625
+ }
626
+ else if (wi.status === 'proposed' || wi.status === 'accepted' || wi.status === 'escalated') {
627
+ await pool.transitionStatus?.(wi.id, 'cancelled', 'system', undefined, reason);
628
+ }
629
+ else {
630
+ this.logger.debug('Expired goal replan left as is (not cancellable from its status)', { projectId: project.id, workItemId: wi.id, status: wi.status });
631
+ return;
632
+ }
633
+ this.logger.info('Goal replan expired', { projectId: project.id, workItemId: wi.id, status: wi.status, ttlHours });
634
+ }
635
+ catch (err) {
636
+ // Still expired: it no longer holds triage even if the pool refused.
637
+ this.logger.warn('Could not cancel an expired goal replan (no longer counted as live)', {
638
+ projectId: project.id,
639
+ workItemId: wi.id,
640
+ error: err instanceof Error ? err.message : String(err),
641
+ });
642
+ }
643
+ }
644
+ /**
645
+ * The project's live autopilot item of a kind (triage or goal replan), if
646
+ * any. A triage still queued (never picked up) after
647
+ * {@link TICKET_AUTOPILOT_CONSTANTS.TRIAGE_STALE_QUEUED_MS} is cancelled so
648
+ * a fresh one can replace it. A replan older than its TTL in any live
649
+ * status is expired ({@link expireReplan}) and not returned.
396
650
  *
397
651
  * @param project - Project
398
652
  * @param nowMs - Clock
653
+ * @param kind - `metadata.kind` (triage or replan)
654
+ * @param replanTtlHours - Replan TTL (replan kind only)
399
655
  * @returns The live item, or null
400
656
  */
401
- async liveTriageItem(project, nowMs) {
402
- const items = (await this.deps.pool.getAllItems()).filter((wi) => wi.metadata?.kind === TICKET_AUTOPILOT_CONSTANTS.TRIAGE_METADATA_KIND && wi.metadata?.projectId === project.id && LIVE_STATUSES.has(wi.status));
657
+ async liveItem(project, nowMs, kind, replanTtlHours) {
658
+ const items = (await this.deps.pool.getAllItems()).filter((wi) => wi.metadata?.kind === kind && wi.metadata?.projectId === project.id && LIVE_STATUSES.has(wi.status));
659
+ const what = kind === TICKET_AUTOPILOT_CONSTANTS.REPLAN_METADATA_KIND ? 'goal replan' : 'ticket triage';
403
660
  let live = null;
404
661
  for (const wi of items) {
405
662
  const age = nowMs - (Date.parse(wi.createdAt) || nowMs);
406
- if (wi.status === 'queued' && age >= TICKET_AUTOPILOT_CONSTANTS.TRIAGE_STALE_QUEUED_MS) {
407
- await this.deps.pool.cancelQueued(wi.id, 'ticket triage never picked up; replaced by a fresh one').catch(() => undefined);
408
- this.logger.info('Stale ticket triage cancelled', { projectId: project.id, workItemId: wi.id });
663
+ if (replanTtlHours !== undefined) {
664
+ if (age >= replanTtlHours * 60 * 60 * 1000) {
665
+ await this.expireReplan(project, wi, replanTtlHours);
666
+ continue;
667
+ }
668
+ }
669
+ else if (wi.status === 'queued' && age >= TICKET_AUTOPILOT_CONSTANTS.TRIAGE_STALE_QUEUED_MS) {
670
+ await this.deps.pool.cancelQueued(wi.id, `${what} never picked up; replaced by a fresh one`).catch(() => undefined);
671
+ this.logger.info(`Stale ${what} cancelled`, { projectId: project.id, workItemId: wi.id });
409
672
  continue;
410
673
  }
411
674
  live = wi;
@@ -878,7 +1141,7 @@ export class TicketAutopilotService {
878
1141
  ps.retroScheduledFor = day;
879
1142
  // Only a day with real autopilot work gets a retro (skips and budget
880
1143
  // notices alone are not a run).
881
- if (dayStats.triaged + dayStats.started + dayStats.done + dayStats.verified === 0) {
1144
+ if (dayStats.triaged + dayStats.replans + dayStats.started + dayStats.done + dayStats.verified === 0) {
882
1145
  this.logger.debug('Autopilot retro skipped: no autopilot work that day', { projectId: project.id, day });
883
1146
  continue;
884
1147
  }
@@ -1178,7 +1441,15 @@ export class TicketAutopilotService {
1178
1441
  const budget = this.budgetToday(settings, teams);
1179
1442
  const state = await this.loadState();
1180
1443
  const ps = state.projects[project.id];
1181
- const live = (await this.deps.pool.getAllItems()).some((wi) => wi.metadata?.kind === TICKET_AUTOPILOT_CONSTANTS.TRIAGE_METADATA_KIND && wi.metadata?.projectId === project.id && LIVE_STATUSES.has(wi.status));
1444
+ const items = await this.deps.pool.getAllItems();
1445
+ const nowMs = this.now().getTime();
1446
+ const ttlMs = settings.replanTtlHours * 60 * 60 * 1000;
1447
+ const liveOf = (kind) => items.some((wi) => wi.metadata?.kind === kind &&
1448
+ wi.metadata?.projectId === project.id &&
1449
+ LIVE_STATUSES.has(wi.status) &&
1450
+ (kind !== TICKET_AUTOPILOT_CONSTANTS.REPLAN_METADATA_KIND || nowMs - (Date.parse(wi.createdAt) || nowMs) < ttlMs));
1451
+ const live = liveOf(TICKET_AUTOPILOT_CONSTANTS.TRIAGE_METADATA_KIND);
1452
+ const today = localDateKey(this.now());
1182
1453
  return {
1183
1454
  project: { id: project.id, name: project.name, path: project.path },
1184
1455
  settings,
@@ -1189,6 +1460,10 @@ export class TicketAutopilotService {
1189
1460
  pausedForToday: settings.enabled && spent >= budget.tokens,
1190
1461
  triageInFlight: live,
1191
1462
  lastTriageAt: ps?.lastTriageAt ? new Date(ps.lastTriageAt).toISOString() : null,
1463
+ replanInFlight: liveOf(TICKET_AUTOPILOT_CONSTANTS.REPLAN_METADATA_KIND),
1464
+ replansToday: ps?.replans?.day === today ? ps.replans.count : 0,
1465
+ lastReplanAt: ps?.replans?.lastAt ? new Date(ps.replans.lastAt).toISOString() : null,
1466
+ replanBackoffUntil: ps?.replanBackoff && today < ps.replanBackoff.resumeDay ? ps.replanBackoff.resumeDay : null,
1192
1467
  retroOn: await this.retroOn(project),
1193
1468
  };
1194
1469
  }