crewly 1.20.362 → 1.20.364

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 (320) hide show
  1. package/config/hooks/destructive-git-guard/guard.mjs +96 -0
  2. package/config/roles/orchestrator/prompt.md +3 -3
  3. package/config/roles/team-leader/prompt.md +2 -0
  4. package/config/skills/agent/core/ask-owner/SKILL.md +31 -0
  5. package/config/skills/agent/core/ask-owner/execute.sh +14 -3
  6. package/config/skills/agent/core/ask-owner/execute.test.sh +8 -0
  7. package/config/skills/agent/core/gmail-read/SKILL.md +8 -0
  8. package/config/skills/agent/core/gmail-send/SKILL.md +10 -3
  9. package/config/skills/agent/core/gmail-send/execute.sh +2 -1
  10. package/config/skills/agent/core/gmail-watch-thread/SKILL.md +69 -0
  11. package/config/skills/agent/core/gmail-watch-thread/execute.sh +51 -0
  12. package/config/skills/agent/core/project-tickets/SKILL.md +12 -0
  13. package/config/skills/agent/core/project-tickets/execute.sh +13 -3
  14. package/config/skills/agent/core/project-tickets/execute.test.sh +4 -0
  15. package/config/skills/agent/core/watch-for-event/SKILL.md +22 -0
  16. package/config/skills/orchestrator/delegate-task/SKILL.md +14 -0
  17. package/config/skills/orchestrator/delegate-task/execute.sh +19 -2
  18. package/config/skills/orchestrator/delegate-task/execute.test.sh +30 -0
  19. package/config/skills/orchestrator/harness-login/SKILL.md +1 -1
  20. package/config/skills/team-leader/delegate-task/execute.sh +6 -0
  21. package/dist/backend/backend/src/constants.d.ts +74 -2
  22. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  23. package/dist/backend/backend/src/constants.js +75 -3
  24. package/dist/backend/backend/src/constants.js.map +1 -1
  25. package/dist/backend/backend/src/controllers/chat/chat.controller.d.ts.map +1 -1
  26. package/dist/backend/backend/src/controllers/chat/chat.controller.js +14 -1
  27. package/dist/backend/backend/src/controllers/chat/chat.controller.js.map +1 -1
  28. package/dist/backend/backend/src/controllers/google/google.controller.d.ts +44 -0
  29. package/dist/backend/backend/src/controllers/google/google.controller.d.ts.map +1 -1
  30. package/dist/backend/backend/src/controllers/google/google.controller.js +121 -3
  31. package/dist/backend/backend/src/controllers/google/google.controller.js.map +1 -1
  32. package/dist/backend/backend/src/controllers/google/google.routes.d.ts.map +1 -1
  33. package/dist/backend/backend/src/controllers/google/google.routes.js +5 -1
  34. package/dist/backend/backend/src/controllers/google/google.routes.js.map +1 -1
  35. package/dist/backend/backend/src/controllers/harness/owner-login.controller.d.ts +1 -1
  36. package/dist/backend/backend/src/controllers/harness/owner-login.controller.js +1 -1
  37. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.d.ts +1 -1
  38. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.d.ts.map +1 -1
  39. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.js +2 -1
  40. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.js.map +1 -1
  41. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.d.ts.map +1 -1
  42. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js +7 -0
  43. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js.map +1 -1
  44. package/dist/backend/backend/src/index.d.ts.map +1 -1
  45. package/dist/backend/backend/src/index.js +61 -0
  46. package/dist/backend/backend/src/index.js.map +1 -1
  47. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
  48. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js +2 -1
  49. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  50. package/dist/backend/backend/src/services/decisions/decision-card.d.ts +46 -13
  51. package/dist/backend/backend/src/services/decisions/decision-card.d.ts.map +1 -1
  52. package/dist/backend/backend/src/services/decisions/decision-card.js +100 -50
  53. package/dist/backend/backend/src/services/decisions/decision-card.js.map +1 -1
  54. package/dist/backend/backend/src/services/decisions/decision-contract.d.ts +23 -1
  55. package/dist/backend/backend/src/services/decisions/decision-contract.d.ts.map +1 -1
  56. package/dist/backend/backend/src/services/decisions/decision-contract.js +76 -4
  57. package/dist/backend/backend/src/services/decisions/decision-contract.js.map +1 -1
  58. package/dist/backend/backend/src/services/decisions/decision-wording.d.ts +167 -0
  59. package/dist/backend/backend/src/services/decisions/decision-wording.d.ts.map +1 -0
  60. package/dist/backend/backend/src/services/decisions/decision-wording.js +406 -0
  61. package/dist/backend/backend/src/services/decisions/decision-wording.js.map +1 -0
  62. package/dist/backend/backend/src/services/decisions/decision.service.d.ts +30 -1
  63. package/dist/backend/backend/src/services/decisions/decision.service.d.ts.map +1 -1
  64. package/dist/backend/backend/src/services/decisions/decision.service.js +44 -8
  65. package/dist/backend/backend/src/services/decisions/decision.service.js.map +1 -1
  66. package/dist/backend/backend/src/services/decisions/decision.wiring.d.ts.map +1 -1
  67. package/dist/backend/backend/src/services/decisions/decision.wiring.js +17 -0
  68. package/dist/backend/backend/src/services/decisions/decision.wiring.js.map +1 -1
  69. package/dist/backend/backend/src/services/event-bus/event-bus.service.d.ts.map +1 -1
  70. package/dist/backend/backend/src/services/event-bus/event-bus.service.js +6 -1
  71. package/dist/backend/backend/src/services/event-bus/event-bus.service.js.map +1 -1
  72. package/dist/backend/backend/src/services/google/gmail-reply-watch.service.d.ts +142 -0
  73. package/dist/backend/backend/src/services/google/gmail-reply-watch.service.d.ts.map +1 -0
  74. package/dist/backend/backend/src/services/google/gmail-reply-watch.service.js +304 -0
  75. package/dist/backend/backend/src/services/google/gmail-reply-watch.service.js.map +1 -0
  76. package/dist/backend/backend/src/services/google/gmail-send-approval.service.d.ts +130 -0
  77. package/dist/backend/backend/src/services/google/gmail-send-approval.service.d.ts.map +1 -0
  78. package/dist/backend/backend/src/services/google/gmail-send-approval.service.js +216 -0
  79. package/dist/backend/backend/src/services/google/gmail-send-approval.service.js.map +1 -0
  80. package/dist/backend/backend/src/services/google/gmail-send-gate.d.ts +28 -13
  81. package/dist/backend/backend/src/services/google/gmail-send-gate.d.ts.map +1 -1
  82. package/dist/backend/backend/src/services/google/gmail-send-gate.js +82 -29
  83. package/dist/backend/backend/src/services/google/gmail-send-gate.js.map +1 -1
  84. package/dist/backend/backend/src/services/google/gmail.service.d.ts +90 -0
  85. package/dist/backend/backend/src/services/google/gmail.service.d.ts.map +1 -1
  86. package/dist/backend/backend/src/services/google/gmail.service.js +122 -0
  87. package/dist/backend/backend/src/services/google/gmail.service.js.map +1 -1
  88. package/dist/backend/backend/src/services/harness/claude-accounts.d.ts +24 -14
  89. package/dist/backend/backend/src/services/harness/claude-accounts.d.ts.map +1 -1
  90. package/dist/backend/backend/src/services/harness/claude-accounts.js +35 -21
  91. package/dist/backend/backend/src/services/harness/claude-accounts.js.map +1 -1
  92. package/dist/backend/backend/src/services/harness/claude-native-login.d.ts +106 -0
  93. package/dist/backend/backend/src/services/harness/claude-native-login.d.ts.map +1 -0
  94. package/dist/backend/backend/src/services/harness/claude-native-login.js +174 -0
  95. package/dist/backend/backend/src/services/harness/claude-native-login.js.map +1 -0
  96. package/dist/backend/backend/src/services/harness/claude-token-migration.service.d.ts +158 -0
  97. package/dist/backend/backend/src/services/harness/claude-token-migration.service.d.ts.map +1 -0
  98. package/dist/backend/backend/src/services/harness/claude-token-migration.service.js +283 -0
  99. package/dist/backend/backend/src/services/harness/claude-token-migration.service.js.map +1 -0
  100. package/dist/backend/backend/src/services/harness/harness-credentials.store.d.ts +44 -24
  101. package/dist/backend/backend/src/services/harness/harness-credentials.store.d.ts.map +1 -1
  102. package/dist/backend/backend/src/services/harness/harness-credentials.store.js +96 -39
  103. package/dist/backend/backend/src/services/harness/harness-credentials.store.js.map +1 -1
  104. package/dist/backend/backend/src/services/harness/harness-registry.js +1 -1
  105. package/dist/backend/backend/src/services/harness/harness-registry.js.map +1 -1
  106. package/dist/backend/backend/src/services/harness/harness-relogin.service.d.ts +2 -2
  107. package/dist/backend/backend/src/services/harness/harness-relogin.service.js +2 -2
  108. package/dist/backend/backend/src/services/harness/harness.service.d.ts.map +1 -1
  109. package/dist/backend/backend/src/services/harness/harness.service.js +10 -1
  110. package/dist/backend/backend/src/services/harness/harness.service.js.map +1 -1
  111. package/dist/backend/backend/src/services/harness/login-broker.service.d.ts +24 -21
  112. package/dist/backend/backend/src/services/harness/login-broker.service.d.ts.map +1 -1
  113. package/dist/backend/backend/src/services/harness/login-broker.service.js +50 -64
  114. package/dist/backend/backend/src/services/harness/login-broker.service.js.map +1 -1
  115. package/dist/backend/backend/src/services/harness/login-rules.d.ts +17 -11
  116. package/dist/backend/backend/src/services/harness/login-rules.d.ts.map +1 -1
  117. package/dist/backend/backend/src/services/harness/login-rules.js +22 -25
  118. package/dist/backend/backend/src/services/harness/login-rules.js.map +1 -1
  119. package/dist/backend/backend/src/services/open-items/open-item-extractor.d.ts +36 -0
  120. package/dist/backend/backend/src/services/open-items/open-item-extractor.d.ts.map +1 -1
  121. package/dist/backend/backend/src/services/open-items/open-item-extractor.js +70 -0
  122. package/dist/backend/backend/src/services/open-items/open-item-extractor.js.map +1 -1
  123. package/dist/backend/backend/src/services/open-items/open-items.service.d.ts +25 -0
  124. package/dist/backend/backend/src/services/open-items/open-items.service.d.ts.map +1 -1
  125. package/dist/backend/backend/src/services/open-items/open-items.service.js +65 -5
  126. package/dist/backend/backend/src/services/open-items/open-items.service.js.map +1 -1
  127. package/dist/backend/backend/src/services/orc/orc-status-router.service.d.ts.map +1 -1
  128. package/dist/backend/backend/src/services/orc/orc-status-router.service.js +5 -2
  129. package/dist/backend/backend/src/services/orc/orc-status-router.service.js.map +1 -1
  130. package/dist/backend/backend/src/services/orc/owner-completion-report.service.d.ts +50 -2
  131. package/dist/backend/backend/src/services/orc/owner-completion-report.service.d.ts.map +1 -1
  132. package/dist/backend/backend/src/services/orc/owner-completion-report.service.js +103 -11
  133. package/dist/backend/backend/src/services/orc/owner-completion-report.service.js.map +1 -1
  134. package/dist/backend/backend/src/services/orc/owner-completion-report.text.d.ts +27 -0
  135. package/dist/backend/backend/src/services/orc/owner-completion-report.text.d.ts.map +1 -1
  136. package/dist/backend/backend/src/services/orc/owner-completion-report.text.js +25 -0
  137. package/dist/backend/backend/src/services/orc/owner-completion-report.text.js.map +1 -1
  138. package/dist/backend/backend/src/services/orc/reply-destination-resolver.d.ts.map +1 -1
  139. package/dist/backend/backend/src/services/orc/reply-destination-resolver.js +4 -2
  140. package/dist/backend/backend/src/services/orc/reply-destination-resolver.js.map +1 -1
  141. package/dist/backend/backend/src/services/orc/team-lead-report.d.ts +120 -0
  142. package/dist/backend/backend/src/services/orc/team-lead-report.d.ts.map +1 -0
  143. package/dist/backend/backend/src/services/orc/team-lead-report.js +198 -0
  144. package/dist/backend/backend/src/services/orc/team-lead-report.js.map +1 -0
  145. package/dist/backend/backend/src/services/orc/team-lead-report.wiring.d.ts +53 -0
  146. package/dist/backend/backend/src/services/orc/team-lead-report.wiring.d.ts.map +1 -0
  147. package/dist/backend/backend/src/services/orc/team-lead-report.wiring.js +85 -0
  148. package/dist/backend/backend/src/services/orc/team-lead-report.wiring.js.map +1 -0
  149. package/dist/backend/backend/src/services/orc/work-item-destination.d.ts +35 -0
  150. package/dist/backend/backend/src/services/orc/work-item-destination.d.ts.map +1 -1
  151. package/dist/backend/backend/src/services/orc/work-item-destination.js +38 -1
  152. package/dist/backend/backend/src/services/orc/work-item-destination.js.map +1 -1
  153. package/dist/backend/backend/src/services/project-tickets/project-ticket.service.d.ts +5 -0
  154. package/dist/backend/backend/src/services/project-tickets/project-ticket.service.d.ts.map +1 -1
  155. package/dist/backend/backend/src/services/project-tickets/project-ticket.service.js +30 -6
  156. package/dist/backend/backend/src/services/project-tickets/project-ticket.service.js.map +1 -1
  157. package/dist/backend/backend/src/services/project-tickets/ticket-file.d.ts.map +1 -1
  158. package/dist/backend/backend/src/services/project-tickets/ticket-file.js +6 -1
  159. package/dist/backend/backend/src/services/project-tickets/ticket-file.js.map +1 -1
  160. package/dist/backend/backend/src/services/project-tickets/ticket-hold-registry.d.ts +64 -0
  161. package/dist/backend/backend/src/services/project-tickets/ticket-hold-registry.d.ts.map +1 -0
  162. package/dist/backend/backend/src/services/project-tickets/ticket-hold-registry.js +93 -0
  163. package/dist/backend/backend/src/services/project-tickets/ticket-hold-registry.js.map +1 -0
  164. package/dist/backend/backend/src/services/project-tickets/ticket-hold.d.ts +176 -0
  165. package/dist/backend/backend/src/services/project-tickets/ticket-hold.d.ts.map +1 -0
  166. package/dist/backend/backend/src/services/project-tickets/ticket-hold.js +301 -0
  167. package/dist/backend/backend/src/services/project-tickets/ticket-hold.js.map +1 -0
  168. package/dist/backend/backend/src/services/reconciler/reconcile-rules.d.ts +33 -3
  169. package/dist/backend/backend/src/services/reconciler/reconcile-rules.d.ts.map +1 -1
  170. package/dist/backend/backend/src/services/reconciler/reconcile-rules.js +50 -4
  171. package/dist/backend/backend/src/services/reconciler/reconcile-rules.js.map +1 -1
  172. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts +8 -1
  173. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts.map +1 -1
  174. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js +26 -0
  175. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js.map +1 -1
  176. package/dist/backend/backend/src/services/reconciler/reconciler.service.d.ts +6 -1
  177. package/dist/backend/backend/src/services/reconciler/reconciler.service.d.ts.map +1 -1
  178. package/dist/backend/backend/src/services/reconciler/reconciler.service.js +3 -2
  179. package/dist/backend/backend/src/services/reconciler/reconciler.service.js.map +1 -1
  180. package/dist/backend/backend/src/services/runtime-fallback/runtime-fallback.wiring.d.ts.map +1 -1
  181. package/dist/backend/backend/src/services/runtime-fallback/runtime-fallback.wiring.js +4 -1
  182. package/dist/backend/backend/src/services/runtime-fallback/runtime-fallback.wiring.js.map +1 -1
  183. package/dist/backend/backend/src/services/stall/stall-watcher.service.d.ts +34 -0
  184. package/dist/backend/backend/src/services/stall/stall-watcher.service.d.ts.map +1 -1
  185. package/dist/backend/backend/src/services/stall/stall-watcher.service.js +70 -2
  186. package/dist/backend/backend/src/services/stall/stall-watcher.service.js.map +1 -1
  187. package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts +36 -0
  188. package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
  189. package/dist/backend/backend/src/services/task-pool/task-pool.service.js +72 -1
  190. package/dist/backend/backend/src/services/task-pool/task-pool.service.js.map +1 -1
  191. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.d.ts +3 -0
  192. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.d.ts.map +1 -1
  193. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.js +5 -2
  194. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.js.map +1 -1
  195. package/dist/backend/backend/src/services/worktree/worktree-unmerged-notice.d.ts +7 -2
  196. package/dist/backend/backend/src/services/worktree/worktree-unmerged-notice.d.ts.map +1 -1
  197. package/dist/backend/backend/src/services/worktree/worktree-unmerged-notice.js +11 -2
  198. package/dist/backend/backend/src/services/worktree/worktree-unmerged-notice.js.map +1 -1
  199. package/dist/backend/backend/src/types/decision.types.d.ts +53 -1
  200. package/dist/backend/backend/src/types/decision.types.d.ts.map +1 -1
  201. package/dist/backend/backend/src/types/event-bus.types.d.ts +10 -2
  202. package/dist/backend/backend/src/types/event-bus.types.d.ts.map +1 -1
  203. package/dist/backend/backend/src/types/event-bus.types.js +8 -0
  204. package/dist/backend/backend/src/types/event-bus.types.js.map +1 -1
  205. package/dist/backend/backend/src/types/project-ticket.types.d.ts +7 -0
  206. package/dist/backend/backend/src/types/project-ticket.types.d.ts.map +1 -1
  207. package/dist/backend/backend/src/types/project-ticket.types.js +1 -0
  208. package/dist/backend/backend/src/types/project-ticket.types.js.map +1 -1
  209. package/dist/backend/backend/src/types/v2/open-item.types.d.ts +8 -1
  210. package/dist/backend/backend/src/types/v2/open-item.types.d.ts.map +1 -1
  211. package/dist/backend/build-info.json +2 -2
  212. package/dist/cli/backend/src/constants.d.ts +74 -2
  213. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  214. package/dist/cli/backend/src/constants.js +75 -3
  215. package/dist/cli/backend/src/constants.js.map +1 -1
  216. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
  217. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js +2 -1
  218. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  219. package/dist/cli/backend/src/services/decisions/decision-card.d.ts +46 -13
  220. package/dist/cli/backend/src/services/decisions/decision-card.d.ts.map +1 -1
  221. package/dist/cli/backend/src/services/decisions/decision-card.js +100 -50
  222. package/dist/cli/backend/src/services/decisions/decision-card.js.map +1 -1
  223. package/dist/cli/backend/src/services/decisions/decision-contract.d.ts +23 -1
  224. package/dist/cli/backend/src/services/decisions/decision-contract.d.ts.map +1 -1
  225. package/dist/cli/backend/src/services/decisions/decision-contract.js +76 -4
  226. package/dist/cli/backend/src/services/decisions/decision-contract.js.map +1 -1
  227. package/dist/cli/backend/src/services/decisions/decision-wording.d.ts +167 -0
  228. package/dist/cli/backend/src/services/decisions/decision-wording.d.ts.map +1 -0
  229. package/dist/cli/backend/src/services/decisions/decision-wording.js +406 -0
  230. package/dist/cli/backend/src/services/decisions/decision-wording.js.map +1 -0
  231. package/dist/cli/backend/src/services/decisions/decision.service.d.ts +30 -1
  232. package/dist/cli/backend/src/services/decisions/decision.service.d.ts.map +1 -1
  233. package/dist/cli/backend/src/services/decisions/decision.service.js +44 -8
  234. package/dist/cli/backend/src/services/decisions/decision.service.js.map +1 -1
  235. package/dist/cli/backend/src/services/event-bus/event-bus.service.d.ts.map +1 -1
  236. package/dist/cli/backend/src/services/event-bus/event-bus.service.js +6 -1
  237. package/dist/cli/backend/src/services/event-bus/event-bus.service.js.map +1 -1
  238. package/dist/cli/backend/src/services/harness/claude-accounts.d.ts +24 -14
  239. package/dist/cli/backend/src/services/harness/claude-accounts.d.ts.map +1 -1
  240. package/dist/cli/backend/src/services/harness/claude-accounts.js +35 -21
  241. package/dist/cli/backend/src/services/harness/claude-accounts.js.map +1 -1
  242. package/dist/cli/backend/src/services/harness/claude-native-login.d.ts +106 -0
  243. package/dist/cli/backend/src/services/harness/claude-native-login.d.ts.map +1 -0
  244. package/dist/cli/backend/src/services/harness/claude-native-login.js +174 -0
  245. package/dist/cli/backend/src/services/harness/claude-native-login.js.map +1 -0
  246. package/dist/cli/backend/src/services/harness/claude-token-migration.service.d.ts +158 -0
  247. package/dist/cli/backend/src/services/harness/claude-token-migration.service.d.ts.map +1 -0
  248. package/dist/cli/backend/src/services/harness/claude-token-migration.service.js +283 -0
  249. package/dist/cli/backend/src/services/harness/claude-token-migration.service.js.map +1 -0
  250. package/dist/cli/backend/src/services/harness/harness-credentials.store.d.ts +44 -24
  251. package/dist/cli/backend/src/services/harness/harness-credentials.store.d.ts.map +1 -1
  252. package/dist/cli/backend/src/services/harness/harness-credentials.store.js +96 -39
  253. package/dist/cli/backend/src/services/harness/harness-credentials.store.js.map +1 -1
  254. package/dist/cli/backend/src/services/harness/harness-registry.js +1 -1
  255. package/dist/cli/backend/src/services/harness/harness-registry.js.map +1 -1
  256. package/dist/cli/backend/src/services/harness/harness-relogin.service.d.ts +2 -2
  257. package/dist/cli/backend/src/services/harness/harness-relogin.service.js +2 -2
  258. package/dist/cli/backend/src/services/harness/harness.service.d.ts.map +1 -1
  259. package/dist/cli/backend/src/services/harness/harness.service.js +10 -1
  260. package/dist/cli/backend/src/services/harness/harness.service.js.map +1 -1
  261. package/dist/cli/backend/src/services/harness/login-broker.service.d.ts +24 -21
  262. package/dist/cli/backend/src/services/harness/login-broker.service.d.ts.map +1 -1
  263. package/dist/cli/backend/src/services/harness/login-broker.service.js +50 -64
  264. package/dist/cli/backend/src/services/harness/login-broker.service.js.map +1 -1
  265. package/dist/cli/backend/src/services/harness/login-rules.d.ts +17 -11
  266. package/dist/cli/backend/src/services/harness/login-rules.d.ts.map +1 -1
  267. package/dist/cli/backend/src/services/harness/login-rules.js +22 -25
  268. package/dist/cli/backend/src/services/harness/login-rules.js.map +1 -1
  269. package/dist/cli/backend/src/services/open-items/open-item-extractor.d.ts +36 -0
  270. package/dist/cli/backend/src/services/open-items/open-item-extractor.d.ts.map +1 -1
  271. package/dist/cli/backend/src/services/open-items/open-item-extractor.js +70 -0
  272. package/dist/cli/backend/src/services/open-items/open-item-extractor.js.map +1 -1
  273. package/dist/cli/backend/src/services/open-items/open-items.service.d.ts +25 -0
  274. package/dist/cli/backend/src/services/open-items/open-items.service.d.ts.map +1 -1
  275. package/dist/cli/backend/src/services/open-items/open-items.service.js +65 -5
  276. package/dist/cli/backend/src/services/open-items/open-items.service.js.map +1 -1
  277. package/dist/cli/backend/src/services/orc/owner-completion-report.text.d.ts +113 -0
  278. package/dist/cli/backend/src/services/orc/owner-completion-report.text.d.ts.map +1 -0
  279. package/dist/cli/backend/src/services/orc/owner-completion-report.text.js +195 -0
  280. package/dist/cli/backend/src/services/orc/owner-completion-report.text.js.map +1 -0
  281. package/dist/cli/backend/src/services/orc/team-lead-report.d.ts +120 -0
  282. package/dist/cli/backend/src/services/orc/team-lead-report.d.ts.map +1 -0
  283. package/dist/cli/backend/src/services/orc/team-lead-report.js +198 -0
  284. package/dist/cli/backend/src/services/orc/team-lead-report.js.map +1 -0
  285. package/dist/cli/backend/src/services/orc/team-lead-report.wiring.d.ts +53 -0
  286. package/dist/cli/backend/src/services/orc/team-lead-report.wiring.d.ts.map +1 -0
  287. package/dist/cli/backend/src/services/orc/team-lead-report.wiring.js +85 -0
  288. package/dist/cli/backend/src/services/orc/team-lead-report.wiring.js.map +1 -0
  289. package/dist/cli/backend/src/services/orc/work-item-destination.d.ts +35 -0
  290. package/dist/cli/backend/src/services/orc/work-item-destination.d.ts.map +1 -1
  291. package/dist/cli/backend/src/services/orc/work-item-destination.js +38 -1
  292. package/dist/cli/backend/src/services/orc/work-item-destination.js.map +1 -1
  293. package/dist/cli/backend/src/services/project-tickets/project-ticket.service.d.ts +5 -0
  294. package/dist/cli/backend/src/services/project-tickets/project-ticket.service.d.ts.map +1 -1
  295. package/dist/cli/backend/src/services/project-tickets/project-ticket.service.js +30 -6
  296. package/dist/cli/backend/src/services/project-tickets/project-ticket.service.js.map +1 -1
  297. package/dist/cli/backend/src/services/project-tickets/ticket-file.d.ts.map +1 -1
  298. package/dist/cli/backend/src/services/project-tickets/ticket-file.js +6 -1
  299. package/dist/cli/backend/src/services/project-tickets/ticket-file.js.map +1 -1
  300. package/dist/cli/backend/src/services/project-tickets/ticket-hold.d.ts +176 -0
  301. package/dist/cli/backend/src/services/project-tickets/ticket-hold.d.ts.map +1 -0
  302. package/dist/cli/backend/src/services/project-tickets/ticket-hold.js +301 -0
  303. package/dist/cli/backend/src/services/project-tickets/ticket-hold.js.map +1 -0
  304. package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts +36 -0
  305. package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
  306. package/dist/cli/backend/src/services/task-pool/task-pool.service.js +72 -1
  307. package/dist/cli/backend/src/services/task-pool/task-pool.service.js.map +1 -1
  308. package/dist/cli/backend/src/types/decision.types.d.ts +53 -1
  309. package/dist/cli/backend/src/types/decision.types.d.ts.map +1 -1
  310. package/dist/cli/backend/src/types/event-bus.types.d.ts +10 -2
  311. package/dist/cli/backend/src/types/event-bus.types.d.ts.map +1 -1
  312. package/dist/cli/backend/src/types/event-bus.types.js +8 -0
  313. package/dist/cli/backend/src/types/event-bus.types.js.map +1 -1
  314. package/dist/cli/backend/src/types/project-ticket.types.d.ts +7 -0
  315. package/dist/cli/backend/src/types/project-ticket.types.d.ts.map +1 -1
  316. package/dist/cli/backend/src/types/project-ticket.types.js +1 -0
  317. package/dist/cli/backend/src/types/project-ticket.types.js.map +1 -1
  318. package/dist/cli/backend/src/types/v2/open-item.types.d.ts +8 -1
  319. package/dist/cli/backend/src/types/v2/open-item.types.d.ts.map +1 -1
  320. package/package.json +1 -1
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Gmail Send Approval
3
+ *
4
+ * Puts a drafted email in front of the owner as a decision card and sends
5
+ * it only when the owner taps Send (CREW-257,
6
+ * specs/2026-10-06-gmail-approve-send-and-reply-wake.md).
7
+ *
8
+ * The send happens in exactly one place, {@link GmailSendApprovalService.onSettled},
9
+ * which the decision service calls when an owner answer settles a
10
+ * `gmail_send` card (button, reaction, thread reply, dashboard). Nothing an
11
+ * agent can call reaches it, so an agent cannot approve its own mail.
12
+ *
13
+ * Each approval is bound to one hold (`agent:draftId`) and to a fingerprint
14
+ * of the draft as the owner saw it. A hold settles once; a draft that was
15
+ * edited after the card was raised is not sent, a new card is raised for the
16
+ * edited text instead.
17
+ *
18
+ * @module services/google/gmail-send-approval
19
+ */
20
+ import type { OwnerDecision, DecisionOption, GmailSendSubject } from '../../types/decision.types.js';
21
+ import type { DecisionKindHandler } from '../decisions/decision.service.js';
22
+ import type { GmailDraftContent, GmailSendResult } from './gmail.service.js';
23
+ import { type HeldSend } from './gmail-send-gate.js';
24
+ /** Option keys on the card. */
25
+ export declare const GMAIL_SEND_KEYS: {
26
+ readonly SEND: "send";
27
+ readonly NOT_NOW: "not_now";
28
+ };
29
+ /** How long the card waits before the (safe) default, Discard, applies. */
30
+ export declare const GMAIL_SEND_DEADLINE_MS: number;
31
+ /** The Gmail calls the approval needs. */
32
+ export interface GmailDraftApi {
33
+ getDraft(draftId: string): Promise<GmailDraftContent>;
34
+ sendDraft(draftId: string): Promise<GmailSendResult>;
35
+ }
36
+ /** The decision-service surface used here. */
37
+ export interface GmailApprovalDecisions {
38
+ askPrebuilt(ask: {
39
+ kind: 'gmail_send';
40
+ asker: string;
41
+ question: string;
42
+ options: DecisionOption[];
43
+ defaultKey: string;
44
+ yesKey: string;
45
+ deadline: Date;
46
+ sensitive: 'email';
47
+ gmail: GmailSendSubject;
48
+ }): Promise<OwnerDecision>;
49
+ chooseFromDashboard(id: string, optionKey: string): Promise<OwnerDecision>;
50
+ replyInThread?(id: string, text: string): Promise<boolean>;
51
+ cancelWhere?(predicate: (d: OwnerDecision) => boolean, reason: string): Promise<unknown>;
52
+ }
53
+ /** Dependencies, injected so tests need no Slack or Google. */
54
+ export interface GmailSendApprovalDeps {
55
+ /** Gmail access for the account the draft lives in */
56
+ gmailFor: (account?: string) => GmailDraftApi;
57
+ /** The decision service, once cards run */
58
+ decisions: () => GmailApprovalDecisions | null;
59
+ /** Whether a card can be posted at all */
60
+ canAskInSlack: () => boolean;
61
+ /** Tell the drafting agent what happened */
62
+ tellAgent: (session: string, message: string) => Promise<unknown>;
63
+ /** Display name for the card */
64
+ agentNameOf?: (session: string) => Promise<string | undefined>;
65
+ /** Called after a send so replies on the thread wake the agent */
66
+ onSent?: (info: {
67
+ threadId: string;
68
+ agentSession: string;
69
+ account?: string;
70
+ }) => Promise<void> | void;
71
+ }
72
+ /**
73
+ * sha256 of everything that gets sent: sender, every recipient (including
74
+ * Bcc, a recipient the other parties never see), subject, both bodies and
75
+ * the attachments' names and sizes. A change to any of them is a different
76
+ * email from the one the owner approved.
77
+ *
78
+ * @param d - Draft content
79
+ * @returns Hex digest
80
+ */
81
+ export declare function fingerprintDraft(d: GmailDraftContent): string;
82
+ /**
83
+ * The card question: who it goes to (all of them, Bcc included), what it says.
84
+ *
85
+ * @param agentName - Drafting agent
86
+ * @param d - The draft
87
+ * @returns mrkdwn
88
+ */
89
+ export declare function approvalQuestion(agentName: string, d: GmailDraftContent): string;
90
+ /**
91
+ * Raises the send card and applies the owner's answer.
92
+ */
93
+ export declare class GmailSendApprovalService implements DecisionKindHandler {
94
+ private readonly deps;
95
+ private readonly logger;
96
+ /** Holds being discarded by the owner right now (their settle note says the draft is gone) */
97
+ private readonly discarding;
98
+ constructor(deps: GmailSendApprovalDeps);
99
+ /**
100
+ * A draft was held: read it, remember what the owner will be shown and ask.
101
+ *
102
+ * @param hold - The held send
103
+ * @returns True when a card was raised
104
+ */
105
+ onHeld(hold: HeldSend): Promise<boolean>;
106
+ /**
107
+ * The owner answered on the dashboard route: settle the card so it and the
108
+ * send agree.
109
+ *
110
+ * @param holdId - Hold id
111
+ * @param answer - What the owner chose
112
+ * @returns True when a card carried the answer
113
+ */
114
+ answerFromOwner(holdId: string, answer: 'send' | 'discard'): Promise<boolean>;
115
+ /**
116
+ * The owner discarded the draft on the owner-only route (which deletes it).
117
+ * Settles the card as "not now" and tells the agent the draft is gone.
118
+ *
119
+ * @param holdId - Hold id
120
+ */
121
+ discarded(holdId: string): Promise<void>;
122
+ /**
123
+ * A `gmail_send` card settled. The only place a held draft is sent.
124
+ *
125
+ * @param decision - The settled decision
126
+ * @returns Note for the drafting agent, or null
127
+ */
128
+ onSettled(decision: OwnerDecision): Promise<string | null>;
129
+ }
130
+ //# sourceMappingURL=gmail-send-approval.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gmail-send-approval.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/google/gmail-send-approval.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Gmail Send Approval
3
+ *
4
+ * Puts a drafted email in front of the owner as a decision card and sends
5
+ * it only when the owner taps Send (CREW-257,
6
+ * specs/2026-10-06-gmail-approve-send-and-reply-wake.md).
7
+ *
8
+ * The send happens in exactly one place, {@link GmailSendApprovalService.onSettled},
9
+ * which the decision service calls when an owner answer settles a
10
+ * `gmail_send` card (button, reaction, thread reply, dashboard). Nothing an
11
+ * agent can call reaches it, so an agent cannot approve its own mail.
12
+ *
13
+ * Each approval is bound to one hold (`agent:draftId`) and to a fingerprint
14
+ * of the draft as the owner saw it. A hold settles once; a draft that was
15
+ * edited after the card was raised is not sent, a new card is raised for the
16
+ * edited text instead.
17
+ *
18
+ * @module services/google/gmail-send-approval
19
+ */
20
+ import { createHash } from 'node:crypto';
21
+ import { LoggerService } from '../core/logger.service.js';
22
+ import { clearHeldSend, getHeldSend, holdGmailSend, markSettled, updateHeldSend, wasSettled } from './gmail-send-gate.js';
23
+ /** Option keys on the card. */
24
+ export const GMAIL_SEND_KEYS = { SEND: 'send', NOT_NOW: 'not_now' };
25
+ /** How long the card waits before the (safe) default, Discard, applies. */
26
+ export const GMAIL_SEND_DEADLINE_MS = 24 * 60 * 60 * 1000;
27
+ /** Body characters shown on the card. */
28
+ const PREVIEW_CHARS = 700;
29
+ /**
30
+ * sha256 of everything that gets sent: sender, every recipient (including
31
+ * Bcc, a recipient the other parties never see), subject, both bodies and
32
+ * the attachments' names and sizes. A change to any of them is a different
33
+ * email from the one the owner approved.
34
+ *
35
+ * @param d - Draft content
36
+ * @returns Hex digest
37
+ */
38
+ export function fingerprintDraft(d) {
39
+ return createHash('sha256')
40
+ .update(JSON.stringify([d.from, d.to, d.cc, d.bcc, d.subject, d.body, d.html, d.attachments.map((a) => [a.filename, a.size])]))
41
+ .digest('hex');
42
+ }
43
+ /**
44
+ * The card question: who it goes to (all of them, Bcc included), what it says.
45
+ *
46
+ * @param agentName - Drafting agent
47
+ * @param d - The draft
48
+ * @returns mrkdwn
49
+ */
50
+ export function approvalQuestion(agentName, d) {
51
+ const body = d.body.length > PREVIEW_CHARS ? `${d.body.slice(0, PREVIEW_CHARS)}…` : d.body;
52
+ const lines = [`Send this to ${d.to}?`, `${agentName} drafted an email and wants it sent.`];
53
+ if (d.from)
54
+ lines.push(`*From:* ${d.from}`);
55
+ lines.push(`*To:* ${d.to}`);
56
+ if (d.cc)
57
+ lines.push(`*Cc:* ${d.cc}`);
58
+ if (d.bcc)
59
+ lines.push(`*Bcc:* ${d.bcc} _(the other recipients will not see this)_`);
60
+ lines.push(`*Subject:* ${d.subject}`);
61
+ if (d.attachments.length)
62
+ lines.push(`*Attachments:* ${d.attachments.map((a) => `${a.filename} (${a.size} B)`).join(', ')}`);
63
+ return `${lines.join('\n')}\n\n${body || '(no plain-text body)'}`;
64
+ }
65
+ /**
66
+ * Raises the send card and applies the owner's answer.
67
+ */
68
+ export class GmailSendApprovalService {
69
+ deps;
70
+ logger;
71
+ /** Holds being discarded by the owner right now (their settle note says the draft is gone) */
72
+ discarding = new Set();
73
+ constructor(deps) {
74
+ this.deps = deps;
75
+ this.logger = LoggerService.getInstance().createComponentLogger('GmailSendApproval');
76
+ }
77
+ /**
78
+ * A draft was held: read it, remember what the owner will be shown and ask.
79
+ *
80
+ * @param hold - The held send
81
+ * @returns True when a card was raised
82
+ */
83
+ async onHeld(hold) {
84
+ const decisions = this.deps.decisions();
85
+ if (!decisions || !this.deps.canAskInSlack())
86
+ return false;
87
+ const gmail = this.deps.gmailFor(hold.account);
88
+ const draft = await gmail.getDraft(hold.draftId);
89
+ const fingerprint = fingerprintDraft(draft);
90
+ const agentName = (await this.deps.agentNameOf?.(hold.agentSession).catch(() => undefined)) ?? hold.agentSession;
91
+ const decision = await decisions.askPrebuilt({
92
+ kind: 'gmail_send',
93
+ asker: hold.agentSession,
94
+ question: approvalQuestion(agentName, draft),
95
+ options: [
96
+ { key: GMAIL_SEND_KEYS.SEND, label: 'Yes', detail: 'send exactly this email, once' },
97
+ { key: GMAIL_SEND_KEYS.NOT_NOW, label: 'Not now', detail: 'nothing is sent; the draft stays in Gmail' },
98
+ ],
99
+ defaultKey: GMAIL_SEND_KEYS.NOT_NOW,
100
+ yesKey: GMAIL_SEND_KEYS.SEND,
101
+ deadline: new Date(Date.now() + GMAIL_SEND_DEADLINE_MS),
102
+ sensitive: 'email',
103
+ gmail: { agentSession: hold.agentSession, agentName, holdId: hold.id, draftId: hold.draftId, to: hold.to, subject: hold.subject },
104
+ });
105
+ updateHeldSend(hold.id, { fingerprint, decisionId: decision.id });
106
+ this.logger.info('Asked the owner about a drafted email', { holdId: hold.id, decisionId: decision.id });
107
+ return true;
108
+ }
109
+ /**
110
+ * The owner answered on the dashboard route: settle the card so it and the
111
+ * send agree.
112
+ *
113
+ * @param holdId - Hold id
114
+ * @param answer - What the owner chose
115
+ * @returns True when a card carried the answer
116
+ */
117
+ async answerFromOwner(holdId, answer) {
118
+ const hold = getHeldSend(holdId);
119
+ const decisions = this.deps.decisions();
120
+ if (!hold?.decisionId || !decisions)
121
+ return false;
122
+ await decisions.chooseFromDashboard(hold.decisionId, answer === 'send' ? GMAIL_SEND_KEYS.SEND : GMAIL_SEND_KEYS.NOT_NOW);
123
+ return true;
124
+ }
125
+ /**
126
+ * The owner discarded the draft on the owner-only route (which deletes it).
127
+ * Settles the card as "not now" and tells the agent the draft is gone.
128
+ *
129
+ * @param holdId - Hold id
130
+ */
131
+ async discarded(holdId) {
132
+ this.discarding.add(holdId);
133
+ try {
134
+ await this.answerFromOwner(holdId, 'discard');
135
+ }
136
+ finally {
137
+ this.discarding.delete(holdId);
138
+ }
139
+ }
140
+ /**
141
+ * A `gmail_send` card settled. The only place a held draft is sent.
142
+ *
143
+ * @param decision - The settled decision
144
+ * @returns Note for the drafting agent, or null
145
+ */
146
+ async onSettled(decision) {
147
+ const subject = decision.gmail;
148
+ if (!subject)
149
+ return null;
150
+ const hold = getHeldSend(subject.holdId);
151
+ const sendChosen = decision.status === 'resolved' && decision.chosenKey === GMAIL_SEND_KEYS.SEND;
152
+ if (!hold || hold.decisionId !== decision.id) {
153
+ // Nothing to send. A Not now / expiry with no hold needs no news, but a
154
+ // Yes must never vanish: the card shows Yes, so say it was not sent.
155
+ if (!sendChosen || wasSettled(decision.id))
156
+ return null;
157
+ const why = !hold ? 'the pending send was lost (the draft is no longer held)' : 'this card was replaced by a newer one for the same draft';
158
+ const note = `Not sent: ${why}. The draft is still in Gmail.`;
159
+ this.logger.warn('Send approved but no matching hold — not sent', { holdId: subject.holdId, decisionId: decision.id, held: !!hold });
160
+ await this.deps.decisions()?.replyInThread?.(decision.id, note).catch(() => false);
161
+ return `[GMAIL] The owner tapped Yes on "${subject.subject}" to ${subject.to}, but it was NOT sent: ${why}. The draft is still in Gmail; ask the owner whether to try again.`;
162
+ }
163
+ markSettled(decision.id);
164
+ if (decision.status !== 'resolved' || decision.chosenKey !== GMAIL_SEND_KEYS.SEND) {
165
+ clearHeldSend(hold.id);
166
+ this.logger.info('Drafted email not sent', { holdId: hold.id, status: decision.status });
167
+ // Never deletes the draft: only an owner tap on Discard does (via discarded()).
168
+ if (this.discarding.has(hold.id))
169
+ return `[GMAIL] The owner discarded "${hold.subject}" to ${hold.to}; the draft was deleted. Do not send it another way.`;
170
+ if (decision.status === 'defaulted' || decision.status === 'expired') {
171
+ return `[GMAIL] "${hold.subject}" to ${hold.to} was not approved in 24h; nothing was sent and the draft is left in Gmail.`;
172
+ }
173
+ if (decision.status === 'resolved')
174
+ return `[GMAIL] The owner said not now to "${hold.subject}" to ${hold.to}. Nothing was sent; the draft stays in Gmail. Do not send it another way.`;
175
+ return `[GMAIL] The approval card for "${hold.subject}" to ${hold.to} was withdrawn. Nothing was sent; the draft is left in Gmail.`;
176
+ }
177
+ const gmail = this.deps.gmailFor(hold.account);
178
+ const current = await gmail.getDraft(hold.draftId);
179
+ if (!hold.fingerprint || fingerprintDraft(current) !== hold.fingerprint) {
180
+ // The text changed after the owner looked at it: that approval is for
181
+ // a different email. Ask again, with what is there now.
182
+ clearHeldSend(hold.id);
183
+ const reheld = holdGmailSend({
184
+ agentSession: hold.agentSession,
185
+ draftId: hold.draftId,
186
+ to: hold.to,
187
+ subject: hold.subject,
188
+ ...(hold.claim ? { claim: hold.claim } : {}),
189
+ ...(hold.account ? { account: hold.account } : {}),
190
+ });
191
+ await this.onHeld(reheld);
192
+ this.logger.warn('Drafted email changed after approval — not sent, asked again', { holdId: hold.id });
193
+ return `[GMAIL] "${hold.subject}" was edited after the owner approved it, so it was NOT sent. A new card with the edited text is waiting for the owner.`;
194
+ }
195
+ const sent = await gmail.sendDraft(hold.draftId);
196
+ clearHeldSend(hold.id);
197
+ // Audit line: who, what, to whom, on which owner decision.
198
+ this.logger.info('Owner approved a drafted email — sent', {
199
+ holdId: hold.id,
200
+ decisionId: decision.id,
201
+ to: hold.to,
202
+ subject: hold.subject,
203
+ messageId: sent.id,
204
+ threadId: sent.threadId,
205
+ agentSession: hold.agentSession,
206
+ });
207
+ try {
208
+ await this.deps.onSent?.({ threadId: sent.threadId, agentSession: hold.agentSession, ...(hold.account ? { account: hold.account } : {}) });
209
+ }
210
+ catch (err) {
211
+ this.logger.warn('Could not watch the thread after sending', { threadId: sent.threadId, error: err instanceof Error ? err.message : String(err) });
212
+ }
213
+ return `[GMAIL] The owner approved and "${hold.subject}" was sent to ${hold.to} (thread ${sent.threadId}). Replies on that thread will wake you.`;
214
+ }
215
+ }
216
+ //# sourceMappingURL=gmail-send-approval.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gmail-send-approval.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/google/gmail-send-approval.service.ts"],"names":[],"mappings":"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"}
@@ -57,7 +57,25 @@ export interface HeldSend {
57
57
  claim?: string;
58
58
  /** When it was held (epoch ms) */
59
59
  heldAt: number;
60
+ /** Google account the draft lives in (X-Google-Account), when not the default */
61
+ account?: string;
62
+ /** sha256 of the draft's to/cc/subject/body when the owner was asked; an edit changes it */
63
+ fingerprint?: string;
64
+ /** The decision card the owner answers */
65
+ decisionId?: string;
60
66
  }
