oh-my-codex 0.7.5 → 0.8.0

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 (374) hide show
  1. package/README.de.md +314 -0
  2. package/README.es.md +295 -17
  3. package/README.fr.md +314 -0
  4. package/README.it.md +314 -0
  5. package/README.ja.md +296 -18
  6. package/README.ko.md +295 -17
  7. package/README.md +68 -3
  8. package/README.pt.md +295 -17
  9. package/README.ru.md +295 -17
  10. package/README.tr.md +314 -0
  11. package/README.vi.md +296 -18
  12. package/README.zh.md +292 -17
  13. package/dist/catalog/__tests__/generator.test.js +2 -0
  14. package/dist/catalog/__tests__/generator.test.js.map +1 -1
  15. package/dist/catalog/__tests__/schema.test.js +7 -0
  16. package/dist/catalog/__tests__/schema.test.js.map +1 -1
  17. package/dist/cli/__tests__/ask.test.d.ts +2 -0
  18. package/dist/cli/__tests__/ask.test.d.ts.map +1 -0
  19. package/dist/cli/__tests__/ask.test.js +236 -0
  20. package/dist/cli/__tests__/ask.test.js.map +1 -0
  21. package/dist/cli/__tests__/doctor-warning-copy.test.d.ts +2 -0
  22. package/dist/cli/__tests__/doctor-warning-copy.test.d.ts.map +1 -0
  23. package/dist/cli/__tests__/doctor-warning-copy.test.js +45 -0
  24. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -0
  25. package/dist/cli/__tests__/index.test.js +43 -1
  26. package/dist/cli/__tests__/index.test.js.map +1 -1
  27. package/dist/cli/__tests__/ralph-prd-deep-interview.test.d.ts +2 -0
  28. package/dist/cli/__tests__/ralph-prd-deep-interview.test.d.ts.map +1 -0
  29. package/dist/cli/__tests__/ralph-prd-deep-interview.test.js +15 -0
  30. package/dist/cli/__tests__/ralph-prd-deep-interview.test.js.map +1 -0
  31. package/dist/cli/__tests__/ralph.test.d.ts +2 -0
  32. package/dist/cli/__tests__/ralph.test.d.ts.map +1 -0
  33. package/dist/cli/__tests__/ralph.test.js +40 -0
  34. package/dist/cli/__tests__/ralph.test.js.map +1 -0
  35. package/dist/cli/__tests__/setup-scope.test.js +2 -0
  36. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  37. package/dist/cli/__tests__/team-decompose.test.d.ts +2 -0
  38. package/dist/cli/__tests__/team-decompose.test.d.ts.map +1 -0
  39. package/dist/cli/__tests__/team-decompose.test.js +67 -0
  40. package/dist/cli/__tests__/team-decompose.test.js.map +1 -0
  41. package/dist/cli/__tests__/version.test.d.ts +2 -0
  42. package/dist/cli/__tests__/version.test.d.ts.map +1 -0
  43. package/dist/cli/__tests__/version.test.js +21 -0
  44. package/dist/cli/__tests__/version.test.js.map +1 -0
  45. package/dist/cli/ask.d.ts +13 -0
  46. package/dist/cli/ask.d.ts.map +1 -0
  47. package/dist/cli/ask.js +174 -0
  48. package/dist/cli/ask.js.map +1 -0
  49. package/dist/cli/constants.d.ts +10 -0
  50. package/dist/cli/constants.d.ts.map +1 -0
  51. package/dist/cli/constants.js +10 -0
  52. package/dist/cli/constants.js.map +1 -0
  53. package/dist/cli/doctor.js +16 -5
  54. package/dist/cli/doctor.js.map +1 -1
  55. package/dist/cli/index.d.ts +7 -1
  56. package/dist/cli/index.d.ts.map +1 -1
  57. package/dist/cli/index.js +117 -43
  58. package/dist/cli/index.js.map +1 -1
  59. package/dist/cli/ralph.d.ts +4 -0
  60. package/dist/cli/ralph.d.ts.map +1 -1
  61. package/dist/cli/ralph.js +89 -13
  62. package/dist/cli/ralph.js.map +1 -1
  63. package/dist/cli/setup.js +1 -1
  64. package/dist/cli/setup.js.map +1 -1
  65. package/dist/cli/team.d.ts +18 -0
  66. package/dist/cli/team.d.ts.map +1 -1
  67. package/dist/cli/team.js +108 -16
  68. package/dist/cli/team.js.map +1 -1
  69. package/dist/config/generator.d.ts.map +1 -1
  70. package/dist/config/generator.js +8 -0
  71. package/dist/config/generator.js.map +1 -1
  72. package/dist/hooks/__tests__/deep-interview-contract.test.d.ts +2 -0
  73. package/dist/hooks/__tests__/deep-interview-contract.test.d.ts.map +1 -0
  74. package/dist/hooks/__tests__/deep-interview-contract.test.js +55 -0
  75. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -0
  76. package/dist/hooks/__tests__/emulator.test.js +6 -0
  77. package/dist/hooks/__tests__/emulator.test.js.map +1 -1
  78. package/dist/hooks/__tests__/keyword-detector.test.js +44 -22
  79. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  80. package/dist/hooks/__tests__/notify-hook-session-scope.test.js +59 -0
  81. package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
  82. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +88 -0
  83. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  84. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +199 -0
  85. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  86. package/dist/hooks/__tests__/notify-hook-visual-verdict.test.d.ts +11 -0
  87. package/dist/hooks/__tests__/notify-hook-visual-verdict.test.d.ts.map +1 -0
  88. package/dist/hooks/__tests__/notify-hook-visual-verdict.test.js +266 -0
  89. package/dist/hooks/__tests__/notify-hook-visual-verdict.test.js.map +1 -0
  90. package/dist/hooks/__tests__/openclaw-setup-contract.test.d.ts +2 -0
  91. package/dist/hooks/__tests__/openclaw-setup-contract.test.d.ts.map +1 -0
  92. package/dist/hooks/__tests__/openclaw-setup-contract.test.js +51 -0
  93. package/dist/hooks/__tests__/openclaw-setup-contract.test.js.map +1 -0
  94. package/dist/hooks/__tests__/pre-context-gate-skills.test.d.ts +2 -0
  95. package/dist/hooks/__tests__/pre-context-gate-skills.test.d.ts.map +1 -0
  96. package/dist/hooks/__tests__/pre-context-gate-skills.test.js +34 -0
  97. package/dist/hooks/__tests__/pre-context-gate-skills.test.js.map +1 -0
  98. package/dist/hooks/__tests__/tmux-hook-engine.test.js +36 -1
  99. package/dist/hooks/__tests__/tmux-hook-engine.test.js.map +1 -1
  100. package/dist/hooks/__tests__/visual-verdict-loop.test.d.ts +2 -0
  101. package/dist/hooks/__tests__/visual-verdict-loop.test.d.ts.map +1 -0
  102. package/dist/hooks/__tests__/visual-verdict-loop.test.js +35 -0
  103. package/dist/hooks/__tests__/visual-verdict-loop.test.js.map +1 -0
  104. package/dist/hooks/agents-overlay.d.ts.map +1 -1
  105. package/dist/hooks/agents-overlay.js +18 -16
  106. package/dist/hooks/agents-overlay.js.map +1 -1
  107. package/dist/hooks/codebase-map.d.ts.map +1 -1
  108. package/dist/hooks/codebase-map.js +6 -2
  109. package/dist/hooks/codebase-map.js.map +1 -1
  110. package/dist/hooks/emulator.d.ts.map +1 -1
  111. package/dist/hooks/emulator.js +2 -0
  112. package/dist/hooks/emulator.js.map +1 -1
  113. package/dist/hooks/extensibility/sdk.d.ts.map +1 -1
  114. package/dist/hooks/extensibility/sdk.js +2 -1
  115. package/dist/hooks/extensibility/sdk.js.map +1 -1
  116. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  117. package/dist/hooks/keyword-registry.js +6 -0
  118. package/dist/hooks/keyword-registry.js.map +1 -1
  119. package/dist/hud/index.d.ts.map +1 -1
  120. package/dist/hud/index.js +2 -24
  121. package/dist/hud/index.js.map +1 -1
  122. package/dist/mcp/__tests__/team-server-cleanup.test.d.ts +2 -0
  123. package/dist/mcp/__tests__/team-server-cleanup.test.d.ts.map +1 -0
  124. package/dist/mcp/__tests__/team-server-cleanup.test.js +219 -0
  125. package/dist/mcp/__tests__/team-server-cleanup.test.js.map +1 -0
  126. package/dist/mcp/bootstrap.d.ts +1 -1
  127. package/dist/mcp/bootstrap.d.ts.map +1 -1
  128. package/dist/mcp/bootstrap.js +1 -0
  129. package/dist/mcp/bootstrap.js.map +1 -1
  130. package/dist/mcp/code-intel-server.d.ts.map +1 -1
  131. package/dist/mcp/code-intel-server.js +18 -8
  132. package/dist/mcp/code-intel-server.js.map +1 -1
  133. package/dist/mcp/memory-server.js +72 -11
  134. package/dist/mcp/memory-server.js.map +1 -1
  135. package/dist/mcp/state-paths.d.ts.map +1 -1
  136. package/dist/mcp/state-paths.js +4 -1
  137. package/dist/mcp/state-paths.js.map +1 -1
  138. package/dist/mcp/state-server.d.ts.map +1 -1
  139. package/dist/mcp/state-server.js +18 -5
  140. package/dist/mcp/state-server.js.map +1 -1
  141. package/dist/mcp/team-server.d.ts +24 -0
  142. package/dist/mcp/team-server.d.ts.map +1 -0
  143. package/dist/mcp/team-server.js +425 -0
  144. package/dist/mcp/team-server.js.map +1 -0
  145. package/dist/mcp/trace-server.d.ts.map +1 -1
  146. package/dist/mcp/trace-server.js +8 -3
  147. package/dist/mcp/trace-server.js.map +1 -1
  148. package/dist/notifications/__tests__/verbosity.test.js +35 -0
  149. package/dist/notifications/__tests__/verbosity.test.js.map +1 -1
  150. package/dist/notifications/config.d.ts.map +1 -1
  151. package/dist/notifications/config.js +12 -3
  152. package/dist/notifications/config.js.map +1 -1
  153. package/dist/notifications/dispatcher.d.ts.map +1 -1
  154. package/dist/notifications/dispatcher.js +4 -4
  155. package/dist/notifications/dispatcher.js.map +1 -1
  156. package/dist/notifications/reply-listener.d.ts.map +1 -1
  157. package/dist/notifications/reply-listener.js +6 -2
  158. package/dist/notifications/reply-listener.js.map +1 -1
  159. package/dist/notifications/session-registry.d.ts.map +1 -1
  160. package/dist/notifications/session-registry.js +2 -2
  161. package/dist/notifications/session-registry.js.map +1 -1
  162. package/dist/notifications/tmux.d.ts.map +1 -1
  163. package/dist/notifications/tmux.js +13 -4
  164. package/dist/notifications/tmux.js.map +1 -1
  165. package/dist/notifications/types.d.ts +4 -0
  166. package/dist/notifications/types.d.ts.map +1 -1
  167. package/dist/openclaw/__tests__/index.test.js +40 -0
  168. package/dist/openclaw/__tests__/index.test.js.map +1 -1
  169. package/dist/openclaw/dispatcher.d.ts.map +1 -1
  170. package/dist/openclaw/dispatcher.js +5 -2
  171. package/dist/openclaw/dispatcher.js.map +1 -1
  172. package/dist/openclaw/index.d.ts.map +1 -1
  173. package/dist/openclaw/index.js +1 -0
  174. package/dist/openclaw/index.js.map +1 -1
  175. package/dist/openclaw/types.d.ts +2 -0
  176. package/dist/openclaw/types.d.ts.map +1 -1
  177. package/dist/ralph/__tests__/persistence.test.js +28 -1
  178. package/dist/ralph/__tests__/persistence.test.js.map +1 -1
  179. package/dist/ralph/persistence.d.ts +21 -0
  180. package/dist/ralph/persistence.d.ts.map +1 -1
  181. package/dist/ralph/persistence.js +85 -2
  182. package/dist/ralph/persistence.js.map +1 -1
  183. package/dist/state/paths.d.ts +3 -0
  184. package/dist/state/paths.d.ts.map +1 -0
  185. package/dist/state/paths.js +2 -0
  186. package/dist/state/paths.js.map +1 -0
  187. package/dist/team/__tests__/idle-nudge.test.d.ts +2 -0
  188. package/dist/team/__tests__/idle-nudge.test.d.ts.map +1 -0
  189. package/dist/team/__tests__/idle-nudge.test.js +225 -0
  190. package/dist/team/__tests__/idle-nudge.test.js.map +1 -0
  191. package/dist/team/__tests__/role-router.test.d.ts +2 -0
  192. package/dist/team/__tests__/role-router.test.d.ts.map +1 -0
  193. package/dist/team/__tests__/role-router.test.js +204 -0
  194. package/dist/team/__tests__/role-router.test.js.map +1 -0
  195. package/dist/team/__tests__/runtime-cli.test.d.ts +2 -0
  196. package/dist/team/__tests__/runtime-cli.test.d.ts.map +1 -0
  197. package/dist/team/__tests__/runtime-cli.test.js +72 -0
  198. package/dist/team/__tests__/runtime-cli.test.js.map +1 -0
  199. package/dist/team/__tests__/runtime.test.js +195 -9
  200. package/dist/team/__tests__/runtime.test.js.map +1 -1
  201. package/dist/team/__tests__/scaling.test.js +132 -2
  202. package/dist/team/__tests__/scaling.test.js.map +1 -1
  203. package/dist/team/__tests__/state-root.test.d.ts +2 -0
  204. package/dist/team/__tests__/state-root.test.d.ts.map +1 -0
  205. package/dist/team/__tests__/state-root.test.js +9 -0
  206. package/dist/team/__tests__/state-root.test.js.map +1 -0
  207. package/dist/team/__tests__/state.test.js +52 -17
  208. package/dist/team/__tests__/state.test.js.map +1 -1
  209. package/dist/team/__tests__/team-ops-contract.test.d.ts +2 -0
  210. package/dist/team/__tests__/team-ops-contract.test.d.ts.map +1 -0
  211. package/dist/team/__tests__/team-ops-contract.test.js +90 -0
  212. package/dist/team/__tests__/team-ops-contract.test.js.map +1 -0
  213. package/dist/team/__tests__/tmux-session.test.js +94 -7
  214. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  215. package/dist/team/__tests__/worker-bootstrap.test.js +59 -0
  216. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  217. package/dist/team/__tests__/worktree.test.js +81 -2
  218. package/dist/team/__tests__/worktree.test.js.map +1 -1
  219. package/dist/team/idle-nudge.d.ts +53 -0
  220. package/dist/team/idle-nudge.d.ts.map +1 -0
  221. package/dist/team/idle-nudge.js +140 -0
  222. package/dist/team/idle-nudge.js.map +1 -0
  223. package/dist/team/mcp-comm.d.ts +1 -1
  224. package/dist/team/mcp-comm.d.ts.map +1 -1
  225. package/dist/team/mcp-comm.js +6 -2
  226. package/dist/team/mcp-comm.js.map +1 -1
  227. package/dist/team/orchestrator.d.ts +1 -10
  228. package/dist/team/orchestrator.d.ts.map +1 -1
  229. package/dist/team/orchestrator.js +8 -0
  230. package/dist/team/orchestrator.js.map +1 -1
  231. package/dist/team/role-router.d.ts +32 -0
  232. package/dist/team/role-router.d.ts.map +1 -0
  233. package/dist/team/role-router.js +137 -0
  234. package/dist/team/role-router.js.map +1 -0
  235. package/dist/team/runtime-cli.d.ts +18 -0
  236. package/dist/team/runtime-cli.d.ts.map +1 -0
  237. package/dist/team/runtime-cli.js +244 -0
  238. package/dist/team/runtime-cli.js.map +1 -0
  239. package/dist/team/runtime.d.ts +6 -1
  240. package/dist/team/runtime.d.ts.map +1 -1
  241. package/dist/team/runtime.js +148 -60
  242. package/dist/team/runtime.js.map +1 -1
  243. package/dist/team/scaling.d.ts +1 -0
  244. package/dist/team/scaling.d.ts.map +1 -1
  245. package/dist/team/scaling.js +74 -32
  246. package/dist/team/scaling.js.map +1 -1
  247. package/dist/team/state/approvals.d.ts +25 -0
  248. package/dist/team/state/approvals.d.ts.map +1 -0
  249. package/dist/team/state/approvals.js +31 -0
  250. package/dist/team/state/approvals.js.map +1 -0
  251. package/dist/team/state/config.d.ts +2 -0
  252. package/dist/team/state/config.d.ts.map +1 -0
  253. package/dist/team/state/config.js +2 -0
  254. package/dist/team/state/config.js.map +1 -0
  255. package/dist/team/state/dispatch-lock.d.ts +3 -0
  256. package/dist/team/state/dispatch-lock.d.ts.map +1 -0
  257. package/dist/team/state/dispatch-lock.js +81 -0
  258. package/dist/team/state/dispatch-lock.js.map +1 -0
  259. package/dist/team/state/dispatch.d.ts +61 -0
  260. package/dist/team/state/dispatch.d.ts.map +1 -0
  261. package/dist/team/state/dispatch.js +158 -0
  262. package/dist/team/state/dispatch.js.map +1 -0
  263. package/dist/team/state/events.d.ts +2 -0
  264. package/dist/team/state/events.d.ts.map +1 -0
  265. package/dist/team/state/events.js +2 -0
  266. package/dist/team/state/events.js.map +1 -0
  267. package/dist/team/state/index.d.ts +11 -0
  268. package/dist/team/state/index.d.ts.map +1 -0
  269. package/dist/team/state/index.js +11 -0
  270. package/dist/team/state/index.js.map +1 -0
  271. package/dist/team/state/io.d.ts +2 -0
  272. package/dist/team/state/io.d.ts.map +1 -0
  273. package/dist/team/state/io.js +2 -0
  274. package/dist/team/state/io.js.map +1 -0
  275. package/dist/team/state/locks.d.ts +16 -0
  276. package/dist/team/state/locks.d.ts.map +1 -0
  277. package/dist/team/state/locks.js +201 -0
  278. package/dist/team/state/locks.js.map +1 -0
  279. package/dist/team/state/mailbox.d.ts +39 -0
  280. package/dist/team/state/mailbox.d.ts.map +1 -0
  281. package/dist/team/state/mailbox.js +58 -0
  282. package/dist/team/state/mailbox.js.map +1 -0
  283. package/dist/team/state/monitor.d.ts +96 -0
  284. package/dist/team/state/monitor.d.ts.map +1 -0
  285. package/dist/team/state/monitor.js +163 -0
  286. package/dist/team/state/monitor.js.map +1 -0
  287. package/dist/team/state/shutdown.d.ts +2 -0
  288. package/dist/team/state/shutdown.d.ts.map +1 -0
  289. package/dist/team/state/shutdown.js +2 -0
  290. package/dist/team/state/shutdown.js.map +1 -0
  291. package/dist/team/state/summary.d.ts +2 -0
  292. package/dist/team/state/summary.d.ts.map +1 -0
  293. package/dist/team/state/summary.js +2 -0
  294. package/dist/team/state/summary.js.map +1 -0
  295. package/dist/team/state/tasks.d.ts +49 -0
  296. package/dist/team/state/tasks.d.ts.map +1 -0
  297. package/dist/team/state/tasks.js +182 -0
  298. package/dist/team/state/tasks.js.map +1 -0
  299. package/dist/team/state/types.d.ts +281 -0
  300. package/dist/team/state/types.d.ts.map +1 -0
  301. package/dist/team/state/types.js +3 -0
  302. package/dist/team/state/types.js.map +1 -0
  303. package/dist/team/state/workers.d.ts +2 -0
  304. package/dist/team/state/workers.d.ts.map +1 -0
  305. package/dist/team/state/workers.js +2 -0
  306. package/dist/team/state/workers.js.map +1 -0
  307. package/dist/team/state-root.d.ts +5 -0
  308. package/dist/team/state-root.d.ts.map +1 -0
  309. package/dist/team/state-root.js +8 -0
  310. package/dist/team/state-root.js.map +1 -0
  311. package/dist/team/state.d.ts +6 -2
  312. package/dist/team/state.d.ts.map +1 -1
  313. package/dist/team/state.js +200 -881
  314. package/dist/team/state.js.map +1 -1
  315. package/dist/team/tmux-session.d.ts +42 -2
  316. package/dist/team/tmux-session.d.ts.map +1 -1
  317. package/dist/team/tmux-session.js +229 -74
  318. package/dist/team/tmux-session.js.map +1 -1
  319. package/dist/team/worker-bootstrap.d.ts +2 -0
  320. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  321. package/dist/team/worker-bootstrap.js +47 -20
  322. package/dist/team/worker-bootstrap.js.map +1 -1
  323. package/dist/team/worktree.d.ts +5 -1
  324. package/dist/team/worktree.d.ts.map +1 -1
  325. package/dist/team/worktree.js +71 -17
  326. package/dist/team/worktree.js.map +1 -1
  327. package/dist/utils/safe-json.d.ts +3 -0
  328. package/dist/utils/safe-json.d.ts.map +1 -0
  329. package/dist/utils/safe-json.js +19 -0
  330. package/dist/utils/safe-json.js.map +1 -0
  331. package/dist/utils/sleep.d.ts +3 -0
  332. package/dist/utils/sleep.d.ts.map +1 -0
  333. package/dist/utils/sleep.js +15 -0
  334. package/dist/utils/sleep.js.map +1 -0
  335. package/dist/visual/__tests__/verdict.test.d.ts +2 -0
  336. package/dist/visual/__tests__/verdict.test.d.ts.map +1 -0
  337. package/dist/visual/__tests__/verdict.test.js +81 -0
  338. package/dist/visual/__tests__/verdict.test.js.map +1 -0
  339. package/dist/visual/constants.d.ts +4 -0
  340. package/dist/visual/constants.d.ts.map +1 -0
  341. package/dist/visual/constants.js +3 -0
  342. package/dist/visual/constants.js.map +1 -0
  343. package/dist/visual/verdict.d.ts +17 -0
  344. package/dist/visual/verdict.d.ts.map +1 -0
  345. package/dist/visual/verdict.js +61 -0
  346. package/dist/visual/verdict.js.map +1 -0
  347. package/package.json +10 -3
  348. package/scripts/ask-claude.sh +17 -0
  349. package/scripts/ask-gemini.sh +14 -0
  350. package/scripts/fixtures/ask-advisor-stub.js +12 -0
  351. package/scripts/notify-hook/log.js +5 -0
  352. package/scripts/notify-hook/team-dispatch.js +56 -1
  353. package/scripts/notify-hook/tmux-injection.js +45 -4
  354. package/scripts/notify-hook/visual-verdict.js +158 -0
  355. package/scripts/notify-hook.js +27 -0
  356. package/scripts/run-provider-advisor.js +179 -0
  357. package/scripts/tmux-hook-engine.js +24 -0
  358. package/skills/ask-claude/SKILL.md +61 -0
  359. package/skills/ask-gemini/SKILL.md +61 -0
  360. package/skills/autopilot/SKILL.md +34 -4
  361. package/skills/configure-notifications/SKILL.md +1 -1
  362. package/skills/configure-openclaw/SKILL.md +154 -157
  363. package/skills/deep-interview/SKILL.md +247 -0
  364. package/skills/doctor/SKILL.md +1 -1
  365. package/skills/help/SKILL.md +3 -3
  366. package/skills/ralph/SKILL.md +42 -11
  367. package/skills/ralplan/SKILL.md +17 -0
  368. package/skills/skill/SKILL.md +32 -32
  369. package/skills/team/SKILL.md +60 -0
  370. package/skills/visual-verdict/SKILL.md +76 -0
  371. package/skills/web-clone/SKILL.md +366 -0
  372. package/skills/worker/SKILL.md +5 -4
  373. package/templates/AGENTS.md +9 -0
  374. package/templates/catalog-manifest.json +39 -2
