@telora/daemon 0.12.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (473) hide show
  1. package/.env.example +64 -0
  2. package/README.md +229 -0
  3. package/build-info.json +4 -0
  4. package/dist/activity-tracker.d.ts +13 -0
  5. package/dist/activity-tracker.d.ts.map +1 -0
  6. package/dist/activity-tracker.js +19 -0
  7. package/dist/activity-tracker.js.map +1 -0
  8. package/dist/agent-state.d.ts +45 -0
  9. package/dist/agent-state.d.ts.map +1 -0
  10. package/dist/agent-state.js +61 -0
  11. package/dist/agent-state.js.map +1 -0
  12. package/dist/audit-hooks.d.ts +12 -0
  13. package/dist/audit-hooks.d.ts.map +1 -0
  14. package/dist/audit-hooks.js +45 -0
  15. package/dist/audit-hooks.js.map +1 -0
  16. package/dist/auto-update.d.ts +42 -0
  17. package/dist/auto-update.d.ts.map +1 -0
  18. package/dist/auto-update.js +96 -0
  19. package/dist/auto-update.js.map +1 -0
  20. package/dist/branch-status.d.ts +40 -0
  21. package/dist/branch-status.d.ts.map +1 -0
  22. package/dist/branch-status.js +107 -0
  23. package/dist/branch-status.js.map +1 -0
  24. package/dist/completion-detector.d.ts +87 -0
  25. package/dist/completion-detector.d.ts.map +1 -0
  26. package/dist/completion-detector.js +160 -0
  27. package/dist/completion-detector.js.map +1 -0
  28. package/dist/completion-handler.d.ts +48 -0
  29. package/dist/completion-handler.d.ts.map +1 -0
  30. package/dist/completion-handler.js +200 -0
  31. package/dist/completion-handler.js.map +1 -0
  32. package/dist/condition-evaluators.d.ts +31 -0
  33. package/dist/condition-evaluators.d.ts.map +1 -0
  34. package/dist/condition-evaluators.js +416 -0
  35. package/dist/condition-evaluators.js.map +1 -0
  36. package/dist/config.d.ts +55 -0
  37. package/dist/config.d.ts.map +1 -0
  38. package/dist/config.js +311 -0
  39. package/dist/config.js.map +1 -0
  40. package/dist/control-state.d.ts +41 -0
  41. package/dist/control-state.d.ts.map +1 -0
  42. package/dist/control-state.js +204 -0
  43. package/dist/control-state.js.map +1 -0
  44. package/dist/crash-recovery-cleanup.d.ts +21 -0
  45. package/dist/crash-recovery-cleanup.d.ts.map +1 -0
  46. package/dist/crash-recovery-cleanup.js +198 -0
  47. package/dist/crash-recovery-cleanup.js.map +1 -0
  48. package/dist/crash-recovery-scan.d.ts +19 -0
  49. package/dist/crash-recovery-scan.d.ts.map +1 -0
  50. package/dist/crash-recovery-scan.js +145 -0
  51. package/dist/crash-recovery-scan.js.map +1 -0
  52. package/dist/crash-recovery-types.d.ts +54 -0
  53. package/dist/crash-recovery-types.d.ts.map +1 -0
  54. package/dist/crash-recovery-types.js +13 -0
  55. package/dist/crash-recovery-types.js.map +1 -0
  56. package/dist/crash-recovery.d.ts +88 -0
  57. package/dist/crash-recovery.d.ts.map +1 -0
  58. package/dist/crash-recovery.js +448 -0
  59. package/dist/crash-recovery.js.map +1 -0
  60. package/dist/daemon-logs.d.ts +19 -0
  61. package/dist/daemon-logs.d.ts.map +1 -0
  62. package/dist/daemon-logs.js +81 -0
  63. package/dist/daemon-logs.js.map +1 -0
  64. package/dist/daemon-process.d.ts +154 -0
  65. package/dist/daemon-process.d.ts.map +1 -0
  66. package/dist/daemon-process.js +427 -0
  67. package/dist/daemon-process.js.map +1 -0
  68. package/dist/dag-validator.d.ts +52 -0
  69. package/dist/dag-validator.d.ts.map +1 -0
  70. package/dist/dag-validator.js +199 -0
  71. package/dist/dag-validator.js.map +1 -0
  72. package/dist/delivery-guards.d.ts +41 -0
  73. package/dist/delivery-guards.d.ts.map +1 -0
  74. package/dist/delivery-guards.js +195 -0
  75. package/dist/delivery-guards.js.map +1 -0
  76. package/dist/delivery-lifecycle.d.ts +110 -0
  77. package/dist/delivery-lifecycle.d.ts.map +1 -0
  78. package/dist/delivery-lifecycle.js +353 -0
  79. package/dist/delivery-lifecycle.js.map +1 -0
  80. package/dist/delivery-merge.d.ts +17 -0
  81. package/dist/delivery-merge.d.ts.map +1 -0
  82. package/dist/delivery-merge.js +89 -0
  83. package/dist/delivery-merge.js.map +1 -0
  84. package/dist/dependency-resolver.d.ts +77 -0
  85. package/dist/dependency-resolver.d.ts.map +1 -0
  86. package/dist/dependency-resolver.js +337 -0
  87. package/dist/dependency-resolver.js.map +1 -0
  88. package/dist/evaluation-context.d.ts +49 -0
  89. package/dist/evaluation-context.d.ts.map +1 -0
  90. package/dist/evaluation-context.js +98 -0
  91. package/dist/evaluation-context.js.map +1 -0
  92. package/dist/git-activity.d.ts +24 -0
  93. package/dist/git-activity.d.ts.map +1 -0
  94. package/dist/git-activity.js +97 -0
  95. package/dist/git-activity.js.map +1 -0
  96. package/dist/git-branch.d.ts +33 -0
  97. package/dist/git-branch.d.ts.map +1 -0
  98. package/dist/git-branch.js +88 -0
  99. package/dist/git-branch.js.map +1 -0
  100. package/dist/git-integration.d.ts +27 -0
  101. package/dist/git-integration.d.ts.map +1 -0
  102. package/dist/git-integration.js +82 -0
  103. package/dist/git-integration.js.map +1 -0
  104. package/dist/git-merge-helpers.d.ts +48 -0
  105. package/dist/git-merge-helpers.d.ts.map +1 -0
  106. package/dist/git-merge-helpers.js +105 -0
  107. package/dist/git-merge-helpers.js.map +1 -0
  108. package/dist/git-merge-lock.d.ts +67 -0
  109. package/dist/git-merge-lock.d.ts.map +1 -0
  110. package/dist/git-merge-lock.js +157 -0
  111. package/dist/git-merge-lock.js.map +1 -0
  112. package/dist/git-merge-strategies.d.ts +39 -0
  113. package/dist/git-merge-strategies.d.ts.map +1 -0
  114. package/dist/git-merge-strategies.js +127 -0
  115. package/dist/git-merge-strategies.js.map +1 -0
  116. package/dist/git-merge.d.ts +80 -0
  117. package/dist/git-merge.d.ts.map +1 -0
  118. package/dist/git-merge.js +373 -0
  119. package/dist/git-merge.js.map +1 -0
  120. package/dist/git-state-detector.d.ts +24 -0
  121. package/dist/git-state-detector.d.ts.map +1 -0
  122. package/dist/git-state-detector.js +122 -0
  123. package/dist/git-state-detector.js.map +1 -0
  124. package/dist/git-types.d.ts +40 -0
  125. package/dist/git-types.d.ts.map +1 -0
  126. package/dist/git-types.js +23 -0
  127. package/dist/git-types.js.map +1 -0
  128. package/dist/git-utils.d.ts +28 -0
  129. package/dist/git-utils.d.ts.map +1 -0
  130. package/dist/git-utils.js +57 -0
  131. package/dist/git-utils.js.map +1 -0
  132. package/dist/git.d.ts +24 -0
  133. package/dist/git.d.ts.map +1 -0
  134. package/dist/git.js +64 -0
  135. package/dist/git.js.map +1 -0
  136. package/dist/guard-engine.d.ts +19 -0
  137. package/dist/guard-engine.d.ts.map +1 -0
  138. package/dist/guard-engine.js +21 -0
  139. package/dist/guard-engine.js.map +1 -0
  140. package/dist/guard-evaluator.d.ts +47 -0
  141. package/dist/guard-evaluator.d.ts.map +1 -0
  142. package/dist/guard-evaluator.js +193 -0
  143. package/dist/guard-evaluator.js.map +1 -0
  144. package/dist/heartbeat.d.ts +73 -0
  145. package/dist/heartbeat.d.ts.map +1 -0
  146. package/dist/heartbeat.js +306 -0
  147. package/dist/heartbeat.js.map +1 -0
  148. package/dist/index.d.ts +32 -0
  149. package/dist/index.d.ts.map +1 -0
  150. package/dist/index.js +493 -0
  151. package/dist/index.js.map +1 -0
  152. package/dist/listener-auto-advance.d.ts +29 -0
  153. package/dist/listener-auto-advance.d.ts.map +1 -0
  154. package/dist/listener-auto-advance.js +172 -0
  155. package/dist/listener-auto-advance.js.map +1 -0
  156. package/dist/listener-review.d.ts +37 -0
  157. package/dist/listener-review.d.ts.map +1 -0
  158. package/dist/listener-review.js +217 -0
  159. package/dist/listener-review.js.map +1 -0
  160. package/dist/listener.d.ts +57 -0
  161. package/dist/listener.d.ts.map +1 -0
  162. package/dist/listener.js +361 -0
  163. package/dist/listener.js.map +1 -0
  164. package/dist/log-manager.d.ts +18 -0
  165. package/dist/log-manager.d.ts.map +1 -0
  166. package/dist/log-manager.js +18 -0
  167. package/dist/log-manager.js.map +1 -0
  168. package/dist/otlp-log-parser.d.ts +21 -0
  169. package/dist/otlp-log-parser.d.ts.map +1 -0
  170. package/dist/otlp-log-parser.js +143 -0
  171. package/dist/otlp-log-parser.js.map +1 -0
  172. package/dist/otlp-metric-parser.d.ts +20 -0
  173. package/dist/otlp-metric-parser.d.ts.map +1 -0
  174. package/dist/otlp-metric-parser.js +113 -0
  175. package/dist/otlp-metric-parser.js.map +1 -0
  176. package/dist/otlp-port-manager.d.ts +26 -0
  177. package/dist/otlp-port-manager.d.ts.map +1 -0
  178. package/dist/otlp-port-manager.js +130 -0
  179. package/dist/otlp-port-manager.js.map +1 -0
  180. package/dist/otlp-receiver.d.ts +51 -0
  181. package/dist/otlp-receiver.d.ts.map +1 -0
  182. package/dist/otlp-receiver.js +663 -0
  183. package/dist/otlp-receiver.js.map +1 -0
  184. package/dist/otlp-types.d.ts +92 -0
  185. package/dist/otlp-types.d.ts.map +1 -0
  186. package/dist/otlp-types.js +133 -0
  187. package/dist/otlp-types.js.map +1 -0
  188. package/dist/output-monitor.d.ts +33 -0
  189. package/dist/output-monitor.d.ts.map +1 -0
  190. package/dist/output-monitor.js +67 -0
  191. package/dist/output-monitor.js.map +1 -0
  192. package/dist/planning-prompt-builder.d.ts +67 -0
  193. package/dist/planning-prompt-builder.d.ts.map +1 -0
  194. package/dist/planning-prompt-builder.js +515 -0
  195. package/dist/planning-prompt-builder.js.map +1 -0
  196. package/dist/prompt-builder.d.ts +14 -0
  197. package/dist/prompt-builder.d.ts.map +1 -0
  198. package/dist/prompt-builder.js +174 -0
  199. package/dist/prompt-builder.js.map +1 -0
  200. package/dist/qa-crash-recovery.d.ts +77 -0
  201. package/dist/qa-crash-recovery.d.ts.map +1 -0
  202. package/dist/qa-crash-recovery.js +243 -0
  203. package/dist/qa-crash-recovery.js.map +1 -0
  204. package/dist/qa-dev-server.d.ts +73 -0
  205. package/dist/qa-dev-server.d.ts.map +1 -0
  206. package/dist/qa-dev-server.js +279 -0
  207. package/dist/qa-dev-server.js.map +1 -0
  208. package/dist/qa-orchestrator.d.ts +79 -0
  209. package/dist/qa-orchestrator.d.ts.map +1 -0
  210. package/dist/qa-orchestrator.js +349 -0
  211. package/dist/qa-orchestrator.js.map +1 -0
  212. package/dist/qa-port-allocator.d.ts +34 -0
  213. package/dist/qa-port-allocator.d.ts.map +1 -0
  214. package/dist/qa-port-allocator.js +75 -0
  215. package/dist/qa-port-allocator.js.map +1 -0
  216. package/dist/qa-provisioner.d.ts +33 -0
  217. package/dist/qa-provisioner.d.ts.map +1 -0
  218. package/dist/qa-provisioner.js +141 -0
  219. package/dist/qa-provisioner.js.map +1 -0
  220. package/dist/qa-state.d.ts +93 -0
  221. package/dist/qa-state.d.ts.map +1 -0
  222. package/dist/qa-state.js +74 -0
  223. package/dist/qa-state.js.map +1 -0
  224. package/dist/queries/control-state.d.ts +25 -0
  225. package/dist/queries/control-state.d.ts.map +1 -0
  226. package/dist/queries/control-state.js +34 -0
  227. package/dist/queries/control-state.js.map +1 -0
  228. package/dist/queries/daemon-connection.d.ts +25 -0
  229. package/dist/queries/daemon-connection.d.ts.map +1 -0
  230. package/dist/queries/daemon-connection.js +28 -0
  231. package/dist/queries/daemon-connection.js.map +1 -0
  232. package/dist/queries/deliveries.d.ts +100 -0
  233. package/dist/queries/deliveries.d.ts.map +1 -0
  234. package/dist/queries/deliveries.js +184 -0
  235. package/dist/queries/deliveries.js.map +1 -0
  236. package/dist/queries/git-activity.d.ts +20 -0
  237. package/dist/queries/git-activity.d.ts.map +1 -0
  238. package/dist/queries/git-activity.js +22 -0
  239. package/dist/queries/git-activity.js.map +1 -0
  240. package/dist/queries/guards.d.ts +47 -0
  241. package/dist/queries/guards.d.ts.map +1 -0
  242. package/dist/queries/guards.js +138 -0
  243. package/dist/queries/guards.js.map +1 -0
  244. package/dist/queries/index.d.ts +19 -0
  245. package/dist/queries/index.d.ts.map +1 -0
  246. package/dist/queries/index.js +17 -0
  247. package/dist/queries/index.js.map +1 -0
  248. package/dist/queries/issues.d.ts +41 -0
  249. package/dist/queries/issues.d.ts.map +1 -0
  250. package/dist/queries/issues.js +67 -0
  251. package/dist/queries/issues.js.map +1 -0
  252. package/dist/queries/qa.d.ts +79 -0
  253. package/dist/queries/qa.d.ts.map +1 -0
  254. package/dist/queries/qa.js +85 -0
  255. package/dist/queries/qa.js.map +1 -0
  256. package/dist/queries/roles.d.ts +13 -0
  257. package/dist/queries/roles.d.ts.map +1 -0
  258. package/dist/queries/roles.js +39 -0
  259. package/dist/queries/roles.js.map +1 -0
  260. package/dist/queries/schemas.d.ts +777 -0
  261. package/dist/queries/schemas.d.ts.map +1 -0
  262. package/dist/queries/schemas.js +391 -0
  263. package/dist/queries/schemas.js.map +1 -0
  264. package/dist/queries/sessions.d.ts +64 -0
  265. package/dist/queries/sessions.d.ts.map +1 -0
  266. package/dist/queries/sessions.js +100 -0
  267. package/dist/queries/sessions.js.map +1 -0
  268. package/dist/queries/shared.d.ts +61 -0
  269. package/dist/queries/shared.d.ts.map +1 -0
  270. package/dist/queries/shared.js +187 -0
  271. package/dist/queries/shared.js.map +1 -0
  272. package/dist/queries/strategies.d.ts +69 -0
  273. package/dist/queries/strategies.d.ts.map +1 -0
  274. package/dist/queries/strategies.js +80 -0
  275. package/dist/queries/strategies.js.map +1 -0
  276. package/dist/queries/workflows.d.ts +17 -0
  277. package/dist/queries/workflows.d.ts.map +1 -0
  278. package/dist/queries/workflows.js +49 -0
  279. package/dist/queries/workflows.js.map +1 -0
  280. package/dist/queries/worktrees.d.ts +38 -0
  281. package/dist/queries/worktrees.d.ts.map +1 -0
  282. package/dist/queries/worktrees.js +37 -0
  283. package/dist/queries/worktrees.js.map +1 -0
  284. package/dist/self-update.d.ts +94 -0
  285. package/dist/self-update.d.ts.map +1 -0
  286. package/dist/self-update.js +438 -0
  287. package/dist/self-update.js.map +1 -0
  288. package/dist/session-lifecycle.d.ts +77 -0
  289. package/dist/session-lifecycle.d.ts.map +1 -0
  290. package/dist/session-lifecycle.js +379 -0
  291. package/dist/session-lifecycle.js.map +1 -0
  292. package/dist/shutdown-state.d.ts +17 -0
  293. package/dist/shutdown-state.d.ts.map +1 -0
  294. package/dist/shutdown-state.js +22 -0
  295. package/dist/shutdown-state.js.map +1 -0
  296. package/dist/spawn-cooldown.d.ts +14 -0
  297. package/dist/spawn-cooldown.d.ts.map +1 -0
  298. package/dist/spawn-cooldown.js +34 -0
  299. package/dist/spawn-cooldown.js.map +1 -0
  300. package/dist/spawn-environment.d.ts +35 -0
  301. package/dist/spawn-environment.d.ts.map +1 -0
  302. package/dist/spawn-environment.js +48 -0
  303. package/dist/spawn-environment.js.map +1 -0
  304. package/dist/spawner-liveness.d.ts +23 -0
  305. package/dist/spawner-liveness.d.ts.map +1 -0
  306. package/dist/spawner-liveness.js +99 -0
  307. package/dist/spawner-liveness.js.map +1 -0
  308. package/dist/spawner-resolution.d.ts +27 -0
  309. package/dist/spawner-resolution.d.ts.map +1 -0
  310. package/dist/spawner-resolution.js +99 -0
  311. package/dist/spawner-resolution.js.map +1 -0
  312. package/dist/spawner-timeout.d.ts +32 -0
  313. package/dist/spawner-timeout.d.ts.map +1 -0
  314. package/dist/spawner-timeout.js +124 -0
  315. package/dist/spawner-timeout.js.map +1 -0
  316. package/dist/spawner.d.ts +77 -0
  317. package/dist/spawner.d.ts.map +1 -0
  318. package/dist/spawner.js +734 -0
  319. package/dist/spawner.js.map +1 -0
  320. package/dist/strategy-completion.d.ts +110 -0
  321. package/dist/strategy-completion.d.ts.map +1 -0
  322. package/dist/strategy-completion.js +434 -0
  323. package/dist/strategy-completion.js.map +1 -0
  324. package/dist/strategy-engine.d.ts +47 -0
  325. package/dist/strategy-engine.d.ts.map +1 -0
  326. package/dist/strategy-engine.js +419 -0
  327. package/dist/strategy-engine.js.map +1 -0
  328. package/dist/strategy-executor.d.ts +93 -0
  329. package/dist/strategy-executor.d.ts.map +1 -0
  330. package/dist/strategy-executor.js +775 -0
  331. package/dist/strategy-executor.js.map +1 -0
  332. package/dist/strategy-lifecycle.d.ts +61 -0
  333. package/dist/strategy-lifecycle.d.ts.map +1 -0
  334. package/dist/strategy-lifecycle.js +516 -0
  335. package/dist/strategy-lifecycle.js.map +1 -0
  336. package/dist/strategy-merge.d.ts +72 -0
  337. package/dist/strategy-merge.d.ts.map +1 -0
  338. package/dist/strategy-merge.js +371 -0
  339. package/dist/strategy-merge.js.map +1 -0
  340. package/dist/strategy-prompt-builder.d.ts +62 -0
  341. package/dist/strategy-prompt-builder.d.ts.map +1 -0
  342. package/dist/strategy-prompt-builder.js +538 -0
  343. package/dist/strategy-prompt-builder.js.map +1 -0
  344. package/dist/strategy-provisioning.d.ts +16 -0
  345. package/dist/strategy-provisioning.d.ts.map +1 -0
  346. package/dist/strategy-provisioning.js +119 -0
  347. package/dist/strategy-provisioning.js.map +1 -0
  348. package/dist/strategy-team-state.d.ts +24 -0
  349. package/dist/strategy-team-state.d.ts.map +1 -0
  350. package/dist/strategy-team-state.js +43 -0
  351. package/dist/strategy-team-state.js.map +1 -0
  352. package/dist/strategy-teardown.d.ts +24 -0
  353. package/dist/strategy-teardown.d.ts.map +1 -0
  354. package/dist/strategy-teardown.js +158 -0
  355. package/dist/strategy-teardown.js.map +1 -0
  356. package/dist/strategy-worktree-state.d.ts +47 -0
  357. package/dist/strategy-worktree-state.d.ts.map +1 -0
  358. package/dist/strategy-worktree-state.js +104 -0
  359. package/dist/strategy-worktree-state.js.map +1 -0
  360. package/dist/supabase.d.ts +36 -0
  361. package/dist/supabase.d.ts.map +1 -0
  362. package/dist/supabase.js +50 -0
  363. package/dist/supabase.js.map +1 -0
  364. package/dist/task-converter.d.ts +61 -0
  365. package/dist/task-converter.d.ts.map +1 -0
  366. package/dist/task-converter.js +286 -0
  367. package/dist/task-converter.js.map +1 -0
  368. package/dist/task-dag-builder.d.ts +14 -0
  369. package/dist/task-dag-builder.d.ts.map +1 -0
  370. package/dist/task-dag-builder.js +17 -0
  371. package/dist/task-dag-builder.js.map +1 -0
  372. package/dist/team-prompt-base.d.ts +114 -0
  373. package/dist/team-prompt-base.d.ts.map +1 -0
  374. package/dist/team-prompt-base.js +531 -0
  375. package/dist/team-prompt-base.js.map +1 -0
  376. package/dist/team-prompt-variants.d.ts +27 -0
  377. package/dist/team-prompt-variants.d.ts.map +1 -0
  378. package/dist/team-prompt-variants.js +134 -0
  379. package/dist/team-prompt-variants.js.map +1 -0
  380. package/dist/team-spawner.d.ts +50 -0
  381. package/dist/team-spawner.d.ts.map +1 -0
  382. package/dist/team-spawner.js +410 -0
  383. package/dist/team-spawner.js.map +1 -0
  384. package/dist/telemetry-writer.d.ts +66 -0
  385. package/dist/telemetry-writer.d.ts.map +1 -0
  386. package/dist/telemetry-writer.js +96 -0
  387. package/dist/telemetry-writer.js.map +1 -0
  388. package/dist/trigger-executor.d.ts +56 -0
  389. package/dist/trigger-executor.d.ts.map +1 -0
  390. package/dist/trigger-executor.js +313 -0
  391. package/dist/trigger-executor.js.map +1 -0
  392. package/dist/types/config.d.ts +60 -0
  393. package/dist/types/config.d.ts.map +1 -0
  394. package/dist/types/config.js +5 -0
  395. package/dist/types/config.js.map +1 -0
  396. package/dist/types/dag.d.ts +53 -0
  397. package/dist/types/dag.d.ts.map +1 -0
  398. package/dist/types/dag.js +5 -0
  399. package/dist/types/dag.js.map +1 -0
  400. package/dist/types/delivery.d.ts +71 -0
  401. package/dist/types/delivery.d.ts.map +1 -0
  402. package/dist/types/delivery.js +5 -0
  403. package/dist/types/delivery.js.map +1 -0
  404. package/dist/types/index.d.ts +15 -0
  405. package/dist/types/index.d.ts.map +1 -0
  406. package/dist/types/index.js +15 -0
  407. package/dist/types/index.js.map +1 -0
  408. package/dist/types/issue.d.ts +22 -0
  409. package/dist/types/issue.d.ts.map +1 -0
  410. package/dist/types/issue.js +5 -0
  411. package/dist/types/issue.js.map +1 -0
  412. package/dist/types/merge.d.ts +28 -0
  413. package/dist/types/merge.d.ts.map +1 -0
  414. package/dist/types/merge.js +5 -0
  415. package/dist/types/merge.js.map +1 -0
  416. package/dist/types/session.d.ts +98 -0
  417. package/dist/types/session.d.ts.map +1 -0
  418. package/dist/types/session.js +5 -0
  419. package/dist/types/session.js.map +1 -0
  420. package/dist/types/strategy.d.ts +175 -0
  421. package/dist/types/strategy.d.ts.map +1 -0
  422. package/dist/types/strategy.js +5 -0
  423. package/dist/types/strategy.js.map +1 -0
  424. package/dist/types/workflow.d.ts +34 -0
  425. package/dist/types/workflow.d.ts.map +1 -0
  426. package/dist/types/workflow.js +9 -0
  427. package/dist/types/workflow.js.map +1 -0
  428. package/dist/types.d.ts +9 -0
  429. package/dist/types.d.ts.map +1 -0
  430. package/dist/types.js +9 -0
  431. package/dist/types.js.map +1 -0
  432. package/dist/unified-init.d.ts +16 -0
  433. package/dist/unified-init.d.ts.map +1 -0
  434. package/dist/unified-init.js +183 -0
  435. package/dist/unified-init.js.map +1 -0
  436. package/dist/unified-shell-config.d.ts +34 -0
  437. package/dist/unified-shell-config.d.ts.map +1 -0
  438. package/dist/unified-shell-config.js +238 -0
  439. package/dist/unified-shell-config.js.map +1 -0
  440. package/dist/unified-shell-status.d.ts +15 -0
  441. package/dist/unified-shell-status.d.ts.map +1 -0
  442. package/dist/unified-shell-status.js +100 -0
  443. package/dist/unified-shell-status.js.map +1 -0
  444. package/dist/unified-shell.d.ts +50 -0
  445. package/dist/unified-shell.d.ts.map +1 -0
  446. package/dist/unified-shell.js +682 -0
  447. package/dist/unified-shell.js.map +1 -0
  448. package/dist/version-check.d.ts +19 -0
  449. package/dist/version-check.d.ts.map +1 -0
  450. package/dist/version-check.js +67 -0
  451. package/dist/version-check.js.map +1 -0
  452. package/dist/workflow-engine.d.ts +95 -0
  453. package/dist/workflow-engine.d.ts.map +1 -0
  454. package/dist/workflow-engine.js +165 -0
  455. package/dist/workflow-engine.js.map +1 -0
  456. package/dist/worktree-merge.d.ts +23 -0
  457. package/dist/worktree-merge.d.ts.map +1 -0
  458. package/dist/worktree-merge.js +57 -0
  459. package/dist/worktree-merge.js.map +1 -0
  460. package/dist/worktree-safety.d.ts +48 -0
  461. package/dist/worktree-safety.d.ts.map +1 -0
  462. package/dist/worktree-safety.js +113 -0
  463. package/dist/worktree-safety.js.map +1 -0
  464. package/dist/worktree-strategy.d.ts +69 -0
  465. package/dist/worktree-strategy.d.ts.map +1 -0
  466. package/dist/worktree-strategy.js +214 -0
  467. package/dist/worktree-strategy.js.map +1 -0
  468. package/dist/worktree.d.ts +159 -0
  469. package/dist/worktree.d.ts.map +1 -0
  470. package/dist/worktree.js +512 -0
  471. package/dist/worktree.js.map +1 -0
  472. package/package.json +76 -0
  473. package/scripts/telora-daemon-wrapper.sh +31 -0
