oh-my-codex 0.11.13 → 0.12.1

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 (377) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/README.md +34 -17
  4. package/crates/omx-runtime/src/main.rs +6 -2
  5. package/dist/agents/native-config.js +1 -1
  6. package/dist/agents/native-config.js.map +1 -1
  7. package/dist/cli/__tests__/autoresearch-guided.test.js +74 -2
  8. package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
  9. package/dist/cli/__tests__/cleanup.test.js +37 -30
  10. package/dist/cli/__tests__/cleanup.test.js.map +1 -1
  11. package/dist/cli/__tests__/error-handling-warnings.test.js +3 -1
  12. package/dist/cli/__tests__/error-handling-warnings.test.js.map +1 -1
  13. package/dist/cli/__tests__/index.test.js +276 -5
  14. package/dist/cli/__tests__/index.test.js.map +1 -1
  15. package/dist/cli/__tests__/launch-fallback.test.js +95 -1
  16. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  17. package/dist/cli/__tests__/setup-refresh.test.js +49 -9
  18. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  19. package/dist/cli/__tests__/setup-scope.test.js +9 -0
  20. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  21. package/dist/cli/__tests__/team.test.js +136 -11
  22. package/dist/cli/__tests__/team.test.js.map +1 -1
  23. package/dist/cli/__tests__/uninstall.test.js +10 -0
  24. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  25. package/dist/cli/__tests__/windows-popup-loop-contract.test.js +1 -0
  26. package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
  27. package/dist/cli/autoresearch-guided.d.ts.map +1 -1
  28. package/dist/cli/autoresearch-guided.js +2 -1
  29. package/dist/cli/autoresearch-guided.js.map +1 -1
  30. package/dist/cli/autoresearch.d.ts.map +1 -1
  31. package/dist/cli/autoresearch.js +2 -1
  32. package/dist/cli/autoresearch.js.map +1 -1
  33. package/dist/cli/cleanup.d.ts.map +1 -1
  34. package/dist/cli/cleanup.js +10 -5
  35. package/dist/cli/cleanup.js.map +1 -1
  36. package/dist/cli/index.d.ts +21 -1
  37. package/dist/cli/index.d.ts.map +1 -1
  38. package/dist/cli/index.js +298 -36
  39. package/dist/cli/index.js.map +1 -1
  40. package/dist/cli/omx.js +2 -0
  41. package/dist/cli/omx.js.map +1 -1
  42. package/dist/cli/setup.d.ts +1 -0
  43. package/dist/cli/setup.d.ts.map +1 -1
  44. package/dist/cli/setup.js +41 -7
  45. package/dist/cli/setup.js.map +1 -1
  46. package/dist/cli/team.d.ts.map +1 -1
  47. package/dist/cli/team.js +16 -557
  48. package/dist/cli/team.js.map +1 -1
  49. package/dist/cli/uninstall.d.ts.map +1 -1
  50. package/dist/cli/uninstall.js +34 -9
  51. package/dist/cli/uninstall.js.map +1 -1
  52. package/dist/config/__tests__/generator-idempotent.test.js +79 -2
  53. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  54. package/dist/config/__tests__/generator-notify.test.js +2 -0
  55. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  56. package/dist/config/codex-hooks.d.ts +11 -0
  57. package/dist/config/codex-hooks.d.ts.map +1 -0
  58. package/dist/config/codex-hooks.js +50 -0
  59. package/dist/config/codex-hooks.js.map +1 -0
  60. package/dist/config/generator.d.ts +5 -3
  61. package/dist/config/generator.d.ts.map +1 -1
  62. package/dist/config/generator.js +24 -14
  63. package/dist/config/generator.js.map +1 -1
  64. package/dist/hooks/__tests__/debugger-log-recency-contract.test.d.ts +2 -0
  65. package/dist/hooks/__tests__/debugger-log-recency-contract.test.d.ts.map +1 -0
  66. package/dist/hooks/__tests__/debugger-log-recency-contract.test.js +20 -0
  67. package/dist/hooks/__tests__/debugger-log-recency-contract.test.js.map +1 -0
  68. package/dist/hooks/__tests__/keyword-detector.test.js +132 -0
  69. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  70. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +292 -4
  71. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  72. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +86 -0
  73. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  74. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +40 -0
  75. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  76. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.d.ts +2 -0
  77. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.d.ts.map +1 -0
  78. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +54 -0
  79. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -0
  80. package/dist/hooks/__tests__/notify-hook-modules.test.js +31 -0
  81. package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
  82. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +51 -0
  83. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
  84. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.d.ts +2 -0
  85. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.d.ts.map +1 -0
  86. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js +136 -0
  87. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js.map +1 -0
  88. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +120 -0
  89. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  90. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +145 -20
  91. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  92. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +116 -0
  93. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  94. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +86 -0
  95. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  96. package/dist/hooks/__tests__/pre-context-gate-skills.test.js +1 -0
  97. package/dist/hooks/__tests__/pre-context-gate-skills.test.js.map +1 -1
  98. package/dist/hooks/extensibility/__tests__/runtime.test.js +49 -0
  99. package/dist/hooks/extensibility/__tests__/runtime.test.js.map +1 -1
  100. package/dist/hooks/extensibility/runtime.d.ts.map +1 -1
  101. package/dist/hooks/extensibility/runtime.js +10 -0
  102. package/dist/hooks/extensibility/runtime.js.map +1 -1
  103. package/dist/hooks/extensibility/types.d.ts +1 -1
  104. package/dist/hooks/extensibility/types.d.ts.map +1 -1
  105. package/dist/hooks/keyword-detector.d.ts +2 -0
  106. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  107. package/dist/hooks/keyword-detector.js +76 -4
  108. package/dist/hooks/keyword-detector.js.map +1 -1
  109. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  110. package/dist/hooks/prompt-guidance-contract.js +12 -8
  111. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  112. package/dist/hooks/session.d.ts +5 -1
  113. package/dist/hooks/session.d.ts.map +1 -1
  114. package/dist/hooks/session.js +10 -6
  115. package/dist/hooks/session.js.map +1 -1
  116. package/dist/hud/index.d.ts.map +1 -1
  117. package/dist/hud/index.js +6 -1
  118. package/dist/hud/index.js.map +1 -1
  119. package/dist/mcp/__tests__/bootstrap.test.js +0 -3
  120. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  121. package/dist/mcp/__tests__/code-intel-server.test.js +27 -1
  122. package/dist/mcp/__tests__/code-intel-server.test.js.map +1 -1
  123. package/dist/mcp/__tests__/server-lifecycle.test.js +0 -5
  124. package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
  125. package/dist/mcp/bootstrap.d.ts +1 -1
  126. package/dist/mcp/bootstrap.d.ts.map +1 -1
  127. package/dist/mcp/bootstrap.js +0 -1
  128. package/dist/mcp/bootstrap.js.map +1 -1
  129. package/dist/mcp/code-intel-server.d.ts +20 -0
  130. package/dist/mcp/code-intel-server.d.ts.map +1 -1
  131. package/dist/mcp/code-intel-server.js +6 -5
  132. package/dist/mcp/code-intel-server.js.map +1 -1
  133. package/dist/notifications/__tests__/idle-cooldown.test.js +24 -1
  134. package/dist/notifications/__tests__/idle-cooldown.test.js.map +1 -1
  135. package/dist/notifications/__tests__/reply-listener.test.js +20 -1
  136. package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
  137. package/dist/notifications/__tests__/tmux.test.js +41 -0
  138. package/dist/notifications/__tests__/tmux.test.js.map +1 -1
  139. package/dist/notifications/idle-cooldown.d.ts +13 -0
  140. package/dist/notifications/idle-cooldown.d.ts.map +1 -1
  141. package/dist/notifications/idle-cooldown.js +50 -16
  142. package/dist/notifications/idle-cooldown.js.map +1 -1
  143. package/dist/notifications/reply-listener.d.ts.map +1 -1
  144. package/dist/notifications/reply-listener.js +2 -0
  145. package/dist/notifications/reply-listener.js.map +1 -1
  146. package/dist/notifications/tmux.d.ts.map +1 -1
  147. package/dist/notifications/tmux.js +4 -0
  148. package/dist/notifications/tmux.js.map +1 -1
  149. package/dist/scripts/__tests__/codex-native-hook.test.d.ts +2 -0
  150. package/dist/scripts/__tests__/codex-native-hook.test.d.ts.map +1 -0
  151. package/dist/scripts/__tests__/codex-native-hook.test.js +1050 -0
  152. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -0
  153. package/dist/scripts/codex-native-hook.d.ts +22 -0
  154. package/dist/scripts/codex-native-hook.d.ts.map +1 -0
  155. package/dist/scripts/codex-native-hook.js +792 -0
  156. package/dist/scripts/codex-native-hook.js.map +1 -0
  157. package/dist/scripts/codex-native-pre-post.d.ts +26 -0
  158. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -0
  159. package/dist/scripts/codex-native-pre-post.js +118 -0
  160. package/dist/scripts/codex-native-pre-post.js.map +1 -0
  161. package/dist/scripts/notify-fallback-watcher.js +322 -21
  162. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  163. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  164. package/dist/scripts/notify-hook/auto-nudge.js +5 -6
  165. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  166. package/dist/scripts/notify-hook/log.d.ts +2 -2
  167. package/dist/scripts/notify-hook/log.d.ts.map +1 -1
  168. package/dist/scripts/notify-hook/log.js +10 -2
  169. package/dist/scripts/notify-hook/log.js.map +1 -1
  170. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  171. package/dist/scripts/notify-hook/managed-tmux.js +2 -0
  172. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  173. package/dist/scripts/notify-hook/orchestration-intent.d.ts +18 -0
  174. package/dist/scripts/notify-hook/orchestration-intent.d.ts.map +1 -0
  175. package/dist/scripts/notify-hook/orchestration-intent.js +72 -0
  176. package/dist/scripts/notify-hook/orchestration-intent.js.map +1 -0
  177. package/dist/scripts/notify-hook/process-runner.js.map +1 -1
  178. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
  179. package/dist/scripts/notify-hook/ralph-session-resume.js +7 -0
  180. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  181. package/dist/scripts/notify-hook/team-dispatch.d.ts +15 -6
  182. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  183. package/dist/scripts/notify-hook/team-dispatch.js +125 -6
  184. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  185. package/dist/scripts/notify-hook/team-leader-nudge.d.ts +3 -2
  186. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  187. package/dist/scripts/notify-hook/team-leader-nudge.js +165 -37
  188. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  189. package/dist/scripts/notify-hook/team-tmux-guard.d.ts +4 -1
  190. package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
  191. package/dist/scripts/notify-hook/team-tmux-guard.js +33 -44
  192. package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
  193. package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
  194. package/dist/scripts/notify-hook/team-worker.js +68 -5
  195. package/dist/scripts/notify-hook/team-worker.js.map +1 -1
  196. package/dist/scripts/notify-hook/utils.d.ts +1 -1
  197. package/dist/scripts/notify-hook/utils.d.ts.map +1 -1
  198. package/dist/scripts/notify-hook/utils.js.map +1 -1
  199. package/dist/scripts/notify-hook.js +55 -32
  200. package/dist/scripts/notify-hook.js.map +1 -1
  201. package/dist/team/__tests__/api-interop.test.js +344 -18
  202. package/dist/team/__tests__/api-interop.test.js.map +1 -1
  203. package/dist/team/__tests__/delivery-e2e-smoke.test.d.ts +2 -0
  204. package/dist/team/__tests__/delivery-e2e-smoke.test.d.ts.map +1 -0
  205. package/dist/team/__tests__/delivery-e2e-smoke.test.js +671 -0
  206. package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -0
  207. package/dist/team/__tests__/mcp-comm.test.js +5 -0
  208. package/dist/team/__tests__/mcp-comm.test.js.map +1 -1
  209. package/dist/team/__tests__/runtime.test.js +543 -15
  210. package/dist/team/__tests__/runtime.test.js.map +1 -1
  211. package/dist/team/__tests__/state.test.js +133 -8
  212. package/dist/team/__tests__/state.test.js.map +1 -1
  213. package/dist/team/__tests__/team-ops-contract.test.js +4 -0
  214. package/dist/team/__tests__/team-ops-contract.test.js.map +1 -1
  215. package/dist/team/__tests__/tmux-session.test.js +160 -0
  216. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  217. package/dist/team/__tests__/worker-bootstrap.test.js +19 -1
  218. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  219. package/dist/team/api-interop.d.ts.map +1 -1
  220. package/dist/team/api-interop.js +95 -23
  221. package/dist/team/api-interop.js.map +1 -1
  222. package/dist/team/contracts.d.ts +11 -1
  223. package/dist/team/contracts.d.ts.map +1 -1
  224. package/dist/team/contracts.js +29 -0
  225. package/dist/team/contracts.js.map +1 -1
  226. package/dist/team/delivery-log.d.ts +14 -0
  227. package/dist/team/delivery-log.d.ts.map +1 -0
  228. package/dist/team/delivery-log.js +35 -0
  229. package/dist/team/delivery-log.js.map +1 -0
  230. package/dist/team/idle-nudge.d.ts +2 -2
  231. package/dist/team/idle-nudge.js +2 -2
  232. package/dist/team/mcp-comm.d.ts +4 -0
  233. package/dist/team/mcp-comm.d.ts.map +1 -1
  234. package/dist/team/mcp-comm.js +84 -1
  235. package/dist/team/mcp-comm.js.map +1 -1
  236. package/dist/team/pane-status.d.ts +149 -0
  237. package/dist/team/pane-status.d.ts.map +1 -0
  238. package/dist/team/pane-status.js +558 -0
  239. package/dist/team/pane-status.js.map +1 -0
  240. package/dist/team/reminder-intents.d.ts +11 -0
  241. package/dist/team/reminder-intents.d.ts.map +1 -0
  242. package/dist/team/reminder-intents.js +40 -0
  243. package/dist/team/reminder-intents.js.map +1 -0
  244. package/dist/team/runtime-cli.d.ts +1 -1
  245. package/dist/team/runtime-cli.js +2 -2
  246. package/dist/team/runtime-cli.js.map +1 -1
  247. package/dist/team/runtime.d.ts +2 -1
  248. package/dist/team/runtime.d.ts.map +1 -1
  249. package/dist/team/runtime.js +409 -191
  250. package/dist/team/runtime.js.map +1 -1
  251. package/dist/team/scaling.d.ts.map +1 -1
  252. package/dist/team/scaling.js +6 -5
  253. package/dist/team/scaling.js.map +1 -1
  254. package/dist/team/state/dispatch.d.ts +4 -1
  255. package/dist/team/state/dispatch.d.ts.map +1 -1
  256. package/dist/team/state/dispatch.js +59 -18
  257. package/dist/team/state/dispatch.js.map +1 -1
  258. package/dist/team/state/mailbox.d.ts.map +1 -1
  259. package/dist/team/state/mailbox.js +45 -2
  260. package/dist/team/state/mailbox.js.map +1 -1
  261. package/dist/team/state/monitor.d.ts +2 -1
  262. package/dist/team/state/monitor.d.ts.map +1 -1
  263. package/dist/team/state/monitor.js +30 -1
  264. package/dist/team/state/monitor.js.map +1 -1
  265. package/dist/team/state/types.d.ts +5 -2
  266. package/dist/team/state/types.d.ts.map +1 -1
  267. package/dist/team/state/types.js.map +1 -1
  268. package/dist/team/state.d.ts +30 -3
  269. package/dist/team/state.d.ts.map +1 -1
  270. package/dist/team/state.js +170 -2
  271. package/dist/team/state.js.map +1 -1
  272. package/dist/team/team-ops.d.ts +5 -1
  273. package/dist/team/team-ops.d.ts.map +1 -1
  274. package/dist/team/team-ops.js +4 -0
  275. package/dist/team/team-ops.js.map +1 -1
  276. package/dist/team/tmux-session.d.ts +2 -0
  277. package/dist/team/tmux-session.d.ts.map +1 -1
  278. package/dist/team/tmux-session.js +19 -3
  279. package/dist/team/tmux-session.js.map +1 -1
  280. package/dist/team/worker-bootstrap.d.ts +4 -0
  281. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  282. package/dist/team/worker-bootstrap.js +33 -6
  283. package/dist/team/worker-bootstrap.js.map +1 -1
  284. package/dist/utils/__tests__/paths.test.js +63 -1
  285. package/dist/utils/__tests__/paths.test.js.map +1 -1
  286. package/dist/utils/__tests__/platform-command.test.js +50 -4
  287. package/dist/utils/__tests__/platform-command.test.js.map +1 -1
  288. package/dist/utils/paths.d.ts +12 -0
  289. package/dist/utils/paths.d.ts.map +1 -1
  290. package/dist/utils/paths.js +44 -2
  291. package/dist/utils/paths.js.map +1 -1
  292. package/dist/utils/platform-command.d.ts.map +1 -1
  293. package/dist/utils/platform-command.js +13 -5
  294. package/dist/utils/platform-command.js.map +1 -1
  295. package/dist/utils/sleep.d.ts.map +1 -1
  296. package/dist/utils/sleep.js +10 -1
  297. package/dist/utils/sleep.js.map +1 -1
  298. package/package.json +1 -1
  299. package/prompts/analyst.md +2 -2
  300. package/prompts/api-reviewer.md +2 -2
  301. package/prompts/architect.md +2 -2
  302. package/prompts/build-fixer.md +2 -2
  303. package/prompts/code-reviewer.md +2 -2
  304. package/prompts/code-simplifier.md +1 -1
  305. package/prompts/critic.md +2 -2
  306. package/prompts/debugger.md +3 -2
  307. package/prompts/dependency-expert.md +2 -2
  308. package/prompts/designer.md +2 -2
  309. package/prompts/executor.md +3 -2
  310. package/prompts/explore.md +2 -2
  311. package/prompts/git-master.md +2 -2
  312. package/prompts/information-architect.md +15 -102
  313. package/prompts/performance-reviewer.md +2 -2
  314. package/prompts/planner.md +3 -2
  315. package/prompts/product-analyst.md +2 -2
  316. package/prompts/product-manager.md +2 -2
  317. package/prompts/qa-tester.md +2 -2
  318. package/prompts/quality-reviewer.md +2 -2
  319. package/prompts/quality-strategist.md +2 -2
  320. package/prompts/researcher.md +2 -2
  321. package/prompts/security-reviewer.md +2 -2
  322. package/prompts/sisyphus-lite.md +2 -2
  323. package/prompts/style-reviewer.md +2 -2
  324. package/prompts/team-executor.md +2 -2
  325. package/prompts/test-engineer.md +2 -2
  326. package/prompts/ux-researcher.md +2 -2
  327. package/prompts/verifier.md +3 -2
  328. package/prompts/vision.md +2 -2
  329. package/prompts/writer.md +2 -2
  330. package/skills/team/SKILL.md +18 -33
  331. package/src/scripts/__tests__/codex-native-hook.test.ts +1346 -0
  332. package/src/scripts/codex-native-hook.ts +983 -0
  333. package/src/scripts/codex-native-pre-post.ts +161 -0
  334. package/src/scripts/notify-fallback-watcher.ts +378 -29
  335. package/src/scripts/notify-hook/auto-nudge.ts +5 -10
  336. package/src/scripts/notify-hook/log.ts +18 -4
  337. package/src/scripts/notify-hook/managed-tmux.ts +1 -0
  338. package/src/scripts/notify-hook/orchestration-intent.ts +82 -0
  339. package/src/scripts/notify-hook/process-runner.ts +4 -4
  340. package/src/scripts/notify-hook/ralph-session-resume.ts +9 -0
  341. package/src/scripts/notify-hook/team-dispatch.ts +134 -6
  342. package/src/scripts/notify-hook/team-leader-nudge.ts +183 -37
  343. package/src/scripts/notify-hook/team-tmux-guard.ts +35 -43
  344. package/src/scripts/notify-hook/team-worker.ts +73 -4
  345. package/src/scripts/notify-hook/utils.ts +1 -1
  346. package/src/scripts/notify-hook.ts +64 -32
  347. package/templates/AGENTS.md +21 -11
  348. package/README.de.md +0 -263
  349. package/README.el.md +0 -223
  350. package/README.es.md +0 -263
  351. package/README.fr.md +0 -263
  352. package/README.it.md +0 -263
  353. package/README.ja.md +0 -264
  354. package/README.ko.md +0 -264
  355. package/README.pl.md +0 -216
  356. package/README.pt.md +0 -263
  357. package/README.ru.md +0 -263
  358. package/README.tr.md +0 -263
  359. package/README.vi.md +0 -223
  360. package/README.zh-TW.md +0 -293
  361. package/README.zh.md +0 -264
  362. package/dist/mcp/__tests__/team-server-cleanup.test.d.ts +0 -2
  363. package/dist/mcp/__tests__/team-server-cleanup.test.d.ts.map +0 -1
  364. package/dist/mcp/__tests__/team-server-cleanup.test.js +0 -219
  365. package/dist/mcp/__tests__/team-server-cleanup.test.js.map +0 -1
  366. package/dist/mcp/__tests__/team-server-runtime-deps.test.d.ts +0 -2
  367. package/dist/mcp/__tests__/team-server-runtime-deps.test.d.ts.map +0 -1
  368. package/dist/mcp/__tests__/team-server-runtime-deps.test.js +0 -13
  369. package/dist/mcp/__tests__/team-server-runtime-deps.test.js.map +0 -1
  370. package/dist/mcp/__tests__/team-server-wait.test.d.ts +0 -2
  371. package/dist/mcp/__tests__/team-server-wait.test.d.ts.map +0 -1
  372. package/dist/mcp/__tests__/team-server-wait.test.js +0 -155
  373. package/dist/mcp/__tests__/team-server-wait.test.js.map +0 -1
  374. package/dist/mcp/team-server.d.ts +0 -24
  375. package/dist/mcp/team-server.d.ts.map +0 -1
  376. package/dist/mcp/team-server.js +0 -482
  377. package/dist/mcp/team-server.js.map +0 -1