@@ -3,11 +3,14 @@ import { existsSync } from 'fs';
3
3
  import { readdir, readFile } from 'fs/promises';
4
4
  import { performance } from 'perf_hooks';
5
5
  import { spawn } from 'child_process';
6
- import { sanitizeTeamName, isTmuxAvailable, createTeamSession, buildWorkerProcessLaunchSpec, resolveTeamWorkerCli, resolveTeamWorkerCliPlan, resolveTeamWorkerLaunchMode, waitForWorkerReady, dismissTrustPromptIfPresent, sleepFractionalSeconds, sendToWorker, sendToWorkerStdin, notifyLeaderStatus, isWorkerAlive, getWorkerPanePid, killWorker, killWorkerByPaneId, unregisterResizeHook, destroyTeamSession, listTeamSessions, } from './tmux-session.js';
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, teamNormalizePolicy as normalizeTeamPolicy, teamClaimTask as claimTask, teamReleaseTaskClaim as releaseTaskClaim, teamAppendEvent as appendTeamEvent, teamReadTaskApproval as readTaskApproval, teamListMailbox as listMailboxMessages, 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';
6
+ import { sanitizeTeamName, isTmuxAvailable, createTeamSession, buildWorkerProcessLaunchSpec, resolveTeamWorkerCli, resolveTeamWorkerCliPlan, resolveTeamWorkerLaunchMode, waitForWorkerReady, dismissTrustPromptIfPresent, sleepFractionalSeconds, sendToWorker, sendToLeaderPane, sendToWorkerStdin, isWorkerAlive, getWorkerPanePid, killWorkerByPaneIdAsync, teardownWorkerPanes, unregisterResizeHook, destroyTeamSession, listTeamSessions, } from './tmux-session.js';
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, teamNormalizePolicy as normalizeTeamPolicy, teamClaimTask as claimTask, teamReleaseTaskClaim as releaseTaskClaim, 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
9
  import { generateWorkerOverlay, writeTeamWorkerInstructionsFile, removeTeamWorkerInstructionsFile, generateInitialInbox, generateTaskAssignmentInbox, generateShutdownInbox, generateTriggerMessage, generateMailboxTriggerMessage, } from './worker-bootstrap.js';