67
+ /** Remember that a card's answer has been acted on. */
68
+ export declare function markSettled(decisionId: string): void;
69
+ /** True when this card's answer was already acted on. */
70
+ export declare function wasSettled(decisionId: string): boolean;
71
+ /**
72
+ * Persist holds to `file` and load whatever is already there (call once at
73
+ * startup, before cards can settle).
74
+ *
75
+ * @param file - JSON file, e.g. ~/.crewly/gmail-send-holds.json
76
+ * @returns Number of holds loaded
77
+ */
78
+ export declare function loadHeldSends(file: string): number;
61
79
  /**
62
80
  * Record an attempted send and the instruction the agent cites for it.
63
81
  *
@@ -70,20 +88,8 @@ export declare function holdGmailSend(input: {
70
88
  to: string;
71
89
  subject: string;
72
90
  claim?: string;
91
+ account?: string;
73
92
  }): HeldSend;
74
- /**
75
- * Spend an approval the owner gave for this agent.
76
- *
77
- * @param agentSession - The agent attempting to send
78
- * @returns True when an approval was available and has now been used up
79
- */
80
- export declare function consumeSendApproval(agentSession: string): boolean;
81
- /**
82
- * Grant one send to an agent.
83
- *
84
- * @param agentSession - The agent allowed to send
85
- */
86
- export declare function grantSendApproval(agentSession: string): void;
87
93
  /**
88
94
  * Everything waiting on the owner, oldest first.
89
95
  *
@@ -97,6 +103,15 @@ export declare function listHeldSends(): HeldSend[];
97
103
  * @returns The hold, or undefined
98
104
  */