@@ -3,10 +3,11 @@ import { existsSync, appendFileSync, mkdirSync } from 'fs';
3
3
  import { mkdir, readdir, readFile, writeFile } from 'fs/promises';
4
4
  import { performance } from 'perf_hooks';
5
5
  import { spawn, spawnSync } from 'child_process';
6
- import { sanitizeTeamName, isTmuxAvailable, createTeamSession, buildWorkerProcessLaunchSpec, resolveTeamWorkerCli, resolveTeamWorkerCliPlan, resolveTeamWorkerLaunchMode, waitForWorkerReady, dismissTrustPromptIfPresent, sleepFractionalSeconds, sendToWorker, sendToWorkerStdin, isWorkerAlive, getWorkerPanePid, killWorkerByPaneIdAsync, restoreStandaloneHudPane, teardownWorkerPanes, unregisterResizeHook, destroyTeamSession, listTeamSessions, } from './tmux-session.js';
6
+ import { sanitizeTeamName, isTmuxAvailable, hasCurrentTmuxClientContext, createTeamSession, buildWorkerProcessLaunchSpec, resolveTeamWorkerCli, resolveTeamWorkerCliPlan, resolveTeamWorkerLaunchMode, waitForWorkerReady, dismissTrustPromptIfPresent, sleepFractionalSeconds, sendToWorker, sendToWorkerStdin, isWorkerAlive, getWorkerPanePid, killWorkerByPaneIdAsync, restoreStandaloneHudPane, teardownWorkerPanes, unregisterResizeHook, destroyTeamSession, listPaneIds, listTeamSessions, } from './tmux-session.js';
7
7
  import { teamInit as initTeamState, DEFAULT_MAX_WORKERS, teamReadConfig as readTeamConfig, teamWriteWorkerIdentity as writeWorkerIdentity, teamReadWorkerHeartbeat as readWorkerHeartbeat, teamReadWorkerStatus as readWorkerStatus, teamWriteWorkerInbox as writeWorkerInbox, teamCreateTask as createStateTask, teamReadTask as readTask, teamListTasks as listTasks, teamReadManifest as readTeamManifestV2, teamNormalizeGovernance as normalizeTeamGovernance, teamNormalizePolicy as normalizeTeamPolicy, teamClaimTask as claimTask, teamReleaseTaskClaim as releaseTaskClaim, teamReclaimExpiredTaskClaim as reclaimExpiredTaskClaim, teamAppendEvent as appendTeamEvent, teamReadTaskApproval as readTaskApproval, teamListMailbox as listMailboxMessages, teamMarkMessageDelivered as markMessageDelivered, teamMarkMessageNotified as markMessageNotified, teamEnqueueDispatchRequest as enqueueDispatchRequest, teamMarkDispatchRequestNotified as markDispatchRequestNotified, teamTransitionDispatchRequest as transitionDispatchRequest, teamReadDispatchRequest as readDispatchRequest, teamCleanup as cleanupTeamState, teamSaveConfig as saveTeamConfig, teamWriteShutdownRequest as writeShutdownRequest, teamReadShutdownAck as readShutdownAck, teamReadMonitorSnapshot as readMonitorSnapshot, teamWriteMonitorSnapshot as writeMonitorSnapshot, teamReadPhase as readTeamPhaseState, teamWritePhase as writeTeamPhaseState, } from './team-ops.js';
