crewly 1.20.210 → 1.20.212

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 (246) hide show
  1. package/config/roles/orchestrator/prompt.md +1 -0
  2. package/config/skills/agent/core/app-data/SKILL.md +30 -1
  3. package/config/skills/agent/core/publish-app/SKILL.md +73 -6
  4. package/config/skills/agent/core/publish-app/execute.sh +125 -4
  5. package/config/skills/agent/core/publish-app/execute.test.sh +95 -4
  6. package/config/skills/agent/core/send-message/SKILL.md +9 -0
  7. package/config/skills/orchestrator/delegate-task/SKILL.md +9 -0
  8. package/config/skills/orchestrator/get-team-status/SKILL.md +9 -0
  9. package/config/skills/orchestrator/get-team-status/execute.sh +5 -1
  10. package/config/skills/orchestrator/send-message/SKILL.md +9 -0
  11. package/config/skills/orchestrator/start-agent/SKILL.md +9 -0
  12. package/config/skills/orchestrator/start-team/SKILL.md +9 -0
  13. package/config/skills/team-leader/assign-ticket/SKILL.md +9 -0
  14. package/config/skills/team-leader/delegate-task/SKILL.md +9 -0
  15. package/dist/backend/backend/src/constants.d.ts +78 -0
  16. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  17. package/dist/backend/backend/src/constants.js +78 -0
  18. package/dist/backend/backend/src/constants.js.map +1 -1
  19. package/dist/backend/backend/src/controllers/apps/apps.controller.d.ts +17 -1
  20. package/dist/backend/backend/src/controllers/apps/apps.controller.d.ts.map +1 -1
  21. package/dist/backend/backend/src/controllers/apps/apps.controller.js +45 -8
  22. package/dist/backend/backend/src/controllers/apps/apps.controller.js.map +1 -1
  23. package/dist/backend/backend/src/controllers/apps/apps.routes.d.ts +7 -0
  24. package/dist/backend/backend/src/controllers/apps/apps.routes.d.ts.map +1 -1
  25. package/dist/backend/backend/src/controllers/apps/apps.routes.js +15 -1
  26. package/dist/backend/backend/src/controllers/apps/apps.routes.js.map +1 -1
  27. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.d.ts +12 -0
  28. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.d.ts.map +1 -1
  29. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js +36 -1
  30. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js.map +1 -1
  31. package/dist/backend/backend/src/controllers/request-types.d.ts +10 -1
  32. package/dist/backend/backend/src/controllers/request-types.d.ts.map +1 -1
  33. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.d.ts +0 -36
  34. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.d.ts.map +1 -1
  35. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js +41 -1
  36. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js.map +1 -1
  37. package/dist/backend/backend/src/controllers/team/team-pause.controller.d.ts +59 -0
  38. package/dist/backend/backend/src/controllers/team/team-pause.controller.d.ts.map +1 -0
  39. package/dist/backend/backend/src/controllers/team/team-pause.controller.js +161 -0
  40. package/dist/backend/backend/src/controllers/team/team-pause.controller.js.map +1 -0
  41. package/dist/backend/backend/src/controllers/team/team.controller.d.ts +14 -0
  42. package/dist/backend/backend/src/controllers/team/team.controller.d.ts.map +1 -1
  43. package/dist/backend/backend/src/controllers/team/team.controller.js +78 -5
  44. package/dist/backend/backend/src/controllers/team/team.controller.js.map +1 -1
  45. package/dist/backend/backend/src/controllers/team/team.routes.d.ts.map +1 -1
  46. package/dist/backend/backend/src/controllers/team/team.routes.js +7 -1
  47. package/dist/backend/backend/src/controllers/team/team.routes.js.map +1 -1
  48. package/dist/backend/backend/src/index.d.ts.map +1 -1
  49. package/dist/backend/backend/src/index.js +18 -1
  50. package/dist/backend/backend/src/index.js.map +1 -1
  51. package/dist/backend/backend/src/models/Team.d.ts +5 -1
  52. package/dist/backend/backend/src/models/Team.d.ts.map +1 -1
  53. package/dist/backend/backend/src/models/Team.js +8 -0
  54. package/dist/backend/backend/src/models/Team.js.map +1 -1
  55. package/dist/backend/backend/src/services/agent/owner-stopped.registry.d.ts +7 -2
  56. package/dist/backend/backend/src/services/agent/owner-stopped.registry.d.ts.map +1 -1
  57. package/dist/backend/backend/src/services/agent/owner-stopped.registry.js +9 -3
  58. package/dist/backend/backend/src/services/agent/owner-stopped.registry.js.map +1 -1
  59. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
  60. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js +3 -2
  61. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  62. package/dist/backend/backend/src/services/apps/app-open-link.d.ts +61 -0
  63. package/dist/backend/backend/src/services/apps/app-open-link.d.ts.map +1 -0
  64. package/dist/backend/backend/src/services/apps/app-open-link.js +112 -0
  65. package/dist/backend/backend/src/services/apps/app-open-link.js.map +1 -0
  66. package/dist/backend/backend/src/services/apps/app-wake-message.d.ts +44 -3
  67. package/dist/backend/backend/src/services/apps/app-wake-message.d.ts.map +1 -1
  68. package/dist/backend/backend/src/services/apps/app-wake-message.js +99 -7
  69. package/dist/backend/backend/src/services/apps/app-wake-message.js.map +1 -1
  70. package/dist/backend/backend/src/services/apps/app-wake.service.d.ts +29 -2
  71. package/dist/backend/backend/src/services/apps/app-wake.service.d.ts.map +1 -1
  72. package/dist/backend/backend/src/services/apps/app-wake.service.js +151 -21
  73. package/dist/backend/backend/src/services/apps/app-wake.service.js.map +1 -1
  74. package/dist/backend/backend/src/services/apps/apps-registry.service.d.ts +18 -0
  75. package/dist/backend/backend/src/services/apps/apps-registry.service.d.ts.map +1 -1
  76. package/dist/backend/backend/src/services/apps/apps-registry.service.js +14 -0
  77. package/dist/backend/backend/src/services/apps/apps-registry.service.js.map +1 -1
  78. package/dist/backend/backend/src/services/apps/apps.service.d.ts +261 -12
  79. package/dist/backend/backend/src/services/apps/apps.service.d.ts.map +1 -1
  80. package/dist/backend/backend/src/services/apps/apps.service.js +387 -30
  81. package/dist/backend/backend/src/services/apps/apps.service.js.map +1 -1
  82. package/dist/backend/backend/src/services/apps/apps.wiring.d.ts +15 -1
  83. package/dist/backend/backend/src/services/apps/apps.wiring.d.ts.map +1 -1
  84. package/dist/backend/backend/src/services/apps/apps.wiring.js +27 -7
  85. package/dist/backend/backend/src/services/apps/apps.wiring.js.map +1 -1
  86. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts.map +1 -1
  87. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js +17 -5
  88. package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js.map +1 -1
  89. package/dist/backend/backend/src/services/core/logger.service.d.ts +5 -0
  90. package/dist/backend/backend/src/services/core/logger.service.d.ts.map +1 -1
  91. package/dist/backend/backend/src/services/core/logger.service.js +8 -0
  92. package/dist/backend/backend/src/services/core/logger.service.js.map +1 -1
  93. package/dist/backend/backend/src/services/core/storage.service.d.ts.map +1 -1
  94. package/dist/backend/backend/src/services/core/storage.service.js +6 -0
  95. package/dist/backend/backend/src/services/core/storage.service.js.map +1 -1
  96. package/dist/backend/backend/src/services/messaging/owner-message-watchdog.wiring.d.ts.map +1 -1
  97. package/dist/backend/backend/src/services/messaging/owner-message-watchdog.wiring.js +6 -0
  98. package/dist/backend/backend/src/services/messaging/owner-message-watchdog.wiring.js.map +1 -1
  99. package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.d.ts +12 -0
  100. package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.d.ts.map +1 -1
  101. package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.js +59 -0
  102. package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.js.map +1 -1
  103. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.d.ts.map +1 -1
  104. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.js +12 -6
  105. package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.js.map +1 -1
  106. package/dist/backend/backend/src/services/reconciler/reconcile-rules.d.ts.map +1 -1
  107. package/dist/backend/backend/src/services/reconciler/reconcile-rules.js +5 -0
  108. package/dist/backend/backend/src/services/reconciler/reconcile-rules.js.map +1 -1
  109. package/dist/backend/backend/src/services/reconciler/reconciler.service.d.ts.map +1 -1
  110. package/dist/backend/backend/src/services/reconciler/reconciler.service.js +4 -0
  111. package/dist/backend/backend/src/services/reconciler/reconciler.service.js.map +1 -1
  112. package/dist/backend/backend/src/services/slack/slack-agent-dm.service.d.ts.map +1 -1
  113. package/dist/backend/backend/src/services/slack/slack-agent-dm.service.js +22 -0
  114. package/dist/backend/backend/src/services/slack/slack-agent-dm.service.js.map +1 -1
  115. package/dist/backend/backend/src/services/slack/slack-channel-members.d.ts +27 -0
  116. package/dist/backend/backend/src/services/slack/slack-channel-members.d.ts.map +1 -0
  117. package/dist/backend/backend/src/services/slack/slack-channel-members.js +54 -0
  118. package/dist/backend/backend/src/services/slack/slack-channel-members.js.map +1 -0
  119. package/dist/backend/backend/src/services/slack/slack-directory.service.d.ts.map +1 -1
  120. package/dist/backend/backend/src/services/slack/slack-directory.service.js +7 -2
  121. package/dist/backend/backend/src/services/slack/slack-directory.service.js.map +1 -1
  122. package/dist/backend/backend/src/services/slack/slack-initializer.d.ts.map +1 -1
  123. package/dist/backend/backend/src/services/slack/slack-initializer.js +3 -0
  124. package/dist/backend/backend/src/services/slack/slack-initializer.js.map +1 -1
  125. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  126. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +19 -0
  127. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  128. package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts +48 -0
  129. package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
  130. package/dist/backend/backend/src/services/slack/slack-team-channel.service.js +155 -4
  131. package/dist/backend/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
  132. package/dist/backend/backend/src/services/slack/slack-thread-context.service.d.ts.map +1 -1
  133. package/dist/backend/backend/src/services/slack/slack-thread-context.service.js +3 -1
  134. package/dist/backend/backend/src/services/slack/slack-thread-context.service.js.map +1 -1
  135. package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts +11 -0
  136. package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
  137. package/dist/backend/backend/src/services/task-pool/task-pool.service.js +36 -1
  138. package/dist/backend/backend/src/services/task-pool/task-pool.service.js.map +1 -1
  139. package/dist/backend/backend/src/services/task-pool/untargeted-router.d.ts.map +1 -1
  140. package/dist/backend/backend/src/services/task-pool/untargeted-router.js +14 -6
  141. package/dist/backend/backend/src/services/task-pool/untargeted-router.js.map +1 -1
  142. package/dist/backend/backend/src/services/team/team-pause-command.d.ts +97 -0
  143. package/dist/backend/backend/src/services/team/team-pause-command.d.ts.map +1 -0
  144. package/dist/backend/backend/src/services/team/team-pause-command.js +177 -0
  145. package/dist/backend/backend/src/services/team/team-pause-command.js.map +1 -0
  146. package/dist/backend/backend/src/services/team/team-pause-notice.d.ts +43 -0
  147. package/dist/backend/backend/src/services/team/team-pause-notice.d.ts.map +1 -0
  148. package/dist/backend/backend/src/services/team/team-pause-notice.js +61 -0
  149. package/dist/backend/backend/src/services/team/team-pause-notice.js.map +1 -0
  150. package/dist/backend/backend/src/services/team/team-pause.registry.d.ts +167 -0
  151. package/dist/backend/backend/src/services/team/team-pause.registry.d.ts.map +1 -0
  152. package/dist/backend/backend/src/services/team/team-pause.registry.js +263 -0
  153. package/dist/backend/backend/src/services/team/team-pause.registry.js.map +1 -0
  154. package/dist/backend/backend/src/services/team/team-pause.service.d.ts +172 -0
  155. package/dist/backend/backend/src/services/team/team-pause.service.d.ts.map +1 -0
  156. package/dist/backend/backend/src/services/team/team-pause.service.js +254 -0
  157. package/dist/backend/backend/src/services/team/team-pause.service.js.map +1 -0
  158. package/dist/backend/backend/src/services/team/team-pause.wiring.d.ts +30 -0
  159. package/dist/backend/backend/src/services/team/team-pause.wiring.d.ts.map +1 -0
  160. package/dist/backend/backend/src/services/team/team-pause.wiring.js +48 -0
  161. package/dist/backend/backend/src/services/team/team-pause.wiring.js.map +1 -0
  162. package/dist/backend/backend/src/services/v3/agent-auto-claim.service.d.ts.map +1 -1
  163. package/dist/backend/backend/src/services/v3/agent-auto-claim.service.js +11 -0
  164. package/dist/backend/backend/src/services/v3/agent-auto-claim.service.js.map +1 -1
  165. package/dist/backend/backend/src/services/v3/trigger-engine.service.d.ts +9 -0
  166. package/dist/backend/backend/src/services/v3/trigger-engine.service.d.ts.map +1 -1
  167. package/dist/backend/backend/src/services/v3/trigger-engine.service.js +27 -2
  168. package/dist/backend/backend/src/services/v3/trigger-engine.service.js.map +1 -1
  169. package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.d.ts.map +1 -1
  170. package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.js +7 -0
  171. package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.js.map +1 -1
  172. package/dist/backend/backend/src/services/workflow/cron-task.service.d.ts.map +1 -1
  173. package/dist/backend/backend/src/services/workflow/cron-task.service.js +14 -0
  174. package/dist/backend/backend/src/services/workflow/cron-task.service.js.map +1 -1
  175. package/dist/backend/backend/src/services/workflow/message-scheduler.service.d.ts.map +1 -1
  176. package/dist/backend/backend/src/services/workflow/message-scheduler.service.js +5 -0
  177. package/dist/backend/backend/src/services/workflow/message-scheduler.service.js.map +1 -1
  178. package/dist/backend/backend/src/services/workflow/scheduler.service.d.ts.map +1 -1
  179. package/dist/backend/backend/src/services/workflow/scheduler.service.js +6 -0
  180. package/dist/backend/backend/src/services/workflow/scheduler.service.js.map +1 -1
  181. package/dist/backend/backend/src/types/index.d.ts +22 -0
  182. package/dist/backend/backend/src/types/index.d.ts.map +1 -1
  183. package/dist/backend/backend/src/types/index.js.map +1 -1
  184. package/dist/backend/build-info.json +2 -2
  185. package/dist/cli/backend/src/constants.d.ts +78 -0
  186. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  187. package/dist/cli/backend/src/constants.js +78 -0
  188. package/dist/cli/backend/src/constants.js.map +1 -1
  189. package/dist/cli/backend/src/models/Team.d.ts +5 -1
  190. package/dist/cli/backend/src/models/Team.d.ts.map +1 -1
  191. package/dist/cli/backend/src/models/Team.js +8 -0
  192. package/dist/cli/backend/src/models/Team.js.map +1 -1
  193. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
  194. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js +3 -2
  195. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  196. package/dist/cli/backend/src/services/apps/app-open-link.d.ts +61 -0
  197. package/dist/cli/backend/src/services/apps/app-open-link.d.ts.map +1 -0
  198. package/dist/cli/backend/src/services/apps/app-open-link.js +112 -0
  199. package/dist/cli/backend/src/services/apps/app-open-link.js.map +1 -0
  200. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts.map +1 -1
  201. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.js +17 -5
  202. package/dist/cli/backend/src/services/chat-v2/chat-v2.dispatcher.service.js.map +1 -1
  203. package/dist/cli/backend/src/services/core/logger.service.d.ts +5 -0
  204. package/dist/cli/backend/src/services/core/logger.service.d.ts.map +1 -1
  205. package/dist/cli/backend/src/services/core/logger.service.js +8 -0
  206. package/dist/cli/backend/src/services/core/logger.service.js.map +1 -1
  207. package/dist/cli/backend/src/services/core/storage.service.d.ts.map +1 -1
  208. package/dist/cli/backend/src/services/core/storage.service.js +6 -0
  209. package/dist/cli/backend/src/services/core/storage.service.js.map +1 -1
  210. package/dist/cli/backend/src/services/slack/slack-agent-dm.service.d.ts.map +1 -1
  211. package/dist/cli/backend/src/services/slack/slack-agent-dm.service.js +22 -0
  212. package/dist/cli/backend/src/services/slack/slack-agent-dm.service.js.map +1 -1
  213. package/dist/cli/backend/src/services/slack/slack-directory.service.d.ts.map +1 -1
  214. package/dist/cli/backend/src/services/slack/slack-directory.service.js +7 -2
  215. package/dist/cli/backend/src/services/slack/slack-directory.service.js.map +1 -1
  216. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  217. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js +19 -0
  218. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  219. package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts +48 -0
  220. package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
  221. package/dist/cli/backend/src/services/slack/slack-team-channel.service.js +155 -4
  222. package/dist/cli/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
  223. package/dist/cli/backend/src/services/slack/slack-thread-context.service.d.ts.map +1 -1
  224. package/dist/cli/backend/src/services/slack/slack-thread-context.service.js +3 -1
  225. package/dist/cli/backend/src/services/slack/slack-thread-context.service.js.map +1 -1
  226. package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts +11 -0
  227. package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
  228. package/dist/cli/backend/src/services/task-pool/task-pool.service.js +36 -1
  229. package/dist/cli/backend/src/services/task-pool/task-pool.service.js.map +1 -1
  230. package/dist/cli/backend/src/services/team/team-pause-notice.d.ts +43 -0
  231. package/dist/cli/backend/src/services/team/team-pause-notice.d.ts.map +1 -0
  232. package/dist/cli/backend/src/services/team/team-pause-notice.js +61 -0
  233. package/dist/cli/backend/src/services/team/team-pause-notice.js.map +1 -0
  234. package/dist/cli/backend/src/services/team/team-pause.registry.d.ts +167 -0
  235. package/dist/cli/backend/src/services/team/team-pause.registry.d.ts.map +1 -0
  236. package/dist/cli/backend/src/services/team/team-pause.registry.js +263 -0
  237. package/dist/cli/backend/src/services/team/team-pause.registry.js.map +1 -0
  238. package/dist/cli/backend/src/services/workflow/cron-task.service.d.ts.map +1 -1
  239. package/dist/cli/backend/src/services/workflow/cron-task.service.js +14 -0
  240. package/dist/cli/backend/src/services/workflow/cron-task.service.js.map +1 -1
  241. package/dist/cli/backend/src/types/index.d.ts +22 -0
  242. package/dist/cli/backend/src/types/index.d.ts.map +1 -1
  243. package/dist/cli/backend/src/types/index.js.map +1 -1
  244. package/frontend/dist/assets/{index-f64b333a.js → index-917db1b8.js} +279 -279
  245. package/frontend/dist/index.html +1 -1
  246. package/package.json +1 -1