99
105
  export declare function getHeldSend(id: string): HeldSend | undefined;
106
+ /**
107
+ * Merge fields into a hold (the fingerprint and card id once the owner has
108
+ * been asked).
109
+ *
110
+ * @param id - Hold id
111
+ * @param patch - Fields to set
112
+ * @returns The updated hold, or undefined when there is none
113
+ */
114
+ export declare function updateHeldSend(id: string, patch: Partial<Pick<HeldSend, 'fingerprint' | 'decisionId'>>): HeldSend | undefined;
100
115
  /**
101
116
  * Drop a hold once it has been answered.
102
117
  *
@@ -1 +1 @@
1
- {"version":3,"file":"gmail-send-gate.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/google/gmail-send-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAMH,8CAA8C;AAC9C,MAAM,WAAW,QAAQ;IACxB,8CAA8C;IAC9C,EAAE,EAAE,MAAM,CAAC;IACX,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,EAAE,EAAE,MAAM,CAAC;IACX,mBAAmB;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;CACf;AAaD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,QAAQ,CAYX;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAKjE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAE5D;AAED;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,QAAQ,EAAE,CAE1C;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAE5D;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED,+BAA+B;AAC/B,wBAAgB,kBAAkB,IAAI,IAAI,CAGzC"}
1
+ {"version":3,"file":"gmail-send-gate.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/google/gmail-send-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAKH,8CAA8C;AAC9C,MAAM,WAAW,QAAQ;IACxB,8CAA8C;IAC9C,EAAE,EAAE,MAAM,CAAC;IACX,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,EAAE,EAAE,MAAM,CAAC;IACX,mBAAmB;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4FAA4F;IAC5F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAYD,uDAAuD;AACvD,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAGpD;AAED,yDAAyD;AACzD,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAEtD;AAoBD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAclD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,QAAQ,CAcX;AAED;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,QAAQ,EAAE,CAE1C;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAE5D;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,aAAa,GAAG,YAAY,CAAC,CAAC,GAAG,QAAQ,GAAG,SAAS,CAO7H;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAIjD;AAED,+BAA+B;AAC/B,wBAAgB,kBAAkB,IAAI,IAAI,CAIzC"}
@@ -36,17 +36,68 @@
36
36
  *
37
37
  * @module services/google/gmail-send-gate
38
38
  */