8
8
  import { queueInboxInstruction, queueDirectMailboxMessage, queueBroadcastMailboxMessage, waitForDispatchReceipt, } from './mcp-comm.js';
9
- import { generateWorkerOverlay, writeTeamWorkerInstructionsFile, removeTeamWorkerInstructionsFile, writeWorkerWorktreeRootAgentsFile, removeWorkerWorktreeRootAgentsFile, generateInitialInbox, generateTaskAssignmentInbox, generateShutdownInbox, generateTriggerMessage, generateMailboxTriggerMessage, generateLeaderMailboxTriggerMessage, writeWorkerRoleInstructionsFile, } from './worker-bootstrap.js';
9
+ import { appendTeamDeliveryLogForCwd } from './delivery-log.js';
10
+ import { generateWorkerOverlay, writeTeamWorkerInstructionsFile, removeTeamWorkerInstructionsFile, writeWorkerWorktreeRootAgentsFile, removeWorkerWorktreeRootAgentsFile, generateInitialInbox, generateTaskAssignmentInbox, generateShutdownInbox, buildTriggerDirective, buildMailboxTriggerDirective, buildLeaderMailboxTriggerDirective, writeWorkerRoleInstructionsFile, } from './worker-bootstrap.js';
10
11
  import { loadRolePrompt } from './role-router.js';
11
12
  import { composeRoleInstructionsForRole } from '../agents/native-config.js';
12
13
  import { codexPromptsDir } from '../utils/paths.js';
@@ -49,6 +50,58 @@ async function syncRootTeamModeStateOnTerminalPhase(teamName, phase, cwd) {
49
50
  // Best-effort compatibility sync only.
50
51
  }
51
52
  }