@@ -14,6 +14,7 @@
14
14
  * @module services/task-pool/untargeted-router
15
15
  */
16
16
  import { pickTeamLead } from '../../utils/team.utils.js';
17
+ import { isTeamPausedNow } from '../team/team-pause.registry.js';
17
18
  /**
18
19
  * Session of a member, preferring the permanent agent id.
19
20
  *
@@ -75,19 +76,24 @@ function isTeamLead(session, teams) {
75
76
  * @returns The session that decides
76
77
  */
77
78
  export function initialDecider(ctx) {
78
- if (ctx.ticketAssignee)
79
+ // A paused team never decides (specs/2026-10-04-team-pause.md): its
80
+ // members are left out of every level, so the item goes one level up.
81
+ const pausedTeams = ctx.teams.filter((t) => isTeamPausedNow(t));
82
+ const teams = ctx.teams.filter((t) => !isTeamPausedNow(t));
83
+ const isPausedSession = (s) => pausedTeams.some((t) => (t.members ?? []).some((m) => sessionOf(m) === s || m.sessionName === s));
84
+ if (ctx.ticketAssignee && !isPausedSession(ctx.ticketAssignee))
79
85
  return ctx.ticketAssignee;
80
86
  if (ctx.teamId) {
81
- const team = ctx.teams.find((t) => t.id === ctx.teamId);
87
+ const team = teams.find((t) => t.id === ctx.teamId);
82
88
  const lead = sessionOf(team ? pickTeamLead(team) ?? undefined : undefined);
83
89
  if (lead)
84
90
  return lead;
85
91
  }
86
92
  const creator = ctx.creatorSession;
87
- if (creator && creator !== ctx.orchestrator) {
88
- if (isTeamLead(creator, ctx.teams))
93
+ if (creator && creator !== ctx.orchestrator && !isPausedSession(creator)) {
94
+ if (isTeamLead(creator, teams))
89
95
  return creator;
90
- const lead = leadAbove(creator, ctx.teams);
96
+ const lead = leadAbove(creator, teams);
91
97
  if (lead)
92
98
  return lead;
93
99
  }
@@ -106,6 +112,8 @@ export function nextDecider(current, teams, orchestrator) {
106
112
  if (current === orchestrator)
107
113
  return null;
108
114
  const lead = leadAbove(current, teams);
109
- return lead && lead !== current ? lead : orchestrator;
115
+ // A paused lead is skipped: the orchestrator decides (specs/2026-10-04-team-pause.md).
116
+ const leadPaused = !!lead && teams.some((t) => isTeamPausedNow(t) && (t.members ?? []).some((m) => sessionOf(m) === lead || m.sessionName === lead));
117
+ return lead && lead !== current && !leadPaused ? lead : orchestrator;
110
118
  }
111
119
  //# sourceMappingURL=untargeted-router.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"untargeted-router.js","sourceRoot":"","sources":["../../../../../../backend/src/services/task-pool/untargeted-router.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"untargeted-router.js","sourceRoot":"","sources":["../../../../../../backend/src/services/task-pool/untargeted-router.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Pause / resume a team from the owner's DM with the orc
3
+ * (specs/2026-10-04-team-pause.md). The owner is usually away from the
4
+ * machine, so the Slack DM is the main control. Handled by the backend as
5
+ * an owner command — never by the orc's LLM — and only for the owner's own
6
+ * messages in their DM with the orc:
7
+ *
8
+ * - `pause <team>` / `pause team <team>`
9
+ * - `pause <team> for 3d` (`m`, `h`, `d`, `w`; also `3 days`, `2 hours`)
10
+ * - `pause <team> until 2026-10-10` (anything `Date.parse` reads)
11
+ * - `pause <team> because <reason>` / `pause <team>: <reason>`
12
+ * - `resume <team>` / `unpause <team>`
13
+ * - Chinese aliases: `暂停 <team>`, `恢复 <team>`, `取消暂停 <team>`
14
+ *
15
+ * A message is consumed only when its target names a team (or it says
16
+ * `pause team <x>` explicitly); anything else goes on to the orc. Replies are
17
+ * English (harness text rule).
18
+ *
19
+ * @module services/team/team-pause-command
20
+ */
21
+ import type { SlackIncomingMessage } from '../../types/slack.types.js';
22
+ import type { Team } from '../../types/index.js';
23
+ /** A parsed command. `target` is the team as typed. */
24
+ export type TeamPauseCommand = {
25
+ kind: 'pause';
26
+ target: string;
27
+ explicitTeam: boolean;
28
+ reason?: string;
29
+ until?: string;
30
+ forMs?: number;
31
+ } | {
32
+ kind: 'resume';
33
+ target: string;
34
+ explicitTeam: boolean;
35
+ };
36
+ /**
37
+ * Parse a pause / resume command.
38
+ *
39
+ * @param text - Message text
40
+ * @returns The command, or null when the message is not one
41
+ *
42
+ * @example
43
+ * parseTeamPauseCommand('pause Crewly for 3d because harness work moved') // { kind: 'pause', target: 'Crewly', forMs: 259200000, reason: 'harness work moved', explicitTeam: false }
44
+ * parseTeamPauseCommand('resume team Crewly') // { kind: 'resume', target: 'Crewly', explicitTeam: true }
45
+ */
46
+ export declare function parseTeamPauseCommand(text: string | undefined): TeamPauseCommand | null;
47
+ /** What the interceptor needs. */
48
+ export interface TeamPauseCommandDeps {
49
+ /** `orc` when the message is the owner writing in their DM with the orc (see SlackReloginDmService.ownerDmScope) */
50
+ ownerDmScope: (message: SlackIncomingMessage) => 'orc' | 'agent' | null;
51
+ /** Reply target for the message */
52
+ replyTargetOf: (message: SlackIncomingMessage) => unknown;
53
+ /** Send a reply to the owner */
54
+ reply: (text: string, target: unknown) => Promise<unknown>;
55
+ /** Known teams, synchronously (the pause registry's index) */
56
+ knownTeams: () => Array<{
57
+ id: string;
58
+ name: string;
59
+ }>;
60
+ pause: (teamId: string, input: {
61
+ reason?: string;
62
+ until?: string;
63
+ }) => Promise<{
64
+ team: Team;
65
+ alreadyPaused: boolean;
66
+ stopped: string[];
67
+ stopFailed: Array<{
68
+ member: string;
69
+ error: string;
70
+ }>;
71
+ releasedWorkItems: string[];
72
+ releasedTickets: string[];
73
+ }>;
74
+ resume: (teamId: string) => Promise<{
75
+ team: Team;
76
+ wasPaused: boolean;
77
+ }>;
78
+ onError?: (err: unknown) => void;
79
+ now?: () => number;
80
+ }
81
+ /**
82
+ * Run a parsed command and build the owner's reply.
83
+ *
84
+ * @param cmd - Command
85
+ * @param teamId - Resolved team
86
+ * @param deps - Collaborators
87
+ * @returns Reply text
88
+ */
89
+ export declare function runTeamPauseCommand(cmd: TeamPauseCommand, teamId: string, deps: TeamPauseCommandDeps): Promise<string>;
90
+ /**
91
+ * The Slack bridge interceptor for the owner's orc DM.
92
+ *
93
+ * @param deps - Collaborators
94
+ * @returns Interceptor: true when the message was a pause / resume command (consumed)
95
+ */
96
+ export declare function createTeamPauseInterceptor(deps: TeamPauseCommandDeps): (message: SlackIncomingMessage) => boolean;
97
+ //# sourceMappingURL=team-pause-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-pause-command.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/team/team-pause-command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAGjD,uDAAuD;AACvD,MAAM,MAAM,gBAAgB,GACzB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACzG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,CAAC;AAqC7D;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,gBAAgB,GAAG,IAAI,CAsCvF;AAED,kCAAkC;AAClC,MAAM,WAAW,oBAAoB;IACpC,oHAAoH;IACpH,YAAY,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,KAAK,GAAG,OAAO,GAAG,IAAI,CAAC;IACxE,mCAAmC;IACnC,aAAa,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,OAAO,CAAC;IAC1D,gCAAgC;IAChC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3D,8DAA8D;IAC9D,UAAU,EAAE,MAAM,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtD,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,aAAa,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,UAAU,EAAE,KAAK,CAAC;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAAC,iBAAiB,EAAE,MAAM,EAAE,CAAC;QAAC,eAAe,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACxP,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACxE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IACjC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAsB5H;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,oBAAoB,GAAG,CAAC,OAAO,EAAE,oBAAoB,KAAK,OAAO,CA0BjH"}
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Pause / resume a team from the owner's DM with the orc
3
+ * (specs/2026-10-04-team-pause.md). The owner is usually away from the
4
+ * machine, so the Slack DM is the main control. Handled by the backend as
5
+ * an owner command — never by the orc's LLM — and only for the owner's own
6
+ * messages in their DM with the orc:
7
+ *
8
+ * - `pause <team>` / `pause team <team>`
9
+ * - `pause <team> for 3d` (`m`, `h`, `d`, `w`; also `3 days`, `2 hours`)
10
+ * - `pause <team> until 2026-10-10` (anything `Date.parse` reads)
11
+ * - `pause <team> because <reason>` / `pause <team>: <reason>`
12
+ * - `resume <team>` / `unpause <team>`
13
+ * - Chinese aliases: `暂停 <team>`, `恢复 <team>`, `取消暂停 <team>`
14
+ *
15
+ * A message is consumed only when its target names a team (or it says
16
+ * `pause team <x>` explicitly); anything else goes on to the orc. Replies are
17
+ * English (harness text rule).
18
+ *
19
+ * @module services/team/team-pause-command
20
+ */
21
+ import { findTeamByRef } from './team-pause.service.js';
22
+ const UNIT_MS = {
23
+ m: 60_000,
24
+ min: 60_000,
25
+ mins: 60_000,
26
+ minute: 60_000,
27
+ minutes: 60_000,
28
+ h: 3_600_000,
29
+ hr: 3_600_000,
30
+ hrs: 3_600_000,
31
+ hour: 3_600_000,
32
+ hours: 3_600_000,
33
+ d: 86_400_000,
34
+ day: 86_400_000,
35
+ days: 86_400_000,
36
+ w: 604_800_000,
37
+ week: 604_800_000,
38
+ weeks: 604_800_000,
39
+ };
40
+ /**
41
+ * Normalise a message for matching.
42
+ *
43
+ * @param text - Raw text
44
+ * @returns Mentions, emphasis and trailing punctuation removed; whitespace collapsed
45
+ */
46
+ function norm(text) {
47
+ return text
48
+ .replace(/<@[A-Z0-9]+>/g, '')
49
+ .replace(/[`*_]/g, '')
50
+ .replace(/[\s.。!!~~]+$/u, '')
51
+ .replace(/^(?:please|pls|请)\s*/iu, '')
52
+ .replace(/\s+/g, ' ')
53
+ .trim();
54
+ }
55
+ /**
56
+ * Parse a pause / resume command.
57
+ *
58
+ * @param text - Message text
59
+ * @returns The command, or null when the message is not one
60
+ *
61
+ * @example
62
+ * parseTeamPauseCommand('pause Crewly for 3d because harness work moved') // { kind: 'pause', target: 'Crewly', forMs: 259200000, reason: 'harness work moved', explicitTeam: false }
63
+ * parseTeamPauseCommand('resume team Crewly') // { kind: 'resume', target: 'Crewly', explicitTeam: true }
64
+ */
65
+ export function parseTeamPauseCommand(text) {
66
+ if (!text)
67
+ return null;
68
+ const t = norm(text);
69
+ if (!t || t.includes('\n'))
70
+ return null;
71
+ let m = /^(?:resume|unpause|un-pause)\s+(team\s+)?(.+)$/i.exec(t) ?? /^(?:恢复|取消暂停)\s*(团队)?\s*(.+)$/u.exec(t);
72
+ if (m) {
73
+ const target = m[2].trim().replace(/^the\s+/i, '').replace(/\s+team$/i, '');
74
+ return target ? { kind: 'resume', target, explicitTeam: !!m[1] } : null;
75
+ }
76
+ m = /^pause\s+(team\s+)?(.+)$/i.exec(t) ?? /^暂停\s*(团队)?\s*(.+)$/u.exec(t);
77
+ if (!m)
78
+ return null;
79
+ let rest = m[2].trim();
80
+ const explicitTeam = !!m[1];
81
+ let reason;
82
+ // A colon starts a reason only when no "until <time>" could contain it.
83
+ const why = /^(.+?)(?:\s+because\s+|\s+reason:?\s+|\s+因为\s*)(.+)$/i.exec(rest) ??
84
+ (/\b(?:until|till|til)\b/i.test(rest) ? null : /^(.+?)\s*[::]\s*(.+)$/.exec(rest));
85
+ if (why) {
86
+ rest = why[1].trim();
87
+ reason = why[2].trim();
88
+ }
89
+ let until;
90
+ let forMs;
91
+ const dur = /^(.+?)\s+for\s+(\d+(?:\.\d+)?)\s*([a-z]+)$/i.exec(rest);
92
+ const till = /^(.+?)\s+(?:until|till|til)\s+(.+)$/i.exec(rest);
93
+ if (dur && UNIT_MS[dur[3].toLowerCase()]) {
94
+ rest = dur[1].trim();
95
+ forMs = Number(dur[2]) * UNIT_MS[dur[3].toLowerCase()];
96
+ }
97
+ else if (till) {
98
+ rest = till[1].trim();
99
+ until = till[2].trim();
100
+ }
101
+ const target = rest.replace(/^the\s+/i, '').replace(/\s+team$/i, '').trim();
102
+ if (!target)
103
+ return null;
104
+ return { kind: 'pause', target, explicitTeam, ...(reason ? { reason } : {}), ...(until ? { until } : {}), ...(forMs ? { forMs } : {}) };
105
+ }
106
+ /**
107
+ * Run a parsed command and build the owner's reply.
108
+ *
109
+ * @param cmd - Command
110
+ * @param teamId - Resolved team
111
+ * @param deps - Collaborators
112
+ * @returns Reply text
113
+ */
114
+ export async function runTeamPauseCommand(cmd, teamId, deps) {
115
+ if (cmd.kind === 'resume') {
116
+ const out = await deps.resume(teamId);
117
+ return out.wasPaused
118
+ ? `${out.team.name} is resumed. Its members start again when there is work for them, or when you start them.`
119
+ : `${out.team.name} was not paused.`;
120
+ }
121
+ const now = deps.now?.() ?? Date.now();
122
+ const until = cmd.forMs ? new Date(now + cmd.forMs).toISOString() : cmd.until;
123
+ const out = await deps.pause(teamId, { ...(cmd.reason ? { reason: cmd.reason } : {}), ...(until ? { until } : {}) });
124
+ const parts = [
125
+ `${out.team.name} is ${out.alreadyPaused ? 'still ' : ''}paused${out.team.paused?.until ? ` until ${out.team.paused.until}` : ''}.`,
126
+ 'No automation will wake it, and other agents are told to file a GitHub issue' +
127
+ (out.team.issueRepo ? ` in ${out.team.issueRepo}` : ' (no issue repo set: they tell the orc)') +
128
+ ' instead of handing it work.',
129
+ ];
130
+ if (out.stopped.length > 0)
131
+ parts.push(`Stopped: ${out.stopped.join(', ')}.`);
132
+ if (out.stopFailed.length > 0)
133
+ parts.push(`Could not stop: ${out.stopFailed.map((f) => `${f.member} (${f.error})`).join(', ')}.`);
134
+ const released = out.releasedWorkItems.length + out.releasedTickets.length;
135
+ if (released > 0)
136
+ parts.push(`Unassigned ${released} item(s) that had not started.`);
137
+ parts.push(`Say "resume ${out.team.name}" to undo.`);
138
+ return parts.join(' ');
139
+ }
140
+ /**
141
+ * The Slack bridge interceptor for the owner's orc DM.
142
+ *
143
+ * @param deps - Collaborators
144
+ * @returns Interceptor: true when the message was a pause / resume command (consumed)
145
+ */
146
+ export function createTeamPauseInterceptor(deps) {
147
+ return (message) => {
148
+ if (message.hasFiles)
149
+ return false;
150
+ if (deps.ownerDmScope(message) !== 'orc')
151
+ return false;
152
+ const cmd = parseTeamPauseCommand(message.text);
153
+ if (!cmd)
154
+ return false;
155
+ const teams = deps.knownTeams();
156
+ const team = findTeamByRef(teams.map((t) => ({ id: t.id, name: t.name, members: [], projectIds: [], createdAt: '', updatedAt: '' })), cmd.target);
157
+ const target = deps.replyTargetOf(message);
158
+ if (!team) {
159
+ // "pause team X" is unmistakably a command: answer it. Anything else
160
+ // ("pause the deploy") is the orc's.
161
+ if (!cmd.explicitTeam)
162
+ return false;
163
+ void deps
164
+ .reply(`No team named "${cmd.target}". Teams: ${teams.map((t) => t.name).join(', ') || 'none'}.`, target)
165
+ .catch((err) => deps.onError?.(err));
166
+ return true;
167
+ }
168
+ void runTeamPauseCommand(cmd, team.id, deps)
169
+ .then((text) => deps.reply(text, target))
170
+ .catch((err) => {
171
+ deps.onError?.(err);
172
+ return deps.reply(`Couldn't ${cmd.kind} ${team.name}: ${err instanceof Error ? err.message : String(err)}`, target).catch(() => undefined);
173
+ });
174
+ return true;
175
+ };
176
+ }
177
+ //# sourceMappingURL=team-pause-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-pause-command.js","sourceRoot":"","sources":["../../../../../../backend/src/services/team/team-pause-command.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The Slack auto-reply for a paused agent (specs/2026-10-04-team-pause.md):
3
+ * an @mention or DM of a paused team's agent does not wake it; a short
4
+ * English line in the thread says why — at most once per thread.
5
+ *
6
+ * @module services/team/team-pause-notice
7
+ */
8
+ import { type PausedTeamInfo } from './team-pause.registry.js';
9
+ /**
10
+ * The auto-reply text.
11
+ *
12
+ * @param info - The paused team
13
+ * @param agentName - Display name of the agent that was addressed
14
+ * @param options - `toAgent`: the author is another agent (it gets the refusal: file an issue / tell the orc)
15
+ * @returns Text
16
+ *
17
+ * @example
18
+ * pausedSlackNotice(info, 'Leo') // 'Leo is on Crewly, which the owner has paused, so Leo won't pick this up. …'
19
+ */
20
+ export declare function pausedSlackNotice(info: PausedTeamInfo, agentName: string, options?: {
21
+ toAgent?: boolean;
22
+ }): string;
23
+ /** Remembers which threads were already told (one notice per thread per team). */
24
+ export declare class PausedThreadNotices {
25
+ private readonly max;
26
+ private readonly told;
27
+ /**
28
+ * @param max - Threads remembered before the oldest are forgotten
29
+ */
30
+ constructor(max?: number);
31
+ /**
32
+ * Claim the notice for a thread: true the first time, false after.
33
+ *
34
+ * @param channelId - Slack channel
35
+ * @param threadTs - Thread (root ts)
36
+ * @param teamId - Paused team
37
+ * @returns True when the notice should be posted now
38
+ */
39
+ claim(channelId: string, threadTs: string | undefined, teamId: string): boolean;
40
+ }
41
+ /** Shared by the Slack paths so a thread is told once whichever path saw it. */
42
+ export declare const pausedThreadNotices: PausedThreadNotices;
43
+ //# sourceMappingURL=team-pause-notice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-pause-notice.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/team/team-pause-notice.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAErF;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,MAAM,CAOtH;AAED,kFAAkF;AAClF,qBAAa,mBAAmB;IAMnB,OAAO,CAAC,QAAQ,CAAC,GAAG;IALhC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqB;IAE1C;;OAEG;gBAC0B,GAAG,GAAE,MAAiD;IAEnF;;;;;;;OAOG;IACH,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO;CAU/E;AAED,gFAAgF;AAChF,eAAO,MAAM,mBAAmB,qBAA4B,CAAC"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * The Slack auto-reply for a paused agent (specs/2026-10-04-team-pause.md):
3
+ * an @mention or DM of a paused team's agent does not wake it; a short
4
+ * English line in the thread says why — at most once per thread.
5
+ *
6
+ * @module services/team/team-pause-notice
7
+ */
8
+ import { TEAM_PAUSE_CONSTANTS } from '../../constants.js';
9
+ import { pausedRefusalMessage } from './team-pause.registry.js';
10
+ /**
11
+ * The auto-reply text.
12
+ *
13
+ * @param info - The paused team
14
+ * @param agentName - Display name of the agent that was addressed
15
+ * @param options - `toAgent`: the author is another agent (it gets the refusal: file an issue / tell the orc)
16
+ * @returns Text
17
+ *
18
+ * @example
19
+ * pausedSlackNotice(info, 'Leo') // 'Leo is on Crewly, which the owner has paused, so Leo won't pick this up. …'
20
+ */
21
+ export function pausedSlackNotice(info, agentName, options = {}) {
22
+ if (options.toAgent)
23
+ return pausedRefusalMessage(info);
24
+ const until = info.pause.until ? ` (until ${info.pause.until})` : '';
25
+ return (`${agentName} is on ${info.teamName}, which the owner has paused${until}, so ${agentName} won't pick this up. ` +
26
+ `To bring the team back, DM the orc "resume ${info.teamName}".`);
27
+ }
28
+ /** Remembers which threads were already told (one notice per thread per team). */
29
+ export class PausedThreadNotices {
30
+ max;
31
+ told = new Set();
32
+ /**
33
+ * @param max - Threads remembered before the oldest are forgotten
34
+ */
35
+ constructor(max = TEAM_PAUSE_CONSTANTS.SLACK_NOTICE_MEMORY) {
36
+ this.max = max;
37
+ }
38
+ /**
39
+ * Claim the notice for a thread: true the first time, false after.
40
+ *
41
+ * @param channelId - Slack channel
42
+ * @param threadTs - Thread (root ts)
43
+ * @param teamId - Paused team
44
+ * @returns True when the notice should be posted now
45
+ */
46
+ claim(channelId, threadTs, teamId) {
47
+ const key = `${channelId}:${threadTs ?? ''}:${teamId}`;
48
+ if (this.told.has(key))
49
+ return false;
50
+ this.told.add(key);
51
+ if (this.told.size > this.max) {
52
+ const oldest = this.told.values().next().value;
53
+ if (oldest !== undefined)
54
+ this.told.delete(oldest);
55
+ }
56
+ return true;
57
+ }
58
+ }
59
+ /** Shared by the Slack paths so a thread is told once whichever path saw it. */
60
+ export const pausedThreadNotices = new PausedThreadNotices();
61
+ //# sourceMappingURL=team-pause-notice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-pause-notice.js","sourceRoot":"","sources":["../../../../../../backend/src/services/team/team-pause-notice.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAuB,MAAM,0BAA0B,CAAC;AAErF;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAoB,EAAE,SAAiB,EAAE,UAAiC,EAAE;IAC7G,IAAI,OAAO,CAAC,OAAO;QAAE,OAAO,oBAAoB,CAAC,IAAI,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IACrE,OAAO,CACN,GAAG,SAAS,UAAU,IAAI,CAAC,QAAQ,+BAA+B,KAAK,QAAQ,SAAS,uBAAuB;QAC/G,8CAA8C,IAAI,CAAC,QAAQ,IAAI,CAC/D,CAAC;AACH,CAAC;AAED,kFAAkF;AAClF,MAAM,OAAO,mBAAmB;IAMF;IALZ,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE1C;;OAEG;IACH,YAA6B,MAAc,oBAAoB,CAAC,mBAAmB;QAAtD,QAAG,GAAH,GAAG,CAAmD;IAAG,CAAC;IAEvF;;;;;;;OAOG;IACH,KAAK,CAAC,SAAiB,EAAE,QAA4B,EAAE,MAAc;QACpE,MAAM,GAAG,GAAG,GAAG,SAAS,IAAI,QAAQ,IAAI,EAAE,IAAI,MAAM,EAAE,CAAC;QACvD,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QACrC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACnB,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;YAC/C,IAAI,MAAM,KAAK,SAAS;gBAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACpD,CAAC;QACD,OAAO,IAAI,CAAC;IACb,CAAC;CACD;AAED,gFAAgF;AAChF,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,mBAAmB,EAAE,CAAC"}
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Paused teams — the in-memory index every automation path consults
3
+ * (specs/2026-10-04-team-pause.md).
4
+ *
5
+ * The pause itself is stored on the team (`Team.paused`, persisted in the
6
+ * team's config.json), so it survives restarts. This module keeps a
7
+ * synchronous index of it — team id → pause, and every session name a
8
+ * member of a paused team may run under → team — because most of the gates
9
+ * (the reconciler's wake rule, the queued-message wake, the dispatcher's
10
+ * target planning) only have a session name and must not await storage.
11
+ *
12
+ * The index is rebuilt from storage: {@link syncPausedTeams} runs on every
13
+ * `StorageService.getTeams()` read and at boot, and {@link notePausedTeam}
14
+ * on every save. A pause whose `until` has passed counts as resumed at once,
15
+ * before the auto-resume sweep clears it from storage.
16
+ *
17
+ * Module-level and dependency-free (types and two utils only), so the team
18
+ * controller, the storage service and the services can share it without
19
+ * import cycles.
20
+ *
21
+ * @module services/team/team-pause.registry
22
+ */
23
+ import type { Team, TeamMember, TeamPauseState } from '../../types/index.js';
24
+ /** What the gates need to know about a paused team. */
25
+ export interface PausedTeamInfo {
26
+ teamId: string;
27
+ teamName: string;
28
+ /** GitHub repository for issues while paused (`owner/name`), when configured */
29
+ issueRepo?: string;
30
+ pause: TeamPauseState;
31
+ /** Every session name a member may run under (session, agent id, derived name) */
32
+ sessions: string[];
33
+ /** Member display names */
34
+ memberNames: string[];
35
+ }
36
+ /**
37
+ * Whether a pause is in force at `now` (set, and its `until` not reached).
38
+ *
39
+ * @param pause - The team's pause, if any
40
+ * @param now - Clock (ms)
41
+ * @returns True while paused
42
+ *
43
+ * @example
44
+ * isPauseActive({ pausedAt: '…', by: 'owner', until: '2099-01-01T00:00:00Z' }) // true
45
+ */
46
+ export declare function isPauseActive(pause: TeamPauseState | null | undefined, now?: number): boolean;
47
+ /**
48
+ * Whether a team object is paused now. Prefer this where the team is at
49
+ * hand: it reads the team itself, not the index.
50
+ *
51
+ * @param team - Team (only `paused` is read)
52
+ * @param now - Clock (ms)
53
+ * @returns True while paused
54
+ */
55
+ export declare function isTeamPausedNow(team: Pick<Team, 'paused'> | null | undefined, now?: number): boolean;
56
+ /**
57
+ * Every session name a member may run under: its live session, its
58
+ * permanent agent id, and the name the controller derives on start.
59
+ *
60
+ * @param teamName - Team display name
61
+ * @param member - Member
62
+ * @returns Distinct, non-empty session names
63
+ */
64
+ export declare function memberSessionKeys(teamName: string, member: Pick<TeamMember, 'sessionName' | 'agentId' | 'name' | 'id'>): string[];
65
+ /**
66
+ * Update the index for one team (after a save). A team that is not paused
67
+ * is removed from it.
68
+ *
69
+ * @param team - The saved team
70
+ */
71
+ export declare function notePausedTeam(team: Team | null | undefined): void;
72
+ /**
73
+ * Forget a deleted team.
74
+ *
75
+ * @param teamId - Deleted team
76
+ */
77
+ export declare function noteTeamDeleted(teamId: string): void;
78
+ /**
79
+ * Every team the index has seen (id and name), archived ones excluded.
80
+ *
81
+ * @returns Teams
82
+ */
83
+ export declare function listKnownTeams(): Array<{
84
+ id: string;
85
+ name: string;
86
+ }>;
87
+ /**
88
+ * Rebuild the index from a full team list (a storage read, boot).
89
+ *
90
+ * @param teams - Every stored team
91
+ */
92
+ export declare function syncPausedTeams(teams: readonly Team[]): void;
93
+ /**
94
+ * The paused team with this id, if its pause is in force.
95
+ *
96
+ * @param teamId - Team id
97
+ * @param now - Clock (ms)
98
+ * @returns Info, or null
99
+ */
100
+ export declare function pausedTeamById(teamId: string | null | undefined, now?: number): PausedTeamInfo | null;
101
+ /**
102
+ * Whether the team with this id is paused now.
103
+ *
104
+ * @param teamId - Team id
105
+ * @returns True while paused
106
+ */
107
+ export declare function isTeamIdPaused(teamId: string | null | undefined): boolean;
108
+ /**
109
+ * The paused team a session belongs to, if its pause is in force.
110
+ *
111
+ * @param session - Agent session (or agent id)
112
+ * @param now - Clock (ms)
113
+ * @returns Info, or null
114
+ */
115
+ export declare function pausedTeamOfSession(session: string | null | undefined, now?: number): PausedTeamInfo | null;
116
+ /**
117
+ * Whether a session belongs to a paused team.
118
+ *
119
+ * @param session - Agent session (or agent id)
120
+ * @returns True while its team is paused
121
+ *
122
+ * @example
123
+ * if (isSessionPaused(wi.target)) return false; // do not dispatch
124
+ */
125
+ export declare function isSessionPaused(session: string | null | undefined): boolean;
126
+ /**
127
+ * The paused team named by an id, a name (case-insensitive) or a member
128
+ * session — whatever an agent typed.
129
+ *
130
+ * @param ref - Team id, team name, or member session
131
+ * @returns Info, or null
132
+ */
133
+ export declare function pausedTeamByRef(ref: string | null | undefined): PausedTeamInfo | null;
134
+ /**
135
+ * Every team paused now.
136
+ *
137
+ * @param now - Clock (ms)
138
+ * @returns Infos
139
+ */
140
+ export declare function listPausedTeams(now?: number): PausedTeamInfo[];
141
+ /**
142
+ * Teams whose pause has an `until` that has passed (still stored as paused).
143
+ *
144
+ * @param now - Clock (ms)
145
+ * @returns Infos due for auto-resume
146
+ */
147
+ export declare function listExpiredPauses(now?: number): PausedTeamInfo[];
148
+ /**
149
+ * The refusal an agent gets when it targets a paused team (English: harness
150
+ * text rule).
151
+ *
152
+ * @param info - The paused team
153
+ * @param options - `callerIsOrc`: the orchestrator asked (it tells the owner, not itself)
154
+ * @returns Message
155
+ *
156
+ * @example
157
+ * pausedRefusalMessage({ teamName: 'Crewly', issueRepo: 'stevehuang0115/crewly' })
158
+ * // 'Crewly is paused by the owner. File a GitHub issue instead: `gh issue create -R stevehuang0115/crewly --title "…" --body "…"`'
159
+ */
160
+ export declare function pausedRefusalMessage(info: Pick<PausedTeamInfo, 'teamName' | 'issueRepo'> & {
161
+ pause?: TeamPauseState;
162
+ }, options?: {
163
+ callerIsOrc?: boolean;
164
+ }): string;
165
+ /** Clear the index (tests only). */
166
+ export declare function resetTeamPauseRegistryForTesting(): void;
167
+ //# sourceMappingURL=team-pause.registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-pause.registry.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/team/team-pause.registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG7E,uDAAuD;AACvD,MAAM,WAAW,cAAc;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,cAAc,CAAC;IACtB,kFAAkF;IAClF,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,2BAA2B;IAC3B,WAAW,EAAE,MAAM,EAAE,CAAC;CACtB;AAOD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,GAAE,MAAmB,GAAG,OAAO,CAKzG;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,GAAE,MAAmB,GAAG,OAAO,CAEhH;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,aAAa,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,MAAM,EAAE,CAGjI;AAiCD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAQlE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAGpD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAEpE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,IAAI,CAK5D;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,GAAE,MAAmB,GAAG,cAAc,GAAG,IAAI,CAIjH;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAEzE;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,GAAE,MAAmB,GAAG,cAAc,GAAG,IAAI,CAIvH;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAE3E;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,cAAc,GAAG,IAAI,CASrF;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAmB,GAAG,cAAc,EAAE,CAE1E;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAmB,GAAG,cAAc,EAAE,CAE5E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CACnC,IAAI,EAAE,IAAI,CAAC,cAAc,EAAE,UAAU,GAAG,WAAW,CAAC,GAAG;IAAE,KAAK,CAAC,EAAE,cAAc,CAAA;CAAE,EACjF,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,GACrC,MAAM,CASR;AAED,oCAAoC;AACpC,wBAAgB,gCAAgC,IAAI,IAAI,CAIvD"}