10
+ import { loadRolePrompt } from './role-router.js';
11
+ import { codexPromptsDir } from '../utils/paths.js';
10
12
  import { isLowComplexityAgentType, resolveTeamWorkerLaunchArgs, TEAM_LOW_COMPLEXITY_DEFAULT_MODEL, resolveTeamLowComplexityDefaultModel, parseTeamWorkerLaunchArgs, splitWorkerLaunchArgs, } from './model-contract.js';
13
+ import { resolveCanonicalTeamStateRoot } from './state-root.js';
11
14
  import { inferPhaseTargetFromTaskCounts, reconcilePhaseStateForMonitor } from './phase-controller.js';
12
15
  import { getTeamTmuxSessions } from '../notifications/tmux.js';
13
16
  import { hasStructuredVerificationEvidence } from '../verification/verifier.js';
@@ -83,7 +86,8 @@ function isPidAlive(pid) {
83
86
  process.kill(pid, 0);
84
87
  return true;
85
88
  }
86
- catch {
89
+ catch (err) {
90
+ process.stderr.write(`[team/runtime] operation failed: ${err}\n`);
87
91
  return false;
88
92
  }
89
93
  }
@@ -100,8 +104,9 @@ async function waitForPidExit(pid, timeoutMs) {
100
104
  }