@@ -0,0 +1,775 @@
1
+ /**
2
+ * Strategy executor - manages Agent Team lifecycle per strategy.
3
+ *
4
+ * Replaces the per-delivery agent spawning model. When a strategy has
5
+ * an assigned agent role and queued deliveries, the executor spawns
6
+ * a single Agent Team (one lead agent process) scoped to the entire strategy.
7
+ *
8
+ * The lead agent:
9
+ * 1. Reads all deliveries + issues for the strategy
10
+ * 2. Builds a task DAG with dependencies
11
+ * 3. Spawns worker teammates to execute issues in parallel
12
+ * 4. Advances delivery stages as their issues complete
13
+ * 5. Terminates when all work is done; fresh team spawned for new work
14
+ *
15
+ * This module is the orchestration entry point. Pure logic is extracted to:
16
+ * - strategy-team-state.ts -- active teams map, config derivation
17
+ * - strategy-merge.ts -- branch merge logic
18
+ * - strategy-completion.ts -- post-execution completion handling
19
+ */
20
+ import { spawn } from 'node:child_process';
21
+ import { mkdirSync, existsSync, createWriteStream } from 'node:fs';
22
+ import { join, resolve } from 'node:path';
23
+ import { createSession, updateSession, getReadyStrategies, } from './supabase.js';
24
+ import { createWorktree, runGitSync } from './git.js';
25
+ import { installAuditPreCommitHook } from './audit-hooks.js';
26
+ import { getStrategyWorktree, setStrategyWorktree } from './strategy-worktree-state.js';
27
+ import { withRetry, StreamJsonParser, sendMessage, productLabel } from '@telora/daemon-core';
28
+ import { recordActivity, setNarration, clearNarration } from './heartbeat.js';
29
+ import { CompletionDetector } from './completion-detector.js';
30
+ import { formatEventForLog } from '@telora/daemon-core';
31
+ import { ActivityTracker } from './activity-tracker.js';
32
+ import { buildStrategyTeamPrompt, buildWakeMessage, buildReviewDirective } from './strategy-prompt-builder.js';
33
+ import { getStrategyDeliveries, getStrategyIssues, getProductContextForStrategy, getProductDeploymentProfileSnapshot, updateStrategyClaudeSessionId } from './queries/strategies.js';
34
+ import { buildSpawnEnvironment } from './spawn-environment.js';
35
+ import { sanitizeGitSegment } from './git-utils.js';
36
+ import { configForProduct, findProduct } from './config.js';
37
+ // ── Re-exports from extracted modules (backward compatibility) ───────
38
+ export { getActiveTeams, hasActiveTeam, getActiveTeamCount, } from './strategy-team-state.js';
39
+ export { mergeStrategyBranch, } from './strategy-merge.js';
40
+ export { handleTeamCompletion, advanceDeliveryStatuses, TERMINAL_DELIVERY_STATUSES, TEAM_WORK_STATUSES, } from './strategy-completion.js';
41
+ // ── Imports from extracted modules (used internally) ─────────────────
42
+ import { getActiveTeams, deriveExecutionConfig, } from './strategy-team-state.js';
43
+ import { mergeStrategyBranch, escalateMergeConflict } from './strategy-merge.js';
44
+ import { handleTeamCompletion, TERMINAL_DELIVERY_STATUSES, TEAM_WORK_STATUSES, } from './strategy-completion.js';
45
+ // ── Resource governor (optional, injected by StrategyEngine) ─────────
46
+ let governor = null;
47
+ /** Inject the resource governor for global concurrency limiting. */
48
+ export function initGovernor(gov) {
49
+ governor = gov;
50
+ }
51
+ /**
52
+ * Handle a 'complete' event from the CompletionDetector.
53
+ *
54
+ * Queries current delivery and issue state to decide whether to:
55
+ * - Terminate the team (all deliveries terminal or no open issues remain), or
56
+ * - Send a mid-strategy handoff message with remaining work.
57
+ *
58
+ * Extracted from the inline event callback in spawnStrategyTeam to eliminate
59
+ * deep nesting inside an async Promise chain inside an event listener.
60
+ */
61
+ async function handleCompletionEvent(ctx) {
62
+ const { strategyId, strategyName, teamState, completionDetector, proc } = ctx;
63
+ const [currentDeliveries, currentIssues] = await Promise.all([
64
+ getStrategyDeliveries(strategyId),
65
+ getStrategyIssues(strategyId),
66
+ ]);
67
+ // If all deliveries are terminal, shut down.
68
+ // In review mode, in_review deliveries are the team's active work -- not terminal.
69
+ const effectiveTerminal = teamState.reviewMode
70
+ ? new Set([...TERMINAL_DELIVERY_STATUSES].filter(s => s !== 'in_review'))
71
+ : TERMINAL_DELIVERY_STATUSES;
72
+ const allTerminal = currentDeliveries.every(d => effectiveTerminal.has(d.executionStatus ?? ''));
73
+ if (allTerminal) {
74
+ console.log(`[strategy-executor] All deliveries terminal for "${strategyName}" -- terminating team`);
75
+ teamState.shutdownReason = 'work_complete';
76
+ terminateTeam(strategyId);
77
+ return;
78
+ }
79
+ // Check if there are actionable deliveries with open issues.
80
+ // In review mode, in_review deliveries are the team's work.
81
+ const actionableStatuses = teamState.reviewMode
82
+ ? new Set(['queued', 'coding', 'in_review'])
83
+ : new Set(['queued', 'coding']);
84
+ const actionableDeliveryIds = new Set(currentDeliveries
85
+ .filter(d => actionableStatuses.has(d.executionStatus ?? ''))
86
+ .map(d => d.id));
87
+ if (actionableDeliveryIds.size === 0) {
88
+ console.log(`[strategy-executor] No actionable deliveries for "${strategyName}" -- terminating team`);
89
+ teamState.shutdownReason = 'work_complete';
90
+ terminateTeam(strategyId);
91
+ return;
92
+ }
93
+ const OPEN_ISSUE_STATUSES = new Set(['To Do', 'In Progress', 'Blocked', 'In Review']);
94
+ const hasOpenIssues = currentIssues.some(i => actionableDeliveryIds.has(i.deliveryId) && OPEN_ISSUE_STATUSES.has(i.status));
95
+ if (!hasOpenIssues) {
96
+ console.log(`[strategy-executor] Actionable deliveries have no open issues for "${strategyName}" -- ` +
97
+ `terminating team. Auto-advance will handle verify transition.`);
98
+ teamState.shutdownReason = 'work_complete';
99
+ terminateTeam(strategyId);
100
+ return;
101
+ }
102
+ // Mid-strategy handoff: actionable work with open issues remains.
103
+ // Send work immediately and reset the completion detector for the next cycle.
104
+ console.log(`[strategy-executor] Mid-strategy handoff for "${strategyName}" -- sending new work`);
105
+ const message = buildWakeMessage(currentDeliveries, currentIssues, teamState.knownDeliveryIds);
106
+ // Update tracking state
107
+ for (const d of currentDeliveries) {
108
+ teamState.knownDeliveryIds.add(d.id);
109
+ teamState.deliveryStageIds.set(d.id, d.currentWorkflowStageId);
110
+ }
111
+ completionDetector.reset();
112
+ sendMessage(proc.stdin, message);
113
+ }
114
+ // ── Team spawning ────────────────────────────────────────────────────
115
+ /**
116
+ * Generate branch name for strategy-level work.
117
+ */
118
+ export function generateStrategyBranchName(role, strategyName, strategyId) {
119
+ const sanitizedRoleName = sanitizeGitSegment(role.name);
120
+ const shortId = strategyId.slice(0, 8);
121
+ const sanitizedStrategyName = sanitizeGitSegment(strategyName);
122
+ return `agent/${sanitizedRoleName}/${sanitizedStrategyName}-${shortId}`;
123
+ }
124
+ /**
125
+ * Build Claude Code CLI arguments for the team lead.
126
+ */
127
+ function buildTeamLeadArgs(config, pipelineConfig, reviewMode = false, resumeSessionId) {
128
+ const args = [];
129
+ // --resume must come early, before --input-format stream-json
130
+ if (resumeSessionId) {
131
+ args.push('--resume', resumeSessionId);
132
+ }
133
+ args.push('--dangerously-skip-permissions');
134
+ args.push('--setting-sources', 'project,local');
135
+ args.push('--input-format', 'stream-json');
136
+ args.push('--output-format', 'stream-json');
137
+ args.push('--verbose');
138
+ // Enable in-process teammate mode for the team lead
139
+ args.push('--teammate-mode', 'in-process');
140
+ if (config.mcpConfigPath) {
141
+ const absoluteMcpPath = resolve(config.mcpConfigPath);
142
+ args.push('--mcp-config', absoluteMcpPath);
143
+ }
144
+ // Reviews always use Sonnet; otherwise honour pipeline config
145
+ const model = reviewMode ? 'claude-sonnet-4-6' : pipelineConfig?.model;
146
+ if (model) {
147
+ args.push('--model', model);
148
+ }
149
+ return args;
150
+ }
151
+ /**
152
+ * Spawn a strategy team to execute all deliveries in a strategy.
153
+ *
154
+ * Creates a single Agent Team (lead process) that reads all deliveries
155
+ * and issues, builds a task DAG, and coordinates worker execution.
156
+ */
157
+ export async function spawnStrategyTeam(params) {
158
+ const { config, strategyId, strategyName, role, pipelineConfig, readOnly = false, reviewMode = false, lastClaudeSessionId } = params;
159
+ const activeTeams = getActiveTeams();
160
+ // Prevent double-spawn
161
+ if (activeTeams.has(strategyId)) {
162
+ console.warn(`[strategy-executor] Team already active for strategy "${strategyName}", skipping spawn`);
163
+ return;
164
+ }
165
+ const executionConfig = deriveExecutionConfig(pipelineConfig);
166
+ const branchName = generateStrategyBranchName(role, strategyName, strategyId);
167
+ // Initialize team state
168
+ const teamState = {
169
+ strategyId,
170
+ strategyName,
171
+ roleId: role.id,
172
+ roleName: role.name,
173
+ organizationId: config.organizationId,
174
+ productId: config.productId,
175
+ executionConfig,
176
+ pipelineConfig,
177
+ startedAt: new Date(),
178
+ phase: 'initializing',
179
+ knownDeliveryIds: new Set(),
180
+ mergedDeliveryIds: new Set(),
181
+ shutdownReason: null,
182
+ deliveryStageIds: new Map(),
183
+ leadSessionId: null,
184
+ leadPid: null,
185
+ leadStdin: null,
186
+ branchName,
187
+ worktreePath: null,
188
+ resolvingMergeConflict: false,
189
+ readOnly,
190
+ reviewMode,
191
+ completionDetector: null,
192
+ claudeSessionId: null,
193
+ };
194
+ activeTeams.set(strategyId, teamState);
195
+ const currentProduct = config.products.find(p => p.id === config.productId);
196
+ const productTag = config.products.length > 1 && currentProduct ? ` [${productLabel(currentProduct)}]` : '';
197
+ console.log(`[strategy-executor] Spawning team for strategy "${strategyName}"${readOnly ? ' [READ-ONLY]' : ''}${productTag}`);
198
+ console.log(` Role: ${role.name}`);
199
+ console.log(` Model: ${reviewMode ? 'claude-sonnet-4-6' : (pipelineConfig?.model ?? '(CLI default)')}`);
200
+ console.log(` Max workers: ${executionConfig.maxWorkers}`);
201
+ console.log(` Branch: ${branchName}`);
202
+ if (config.products.length > 1) {
203
+ console.log(` Product: ${currentProduct ? productLabel(currentProduct) : config.productId.slice(0, 8)}`);
204
+ console.log(` Repo: ${config.repoPath}`);
205
+ }
206
+ // Fetch all deliveries, issues, product context, and deployment profile for the strategy
207
+ let deliveries;
208
+ let issues;
209
+ let productContextDocs;
210
+ let deploymentProfileSnapshot;
211
+ try {
212
+ [deliveries, issues, productContextDocs, deploymentProfileSnapshot] = await Promise.all([
213
+ getStrategyDeliveries(strategyId),
214
+ getStrategyIssues(strategyId),
215
+ getProductContextForStrategy(config.productId, strategyId),
216
+ getProductDeploymentProfileSnapshot(config.productId).catch((err) => {
217
+ console.debug(`[strategy-executor] Could not fetch deployment profile snapshot (non-fatal):`, err.message);
218
+ return null;
219
+ }),
220
+ ]);
221
+ }
222
+ catch (err) {
223
+ console.error(`[strategy-executor] Failed to fetch context for strategy "${strategyName}":`, err.message);
224
+ activeTeams.delete(strategyId);
225
+ return;
226
+ }
227
+ // Pre-spawn guard: enforce rank-ordered execution.
228
+ // Walk deliveries in priority_rank order (already sorted by API).
229
+ // A delivery is actionable only if all lower-ranked deliveries are non-blocking.
230
+ // Skipped in review mode -- review teams are spawned for verify-only strategies.
231
+ const nonBlockingStatuses = new Set(['done', 'cancelled', 'verify', 'in_review', 'iterating']);
232
+ const actionableStatuses = new Set(['queued', 'coding']);
233
+ let nextActionable = null;
234
+ if (!reviewMode) {
235
+ for (const d of deliveries) {
236
+ const status = d.executionStatus ?? '';
237
+ if (nonBlockingStatuses.has(status))
238
+ continue;
239
+ if (actionableStatuses.has(status)) {
240
+ nextActionable = d;
241
+ }
242
+ else {
243
+ // Blocking status (planning, iterating, paused) prevents subsequent deliveries
244
+ console.log(`[strategy-executor] Delivery "${d.name}" (rank ${d.priorityRank}) ` +
245
+ `is in "${status}" state -- blocking subsequent deliveries in strategy "${strategyName}"`);
246
+ }
247
+ break;
248
+ }
249
+ if (!nextActionable || !actionableStatuses.has(nextActionable.executionStatus ?? '')) {
250
+ console.log(`[strategy-executor] No actionable (queued/coding) deliveries for strategy "${strategyName}" -- skipping spawn`);
251
+ activeTeams.delete(strategyId);
252
+ return;
253
+ }
254
+ }
255
+ const actionableDeliveries = reviewMode
256
+ ? deliveries.filter(d => d.executionStatus === 'in_review')
257
+ : deliveries.filter(d => actionableStatuses.has(d.executionStatus ?? ''));
258
+ // Track known deliveries, pre-populate already-completed ones
259
+ for (const d of deliveries) {
260
+ teamState.knownDeliveryIds.add(d.id);
261
+ teamState.deliveryStageIds.set(d.id, d.currentWorkflowStageId);
262
+ if (d.executionStatus === 'verify' || d.executionStatus === 'in_review' || d.executionStatus === 'done') {
263
+ teamState.mergedDeliveryIds.add(d.id);
264
+ }
265
+ }
266
+ console.log(` Deliveries: ${deliveries.length} (${actionableDeliveries.length} queued)`);
267
+ console.log(` Issues: ${issues.length}`);
268
+ // Ensure log directory exists
269
+ if (!existsSync(config.logDir)) {
270
+ mkdirSync(config.logDir, { recursive: true, mode: 0o700 });
271
+ }
272
+ // Reuse persistent strategy worktree (created by ensureStrategyWorktrees in poll loop)
273
+ let worktreePath;
274
+ const existingWorktree = getStrategyWorktree(strategyId);
275
+ if (existingWorktree) {
276
+ worktreePath = existingWorktree.worktreePath;
277
+ // Rebase onto integration to pick up latest changes from other strategies
278
+ const rebaseResult = runGitSync(['rebase', config.integrationBranch], worktreePath);
279
+ if (!rebaseResult.success) {
280
+ runGitSync(['rebase', '--abort'], worktreePath);
281
+ console.warn(`[strategy-executor] Rebase failed for "${strategyName}", continuing with existing state`);
282
+ }
283
+ console.log(` Worktree (reused): ${worktreePath}`);
284
+ }
285
+ else {
286
+ // Fallback: worktree doesn't exist yet (race condition or first poll)
287
+ console.warn(`[strategy-executor] No persistent worktree for "${strategyName}", creating inline`);
288
+ try {
289
+ worktreePath = await createWorktree(config, branchName);
290
+ setStrategyWorktree(strategyId, {
291
+ strategyId,
292
+ strategyName,
293
+ worktreePath,
294
+ branchName,
295
+ createdAt: new Date(),
296
+ hasQaDevServer: false,
297
+ });
298
+ console.log(` Worktree (created inline): ${worktreePath}`);
299
+ // Install audit pre-commit hook for read-only strategies
300
+ if (readOnly) {
301
+ installAuditPreCommitHook(worktreePath);
302
+ console.log(` Installed audit pre-commit hook (read-only mode)`);
303
+ }
304
+ }
305
+ catch (err) {
306
+ console.error(`[strategy-executor] Failed to create worktree for strategy "${strategyName}":`, err instanceof Error ? err.message : String(err));
307
+ activeTeams.delete(strategyId);
308
+ return;
309
+ }
310
+ }
311
+ teamState.worktreePath = worktreePath;
312
+ // Create session record for the team lead
313
+ let session;
314
+ try {
315
+ session = await createSession({
316
+ organizationId: config.organizationId,
317
+ roleId: role.id,
318
+ issueId: null,
319
+ strategyId,
320
+ branchName,
321
+ sessionType: reviewMode ? 'review' : 'coding',
322
+ });
323
+ teamState.leadSessionId = session.id;
324
+ }
325
+ catch (err) {
326
+ console.error(`[strategy-executor] Failed to create session for strategy "${strategyName}":`, err.message);
327
+ // Worktree is strategy-owned and persists even if session creation fails
328
+ activeTeams.delete(strategyId);
329
+ return;
330
+ }
331
+ recordActivity();
332
+ // Build the team lead prompt with full strategy context
333
+ const prompt = buildStrategyTeamPrompt(role, {
334
+ strategyId,
335
+ strategyName,
336
+ organizationId: config.organizationId,
337
+ productId: config.productId,
338
+ deliveries,
339
+ issues,
340
+ executionConfig,
341
+ pipelineConfig,
342
+ productContextDocs,
343
+ deploymentProfileSnapshot,
344
+ readOnly,
345
+ });
346
+ // Log file paths
347
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
348
+ const logPrefix = `strategy-${branchName.replace(/\//g, '-')}-${timestamp}`;
349
+ const stdoutPath = join(config.logDir, `${logPrefix}.stdout.log`);
350
+ const stderrPath = join(config.logDir, `${logPrefix}.stderr.log`);
351
+ const jsonlPath = join(config.logDir, `${logPrefix}.stream.jsonl`);
352
+ // Open log files
353
+ const stdoutLogStream = createWriteStream(stdoutPath, { mode: 0o600 });
354
+ const stderrStream = createWriteStream(stderrPath, { mode: 0o600 });
355
+ const jsonlStream = createWriteStream(jsonlPath, { mode: 0o600 });
356
+ // Attach error handlers to prevent unhandled 'error' events from crashing the daemon
357
+ stdoutLogStream.on('error', (err) => {
358
+ console.warn(`[strategy-executor] stdout log stream error for "${strategyName}": ${err.message}`);
359
+ });
360
+ stderrStream.on('error', (err) => {
361
+ console.warn(`[strategy-executor] stderr log stream error for "${strategyName}": ${err.message}`);
362
+ });
363
+ jsonlStream.on('error', (err) => {
364
+ console.warn(`[strategy-executor] jsonl log stream error for "${strategyName}": ${err.message}`);
365
+ });
366
+ // Build args and env
367
+ // Pass lastClaudeSessionId for resume support (never for review teams)
368
+ const resumeId = reviewMode ? null : (lastClaudeSessionId ?? null);
369
+ const args = buildTeamLeadArgs(config, pipelineConfig, reviewMode, resumeId);
370
+ if (resumeId) {
371
+ console.log(`[strategy-executor] Resuming Claude session ${resumeId} for "${strategyName}"`);
372
+ }
373
+ const spawnEnv = buildSpawnEnvironment(config, {
374
+ orgId: config.organizationId,
375
+ strategyId,
376
+ sessionId: session.id,
377
+ });
378
+ // Update session to starting
379
+ await updateSession(session.id, {
380
+ status: 'starting',
381
+ started_at: new Date().toISOString(),
382
+ stdout_path: stdoutPath,
383
+ stderr_path: stderrPath,
384
+ });
385
+ // Acquire governor slot (if governor is configured)
386
+ if (governor) {
387
+ try {
388
+ await governor.acquireSlot('strategy');
389
+ }
390
+ catch (err) {
391
+ console.warn(`[strategy-executor] Governor denied slot for "${strategyName}":`, err.message);
392
+ // Mark session as failed so it doesn't linger in `starting` status
393
+ await updateSession(session.id, {
394
+ status: 'failed',
395
+ exit_reason: 'Governor denied slot',
396
+ ended_at: new Date().toISOString(),
397
+ }).catch(updateErr => {
398
+ console.warn(`[strategy-executor] Failed to update session after governor denial:`, updateErr.message);
399
+ });
400
+ activeTeams.delete(strategyId);
401
+ return;
402
+ }
403
+ }
404
+ // Spawn the team lead process
405
+ const proc = spawn(config.claudeCodePath, args, {
406
+ cwd: worktreePath,
407
+ env: spawnEnv,
408
+ stdio: ['pipe', 'pipe', 'pipe'],
409
+ });
410
+ teamState.leadPid = proc.pid ?? null;
411
+ teamState.leadStdin = proc.stdin;
412
+ teamState.phase = 'executing';
413
+ // Set up stream parsing
414
+ const streamParser = new StreamJsonParser();
415
+ const completionDetector = new CompletionDetector();
416
+ streamParser.attach(proc.stdout);
417
+ completionDetector.attach(streamParser);
418
+ teamState.completionDetector = completionDetector;
419
+ // Attach activity tracker for live activity snapshots
420
+ const activityTracker = new ActivityTracker(session.id);
421
+ activityTracker.onNarration((text) => setNarration(strategyId, text));
422
+ activityTracker.attach(streamParser);
423
+ // Write raw NDJSON lines to jsonl log file
424
+ streamParser.on('event', (event) => {
425
+ jsonlStream.write(JSON.stringify(event) + '\n');
426
+ });
427
+ // Write human-readable lines to stdout log
428
+ streamParser.on('event', (event) => {
429
+ const line = formatEventForLog(event);
430
+ if (line) {
431
+ stdoutLogStream.write(line + '\n');
432
+ }
433
+ });
434
+ // Track session state from stream events
435
+ streamParser.on('init', (event) => {
436
+ console.log(` [strategy-team] Session initialized (model: ${event.model}, tools: ${event.tools.length})`);
437
+ teamState.claudeSessionId = event.session_id;
438
+ // Persist session ID for --resume support on re-spawn
439
+ updateStrategyClaudeSessionId(strategyId, event.session_id).catch((err) => {
440
+ console.warn(`[strategy-executor] Failed to persist Claude session ID for "${strategyName}":`, err.message);
441
+ });
442
+ });
443
+ streamParser.on('teammate', (event) => {
444
+ if (event.subtype === 'teammate_spawned') {
445
+ console.log(` [strategy-team] Worker spawned: ${event.agent_name}`);
446
+ }
447
+ else if (event.subtype === 'teammate_completed') {
448
+ const status = event.is_error ? 'FAILED' : 'completed';
449
+ console.log(` [strategy-team] Worker ${status}: ${event.agent_name}`);
450
+ }
451
+ });
452
+ streamParser.on('result', (result) => {
453
+ const cost = result.total_cost_usd?.toFixed(4) ?? '?';
454
+ console.log(` [strategy-team] Result: ${result.is_error ? 'ERROR' : 'Success'}: ${result.num_turns} turns, $${cost}`);
455
+ });
456
+ // Stderr pipes to log
457
+ proc.stderr?.pipe(stderrStream);
458
+ // Send prompt via stdin
459
+ sendMessage(proc.stdin, prompt);
460
+ // In review mode, immediately send the review directive after the initial prompt
461
+ if (reviewMode) {
462
+ const reviewDeliveries = deliveries.filter(d => d.executionStatus === 'in_review');
463
+ if (reviewDeliveries.length > 0) {
464
+ const reviewDirective = buildReviewDirective(reviewDeliveries, issues);
465
+ sendMessage(proc.stdin, reviewDirective);
466
+ }
467
+ }
468
+ // Update session to running
469
+ await updateSession(session.id, {
470
+ status: 'running',
471
+ pid: proc.pid,
472
+ });
473
+ console.log(`[strategy-executor] Team lead spawned for "${strategyName}" (PID: ${proc.pid}, session: ${session.id})${reviewMode ? ' [REVIEW MODE]' : ''}`);
474
+ // Handle completion -- check for more work or terminate.
475
+ // No idle phase: if actionable work remains, send it immediately (mid-strategy
476
+ // handoff). If all work is done, terminate. A fresh team is spawned if new
477
+ // deliveries arrive later (~$0.50-1.00 exploration cost, but clean context).
478
+ completionDetector.on('complete', (info) => {
479
+ console.log(`[strategy-executor] Team lead for "${strategyName}" completed work cycle: ` +
480
+ `${info.turnCount} turns, $${info.totalCostUsd.toFixed(4)}`);
481
+ handleCompletionEvent({ strategyId, strategyName, teamState, completionDetector, proc }).catch(err => {
482
+ console.warn(`[strategy-executor] Failed to check delivery state for "${strategyName}":`, err.message);
483
+ // Fallback: terminate to avoid idle money burn.
484
+ terminateTeam(strategyId);
485
+ });
486
+ });
487
+ // Handle process exit
488
+ const spawnedAt = Date.now();
489
+ proc.on('close', async (code, signal) => {
490
+ console.log(`[strategy-executor] Team lead for "${strategyName}" exited (code: ${code}, signal: ${signal})`);
491
+ // Detect --resume failure: if the process exits quickly with a non-zero
492
+ // code and --resume was used, retry without resume. This handles cases
493
+ // where the session file was cleaned up, corrupted, or incompatible.
494
+ const RESUME_FAILURE_THRESHOLD_MS = 15_000;
495
+ const elapsedMs = Date.now() - spawnedAt;
496
+ if (resumeId && code !== 0 && code !== null && elapsedMs < RESUME_FAILURE_THRESHOLD_MS) {
497
+ console.warn(`[strategy-executor] Resume failed for "${strategyName}" (exited in ${elapsedMs}ms), retrying without resume`);
498
+ // Release governor slot and clean up minimal state
499
+ governor?.releaseSlot('strategy');
500
+ completionDetector.destroy();
501
+ stdoutLogStream.end();
502
+ stderrStream.end();
503
+ jsonlStream.end();
504
+ // Clean up session and team state
505
+ try {
506
+ await updateSession(session.id, {
507
+ status: 'failed',
508
+ exit_reason: `Resume failed (code ${code}), retrying without resume`,
509
+ ended_at: new Date().toISOString(),
510
+ });
511
+ }
512
+ catch (updateErr) {
513
+ console.warn('[strategy-executor] Failed to update session after resume failure:', updateErr.message);
514
+ }
515
+ teamState.phase = 'terminated';
516
+ activeTeams.delete(strategyId);
517
+ // Re-spawn without --resume
518
+ await spawnStrategyTeam({ ...params, lastClaudeSessionId: null });
519
+ return;
520
+ }
521
+ // Release governor slot (if governor is configured)
522
+ governor?.releaseSlot('strategy');
523
+ // Null out stdin so post-exit merge logic falls through to the
524
+ // fallback resolution agent instead of sending messages to a dead process.
525
+ teamState.leadStdin = null;
526
+ // Final activity flush before cleanup
527
+ try {
528
+ await activityTracker.finalFlush();
529
+ }
530
+ catch (err) {
531
+ console.warn('[strategy-executor] activityTracker.finalFlush failed:', err.message);
532
+ }
533
+ // Clean up streams
534
+ completionDetector.destroy();
535
+ stdoutLogStream.end();
536
+ stderrStream.end();
537
+ jsonlStream.end();
538
+ recordActivity();
539
+ await handleTeamCompletion({
540
+ config,
541
+ teamState,
542
+ sessionId: session.id,
543
+ code,
544
+ signal,
545
+ });
546
+ });
547
+ proc.on('error', async (err) => {
548
+ console.error(`[strategy-executor] Team lead for "${strategyName}" error:`, err.message);
549
+ // Release governor slot (if governor is configured)
550
+ governor?.releaseSlot('strategy');
551
+ try {
552
+ await withRetry(() => updateSession(session.id, {
553
+ status: 'failed',
554
+ exit_reason: `Process error: ${err.message}`,
555
+ ended_at: new Date().toISOString(),
556
+ }), { maxAttempts: 3, baseDelayMs: 1000, label: 'session-update-error' });
557
+ }
558
+ catch (updateErr) {
559
+ console.warn(`[strategy-executor] Failed to update session after retries:`, updateErr.message);
560
+ }
561
+ completionDetector.destroy();
562
+ stdoutLogStream.end();
563
+ stderrStream.end();
564
+ jsonlStream.end();
565
+ // Worktree is strategy-owned — do not remove on team error
566
+ teamState.phase = 'terminated';
567
+ activeTeams.delete(strategyId);
568
+ });
569
+ }
570
+ // ── Team lifecycle management ────────────────────────────────────────
571
+ /**
572
+ * Terminate a strategy team.
573
+ *
574
+ * Sends a deactivation message via stdin (if open) before SIGTERM
575
+ * to allow the team lead to process the shutdown gracefully.
576
+ */
577
+ export function terminateTeam(strategyId) {
578
+ const activeTeams = getActiveTeams();
579
+ const team = activeTeams.get(strategyId);
580
+ if (!team || !team.leadPid)
581
+ return false;
582
+ // Warn if terminating while conflict resolution is in progress
583
+ if (team.resolvingMergeConflict) {
584
+ console.warn(`[strategy-executor] Terminating team "${team.strategyName}" while merge conflict resolution is in progress`);
585
+ }
586
+ console.log(`[strategy-executor] Terminating team for strategy "${team.strategyName}" (phase: ${team.phase})`);
587
+ team.phase = 'shutting_down';
588
+ clearNarration(strategyId);
589
+ try {
590
+ // Send a deactivation message first to let the team lead process it
591
+ if (team.leadStdin) {
592
+ sendMessage(team.leadStdin, 'Pipeline deactivated. Exit now.');
593
+ // Close stdin after a short delay to let the message be processed
594
+ setTimeout(() => {
595
+ try {
596
+ team.leadStdin?.end();
597
+ }
598
+ catch (e) {
599
+ console.debug('[strategy-executor] stdin.end() failed (may already be closed):', e.message);
600
+ }
601
+ }, 5000);
602
+ }
603
+ // SIGTERM to the lead process -- it should clean up workers
604
+ process.kill(team.leadPid, 'SIGTERM');
605
+ // Escalate to SIGKILL after timeout
606
+ setTimeout(() => {
607
+ if (activeTeams.has(strategyId) && team.leadPid) {
608
+ try {
609
+ process.kill(team.leadPid, 'SIGKILL');
610
+ }
611
+ catch (e) {
612
+ console.debug('[strategy-executor] SIGKILL failed (process may have exited):', e.message);
613
+ }
614
+ }
615
+ }, 30000);
616
+ return true;
617
+ }
618
+ catch (e) {
619
+ console.debug(`[strategy-executor] terminateTeam: process may have already exited:`, e.message);
620
+ return false;
621
+ }
622
+ }
623
+ /**
624
+ * Wait for a team's process to exit (leave activeTeams).
625
+ *
626
+ * Polls activeTeams until the strategy is no longer present,
627
+ * which happens when handleTeamCompletion runs on the 'close' event.
628
+ * If the timeout expires, sends SIGKILL and waits briefly.
629
+ *
630
+ * @param strategyId - Strategy to wait for
631
+ * @param timeoutMs - Max wait time in ms (default 30s)
632
+ * @returns true if team exited within timeout, false if forced
633
+ */
634
+ export async function waitForTeamExit(strategyId, timeoutMs = 30000) {
635
+ const activeTeams = getActiveTeams();
636
+ const team = activeTeams.get(strategyId);
637
+ if (!team)
638
+ return true; // Already gone
639
+ const deadline = Date.now() + timeoutMs;
640
+ const pollMs = 500;
641
+ while (Date.now() < deadline) {
642
+ if (!activeTeams.has(strategyId))
643
+ return true;
644
+ await new Promise(resolve => setTimeout(resolve, pollMs));
645
+ }
646
+ // Timeout expired — escalate to SIGKILL
647
+ if (team.leadPid) {
648
+ console.warn(`[strategy-executor] Team "${team.strategyName}" did not exit within ${timeoutMs}ms, sending SIGKILL`);
649
+ try {
650
+ process.kill(team.leadPid, 'SIGKILL');
651
+ }
652
+ catch { /* process may already be gone */ }
653
+ // Brief wait for SIGKILL to take effect
654
+ await new Promise(resolve => setTimeout(resolve, 2000));
655
+ }
656
+ return !activeTeams.has(strategyId);
657
+ }
658
+ /**
659
+ * Terminate all active teams.
660
+ */
661
+ export function terminateAllTeams() {
662
+ const activeTeams = getActiveTeams();
663
+ for (const strategyId of activeTeams.keys()) {
664
+ terminateTeam(strategyId);
665
+ }
666
+ }
667
+ /**
668
+ * Detect strategies that have been deactivated (agent role removed)
669
+ * and shut down their active teams.
670
+ */
671
+ export async function detectDeactivatedStrategies(config) {
672
+ const activeTeams = getActiveTeams();
673
+ if (activeTeams.size === 0)
674
+ return;
675
+ try {
676
+ // Aggregate ready strategies across all configured products
677
+ const allReadyStrategies = [];
678
+ for (const product of config.products) {
679
+ const strategies = await getReadyStrategies(config.organizationId, product.id);
680
+ allReadyStrategies.push(...strategies);
681
+ }
682
+ const activeStrategyIds = new Set(allReadyStrategies.map(s => s.strategy_id));
683
+ for (const [strategyId, team] of activeTeams) {
684
+ if (!activeStrategyIds.has(strategyId) && team.phase !== 'shutting_down' && team.phase !== 'terminated') {
685
+ console.log(`[strategy-executor] Strategy "${team.strategyName}" deactivated -- shutting down team`);
686
+ terminateTeam(strategyId);
687
+ }
688
+ }
689
+ }
690
+ catch (err) {
691
+ console.warn(`[strategy-executor] Failed to check for deactivated strategies:`, err.message);
692
+ }
693
+ }
694
+ /**
695
+ * Check active teams for newly completed deliveries and merge
696
+ * the strategy branch to integration incrementally.
697
+ *
698
+ * Called from the poll loop. For each active team, queries delivery
699
+ * statuses and triggers a merge when any delivery reaches verify/done
700
+ * that hasn't already been merged mid-flight.
701
+ *
702
+ * After processing merges, checks if ALL active deliveries are terminal
703
+ * and merged. If so, terminates the team proactively since no more work
704
+ * remains. The strategy stays active in the DB -- if new deliveries
705
+ * arrive later, the next poll cycle spawns a fresh team.
706
+ */
707
+ export async function checkAndMergeCompletedDeliveries(config) {
708
+ const activeTeams = getActiveTeams();
709
+ if (activeTeams.size === 0)
710
+ return;
711
+ for (const [strategyId, team] of activeTeams) {
712
+ // Only check teams that are actively executing
713
+ if (team.phase !== 'executing')
714
+ continue;
715
+ // Skip merge attempts while team lead is resolving a merge conflict
716
+ if (team.resolvingMergeConflict) {
717
+ console.log(`[strategy-executor] Skipping merge check for "${team.strategyName}" -- conflict resolution in progress`);
718
+ continue;
719
+ }
720
+ // Use product-scoped config for this team's merge operations
721
+ const teamProduct = findProduct(config, team.productId);
722
+ const teamConfig = teamProduct ? configForProduct(config, teamProduct) : config;
723
+ try {
724
+ const deliveries = await getStrategyDeliveries(strategyId);
725
+ // Find deliveries in verify/done that we haven't merged for yet
726
+ const newlyCompleted = deliveries.filter(d => (d.executionStatus === 'verify' || d.executionStatus === 'done')
727
+ && !team.mergedDeliveryIds.has(d.id));
728
+ if (newlyCompleted.length > 0) {
729
+ const completedNames = newlyCompleted.map(d => d.name).join(', ');
730
+ console.log(`[strategy-executor] ${newlyCompleted.length} delivery(ies) completed mid-flight for "${team.strategyName}": ${completedNames} -- merging to integration`);
731
+ // One merge covers all completed deliveries (same branch)
732
+ const mergeResult = await mergeStrategyBranch(teamConfig, team, team.leadSessionId ?? '', team.branchName);
733
+ if (mergeResult.mergeSucceeded) {
734
+ // Track merged deliveries (git state already reported by mergeStrategyBranch)
735
+ for (const d of newlyCompleted) {
736
+ team.mergedDeliveryIds.add(d.id);
737
+ }
738
+ }
739
+ else {
740
+ console.warn(`[strategy-executor] Mid-flight merge failed for "${team.strategyName}": ${mergeResult.exitReason}`);
741
+ // Escalate merge conflict for each unmerged delivery
742
+ for (const d of newlyCompleted) {
743
+ escalateMergeConflict({
744
+ organizationId: team.organizationId,
745
+ sessionId: team.leadSessionId ?? '',
746
+ deliveryId: d.id,
747
+ deliveryName: d.name,
748
+ branchName: team.branchName,
749
+ integrationBranch: config.integrationBranch,
750
+ mergeError: mergeResult.exitReason,
751
+ }).catch(err => console.warn(`[strategy-executor] escalateMergeConflict failed for ${d.id}:`, err.message));
752
+ }
753
+ // Don't mark as merged -- will retry next poll. Team continues working.
754
+ }
755
+ }
756
+ // ── All-done detection ──────────────────────────────────────
757
+ // If any delivery is queued or running, the team still has work.
758
+ // In review mode, in_review deliveries are the team's active work.
759
+ // Otherwise the team is idle, waiting for the daemon to push new work.
760
+ const teamWork = deliveries.filter(d => TEAM_WORK_STATUSES.has(d.executionStatus ?? '')
761
+ || (team.reviewMode && d.executionStatus === 'in_review'));
762
+ if (teamWork.length > 0) {
763
+ console.log(`[strategy-executor] ${teamWork.length} delivery(ies) still queued/running for "${team.strategyName}" -- team continues`);
764
+ continue;
765
+ }
766
+ // No queued/running deliveries -- team is idle, completion detector
767
+ // handles the actual idle transition. Log for observability.
768
+ console.log(`[strategy-executor] All deliveries complete for "${team.strategyName}" -- team is idle`);
769
+ }
770
+ catch (err) {
771
+ console.warn(`[strategy-executor] Failed to check completed deliveries for "${team.strategyName}":`, err.message);
772
+ }
773
+ }
774
+ }
775
+ //# sourceMappingURL=strategy-executor.js.map