39
- import { LoggerService } from '../core/logger.service.js';
40
- const logger = LoggerService.getInstance().createComponentLogger('GmailSendGate');
39
+ import fs from 'node:fs';
40
+ import path from 'node:path';
41
41
  /** Held sends, newest last, keyed by hold id. */
42
42
  const held = new Map();
43
+ /** Where holds are persisted (set at startup); undefined = memory only. */
44
+ let storeFile;
45
+ /** Decision ids already acted on (bounded, persisted) so a repeat settle is not mistaken for a lost hold. */
46
+ const SETTLED_MAX = 200;
47
+ let settled = [];
48
+ /** Remember that a card's answer has been acted on. */
49
+ export function markSettled(decisionId) {
50
+ settled = [...settled.filter((d) => d !== decisionId), decisionId].slice(-SETTLED_MAX);
51
+ persist();
52
+ }
53
+ /** True when this card's answer was already acted on. */
54
+ export function wasSettled(decisionId) {
55
+ return settled.includes(decisionId);
56
+ }
57
+ /**
58
+ * Write the holds to disk (tmp + rename). A card waits up to 24 h and every
59
+ * release restarts the backend, so a hold kept only in memory would be lost
60
+ * while its card still shows Yes. Failure is swallowed: the in-memory hold
61
+ * still works until the next restart.
62
+ */
63
+ function persist() {
64
+ if (!storeFile)
65
+ return;
66
+ try {
67
+ fs.mkdirSync(path.dirname(storeFile), { recursive: true });
68
+ const tmp = `${storeFile}.${process.pid}.tmp`;
69
+ fs.writeFileSync(tmp, JSON.stringify({ holds: Array.from(held.values()), settled }, null, 2));
70
+ fs.renameSync(tmp, storeFile);
71
+ }
72
+ catch {
73
+ /* best effort */
74
+ }
75
+ }
43
76
  /**
44
- * Agents holding a one-shot approval.
77
+ * Persist holds to `file` and load whatever is already there (call once at
78
+ * startup, before cards can settle).
45
79
  *
46
- * Consumed by the next send from that agent, so approving one message does
47
- * not leave the account open.
80
+ * @param file - JSON file, e.g. ~/.crewly/gmail-send-holds.json
81
+ * @returns Number of holds loaded
48
82
  */