101
105
  async function teardownPromptWorker(teamName, workerName, fallbackPid, cwd, context) {
102
106
  const handle = getPromptWorkerHandle(teamName, workerName);
103
- const pid = Number.isFinite(handle?.pid)
104
- ? handle.pid
107
+ const handlePid = handle?.pid;
108
+ const pid = (typeof handlePid === 'number' && Number.isFinite(handlePid))
109
+ ? handlePid
105
110
  : (Number.isFinite(fallbackPid) && (fallbackPid ?? 0) > 0 ? fallbackPid : null);
106
111
  if (pid === null) {
107
112
  removePromptWorkerHandle(teamName, workerName);
@@ -115,7 +120,8 @@ async function teardownPromptWorker(teamName, workerName, fallbackPid, cwd, cont
115
120
  process.kill(pid, 'SIGTERM');
116
121
  }
117
122
  }
118
- catch {
123
+ catch (err) {
124
+ process.stderr.write(`[team/runtime] operation failed: ${err}\n`);
119
125
  // Best effort.
120
126
  }
121
127
  const exitedOnTerm = await waitForPidExit(pid, PROMPT_WORKER_SIGTERM_WAIT_MS);
@@ -136,7 +142,8 @@ async function teardownPromptWorker(teamName, workerName, fallbackPid, cwd, cont
136
142
  process.kill(pid, 'SIGKILL');
137
143
  }
138
144
  }
139
- catch {
145
+ catch (err) {
146
+ process.stderr.write(`[team/runtime] operation failed: ${err}\n`);
140
147
  // Best effort.
141
148
  }
142
149
  const exitedOnKill = await waitForPidExit(pid, PROMPT_WORKER_SIGKILL_WAIT_MS);
@@ -166,14 +173,13 @@ function isPromptWorkerAlive(config, worker) {
166
173
  process.kill(worker.pid, 0);
167
174
  return true;
168
175
  }
169
- catch {
176
+ catch (err) {
177
+ process.stderr.write(`[team/runtime] operation failed: ${err}\n`);
170
178
  return false;
171
179
  }
172
180
  }
173
181
  export { TEAM_LOW_COMPLEXITY_DEFAULT_MODEL };
174
- export function resolveCanonicalTeamStateRoot(leaderCwd) {
175
- return resolve(join(leaderCwd, '.omx', 'state'));
176
- }
182
+ export { resolveCanonicalTeamStateRoot };
177
183
  function spawnPromptWorker(teamName, workerName, workerIndex, workerCwd, launchArgs, workerEnv, workerCli) {
178
184
  const processSpec = buildWorkerProcessLaunchSpec(teamName, workerIndex, launchArgs, workerCwd, workerEnv, workerCli);
179
185
  const child = spawn(processSpec.command, processSpec.args, {
@@ -401,11 +407,24 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
401
407
  const workerWorkspace = workerWorkspaceByName.get(workerName) ?? { cwd: leaderCwd };
402
408
  // Get tasks assigned to this worker
403
409
  const workerTasks = allTasks.filter(t => t.owner === workerName);
410
+ // Resolve per-worker role from assigned task roles
411
+ const taskRoles = workerTasks.map(t => t.role).filter(Boolean);
412
+ const uniqueTaskRoles = new Set(taskRoles);
413
+ const workerRole = taskRoles.length > 0 && uniqueTaskRoles.size === 1
414
+ ? taskRoles[0]
415
+ : agentType;
416
+ if (uniqueTaskRoles.size > 1) {
417
+ console.log(`[omx:team] ${workerName}: mixed task roles [${[...uniqueTaskRoles].join(', ')}], falling back to ${agentType}`);
418
+ }
419
+ // Load role-specific prompt content if role differs from default
420
+ const rolePromptContent = workerRole !== agentType
421
+ ? await loadRolePrompt(workerRole, codexPromptsDir())
422
+ : null;
404
423
  // Write worker identity
405
424
  const identity = {
406
425
  name: workerName,
407
426
  index: i,
408
- role: agentType,
427
+ role: workerRole,
409
428
  worker_cli: workerCliPlan[i - 1],
410
429
  assigned_tasks: workerTasks.map(t => t.id),
411
430
  working_dir: workerWorkspace.cwd,
@@ -448,6 +467,8 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
448
467
  const inbox = generateInitialInbox(workerName, sanitized, agentType, workerTasks, {
449
468
  teamStateRoot,
450
469
  leaderCwd,
470
+ workerRole,
471
+ rolePromptContent: rolePromptContent ?? undefined,
451
472
  });
452
473
  const trigger = generateTriggerMessage(workerName, sanitized);
453
474
  const maxStartupDispatchRetries = 3;
@@ -524,15 +545,19 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
524
545
  if (sessionName.includes(':')) {
525
546
  for (const paneId of createdWorkerPaneIds) {
526
547
  try {
527
- killWorkerByPaneId(paneId, createdLeaderPaneId);
548
+ await killWorkerByPaneIdAsync(paneId, createdLeaderPaneId);
549
+ }
550
+ catch (err) {
551
+ process.stderr.write(`[team/runtime] operation failed: ${err}\n`);
528
552
  }
529
- catch { /* ignore */ }
530
553
  }
531
554
  if (config?.hud_pane_id) {
532
555
  try {
533
- killWorkerByPaneId(config.hud_pane_id, createdLeaderPaneId);
556
+ await killWorkerByPaneIdAsync(config.hud_pane_id, createdLeaderPaneId);
557
+ }
558
+ catch (err) {
559
+ process.stderr.write(`[team/runtime] operation failed: ${err}\n`);
534
560
  }
535
- catch { /* ignore */ }
536
561
  }
537
562
  }
538
563
  else {
@@ -573,7 +598,9 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
573
598
  }
574
599
  if (provisionedWorktrees.length > 0) {
575
600
  try {
576
- await rollbackProvisionedWorktrees(provisionedWorktrees);
601
+ await rollbackProvisionedWorktrees(provisionedWorktrees, {
602
+ skipBranchDeletion: options.ralph === true,
603
+ });
577
604
  }
578
605
  catch (cleanupError) {
579
606
  rollbackErrors.push(`rollbackProvisionedWorktrees: ${String(cleanupError)}`);
@@ -691,6 +718,22 @@ export async function monitorTeam(teamName, cwd) {
691
718
  const mailboxDeliveryStartMs = performance.now();
692
719
  const mailboxNotifiedByMessageId = await deliverPendingMailboxMessages(sanitized, config, workers, previousSnapshot?.mailboxNotifiedByMessageId ?? {}, dispatchPolicy, cwd);
693
720
  const mailboxDeliveryMs = performance.now() - mailboxDeliveryStartMs;
721
+ // Prune ephemeral status messages from leader mailbox (TTL: 60s)
722
+ try {
723
+ const leaderMailbox = await listMailboxMessages(sanitized, 'leader-fixed', cwd);
724
+ const now = Date.now();
725
+ for (const msg of leaderMailbox) {
726
+ if (msg.from_worker === 'system' && msg.created_at) {
727
+ const age = now - new Date(msg.created_at).getTime();
728
+ if (age > 60_000) {
729
+ await markMessageDelivered(sanitized, 'leader-fixed', msg.message_id, cwd);
730
+ }
731
+ }
732
+ }
733
+ }
734
+ catch (err) {
735
+ process.stderr.write(`[team/runtime] operation failed: ${err}\n`);
736
+ }
694
737
  const updatedAt = new Date().toISOString();
695
738
  const totalMs = performance.now() - monitorStartMs;
696
739
  await writeMonitorSnapshot(sanitized, {
@@ -788,7 +831,7 @@ export async function assignTask(teamName, workerName, taskId, cwd) {
788
831
  waitForWorkerReady(config.tmux_session, workerInfo.index, 15_000, workerInfo.pane_id);
789
832
  }
790
833
  else {
791
- sleepFractionalSeconds(assignRetryDelayS);
834
+ await new Promise(r => setTimeout(r, assignRetryDelayS * 1000));
792
835
  }
793
836
  }
794
837
  }
@@ -805,7 +848,8 @@ export async function assignTask(teamName, workerName, taskId, cwd) {
805
848
  try {
806
849
  await writeWorkerInbox(sanitized, workerName, `# Assignment Cancelled\n\nTask ${taskId} was not dispatched due to ${reason}.\nDo not execute this task from prior inbox content.`, cwd);
807
850
  }
808
- catch {
851
+ catch (err) {
852
+ process.stderr.write(`[team/runtime] operation failed: ${err}\n`);
809
853
  // best effort
810
854
  }
811
855
  if (!released.ok) {
@@ -827,6 +871,7 @@ export async function reassignTask(teamName, taskId, _fromWorker, toWorker, cwd)
827
871
  */
828
872
  export async function shutdownTeam(teamName, cwd, options = {}) {
829
873
  const force = options.force === true;
874
+ const ralph = options.ralph === true;
830
875
  const sanitized = sanitizeTeamName(teamName);
831
876
  const config = await readTeamConfig(sanitized, cwd);
832
877
  if (!config) {
@@ -834,7 +879,9 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
834
879
  try {
835
880
  destroyTeamSession(`omx-team-${sanitized}`);
836
881
  }
837
- catch { /* ignore */ }
882
+ catch (err) {
883
+ process.stderr.write(`[team/runtime] operation failed: ${err}\n`);
884
+ }
838
885
  await cleanupTeamState(sanitized, cwd);
839
886
  restoreTeamModelInstructionsFile(sanitized);
840
887
  return;
@@ -854,10 +901,22 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
854
901
  await appendTeamEvent(sanitized, {
855
902
  type: 'shutdown_gate',
856
903
  worker: 'leader-fixed',
857
- reason: `allowed=${gate.allowed} total=${gate.total} pending=${gate.pending} blocked=${gate.blocked} in_progress=${gate.in_progress} completed=${gate.completed} failed=${gate.failed}`,
904
+ reason: `allowed=${gate.allowed} total=${gate.total} pending=${gate.pending} blocked=${gate.blocked} in_progress=${gate.in_progress} completed=${gate.completed} failed=${gate.failed}${ralph ? ' policy=ralph' : ''}`,
858
905
  }, cwd).catch(() => { });
859
906
  if (!gate.allowed) {
860
- throw new Error(`shutdown_gate_blocked:pending=${gate.pending},blocked=${gate.blocked},in_progress=${gate.in_progress},failed=${gate.failed}`);
907
+ const hasActiveWork = gate.pending > 0 || gate.blocked > 0 || gate.in_progress > 0;
908
+ if (ralph && !hasActiveWork) {
909
+ // Ralph policy: bypass on failure-only scenarios (no pending/blocked/in_progress tasks).
910
+ // This allows the ralph loop to retry rather than leaving stale team state.
911
+ await appendTeamEvent(sanitized, {
912
+ type: 'ralph_cleanup_policy',
913
+ worker: 'leader-fixed',
914
+ reason: `gate_bypassed:pending=${gate.pending},blocked=${gate.blocked},in_progress=${gate.in_progress},failed=${gate.failed}`,
915
+ }, cwd).catch(() => { });
916
+ }
917
+ else {
918
+ throw new Error(`shutdown_gate_blocked:pending=${gate.pending},blocked=${gate.blocked},in_progress=${gate.in_progress},failed=${gate.failed}`);
919
+ }
861
920
  }
862
921
  }
863
922
  if (force) {
@@ -890,7 +949,9 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
890
949
  inboxCorrelationKey: `shutdown:${w.name}`,
891
950
  });
892
951
  }
893
- catch { /* worker might already be dead */ }
952
+ catch (err) {
953
+ process.stderr.write(`[team/runtime] operation failed: ${err}\n`);
954
+ }
894
955
  }
895
956
  // 2. Wait up to 15s for workers to exit and collect acks
896
957
  const deadline = Date.now() + 15_000;
@@ -954,25 +1015,21 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
954
1015
  if (resizeHookWarning) {
955
1016
  console.warn(`[team shutdown] ${sanitized}: ${resizeHookWarning}; continuing teardown`);
956
1017
  }
957
- for (const w of config.workers) {
958
- try {
959
- // Guard: never kill the leader's own pane or the HUD pane.
960
- if (leaderPaneId && w.pane_id === leaderPaneId)
961
- continue;
962
- if (hudPaneId && w.pane_id === hudPaneId)
963
- continue;
964
- if (isWorkerAlive(sessionName, w.index, w.pane_id)) {
965
- killWorker(sessionName, w.index, w.pane_id, leaderPaneId ?? undefined);
966
- }
967
- }
968
- catch { /* ignore */ }
969
- }
1018
+ const workerPaneIds = config.workers
1019
+ .map((w) => w.pane_id)
1020
+ .filter((paneId) => typeof paneId === 'string' && paneId.trim().length > 0);
1021
+ await teardownWorkerPanes(workerPaneIds, {
1022
+ leaderPaneId,
1023
+ hudPaneId,
1024
+ });
970
1025
  // 4. Destroy tmux session
971
1026
  if (!sessionName.includes(':')) {
972
1027
  try {
973
1028
  destroyTeamSession(sessionName);
974
1029
  }
975
- catch { /* ignore */ }
1030
+ catch (err) {
1031
+ process.stderr.write(`[team/runtime] operation failed: ${err}\n`);
1032
+ }
976
1033
  }
977
1034
  }
978
1035
  else {
@@ -991,9 +1048,23 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
991
1048
  try {
992
1049
  await removeTeamWorkerInstructionsFile(sanitized, cwd);
993
1050
  }
994
- catch { /* ignore */ }
1051
+ catch (err) {
1052
+ process.stderr.write(`[team/runtime] operation failed: ${err}\n`);
1053
+ }
995
1054
  restoreTeamModelInstructionsFile(sanitized);
996
- // 6. Cleanup state
1055
+ // 6. Ralph stricter completion logging
1056
+ if (ralph) {
1057
+ const finalTasks = await listTasks(sanitized, cwd).catch(() => []);
1058
+ const completed = finalTasks.filter((t) => t.status === 'completed').length;
1059
+ const failed = finalTasks.filter((t) => t.status === 'failed').length;
1060
+ const pending = finalTasks.filter((t) => t.status === 'pending').length;
1061
+ await appendTeamEvent(sanitized, {
1062
+ type: 'ralph_cleanup_summary',
1063
+ worker: 'leader-fixed',
1064
+ reason: `total=${finalTasks.length} completed=${completed} failed=${failed} pending=${pending} force=${force}`,
1065
+ }, cwd).catch(() => { });
1066
+ }
1067
+ // 7. Cleanup state
997
1068
  await cleanupTeamState(sanitized, cwd);
998
1069
  }
999
1070
  /**
@@ -1016,7 +1087,8 @@ export async function resumeTeam(teamName, cwd) {
1016
1087
  process.kill(worker.pid, 0);
1017
1088
  return true;
1018
1089
  }
1019
- catch {
1090
+ catch (err) {
1091
+ process.stderr.write(`[team/runtime] operation failed: ${err}\n`);
1020
1092
  return false;
1021
1093
  }
1022
1094
  })
@@ -1094,7 +1166,8 @@ async function resolveLeaderSessionId(cwd) {
1094
1166
  if (typeof parsed.session_id === 'string' && parsed.session_id.trim() !== '')
1095
1167
  return parsed.session_id.trim();
1096
1168
  }
1097
- catch {
1169
+ catch (err) {
1170
+ process.stderr.write(`[team/runtime] operation failed: ${err}\n`);
1098
1171
  return '';
1099
1172
  }
1100
1173
  return '';
@@ -1144,7 +1217,7 @@ async function emitMonitorDerivedEvents(teamName, tasks, workers, previous, cwd)
1144
1217
  }
1145
1218
  }
1146
1219
  }
1147
- function notifyWorkerOutcome(config, workerIndex, message, workerPaneId) {
1220
+ async function notifyWorkerOutcome(config, workerIndex, message, workerPaneId) {
1148
1221
  const worker = config.workers.find((candidate) => candidate.index === workerIndex);
1149
1222
  if (!worker)
1150
1223
  return { ok: false, transport: 'none', reason: 'worker_not_found' };
@@ -1168,7 +1241,7 @@ function notifyWorkerOutcome(config, workerIndex, message, workerPaneId) {
1168
1241
  return { ok: false, transport: 'tmux_send_keys', reason: 'tmux_unavailable' };
1169
1242
  }
1170
1243
  try {
1171
- sendToWorker(config.tmux_session, workerIndex, message, workerPaneId, worker.worker_cli);
1244
+ await sendToWorker(config.tmux_session, workerIndex, message, workerPaneId, worker.worker_cli);
1172
1245
  return { ok: true, transport: 'tmux_send_keys', reason: 'tmux_send_keys_sent' };
1173
1246
  }
1174
1247
  catch (error) {
@@ -1240,7 +1313,7 @@ async function dispatchCriticalInboxInstruction(params) {
1240
1313
  return { ok: true, transport: 'hook', reason: `hook_receipt_${receipt.status}`, request_id: queued.request_id };
1241
1314
  }
1242
1315
  if (receipt?.status === 'failed') {
1243
- const fallback = notifyWorkerOutcome(config, workerIndex, triggerMessage, paneId);
1316
+ const fallback = await notifyWorkerOutcome(config, workerIndex, triggerMessage, paneId);
1244
1317
  if (fallback.ok) {
1245
1318
  await transitionDispatchRequest(teamName, queued.request_id, 'failed', 'failed', { last_reason: `fallback_confirmed_after_failed_receipt:${fallback.reason}` }, cwd).catch(() => { });
1246
1319
  return {
@@ -1258,7 +1331,7 @@ async function dispatchCriticalInboxInstruction(params) {
1258
1331
  request_id: queued.request_id,
1259
1332
  };
1260
1333
  }
1261
- const fallback = notifyWorkerOutcome(config, workerIndex, triggerMessage, paneId);
1334
+ const fallback = await notifyWorkerOutcome(config, workerIndex, triggerMessage, paneId);
1262
1335
  if (fallback.ok) {
1263
1336
  const marked = await markDispatchRequestNotified(teamName, queued.request_id, { last_reason: `fallback_confirmed:${fallback.reason}` }, cwd);
1264
1337
  if (!marked) {
@@ -1293,9 +1366,9 @@ async function finalizeHookPreferredMailboxDispatch(params) {
1293
1366
  return { ok: true, transport: 'hook', reason: `hook_receipt_${receipt.status}`, request_id: requestId, message_id: messageId };
1294
1367
  }
1295
1368
  const fallback = fallbackNotify
1296
- ? fallbackNotify()
1369
+ ? await fallbackNotify()
1297
1370
  : (typeof workerIndex === 'number'
1298
- ? notifyWorkerOutcome(config, workerIndex, triggerMessage, paneId)
1371
+ ? await notifyWorkerOutcome(config, workerIndex, triggerMessage, paneId)
1299
1372
  : { ok: false, transport: 'none', reason: 'missing_worker_index' });
1300
1373
  if (receipt?.status === 'failed') {
1301
1374
  if (fallback.ok) {
@@ -1344,10 +1417,25 @@ async function finalizeHookPreferredMailboxDispatch(params) {
1344
1417
  message_id: messageId,
1345
1418
  };
1346
1419
  }
1347
- function notifyLeader(config, message) {
1420
+ async function notifyLeaderAsync(config, message, cwd) {
1421
+ // Primary: inject directly into the leader pane via tmux send-keys.
1422
+ // This is the fallback path when hook-based dispatch timed out, so the
1423
+ // leader needs a direct tmux notification to wake up. Fixes #437.
1424
+ if (config.leader_pane_id && isTmuxAvailable()) {
1425
+ try {
1426
+ await sendToLeaderPane(config.leader_pane_id, message);
1427
+ return true;
1428
+ }
1429
+ catch (err) {
1430
+ process.stderr.write(`[team/runtime] operation failed: ${err}\n`);
1431
+ // Fall through to mailbox
1432
+ }
1433
+ }
1434
+ // Fallback: write to leader mailbox (leader picks up on next hook cycle)
1348
1435
  if (!config.tmux_session)
1349
1436
  return false;
1350
- return notifyLeaderStatus(config.tmux_session, message);
1437
+ const { notifyLeaderMailboxAsync } = await import('./tmux-session.js');
1438
+ return notifyLeaderMailboxAsync(config.name, 'system', message, cwd);
1351
1439
  }
1352
1440
  async function deliverPendingMailboxMessages(teamName, config, workers, previousNotifications, dispatchPolicy, cwd) {
1353
1441
  const nextNotifications = {};
@@ -1410,7 +1498,7 @@ async function deliverPendingMailboxMessages(teamName, config, workers, previous
1410
1498
  });
1411
1499
  }
1412
1500
  else {
1413
- const direct = notifyWorkerOutcome(config, workerInfo.index, triggerMessage, workerInfo.pane_id);
1501
+ const direct = await notifyWorkerOutcome(config, workerInfo.index, triggerMessage, workerInfo.pane_id);
1414
1502
  outcome = { ...direct, request_id: queued.request.request_id, message_id: msg.message_id };
1415
1503
  if (outcome.ok) {
1416
1504
  await markMessageNotified(teamName, worker.name, msg.message_id, cwd).catch(() => false);
@@ -1451,9 +1539,9 @@ export async function sendWorkerMessage(teamName, fromWorker, toWorker, body, cw
1451
1539
  cwd,
1452
1540
  transportPreference: leaderTransportPreference,
1453
1541
  fallbackAllowed: leaderTransportPreference === 'hook_preferred_with_fallback',
1454
- notify: (_target, message) => (leaderTransportPreference === 'hook_preferred_with_fallback'
1542
+ notify: async (_target, message) => (leaderTransportPreference === 'hook_preferred_with_fallback'
1455
1543
  ? { ok: true, transport: 'hook', reason: 'queued_for_hook_dispatch' }
1456
- : { ok: notifyLeader(config, message), transport: 'tmux_send_keys', reason: 'leader_notified' }),
1544
+ : { ok: await notifyLeaderAsync(config, message, cwd), transport: 'mailbox', reason: 'leader_mailbox_notified' }),
1457
1545
  });
1458
1546
  let finalOutcome = outcome;
1459
1547
  const canLeaderFallbackDirectly = Boolean(config.leader_pane_id) && isTmuxAvailable();
@@ -1471,10 +1559,10 @@ export async function sendWorkerMessage(teamName, fromWorker, toWorker, body, cw
1471
1559
  config,
1472
1560
  dispatchPolicy,
1473
1561
  cwd,
1474
- fallbackNotify: () => ({
1475
- ok: notifyLeader(config, leaderTriggerMessage),
1476
- transport: 'tmux_send_keys',
1477
- reason: 'leader_notified',
1562
+ fallbackNotify: async () => ({
1563
+ ok: await notifyLeaderAsync(config, leaderTriggerMessage, cwd),
1564
+ transport: 'mailbox',
1565
+ reason: 'leader_mailbox_notified',
1478
1566
  }),
1479
1567
  });
1480
1568
  }
@@ -1500,9 +1588,9 @@ export async function sendWorkerMessage(teamName, fromWorker, toWorker, body, cw
1500
1588
  cwd,
1501
1589
  transportPreference,
1502
1590
  fallbackAllowed: transportPreference === 'hook_preferred_with_fallback',
1503
- notify: (_target, message) => (transportPreference === 'hook_preferred_with_fallback'
1591
+ notify: async (_target, message) => (transportPreference === 'hook_preferred_with_fallback'
1504
1592
  ? { ok: true, transport: 'hook', reason: 'queued_for_hook_dispatch' }
1505
- : notifyWorkerOutcome(config, recipient.index, message, recipient.pane_id)),
1593
+ : await notifyWorkerOutcome(config, recipient.index, message, recipient.pane_id)),
1506
1594
  });
1507
1595
  let finalOutcome = outcome;
1508
1596
  if (transportPreference === 'hook_preferred_with_fallback') {
@@ -1544,10 +1632,10 @@ export async function broadcastWorkerMessage(teamName, fromWorker, body, cwd) {
1544
1632
  triggerFor: (workerName) => generateMailboxTriggerMessage(workerName, sanitized, 1),
1545
1633
  transportPreference,
1546
1634
  fallbackAllowed: transportPreference === 'hook_preferred_with_fallback',
1547
- notify: (target, message) => transportPreference === 'hook_preferred_with_fallback'
1635
+ notify: async (target, message) => transportPreference === 'hook_preferred_with_fallback'
1548
1636
  ? { ok: true, transport: 'hook', reason: 'queued_for_hook_dispatch' }
1549
1637
  : (typeof target.workerIndex === 'number'
1550
- ? notifyWorkerOutcome(config, target.workerIndex, message, target.paneId)
1638
+ ? await notifyWorkerOutcome(config, target.workerIndex, message, target.paneId)
1551
1639
  : { ok: false, transport: 'none', reason: 'missing_worker_index' }),
1552
1640
  });
1553
1641
  const finalizedOutcomes = [];