53
+ export function applyCreatedInteractiveSessionToConfig(config, createdSession, workerPaneIds) {
54
+ config.tmux_session = createdSession.name;
55
+ config.leader_pane_id = createdSession.leaderPaneId;
56
+ config.hud_pane_id = createdSession.hudPaneId;
57
+ config.resize_hook_name = createdSession.resizeHookName;
58
+ config.resize_hook_target = createdSession.resizeHookTarget;
59
+ for (let i = 0; i < createdSession.workerPaneIds.length; i++) {
60
+ const paneId = createdSession.workerPaneIds[i];
61
+ workerPaneIds[i] = paneId;
62
+ if (config.workers[i]) {
63
+ config.workers[i].pane_id = paneId;
64
+ }
65
+ }
66
+ }
67
+ function collectShutdownPaneIds(params) {
68
+ const { config, livePaneIds = [], restoredStandaloneHudPaneId = null } = params;
69
+ const excludedPaneIds = new Set([
70
+ config.leader_pane_id,
71
+ config.hud_pane_id,
72
+ restoredStandaloneHudPaneId,
73
+ ].filter((paneId) => typeof paneId === 'string' && paneId.trim().startsWith('%')));
74
+ const paneIds = new Set();
75
+ for (const paneId of [
76
+ ...config.workers.map((worker) => worker.pane_id),
77
+ ...livePaneIds,
78
+ ]) {
79
+ if (typeof paneId !== 'string')
80
+ continue;
81
+ const normalized = paneId.trim();
82
+ if (!normalized.startsWith('%'))
83
+ continue;
84
+ if (excludedPaneIds.has(normalized))
85
+ continue;
86
+ paneIds.add(normalized);
87
+ }
88
+ return [...paneIds];
89
+ }
90
+ async function logRuntimeDispatchOutcome(params) {
91
+ const { cwd, teamName, workerName, requestId, messageId, intent, outcome, source = 'team.runtime' } = params;
92
+ await appendTeamDeliveryLogForCwd(cwd, {
93
+ event: 'dispatch_result',
94
+ source,
95
+ team: teamName,
96
+ request_id: requestId,
97
+ message_id: messageId,
98
+ to_worker: workerName,
99
+ intent,
100
+ transport: outcome.transport,
101
+ result: outcome.ok ? 'confirmed' : 'failed',
102
+ reason: outcome.reason,
103
+ });
104
+ }
52
105
  function collectProvisionedShutdownWorktrees(config) {
53
106
  const seenWorktreePaths = new Set();
54
107
  const worktrees = [];
@@ -147,6 +200,36 @@ async function appendIntegrationEvent(teamName, type, worker, metadata, cwd) {
147
200
  async function sendIntegrationMessageToLeader(teamName, worker, body, cwd) {
148
201
  await sendWorkerMessage(teamName, worker.name, 'leader-fixed', body, cwd).catch(() => { });
149
202
  }
203
+ function leaderHeadAdvanced(before, after) {
204
+ return typeof after === 'string' && after.length > 0 && after !== before;
205
+ }
206
+ async function recordIntegrationFailure(teamName, worker, state, details, cwd) {
207
+ const leaderHeadAfter = details.leaderHeadAfter ?? details.leaderHeadBefore;
208
+ const sourceShort = details.sourceCommit.slice(0, 12);
209
+ const leaderShort = details.leaderHeadBefore.slice(0, 12);
210
+ state.last_leader_head = leaderHeadAfter;
211
+ state.status = 'integration_failed';
212
+ state.conflict_commit = details.sourceCommit;
213
+ state.conflict_files = undefined;
214
+ state.updated_at = new Date().toISOString();
215
+ await appendIntegrationEvent(teamName, 'worker_integration_failed', worker, {
216
+ worker_name: worker.name,
217
+ operation: details.operation,
218
+ source_commit: details.sourceCommit,
219
+ leader_head_before: details.leaderHeadBefore,
220
+ leader_head_after: leaderHeadAfter,
221
+ worktree_path: details.worktreePath,
222
+ summary: `${details.operation} for ${worker.name} reported success but leader HEAD did not advance`,
223
+ }, cwd);
224
+ appendIntegrationReport(teamName, {
225
+ workerName: worker.name,
226
+ operation: details.operation,
227
+ strategy: details.strategy,
228
+ files: [],
229
+ detail: `${details.operation} reported success for ${sourceShort}, but leader HEAD did not advance from ${leaderShort}; not marking worker as integrated.`,
230
+ }, cwd);
231
+ await sendIntegrationMessageToLeader(teamName, worker, `INTEGRATION FAILED: ${details.operation} for ${worker.name} reported success, but leader HEAD stayed at ${leaderShort}. Not emitting INTEGRATED; retry or inspect leader branch state before continuing.`, cwd);
232
+ }
150
233
  function autoCommitDirtyWorktree(worker) {
151
234
  const worktreePath = resolve(worker.worktree_path);
152
235
  const repoRoot = resolve(worker.worktree_repo_root);
@@ -247,8 +330,17 @@ async function integrateWorkerCommitsIntoLeader(params) {
247
330
  if (merge.ok) {
248
331
  const newLeaderHead = resolveLeaderHead(repoRoot, cwd) ?? leaderHead;
249
332
  const workerIntegrated = leaderContainsCommit(repoRoot, cwd, workerHead);
250
- const leaderAdvanced = newLeaderHead !== leaderHead;
251
- if (workerIntegrated && leaderAdvanced) {
333
+ if (!leaderHeadAdvanced(leaderHead, newLeaderHead)) {
334
+ await recordIntegrationFailure(teamName, worker, state, {
335
+ operation: 'merge',
336
+ sourceCommit: workerHead,
337
+ leaderHeadBefore: leaderHead,
338
+ leaderHeadAfter: newLeaderHead,
339
+ worktreePath,
340
+ strategy: '-X theirs',
341
+ }, cwd);
342
+ }
343
+ else if (workerIntegrated) {
252
344
  state.last_integrated_head = workerHead;
253
345
  state.last_leader_head = newLeaderHead;
254
346
  state.status = 'integrated';
@@ -383,6 +475,18 @@ async function integrateWorkerCommitsIntoLeader(params) {
383
475
  break;
384
476
  }
385
477
  const newLeaderHead = resolveLeaderHead(repoRoot, cwd) ?? leaderHead;
478
+ if (!leaderHeadAdvanced(leaderHead, newLeaderHead)) {
479
+ await recordIntegrationFailure(teamName, worker, state, {
480
+ operation: 'cherry-pick',
481
+ sourceCommit: commit,
482
+ leaderHeadBefore: leaderHead,
483
+ leaderHeadAfter: newLeaderHead,
484
+ worktreePath,
485
+ strategy: '-X theirs',
486
+ }, cwd);
487
+ allPicked = false;
488
+ break;
489
+ }
386
490
  state.last_integrated_head = commit;
387
491
  state.last_leader_head = newLeaderHead;
388
492
  state.status = 'integrated';
@@ -1172,7 +1276,7 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
1172
1276
  if (!isTmuxAvailable()) {
1173
1277
  throw new Error('Team mode requires tmux. Install with: apt install tmux / brew install tmux');
1174
1278
  }
1175
- if (!process.env.TMUX) {
1279
+ if (!hasCurrentTmuxClientContext()) {
1176
1280
  throw new Error('Team mode requires running inside tmux current leader pane');
1177
1281
  }
1178
1282
  }
@@ -1305,7 +1409,8 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
1305
1409
  rolePromptContent: rawRolePromptContent ?? undefined,
1306
1410
  worktreeRootAgentsCanonical: Boolean(workerWorkspace.worktreePath),
1307
1411
  });
1308
- const trigger = generateTriggerMessage(workerName, sanitized, resolveInstructionStateRoot(workerWorkspace.worktreePath));
1412
+ const triggerDirective = buildTriggerDirective(workerName, sanitized, resolveInstructionStateRoot(workerWorkspace.worktreePath));
1413
+ const trigger = triggerDirective.text;
1309
1414
  const initialPrompt = workerCliPlan[i - 1] === 'gemini' ? trigger : undefined;
1310
1415
  if (initialPrompt) {
1311
1416
  await writeWorkerInbox(sanitized, workerName, inbox, leaderCwd);
@@ -1319,6 +1424,7 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
1319
1424
  instructionsFilePath,
1320
1425
  inbox,
1321
1426
  trigger,
1427
+ triggerIntent: triggerDirective.intent,
1322
1428
  initialPrompt,
1323
1429
  workerLaunchArgs,
1324
1430
  workerCli: workerCliPlan[i - 1],
@@ -1355,14 +1461,7 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
1355
1461
  sessionCreated = true;
1356
1462
  createdWorkerPaneIds.push(...createdSession.workerPaneIds);
1357
1463
  createdLeaderPaneId = createdSession.leaderPaneId;
1358
- config.tmux_session = sessionName;
1359
- config.leader_pane_id = createdSession.leaderPaneId;
1360
- config.hud_pane_id = createdSession.hudPaneId;
1361
- config.resize_hook_name = createdSession.resizeHookName;
1362
- config.resize_hook_target = createdSession.resizeHookTarget;
1363
- for (let i = 0; i < createdSession.workerPaneIds.length; i++) {
1364
- workerPaneIds[i] = createdSession.workerPaneIds[i];
1365
- }
1464
+ applyCreatedInteractiveSessionToConfig(config, createdSession, workerPaneIds);
1366
1465
  }
1367
1466
  else {
1368
1467
  config.tmux_session = `prompt-${sanitized}`;
@@ -1388,13 +1487,14 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
1388
1487
  if (!bootstrapPlan) {
1389
1488
  throw new Error(`missing bootstrap plan for worker-${i}`);
1390
1489
  }
1391
- const { workerName, paneId, workerTasks, workerRole, inbox, trigger, initialPrompt } = {
1490
+ const { workerName, paneId, workerTasks, workerRole, inbox, trigger, triggerIntent, initialPrompt } = {
1392
1491
  workerName: bootstrapPlan.workerName,
1393
1492
  paneId: workerPaneIds[i - 1],
1394
1493
  workerTasks: bootstrapPlan.workerTasks,
1395
1494
  workerRole: bootstrapPlan.workerRole,
1396
1495
  inbox: bootstrapPlan.inbox,
1397
1496
  trigger: bootstrapPlan.trigger,
1497
+ triggerIntent: bootstrapPlan.triggerIntent,
1398
1498
  initialPrompt: bootstrapPlan.initialPrompt,
1399
1499
  };
1400
1500
  const workerWorkspace = bootstrapPlan.workerWorkspace;
@@ -1418,7 +1518,7 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
1418
1518
  };
1419
1519
  // Get pane PID and store it (interactive mode) or process PID (prompt mode)
1420
1520
  if (workerLaunchMode === 'interactive') {
1421
- const panePid = getWorkerPanePid(sessionName, i);
1521
+ const panePid = getWorkerPanePid(sessionName, i, paneId);
1422
1522
  if (panePid)
1423
1523
  identity.pid = panePid;
1424
1524
  }
@@ -1428,6 +1528,7 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
1428
1528
  if (paneId)
1429
1529
  identity.pane_id = paneId;
1430
1530
  if (config.workers[i - 1]) {
1531
+ config.workers[i - 1].pid = identity.pid;
1431
1532
  config.workers[i - 1].pane_id = paneId;
1432
1533
  config.workers[i - 1].role = workerRole;
1433
1534
  config.workers[i - 1].worker_cli = workerCliPlan[i - 1];
@@ -1466,6 +1567,7 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
1466
1567
  workerCli: workerCliPlan[i - 1],
1467
1568
  inbox,
1468
1569
  triggerMessage: trigger,
1570
+ intent: triggerIntent,
1469
1571
  cwd: leaderCwd,
1470
1572
  dispatchPolicy,
1471
1573
  inboxCorrelationKey: `startup:${workerName}`,
@@ -1874,6 +1976,7 @@ export async function assignTask(teamName, workerName, taskId, cwd) {
1874
1976
  const maxAssignRetries = 2;
1875
1977
  const assignRetryDelayS = 2;
1876
1978
  let outcome = { ok: false, transport: 'none', reason: 'not_attempted' };
1979
+ const triggerDirective = buildTriggerDirective(workerName, sanitized, resolveInstructionStateRoot(workerInfo.worktree_path));
1877
1980
  for (let attempt = 1; attempt <= maxAssignRetries; attempt++) {
1878
1981
  outcome = await dispatchCriticalInboxInstruction({
1879
1982
  teamName: sanitized,
@@ -1882,7 +1985,8 @@ export async function assignTask(teamName, workerName, taskId, cwd) {
1882
1985
  workerIndex: workerInfo.index,
1883
1986
  paneId: workerInfo.pane_id,
1884
1987
  inbox,
1885
- triggerMessage: generateTriggerMessage(workerName, sanitized, resolveInstructionStateRoot(workerInfo.worktree_path)),
1988
+ triggerMessage: triggerDirective.text,
1989
+ intent: triggerDirective.intent,
1886
1990
  cwd,
1887
1991
  dispatchPolicy,
1888
1992
  inboxCorrelationKey: `assign:${taskId}:${workerName}`,
@@ -1988,6 +2092,7 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
1988
2092
  const requestedAt = new Date().toISOString();
1989
2093
  await writeShutdownRequest(sanitized, w.name, 'leader-fixed', cwd);
1990
2094
  shutdownRequestTimes.set(w.name, requestedAt);
2095
+ const triggerDirective = buildTriggerDirective(w.name, sanitized, resolveInstructionStateRoot(w.worktree_path));
1991
2096
  await dispatchCriticalInboxInstruction({
1992
2097
  teamName: sanitized,
1993
2098
  config,
@@ -1995,7 +2100,8 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
1995
2100
  workerIndex: w.index,
1996
2101
  paneId: w.pane_id,
1997
2102
  inbox: generateShutdownInbox(sanitized, w.name),
1998
- triggerMessage: generateTriggerMessage(w.name, sanitized, resolveInstructionStateRoot(w.worktree_path)),
2103
+ triggerMessage: triggerDirective.text,
2104
+ intent: triggerDirective.intent,
1999
2105
  cwd,
2000
2106
  dispatchPolicy,
2001
2107
  inboxCorrelationKey: `shutdown:${w.name}`,
@@ -2049,8 +2155,10 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
2049
2155
  const leaderPaneId = config.leader_pane_id;
2050
2156
  const hudPaneId = config.hud_pane_id;
2051
2157
  if (config.worker_launch_mode === 'interactive') {
2052
- const workerPanePids = config.workers
2053
- .map((w) => getWorkerPanePid(sessionName, w.index, w.pane_id))
2158
+ const livePaneIds = listPaneIds(sessionName);
2159
+ let shutdownPaneIds = collectShutdownPaneIds({ config, livePaneIds });
2160
+ const workerPanePids = shutdownPaneIds
2161
+ .map((paneId) => getWorkerPanePid(sessionName, 1, paneId))
2054
2162
  .filter((pid) => typeof pid === 'number' && Number.isFinite(pid) && pid > 0);
2055
2163
  for (const panePid of workerPanePids) {
2056
2164
  await terminateTrackedProcessTree(panePid);
@@ -2073,22 +2181,25 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
2073
2181
  if (resizeHookWarning) {
2074
2182
  console.warn(`[team shutdown] ${sanitized}: ${resizeHookWarning}; continuing teardown`);
2075
2183
  }
2076
- const workerPaneIds = config.workers
2077
- .map((w) => w.pane_id)
2078
- .filter((paneId) => typeof paneId === 'string' && paneId.trim().length > 0);
2079
- await teardownWorkerPanes(workerPaneIds, {
2080
- leaderPaneId,
2081
- hudPaneId,
2082
- });
2184
+ let restoredHudPaneId = null;
2083
2185
  if (hudPaneId) {
2084
2186
  await killWorkerByPaneIdAsync(hudPaneId, leaderPaneId ?? undefined);
2085
2187
  if (sessionName.includes(':')) {
2086
- const restoredHudPaneId = restoreStandaloneHudPane(leaderPaneId, cwd);
2188
+ restoredHudPaneId = restoreStandaloneHudPane(leaderPaneId, cwd);
2087
2189
  if (!restoredHudPaneId) {
2088
2190
  console.warn(`[team shutdown] ${sanitized}: failed to restore standalone HUD pane`);
2089
2191
  }
2090
2192
  }
2091
2193
  }
2194
+ shutdownPaneIds = collectShutdownPaneIds({
2195
+ config,
2196
+ livePaneIds: listPaneIds(sessionName),
2197
+ restoredStandaloneHudPaneId: restoredHudPaneId,
2198
+ });
2199
+ await teardownWorkerPanes(shutdownPaneIds, {
2200
+ leaderPaneId,
2201
+ hudPaneId: restoredHudPaneId ?? hudPaneId,
2202
+ });
2092
2203
  // 4. Destroy tmux session
2093
2204
  if (!sessionName.includes(':')) {
2094
2205
  try {
@@ -2416,7 +2527,7 @@ async function markDispatchRequestLeaderPaneMissingDeferred(params) {
2416
2527
  }, cwd).catch(() => { });
2417
2528
  }
2418
2529
  async function dispatchCriticalInboxInstruction(params) {
2419
- const { teamName, config, workerName, workerIndex, paneId, workerCli, inbox, triggerMessage, cwd, dispatchPolicy, inboxCorrelationKey, requireWorkerStartupEvidence, } = params;
2530
+ const { teamName, config, workerName, workerIndex, paneId, workerCli, inbox, triggerMessage, intent, cwd, dispatchPolicy, inboxCorrelationKey, requireWorkerStartupEvidence, } = params;
2420
2531
  if (config.worker_launch_mode === 'prompt') {
2421
2532
  return await queueInboxInstruction({
2422
2533
  teamName,
@@ -2425,6 +2536,7 @@ async function dispatchCriticalInboxInstruction(params) {
2425
2536
  paneId,
2426
2537
  inbox,
2427
2538
  triggerMessage,
2539
+ intent,
2428
2540
  cwd,
2429
2541
  transportPreference: 'prompt_stdin',
2430
2542
  fallbackAllowed: false,
@@ -2440,6 +2552,7 @@ async function dispatchCriticalInboxInstruction(params) {
2440
2552
  paneId,
2441
2553
  inbox,
2442
2554
  triggerMessage,
2555
+ intent,
2443
2556
  cwd,
2444
2557
  transportPreference: 'transport_direct',
2445
2558
  fallbackAllowed: false,
@@ -2454,6 +2567,7 @@ async function dispatchCriticalInboxInstruction(params) {
2454
2567
  paneId,
2455
2568
  inbox,
2456
2569
  triggerMessage,
2570
+ intent,
2457
2571
  cwd,
2458
2572
  transportPreference: 'hook_preferred_with_fallback',
2459
2573
  fallbackAllowed: true,
@@ -2494,7 +2608,7 @@ async function dispatchCriticalInboxInstruction(params) {
2494
2608
  if (receipt?.status === 'failed') {
2495
2609
  const fallback = await notifyWorkerOutcome(config, workerIndex, triggerMessage, paneId);
2496
2610
  if (fallback.ok) {
2497
- await markDispatchRequestNotified(teamName, queued.request_id, { last_reason: `fallback_confirmed_after_failed_receipt:${fallback.reason}`, failed_at: undefined }, cwd).catch(() => null);
2611
+ await transitionDispatchRequest(teamName, queued.request_id, 'failed', 'failed', { last_reason: `fallback_confirmed_after_failed_receipt:${fallback.reason}` }, cwd).catch(() => { });
2498
2612
  return {
2499
2613
  ok: true,
2500
2614
  transport: fallback.transport,
@@ -2543,14 +2657,16 @@ async function dispatchCriticalInboxInstruction(params) {
2543
2657
  };
2544
2658
  }
2545
2659
  async function finalizeHookPreferredMailboxDispatch(params) {
2546
- const { teamName, requestId, workerName, workerIndex, paneId, messageId, triggerMessage, config, dispatchPolicy, cwd, fallbackNotify, } = params;
2660
+ const { teamName, requestId, workerName, workerIndex, paneId, messageId, triggerMessage, intent, config, dispatchPolicy, cwd, fallbackNotify, } = params;
2547
2661
  const receipt = await waitForDispatchReceipt(teamName, requestId, cwd, {
2548
2662
  timeoutMs: dispatchPolicy.dispatch_ack_timeout_ms,
2549
2663
  pollMs: 50,
2550
2664
  });
2551
2665
  if (receipt && (receipt.status === 'notified' || receipt.status === 'delivered')) {
2552
2666
  await markMessageNotified(teamName, workerName, messageId, cwd).catch(() => false);
2553
- return { ok: true, transport: 'hook', reason: `hook_receipt_${receipt.status}`, request_id: requestId, message_id: messageId };
2667
+ const outcome = { ok: true, transport: 'hook', reason: `hook_receipt_${receipt.status}`, request_id: requestId, message_id: messageId };
2668
+ await logRuntimeDispatchOutcome({ cwd, teamName, workerName, requestId, messageId, intent, outcome });
2669
+ return outcome;
2554
2670
  }
2555
2671
  const fallback = fallbackNotify
2556
2672
  ? await fallbackNotify()
@@ -2560,23 +2676,27 @@ async function finalizeHookPreferredMailboxDispatch(params) {
2560
2676
  if (receipt?.status === 'failed') {
2561
2677
  if (fallback.ok) {
2562
2678
  await markMessageNotified(teamName, workerName, messageId, cwd).catch(() => false);
2563
- await markDispatchRequestNotified(teamName, requestId, { message_id: messageId, last_reason: `fallback_confirmed_after_failed_receipt:${fallback.reason}`, failed_at: undefined }, cwd).catch(() => null);
2564
- return {
2679
+ await transitionDispatchRequest(teamName, requestId, 'failed', 'failed', { message_id: messageId, last_reason: `fallback_confirmed_after_failed_receipt:${fallback.reason}` }, cwd).catch(() => { });
2680
+ const outcome = {
2565
2681
  ok: true,
2566
2682
  transport: fallback.transport,
2567
2683
  reason: `fallback_confirmed_after_failed_receipt:${fallback.reason}`,
2568
2684
  request_id: requestId,
2569
2685
  message_id: messageId,
2570
2686
  };
2687
+ await logRuntimeDispatchOutcome({ cwd, teamName, workerName, requestId, messageId, intent, outcome });
2688
+ return outcome;
2571
2689
  }
2572
2690
  await transitionDispatchRequest(teamName, requestId, 'failed', 'failed', { message_id: messageId, last_reason: `fallback_attempted_but_unconfirmed:${fallback.reason}` }, cwd).catch(() => { });
2573
- return {
2691
+ const outcome = {
2574
2692
  ok: false,
2575
2693
  transport: fallback.transport,
2576
2694
  reason: `fallback_attempted_but_unconfirmed:${fallback.reason}`,
2577
2695
  request_id: requestId,
2578
2696
  message_id: messageId,
2579
2697
  };
2698
+ await logRuntimeDispatchOutcome({ cwd, teamName, workerName, requestId, messageId, intent, outcome });
2699
+ return outcome;
2580
2700
  }
2581
2701
  if (fallback.ok) {
2582
2702
  if (isLeaderPaneMissingMailboxPersistedOutcome({ workerName, paneId, outcome: fallback })) {
@@ -2586,38 +2706,44 @@ async function finalizeHookPreferredMailboxDispatch(params) {
2586
2706
  messageId,
2587
2707
  cwd,
2588
2708
  });
2589
- return {
2709
+ const outcome = {
2590
2710
  ok: true,
2591
2711
  transport: fallback.transport,
2592
2712
  reason: 'leader_pane_missing_mailbox_persisted',
2593
2713
  request_id: requestId,
2594
2714
  message_id: messageId,
2595
2715
  };
2716
+ await logRuntimeDispatchOutcome({ cwd, teamName, workerName, requestId, messageId, intent, outcome });
2717
+ return outcome;
2596
2718
  }
2597
2719
  await markMessageNotified(teamName, workerName, messageId, cwd).catch(() => false);
2598
2720
  const marked = await markDispatchRequestNotified(teamName, requestId, { message_id: messageId, last_reason: `fallback_confirmed:${fallback.reason}` }, cwd);
2599
2721
  if (!marked) {
2600
2722
  await transitionDispatchRequest(teamName, requestId, 'failed', 'failed', { message_id: messageId, last_reason: `fallback_confirmed_after_failed_receipt:${fallback.reason}` }, cwd).catch(() => { });
2601
2723
  }
2602
- return {
2724
+ const outcome = {
2603
2725
  ok: true,
2604
2726
  transport: fallback.transport,
2605
2727
  reason: `hook_timeout_fallback_confirmed:${fallback.reason}`,
2606
2728
  request_id: requestId,
2607
2729
  message_id: messageId,
2608
2730
  };
2731
+ await logRuntimeDispatchOutcome({ cwd, teamName, workerName, requestId, messageId, intent, outcome });
2732
+ return outcome;
2609
2733
  }
2610
2734
  const current = await readDispatchRequest(teamName, requestId, cwd);
2611
2735
  if (current) {
2612
2736
  await transitionDispatchRequest(teamName, requestId, current.status, 'failed', { message_id: messageId, last_reason: `fallback_attempted_but_unconfirmed:${fallback.reason}` }, cwd).catch(() => { });
2613
2737
  }
2614
- return {
2738
+ const outcome = {
2615
2739
  ok: false,
2616
2740
  transport: fallback.transport,
2617
2741
  reason: `fallback_attempted_but_unconfirmed:${fallback.reason}`,
2618
2742
  request_id: requestId,
2619
2743
  message_id: messageId,
2620
2744
  };
2745
+ await logRuntimeDispatchOutcome({ cwd, teamName, workerName, requestId, messageId, intent, outcome });
2746
+ return outcome;
2621
2747
  }
2622
2748
  async function notifyLeaderAsync(config, message, cwd) {
2623
2749
  // Canonical leader delivery is durable mailbox persistence plus HUD-owned
@@ -2663,44 +2789,15 @@ async function deliverPendingMailboxMessages(teamName, config, workers, previous
2663
2789
  if (!worker.alive)
2664
2790
  continue;
2665
2791
  for (const msg of unnotified) {
2666
- const triggerMessage = generateMailboxTriggerMessage(worker.name, teamName, 1, resolveInstructionStateRoot(workerInfo.worktree_path));
2667
- const transportPreference = config.worker_launch_mode === 'prompt'
2668
- ? 'prompt_stdin'
2669
- : (dispatchPolicy.dispatch_mode === 'transport_direct' ? 'transport_direct' : 'hook_preferred_with_fallback');
2670
- const fallbackAllowed = transportPreference === 'hook_preferred_with_fallback';
2671
- const queued = await enqueueDispatchRequest(teamName, {
2672
- kind: 'mailbox',
2673
- to_worker: worker.name,
2674
- worker_index: workerInfo.index,
2675
- pane_id: workerInfo.pane_id,
2676
- trigger_message: triggerMessage,
2677
- message_id: msg.message_id,
2678
- transport_preference: transportPreference,
2679
- fallback_allowed: fallbackAllowed,
2680
- }, cwd);
2681
- let outcome;
2682
- if (transportPreference === 'hook_preferred_with_fallback') {
2683
- outcome = await finalizeHookPreferredMailboxDispatch({
2684
- teamName,
2685
- requestId: queued.request.request_id,
2686
- workerName: worker.name,
2687
- workerIndex: workerInfo.index,
2688
- paneId: workerInfo.pane_id,
2689
- messageId: msg.message_id,
2690
- triggerMessage,
2691
- config,
2692
- dispatchPolicy,
2693
- cwd,
2694
- });
2695
- }
2696
- else {
2697
- const direct = await notifyWorkerOutcome(config, workerInfo.index, triggerMessage, workerInfo.pane_id);
2698
- outcome = { ...direct, request_id: queued.request.request_id, message_id: msg.message_id };
2699
- if (outcome.ok) {
2700
- await markMessageNotified(teamName, worker.name, msg.message_id, cwd).catch(() => false);
2701
- await markDispatchRequestNotified(teamName, queued.request.request_id, { message_id: msg.message_id, last_reason: outcome.reason }, cwd).catch(() => null);
2702
- }
2703
- }
2792
+ const outcome = await dispatchPendingMailboxMessage({
2793
+ teamName,
2794
+ workerName: worker.name,
2795
+ workerInfo,
2796
+ messageId: msg.message_id,
2797
+ config,
2798
+ dispatchPolicy,
2799
+ cwd,
2800
+ });
2704
2801
  if (outcome.ok) {
2705
2802
  nextNotifications[msg.message_id] = new Date().toISOString();
2706
2803
  }
@@ -2713,87 +2810,175 @@ async function deliverPendingMailboxMessages(teamName, config, workers, previous
2713
2810
  }
2714
2811
  return pruned;
2715
2812
  }
2716
- export async function sendWorkerMessage(teamName, fromWorker, toWorker, body, cwd) {
2717
- const sanitized = sanitizeTeamName(teamName);
2718
- const config = await readTeamConfig(sanitized, cwd);
2719
- if (!config)
2720
- throw new Error(`Team ${sanitized} not found`);
2721
- const manifest = await readTeamManifestV2(sanitized, cwd);
2722
- const dispatchPolicy = resolveDispatchPolicy(manifest?.policy, config.worker_launch_mode);
2723
- if (toWorker === 'leader-fixed') {
2724
- const leaderTriggerMessage = generateLeaderMailboxTriggerMessage(sanitized, fromWorker);
2725
- const leaderTransportPreference = dispatchPolicy.dispatch_mode === 'transport_direct'
2726
- ? 'transport_direct'
2727
- : 'hook_preferred_with_fallback';
2728
- const outcome = await queueDirectMailboxMessage({
2729
- teamName: sanitized,
2730
- fromWorker,
2731
- toWorker,
2732
- toPaneId: config.leader_pane_id ?? undefined,
2733
- body,
2734
- triggerMessage: leaderTriggerMessage,
2813
+ function resolveWorkerMailboxTransportPreference(config, dispatchPolicy) {
2814
+ return config.worker_launch_mode === 'prompt'
2815
+ ? 'prompt_stdin'
2816
+ : (dispatchPolicy.dispatch_mode === 'transport_direct' ? 'transport_direct' : 'hook_preferred_with_fallback');
2817
+ }
2818
+ function resolveLeaderMailboxTransportPreference(dispatchPolicy) {
2819
+ return dispatchPolicy.dispatch_mode === 'transport_direct' ? 'transport_direct' : 'hook_preferred_with_fallback';
2820
+ }
2821
+ function isExistingMailboxNotificationOutcome(outcome) {
2822
+ return outcome.ok && outcome.reason === 'existing_message_already_notified';
2823
+ }
2824
+ async function dispatchPendingMailboxMessage(params) {
2825
+ const { teamName, workerName, workerInfo, messageId, config, dispatchPolicy, cwd } = params;
2826
+ const triggerDirective = buildMailboxTriggerDirective(workerName, teamName, 1, resolveInstructionStateRoot(workerInfo.worktree_path));
2827
+ const transportPreference = resolveWorkerMailboxTransportPreference(config, dispatchPolicy);
2828
+ const queued = await enqueueDispatchRequest(teamName, {
2829
+ kind: 'mailbox',
2830
+ to_worker: workerName,
2831
+ worker_index: workerInfo.index,
2832
+ pane_id: workerInfo.pane_id,
2833
+ trigger_message: triggerDirective.text,
2834
+ intent: triggerDirective.intent,
2835
+ message_id: messageId,
2836
+ transport_preference: transportPreference,
2837
+ fallback_allowed: transportPreference === 'hook_preferred_with_fallback',
2838
+ }, cwd);
2839
+ if (transportPreference === 'hook_preferred_with_fallback') {
2840
+ return await finalizeQueuedMailboxDispatch({
2841
+ queuedOutcome: {
2842
+ ok: true,
2843
+ transport: 'hook',
2844
+ reason: 'queued_for_hook_dispatch',
2845
+ request_id: queued.request.request_id,
2846
+ message_id: messageId,
2847
+ },
2848
+ transportPreference,
2849
+ teamName,
2850
+ workerName,
2851
+ workerIndex: workerInfo.index,
2852
+ paneId: workerInfo.pane_id,
2853
+ messageId,
2854
+ triggerMessage: triggerDirective.text,
2855
+ intent: triggerDirective.intent,
2856
+ config,
2857
+ dispatchPolicy,
2735
2858
  cwd,
2736
- transportPreference: leaderTransportPreference,
2737
- fallbackAllowed: leaderTransportPreference === 'hook_preferred_with_fallback',
2738
- notify: async (_target, message) => (leaderTransportPreference === 'hook_preferred_with_fallback'
2739
- ? { ok: true, transport: 'hook', reason: 'queued_for_hook_dispatch' }
2740
- : await notifyLeaderAsync(config, message, cwd)),
2741
2859
  });
2742
- let finalOutcome = outcome;
2743
- const mailboxAlreadyNotified = outcome.ok && outcome.reason === 'existing_message_already_notified';
2744
- if (!mailboxAlreadyNotified && leaderTransportPreference === 'hook_preferred_with_fallback' && !config.leader_pane_id) {
2745
- if (outcome.request_id) {
2746
- await markDispatchRequestLeaderPaneMissingDeferred({
2747
- teamName: sanitized,
2748
- requestId: outcome.request_id,
2749
- messageId: outcome.message_id,
2750
- cwd,
2751
- });
2752
- }
2753
- finalOutcome = {
2754
- ...outcome,
2755
- ok: true,
2756
- transport: 'mailbox',
2757
- reason: 'leader_pane_missing_mailbox_persisted',
2758
- };
2759
- }
2760
- const canLeaderFallbackDirectly = Boolean(config.leader_pane_id) && isTmuxAvailable();
2761
- if (!mailboxAlreadyNotified && leaderTransportPreference === 'hook_preferred_with_fallback' && canLeaderFallbackDirectly) {
2762
- if (!outcome.request_id || !outcome.message_id) {
2763
- throw new Error('mailbox_notify_failed:dispatch_request_missing_id');
2764
- }
2765
- finalOutcome = await finalizeHookPreferredMailboxDispatch({
2766
- teamName: sanitized,
2767
- requestId: outcome.request_id,
2768
- workerName: 'leader-fixed',
2769
- paneId: config.leader_pane_id ?? undefined,
2770
- messageId: outcome.message_id,
2771
- triggerMessage: leaderTriggerMessage,
2772
- config,
2773
- dispatchPolicy,
2860
+ }
2861
+ const direct = await notifyWorkerOutcome(config, workerInfo.index, triggerDirective.text, workerInfo.pane_id);
2862
+ const outcome = { ...direct, request_id: queued.request.request_id, message_id: messageId };
2863
+ if (outcome.ok) {
2864
+ await markMessageNotified(teamName, workerName, messageId, cwd).catch(() => false);
2865
+ await markDispatchRequestNotified(teamName, queued.request.request_id, { message_id: messageId, last_reason: outcome.reason }, cwd).catch(() => null);
2866
+ }
2867
+ await logRuntimeDispatchOutcome({
2868
+ cwd,
2869
+ teamName,
2870
+ workerName,
2871
+ requestId: queued.request.request_id,
2872
+ messageId,
2873
+ outcome,
2874
+ });
2875
+ return outcome;
2876
+ }
2877
+ async function finalizeQueuedMailboxDispatch(params) {
2878
+ const { queuedOutcome, transportPreference, teamName, workerName, workerIndex, paneId, messageId, triggerMessage, intent, config, dispatchPolicy, cwd, fallbackNotify, } = params;
2879
+ if (transportPreference !== 'hook_preferred_with_fallback') {
2880
+ return queuedOutcome;
2881
+ }
2882
+ if (isExistingMailboxNotificationOutcome(queuedOutcome)) {
2883
+ return queuedOutcome;
2884
+ }
2885
+ if (!queuedOutcome.request_id || !messageId) {
2886
+ return { ...queuedOutcome, ok: false, reason: 'dispatch_request_missing_id' };
2887
+ }
2888
+ return await finalizeHookPreferredMailboxDispatch({
2889
+ teamName,
2890
+ requestId: queuedOutcome.request_id,
2891
+ workerName,
2892
+ workerIndex,
2893
+ paneId,
2894
+ messageId,
2895
+ triggerMessage,
2896
+ intent,
2897
+ config,
2898
+ dispatchPolicy,
2899
+ cwd,
2900
+ fallbackNotify,
2901
+ });
2902
+ }
2903
+ async function sendLeaderMailboxMessage(params) {
2904
+ const { teamName, fromWorker, body, config, dispatchPolicy, cwd } = params;
2905
+ const triggerDirective = buildLeaderMailboxTriggerDirective(teamName, fromWorker, config.team_state_root || undefined);
2906
+ const transportPreference = resolveLeaderMailboxTransportPreference(dispatchPolicy);
2907
+ const queuedOutcome = await queueDirectMailboxMessage({
2908
+ teamName,
2909
+ fromWorker,
2910
+ toWorker: 'leader-fixed',
2911
+ toPaneId: config.leader_pane_id ?? undefined,
2912
+ body,
2913
+ triggerMessage: triggerDirective.text,
2914
+ intent: triggerDirective.intent,
2915
+ cwd,
2916
+ transportPreference,
2917
+ fallbackAllowed: transportPreference === 'hook_preferred_with_fallback',
2918
+ notify: async (_target, message) => (transportPreference === 'hook_preferred_with_fallback'
2919
+ ? { ok: true, transport: 'hook', reason: 'queued_for_hook_dispatch' }
2920
+ : await notifyLeaderAsync(config, message, cwd)),
2921
+ });
2922
+ if (!isExistingMailboxNotificationOutcome(queuedOutcome)
2923
+ && transportPreference === 'hook_preferred_with_fallback'
2924
+ && !config.leader_pane_id) {
2925
+ if (queuedOutcome.request_id) {
2926
+ await markDispatchRequestLeaderPaneMissingDeferred({
2927
+ teamName,
2928
+ requestId: queuedOutcome.request_id,
2929
+ messageId: queuedOutcome.message_id,
2774
2930
  cwd,
2775
- fallbackNotify: async () => await notifyLeaderAsync(config, leaderTriggerMessage, cwd),
2776
2931
  });
2777
2932
  }
2778
- if (!finalOutcome.ok)
2779
- throw new Error(`mailbox_notify_failed:${finalOutcome.reason}`);
2780
- return finalOutcome;
2933
+ const deferredOutcome = {
2934
+ ...queuedOutcome,
2935
+ ok: true,
2936
+ transport: 'mailbox',
2937
+ reason: 'leader_pane_missing_mailbox_persisted',
2938
+ };
2939
+ await logRuntimeDispatchOutcome({
2940
+ cwd,
2941
+ teamName,
2942
+ workerName: 'leader-fixed',
2943
+ requestId: deferredOutcome.request_id,
2944
+ messageId: deferredOutcome.message_id,
2945
+ intent: triggerDirective.intent,
2946
+ outcome: deferredOutcome,
2947
+ });
2948
+ return deferredOutcome;
2781
2949
  }
2782
- const recipient = config.workers.find((w) => w.name === toWorker);
2950
+ const canLeaderFallbackDirectly = Boolean(config.leader_pane_id) && isTmuxAvailable();
2951
+ return await finalizeQueuedMailboxDispatch({
2952
+ queuedOutcome,
2953
+ transportPreference: canLeaderFallbackDirectly ? transportPreference : 'transport_direct',
2954
+ teamName,
2955
+ workerName: 'leader-fixed',
2956
+ paneId: config.leader_pane_id ?? undefined,
2957
+ messageId: queuedOutcome.message_id,
2958
+ triggerMessage: triggerDirective.text,
2959
+ intent: triggerDirective.intent,
2960
+ config,
2961
+ dispatchPolicy,
2962
+ cwd,
2963
+ fallbackNotify: async () => await notifyLeaderAsync(config, triggerDirective.text, cwd),
2964
+ });
2965
+ }
2966
+ async function sendRecipientMailboxMessage(params) {
2967
+ const { teamName, fromWorker, toWorker, body, config, dispatchPolicy, cwd } = params;
2968
+ const recipient = config.workers.find((worker) => worker.name === toWorker);
2783
2969
  if (!recipient)
2784
2970
  throw new Error(`Worker ${toWorker} not found in team`);
2785
- const triggerMessage = generateMailboxTriggerMessage(toWorker, sanitized, 1, resolveInstructionStateRoot(recipient.worktree_path));
2786
- const transportPreference = config.worker_launch_mode === 'prompt'
2787
- ? 'prompt_stdin'
2788
- : (dispatchPolicy.dispatch_mode === 'transport_direct' ? 'transport_direct' : 'hook_preferred_with_fallback');
2789
- const outcome = await queueDirectMailboxMessage({
2790
- teamName: sanitized,
2971
+ const triggerDirective = buildMailboxTriggerDirective(toWorker, teamName, 1, resolveInstructionStateRoot(recipient.worktree_path));
2972
+ const transportPreference = resolveWorkerMailboxTransportPreference(config, dispatchPolicy);
2973
+ const queuedOutcome = await queueDirectMailboxMessage({
2974
+ teamName,
2791
2975
  fromWorker,
2792
2976
  toWorker,
2793
2977
  toWorkerIndex: recipient.index,
2794
2978
  toPaneId: recipient.pane_id,
2795
2979
  body,
2796
- triggerMessage,
2980
+ triggerMessage: triggerDirective.text,
2981
+ intent: triggerDirective.intent,
2797
2982
  cwd,
2798
2983
  transportPreference,
2799
2984
  fallbackAllowed: transportPreference === 'hook_preferred_with_fallback',
@@ -2801,25 +2986,82 @@ export async function sendWorkerMessage(teamName, fromWorker, toWorker, body, cw
2801
2986
  ? { ok: true, transport: 'hook', reason: 'queued_for_hook_dispatch' }
2802
2987
  : await notifyWorkerOutcome(config, recipient.index, message, recipient.pane_id)),
2803
2988
  });
2804
- let finalOutcome = outcome;
2805
- const mailboxAlreadyNotified = outcome.ok && outcome.reason === 'existing_message_already_notified';
2806
- if (!mailboxAlreadyNotified && transportPreference === 'hook_preferred_with_fallback') {
2807
- if (!outcome.request_id || !outcome.message_id) {
2808
- throw new Error('mailbox_notify_failed:dispatch_request_missing_id');
2989
+ return await finalizeQueuedMailboxDispatch({
2990
+ queuedOutcome,
2991
+ transportPreference,
2992
+ teamName,
2993
+ workerName: recipient.name,
2994
+ workerIndex: recipient.index,
2995
+ paneId: recipient.pane_id,
2996
+ messageId: queuedOutcome.message_id,
2997
+ triggerMessage: triggerDirective.text,
2998
+ intent: triggerDirective.intent,
2999
+ config,
3000
+ dispatchPolicy,
3001
+ cwd,
3002
+ });
3003
+ }
3004
+ async function finalizeBroadcastMailboxOutcomes(params) {
3005
+ const { teamName, outcomes, transportPreference, config, dispatchPolicy, cwd } = params;
3006
+ if (transportPreference !== 'hook_preferred_with_fallback') {
3007
+ return outcomes;
3008
+ }
3009
+ const finalizedOutcomes = [];
3010
+ for (const outcome of outcomes) {
3011
+ const target = outcome.to_worker
3012
+ ? (config.workers.find((worker) => worker.name === outcome.to_worker) ?? null)
3013
+ : null;
3014
+ if (!target) {
3015
+ finalizedOutcomes.push({ ...outcome, ok: false, reason: 'missing_worker_index' });
3016
+ continue;
2809
3017
  }
2810
- finalOutcome = await finalizeHookPreferredMailboxDispatch({
2811
- teamName: sanitized,
2812
- requestId: outcome.request_id,
2813
- workerName: recipient.name,
2814
- workerIndex: recipient.index,
2815
- paneId: recipient.pane_id,
3018
+ const triggerDirective = buildMailboxTriggerDirective(target.name, teamName, 1, resolveInstructionStateRoot(target.worktree_path));
3019
+ finalizedOutcomes.push(await finalizeQueuedMailboxDispatch({
3020
+ queuedOutcome: outcome,
3021
+ transportPreference,
3022
+ teamName,
3023
+ workerName: target.name,
3024
+ workerIndex: target.index,
3025
+ paneId: target.pane_id,
2816
3026
  messageId: outcome.message_id,
2817
- triggerMessage,
3027
+ triggerMessage: triggerDirective.text,
3028
+ intent: triggerDirective.intent,
3029
+ config,
3030
+ dispatchPolicy,
3031
+ cwd,
3032
+ }));
3033
+ }
3034
+ return finalizedOutcomes;
3035
+ }
3036
+ export async function sendWorkerMessage(teamName, fromWorker, toWorker, body, cwd) {
3037
+ const sanitized = sanitizeTeamName(teamName);
3038
+ const config = await readTeamConfig(sanitized, cwd);
3039
+ if (!config)
3040
+ throw new Error(`Team ${sanitized} not found`);
3041
+ const manifest = await readTeamManifestV2(sanitized, cwd);
3042
+ const dispatchPolicy = resolveDispatchPolicy(manifest?.policy, config.worker_launch_mode);
3043
+ if (toWorker === 'leader-fixed') {
3044
+ const finalOutcome = await sendLeaderMailboxMessage({
3045
+ teamName: sanitized,
3046
+ fromWorker,
3047
+ body,
2818
3048
  config,
2819
3049
  dispatchPolicy,
2820
3050
  cwd,
2821
3051
  });
3052
+ if (!finalOutcome.ok)
3053
+ throw new Error(`mailbox_notify_failed:${finalOutcome.reason}`);
3054
+ return finalOutcome;
2822
3055
  }
3056
+ const finalOutcome = await sendRecipientMailboxMessage({
3057
+ teamName: sanitized,
3058
+ fromWorker,
3059
+ toWorker,
3060
+ body,
3061
+ config,
3062
+ dispatchPolicy,
3063
+ cwd,
3064
+ });
2823
3065
  if (!finalOutcome.ok)
2824
3066
  throw new Error(`mailbox_notify_failed:${finalOutcome.reason}`);
2825
3067
  return finalOutcome;
@@ -2831,16 +3073,15 @@ export async function broadcastWorkerMessage(teamName, fromWorker, body, cwd) {
2831
3073
  throw new Error(`Team ${sanitized} not found`);
2832
3074
  const manifest = await readTeamManifestV2(sanitized, cwd);
2833
3075
  const dispatchPolicy = resolveDispatchPolicy(manifest?.policy, config.worker_launch_mode);
2834
- const transportPreference = config.worker_launch_mode === 'prompt'
2835
- ? 'prompt_stdin'
2836
- : (dispatchPolicy.dispatch_mode === 'transport_direct' ? 'transport_direct' : 'hook_preferred_with_fallback');
3076
+ const transportPreference = resolveWorkerMailboxTransportPreference(config, dispatchPolicy);
2837
3077
  const outcomes = await queueBroadcastMailboxMessage({
2838
3078
  teamName: sanitized,
2839
3079
  fromWorker,
2840
3080
  recipients: config.workers.map((w) => ({ workerName: w.name, workerIndex: w.index, paneId: w.pane_id })),
2841
3081
  body,
2842
3082
  cwd,
2843
- triggerFor: (workerName) => generateMailboxTriggerMessage(workerName, sanitized, 1, resolveInstructionStateRoot(config.workers.find((worker) => worker.name === workerName)?.worktree_path)),
3083
+ triggerFor: (workerName) => buildMailboxTriggerDirective(workerName, sanitized, 1, resolveInstructionStateRoot(config.workers.find((worker) => worker.name === workerName)?.worktree_path)).text,
3084
+ intentFor: () => 'pending-mailbox-review',
2844
3085
  transportPreference,
2845
3086
  fallbackAllowed: transportPreference === 'hook_preferred_with_fallback',
2846
3087
  notify: async (target, message) => transportPreference === 'hook_preferred_with_fallback'
@@ -2849,37 +3090,14 @@ export async function broadcastWorkerMessage(teamName, fromWorker, body, cwd) {
2849
3090
  ? await notifyWorkerOutcome(config, target.workerIndex, message, target.paneId)
2850
3091
  : { ok: false, transport: 'none', reason: 'missing_worker_index' }),
2851
3092
  });
2852
- const finalizedOutcomes = [];
2853
- for (const outcome of outcomes) {
2854
- if (transportPreference !== 'hook_preferred_with_fallback') {
2855
- finalizedOutcomes.push(outcome);
2856
- continue;
2857
- }
2858
- if (!outcome.request_id || !outcome.message_id) {
2859
- finalizedOutcomes.push({ ...outcome, ok: false, reason: 'dispatch_request_missing_id' });
2860
- continue;
2861
- }
2862
- const target = outcome.to_worker
2863
- ? (config.workers.find((w) => w.name === outcome.to_worker) ?? null)
2864
- : null;
2865
- if (!target) {
2866
- finalizedOutcomes.push({ ...outcome, ok: false, reason: 'missing_worker_index' });
2867
- continue;
2868
- }
2869
- finalizedOutcomes.push(await finalizeHookPreferredMailboxDispatch({
2870
- teamName: sanitized,
2871
- requestId: outcome.request_id,
2872
- workerName: target.name,
2873
- workerIndex: target.index,
2874
- paneId: target.pane_id,
2875
- messageId: outcome.message_id,
2876
- triggerMessage: generateMailboxTriggerMessage(target.name, sanitized, 1, resolveInstructionStateRoot(target.worktree_path)),
2877
- config,
2878
- dispatchPolicy,
2879
- cwd,
2880
- }));
2881
- }
2882
- const results = transportPreference === 'hook_preferred_with_fallback' ? finalizedOutcomes : outcomes;
3093
+ const results = await finalizeBroadcastMailboxOutcomes({
3094
+ teamName: sanitized,
3095
+ outcomes,
3096
+ transportPreference,
3097
+ config,
3098
+ dispatchPolicy,
3099
+ cwd,
3100
+ });
2883
3101
  if (results.some((result) => !result.ok)) {
2884
3102
  const firstFailure = results.find((result) => !result.ok);
2885
3103
  throw new Error(`mailbox_notify_failed:${firstFailure?.reason ?? 'unknown'}`);