49
- const approved = new Set();
83
+ export function loadHeldSends(file) {
84
+ storeFile = file;
85
+ held.clear();
86
+ settled = [];
87
+ try {
88
+ const parsed = JSON.parse(fs.readFileSync(file, 'utf-8'));
89
+ if (Array.isArray(parsed.settled))
90
+ settled = parsed.settled.filter((x) => typeof x === 'string');
91
+ for (const h of Array.isArray(parsed.holds) ? parsed.holds : []) {
92
+ if (h && typeof h.id === 'string' && typeof h.draftId === 'string')
93
+ held.set(h.id, h);
94
+ }
95
+ }
96
+ catch {
97
+ /* no file yet */
98
+ }
99
+ return held.size;
100
+ }
50
101
  /**
51
102
  * Record an attempted send and the instruction the agent cites for it.
52
103
  *
@@ -61,32 +112,13 @@ export function holdGmailSend(input) {
61
112
  to: input.to,
62
113
  subject: input.subject,
63
114
  ...(input.claim ? { claim: input.claim } : {}),
115
+ ...(input.account ? { account: input.account } : {}),
64
116
  heldAt: Date.now(),
65
117
  };
66
118
  held.set(entry.id, entry);
119
+ persist();
67
120
  return entry;
68
121
  }
69
- /**
70
- * Spend an approval the owner gave for this agent.
71
- *
72
- * @param agentSession - The agent attempting to send
73
- * @returns True when an approval was available and has now been used up
74
- */
75
- export function consumeSendApproval(agentSession) {
76
- if (!approved.has(agentSession))
77
- return false;
78
- approved.delete(agentSession);
79
- logger.info('Agent spent an owner approval to send mail', { agentSession });
80
- return true;
81
- }
82
- /**
83
- * Grant one send to an agent.
84
- *
85
- * @param agentSession - The agent allowed to send
86
- */
87
- export function grantSendApproval(agentSession) {
88
- approved.add(agentSession);
89
- }
90
122
  /**
91
123
  * Everything waiting on the owner, oldest first.
92
124
  *
@@ -104,6 +136,23 @@ export function listHeldSends() {
104
136
  export function getHeldSend(id) {
105
137
  return held.get(id);
106
138
  }
139
+ /**
140
+ * Merge fields into a hold (the fingerprint and card id once the owner has
141
+ * been asked).
142
+ *
143
+ * @param id - Hold id
144
+ * @param patch - Fields to set
145
+ * @returns The updated hold, or undefined when there is none
146
+ */
147
+ export function updateHeldSend(id, patch) {
148
+ const cur = held.get(id);
149
+ if (!cur)
150
+ return undefined;
151
+ const next = { ...cur, ...patch };
152
+ held.set(id, next);
153
+ persist();
154
+ return next;
155
+ }
107
156
  /**
108
157
  * Drop a hold once it has been answered.
109
158
  *
@@ -111,11 +160,15 @@ export function getHeldSend(id) {
111
160
  * @returns True when there was one to drop
112
161
  */
113
162
  export function clearHeldSend(id) {
114
- return held.delete(id);
163
+ const had = held.delete(id);
164
+ if (had)
165
+ persist();
166
+ return had;
115
167
  }
116
168
  /** Drops all state (tests). */
117
169
  export function resetGmailSendGate() {
118
170
  held.clear();
119
- approved.clear();
171
+ settled = [];
172
+ storeFile = undefined;
120
173
  }
121
174
  //# sourceMappingURL=gmail-send-gate.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gmail-send-gate.js","sourceRoot":"","sources":["../../../../../../backend/src/services/google/gmail-send-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE1D,MAAM,MAAM,GAAG,aAAa,CAAC,WAAW,EAAE,CAAC,qBAAqB,CAAC,eAAe,CAAC,CAAC;AAyBlF,iDAAiD;AACjD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAoB,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;AAEnC;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,KAM7B;IACA,MAAM,KAAK,GAAa;QACvB,EAAE,EAAE,GAAG,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,OAAO,EAAE;QAC5C,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9C,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE;KAClB,CAAC;IACF,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAC1B,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,YAAoB;IACvD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9C,QAAQ,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IAC9B,MAAM,CAAC,IAAI,CAAC,4CAA4C,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;IAC5E,OAAO,IAAI,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,YAAoB;IACrD,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;AAC5B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa;IAC5B,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;AACtE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,EAAU;IACrC,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AACrB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,EAAU;IACvC,OAAO,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AACxB,CAAC;AAED,+BAA+B;AAC/B,MAAM,UAAU,kBAAkB;IACjC,IAAI,CAAC,KAAK,EAAE,CAAC;IACb,QAAQ,CAAC,KAAK,EAAE,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"gmail-send-gate.js","sourceRoot":"","sources":["../../../../../../backend/src/services/google/gmail-send-gate.ts"],"names":[],"mappings":"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"}