oh-my-codex 0.12.4 → 0.12.6

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 (475) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/README.md +27 -3
  4. package/dist/cli/__tests__/ask.test.js +26 -0
  5. package/dist/cli/__tests__/ask.test.js.map +1 -1
  6. package/dist/cli/__tests__/doctor-warning-copy.test.js +28 -0
  7. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  8. package/dist/cli/__tests__/explore.test.js +95 -8
  9. package/dist/cli/__tests__/explore.test.js.map +1 -1
  10. package/dist/cli/__tests__/index.test.js +102 -4
  11. package/dist/cli/__tests__/index.test.js.map +1 -1
  12. package/dist/cli/__tests__/launch-fallback.test.js +169 -0
  13. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  14. package/dist/cli/__tests__/mcp-parity.test.js +31 -0
  15. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  16. package/dist/cli/__tests__/setup-agents-overwrite.test.js +66 -2
  17. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  18. package/dist/cli/__tests__/setup-refresh.test.js +51 -1
  19. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  20. package/dist/cli/__tests__/team.test.js +148 -3
  21. package/dist/cli/__tests__/team.test.js.map +1 -1
  22. package/dist/cli/__tests__/uninstall.test.js +14 -1
  23. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  24. package/dist/cli/cleanup.js +1 -1
  25. package/dist/cli/cleanup.js.map +1 -1
  26. package/dist/cli/constants.d.ts +1 -0
  27. package/dist/cli/constants.d.ts.map +1 -1
  28. package/dist/cli/constants.js +1 -0
  29. package/dist/cli/constants.js.map +1 -1
  30. package/dist/cli/doctor.d.ts.map +1 -1
  31. package/dist/cli/doctor.js +15 -0
  32. package/dist/cli/doctor.js.map +1 -1
  33. package/dist/cli/explore.d.ts +1 -0
  34. package/dist/cli/explore.d.ts.map +1 -1
  35. package/dist/cli/explore.js +49 -1
  36. package/dist/cli/explore.js.map +1 -1
  37. package/dist/cli/index.d.ts +2 -1
  38. package/dist/cli/index.d.ts.map +1 -1
  39. package/dist/cli/index.js +127 -14
  40. package/dist/cli/index.js.map +1 -1
  41. package/dist/cli/mcp-parity.d.ts +1 -1
  42. package/dist/cli/mcp-parity.d.ts.map +1 -1
  43. package/dist/cli/mcp-parity.js +24 -0
  44. package/dist/cli/mcp-parity.js.map +1 -1
  45. package/dist/cli/setup.d.ts.map +1 -1
  46. package/dist/cli/setup.js +17 -5
  47. package/dist/cli/setup.js.map +1 -1
  48. package/dist/cli/team.d.ts.map +1 -1
  49. package/dist/cli/team.js +80 -6
  50. package/dist/cli/team.js.map +1 -1
  51. package/dist/cli/uninstall.d.ts.map +1 -1
  52. package/dist/cli/uninstall.js +1 -0
  53. package/dist/cli/uninstall.js.map +1 -1
  54. package/dist/config/__tests__/generator-idempotent.test.js +60 -0
  55. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  56. package/dist/config/__tests__/mcp-registry.test.js +61 -0
  57. package/dist/config/__tests__/mcp-registry.test.js.map +1 -1
  58. package/dist/config/__tests__/wiki-config-contract.test.d.ts +2 -0
  59. package/dist/config/__tests__/wiki-config-contract.test.d.ts.map +1 -0
  60. package/dist/config/__tests__/wiki-config-contract.test.js +19 -0
  61. package/dist/config/__tests__/wiki-config-contract.test.js.map +1 -0
  62. package/dist/config/generator.d.ts +1 -0
  63. package/dist/config/generator.d.ts.map +1 -1
  64. package/dist/config/generator.js +88 -3
  65. package/dist/config/generator.js.map +1 -1
  66. package/dist/config/mcp-registry.d.ts +2 -0
  67. package/dist/config/mcp-registry.d.ts.map +1 -1
  68. package/dist/config/mcp-registry.js +12 -0
  69. package/dist/config/mcp-registry.js.map +1 -1
  70. package/dist/hooks/__tests__/agents-overlay.test.js +39 -0
  71. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  72. package/dist/hooks/__tests__/keyword-detector.test.js +297 -4
  73. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  74. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +392 -22
  75. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  76. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +166 -67
  77. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  78. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +112 -2
  79. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  80. package/dist/hooks/__tests__/notify-hook-modules.test.js +52 -12
  81. package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
  82. package/dist/hooks/__tests__/notify-hook-regression-205.test.d.ts +2 -3
  83. package/dist/hooks/__tests__/notify-hook-regression-205.test.d.ts.map +1 -1
  84. package/dist/hooks/__tests__/notify-hook-regression-205.test.js +18 -23
  85. package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
  86. package/dist/hooks/__tests__/notify-hook-session-scope.test.js +33 -0
  87. package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
  88. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +176 -1
  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 +355 -7
  91. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  92. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +90 -2
  93. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  94. package/dist/hooks/__tests__/session.test.js +142 -2
  95. package/dist/hooks/__tests__/session.test.js.map +1 -1
  96. package/dist/hooks/__tests__/wiki-docs-contract.test.d.ts +2 -0
  97. package/dist/hooks/__tests__/wiki-docs-contract.test.d.ts.map +1 -0
  98. package/dist/hooks/__tests__/wiki-docs-contract.test.js +34 -0
  99. package/dist/hooks/__tests__/wiki-docs-contract.test.js.map +1 -0
  100. package/dist/hooks/agents-overlay.d.ts.map +1 -1
  101. package/dist/hooks/agents-overlay.js +0 -1
  102. package/dist/hooks/agents-overlay.js.map +1 -1
  103. package/dist/hooks/extensibility/__tests__/dispatcher.test.js +32 -0
  104. package/dist/hooks/extensibility/__tests__/dispatcher.test.js.map +1 -1
  105. package/dist/hooks/extensibility/__tests__/runtime.test.js +31 -0
  106. package/dist/hooks/extensibility/__tests__/runtime.test.js.map +1 -1
  107. package/dist/hooks/extensibility/__tests__/sdk.test.js +33 -3
  108. package/dist/hooks/extensibility/__tests__/sdk.test.js.map +1 -1
  109. package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
  110. package/dist/hooks/extensibility/dispatcher.js +41 -0
  111. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  112. package/dist/hooks/extensibility/sdk/runtime-state.d.ts.map +1 -1
  113. package/dist/hooks/extensibility/sdk/runtime-state.js +7 -1
  114. package/dist/hooks/extensibility/sdk/runtime-state.js.map +1 -1
  115. package/dist/hooks/extensibility/types.d.ts +1 -0
  116. package/dist/hooks/extensibility/types.d.ts.map +1 -1
  117. package/dist/hooks/keyword-detector.d.ts +6 -1
  118. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  119. package/dist/hooks/keyword-detector.js +207 -10
  120. package/dist/hooks/keyword-detector.js.map +1 -1
  121. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  122. package/dist/hooks/keyword-registry.js +3 -0
  123. package/dist/hooks/keyword-registry.js.map +1 -1
  124. package/dist/hooks/session.d.ts +14 -2
  125. package/dist/hooks/session.d.ts.map +1 -1
  126. package/dist/hooks/session.js +120 -16
  127. package/dist/hooks/session.js.map +1 -1
  128. package/dist/hud/__tests__/state.test.js +111 -2
  129. package/dist/hud/__tests__/state.test.js.map +1 -1
  130. package/dist/hud/state.d.ts.map +1 -1
  131. package/dist/hud/state.js +18 -21
  132. package/dist/hud/state.js.map +1 -1
  133. package/dist/mcp/__tests__/bootstrap.test.js +88 -1
  134. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  135. package/dist/mcp/__tests__/server-lifecycle.test.js +3 -0
  136. package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
  137. package/dist/mcp/__tests__/state-paths.test.js +30 -2
  138. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  139. package/dist/mcp/__tests__/state-server.test.js +415 -0
  140. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  141. package/dist/mcp/__tests__/wiki-server.test.d.ts +2 -0
  142. package/dist/mcp/__tests__/wiki-server.test.d.ts.map +1 -0
  143. package/dist/mcp/__tests__/wiki-server.test.js +30 -0
  144. package/dist/mcp/__tests__/wiki-server.test.js.map +1 -0
  145. package/dist/mcp/bootstrap.d.ts +19 -1
  146. package/dist/mcp/bootstrap.d.ts.map +1 -1
  147. package/dist/mcp/bootstrap.js +185 -0
  148. package/dist/mcp/bootstrap.js.map +1 -1
  149. package/dist/mcp/state-paths.d.ts +5 -0
  150. package/dist/mcp/state-paths.d.ts.map +1 -1
  151. package/dist/mcp/state-paths.js +41 -11
  152. package/dist/mcp/state-paths.js.map +1 -1
  153. package/dist/mcp/state-server.d.ts +4 -4
  154. package/dist/mcp/state-server.d.ts.map +1 -1
  155. package/dist/mcp/state-server.js +49 -2
  156. package/dist/mcp/state-server.js.map +1 -1
  157. package/dist/mcp/wiki-server.d.ts +181 -0
  158. package/dist/mcp/wiki-server.d.ts.map +1 -0
  159. package/dist/mcp/wiki-server.js +235 -0
  160. package/dist/mcp/wiki-server.js.map +1 -0
  161. package/dist/modes/__tests__/base-autoresearch-contract.test.js +74 -2
  162. package/dist/modes/__tests__/base-autoresearch-contract.test.js.map +1 -1
  163. package/dist/modes/__tests__/base-multi-state-compat.test.d.ts +2 -0
  164. package/dist/modes/__tests__/base-multi-state-compat.test.d.ts.map +1 -0
  165. package/dist/modes/__tests__/base-multi-state-compat.test.js +38 -0
  166. package/dist/modes/__tests__/base-multi-state-compat.test.js.map +1 -0
  167. package/dist/modes/__tests__/base-tmux-pane.test.js +1 -1
  168. package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
  169. package/dist/modes/base.d.ts +2 -1
  170. package/dist/modes/base.d.ts.map +1 -1
  171. package/dist/modes/base.js +55 -31
  172. package/dist/modes/base.js.map +1 -1
  173. package/dist/notifications/__tests__/formatter.test.js +11 -0
  174. package/dist/notifications/__tests__/formatter.test.js.map +1 -1
  175. package/dist/notifications/__tests__/idle-cooldown.test.js +32 -1
  176. package/dist/notifications/__tests__/idle-cooldown.test.js.map +1 -1
  177. package/dist/notifications/__tests__/index.test.d.ts +2 -0
  178. package/dist/notifications/__tests__/index.test.d.ts.map +1 -0
  179. package/dist/notifications/__tests__/index.test.js +113 -0
  180. package/dist/notifications/__tests__/index.test.js.map +1 -0
  181. package/dist/notifications/__tests__/lifecycle-dedupe.test.d.ts +2 -0
  182. package/dist/notifications/__tests__/lifecycle-dedupe.test.d.ts.map +1 -0
  183. package/dist/notifications/__tests__/lifecycle-dedupe.test.js +86 -0
  184. package/dist/notifications/__tests__/lifecycle-dedupe.test.js.map +1 -0
  185. package/dist/notifications/__tests__/reply-listener.test.js +174 -0
  186. package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
  187. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.d.ts +2 -0
  188. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.d.ts.map +1 -0
  189. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js +93 -0
  190. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js.map +1 -0
  191. package/dist/notifications/__tests__/session-registry.test.js +48 -1
  192. package/dist/notifications/__tests__/session-registry.test.js.map +1 -1
  193. package/dist/notifications/__tests__/session-status.test.d.ts +2 -0
  194. package/dist/notifications/__tests__/session-status.test.d.ts.map +1 -0
  195. package/dist/notifications/__tests__/session-status.test.js +159 -0
  196. package/dist/notifications/__tests__/session-status.test.js.map +1 -0
  197. package/dist/notifications/__tests__/tmux.test.js +58 -1
  198. package/dist/notifications/__tests__/tmux.test.js.map +1 -1
  199. package/dist/notifications/idle-cooldown.d.ts +11 -0
  200. package/dist/notifications/idle-cooldown.d.ts.map +1 -1
  201. package/dist/notifications/idle-cooldown.js +42 -8
  202. package/dist/notifications/idle-cooldown.js.map +1 -1
  203. package/dist/notifications/index.d.ts +1 -1
  204. package/dist/notifications/index.d.ts.map +1 -1
  205. package/dist/notifications/index.js +41 -8
  206. package/dist/notifications/index.js.map +1 -1
  207. package/dist/notifications/lifecycle-dedupe.d.ts +8 -0
  208. package/dist/notifications/lifecycle-dedupe.d.ts.map +1 -0
  209. package/dist/notifications/lifecycle-dedupe.js +112 -0
  210. package/dist/notifications/lifecycle-dedupe.js.map +1 -0
  211. package/dist/notifications/reply-listener.d.ts +10 -1
  212. package/dist/notifications/reply-listener.d.ts.map +1 -1
  213. package/dist/notifications/reply-listener.js +49 -11
  214. package/dist/notifications/reply-listener.js.map +1 -1
  215. package/dist/notifications/session-registry.d.ts.map +1 -1
  216. package/dist/notifications/session-registry.js +7 -1
  217. package/dist/notifications/session-registry.js.map +1 -1
  218. package/dist/notifications/session-status.d.ts +23 -0
  219. package/dist/notifications/session-status.d.ts.map +1 -0
  220. package/dist/notifications/session-status.js +187 -0
  221. package/dist/notifications/session-status.js.map +1 -0
  222. package/dist/notifications/tmux.d.ts +10 -0
  223. package/dist/notifications/tmux.d.ts.map +1 -1
  224. package/dist/notifications/tmux.js +59 -5
  225. package/dist/notifications/tmux.js.map +1 -1
  226. package/dist/notifications/types.d.ts +2 -0
  227. package/dist/notifications/types.d.ts.map +1 -1
  228. package/dist/openclaw/__tests__/index.test.js +84 -0
  229. package/dist/openclaw/__tests__/index.test.js.map +1 -1
  230. package/dist/openclaw/index.d.ts.map +1 -1
  231. package/dist/openclaw/index.js +7 -14
  232. package/dist/openclaw/index.js.map +1 -1
  233. package/dist/openclaw/types.d.ts +2 -2
  234. package/dist/openclaw/types.d.ts.map +1 -1
  235. package/dist/scripts/__tests__/codex-native-hook.test.js +692 -40
  236. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  237. package/dist/scripts/__tests__/hook-derived-watcher.test.d.ts +2 -0
  238. package/dist/scripts/__tests__/hook-derived-watcher.test.d.ts.map +1 -0
  239. package/dist/scripts/__tests__/hook-derived-watcher.test.js +87 -0
  240. package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -0
  241. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  242. package/dist/scripts/codex-native-hook.js +309 -77
  243. package/dist/scripts/codex-native-hook.js.map +1 -1
  244. package/dist/scripts/hook-derived-watcher.js +43 -1
  245. package/dist/scripts/hook-derived-watcher.js.map +1 -1
  246. package/dist/scripts/notify-fallback-watcher.js +95 -21
  247. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  248. package/dist/scripts/notify-hook/active-team.d.ts +9 -0
  249. package/dist/scripts/notify-hook/active-team.d.ts.map +1 -0
  250. package/dist/scripts/notify-hook/active-team.js +44 -0
  251. package/dist/scripts/notify-hook/active-team.js.map +1 -0
  252. package/dist/scripts/notify-hook/auto-nudge.d.ts +5 -3
  253. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  254. package/dist/scripts/notify-hook/auto-nudge.js +121 -78
  255. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  256. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  257. package/dist/scripts/notify-hook/managed-tmux.js +18 -4
  258. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  259. package/dist/scripts/notify-hook/operational-events.d.ts.map +1 -1
  260. package/dist/scripts/notify-hook/operational-events.js +21 -0
  261. package/dist/scripts/notify-hook/operational-events.js.map +1 -1
  262. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
  263. package/dist/scripts/notify-hook/ralph-session-resume.js +3 -2
  264. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  265. package/dist/scripts/notify-hook/state-io.d.ts +10 -1
  266. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  267. package/dist/scripts/notify-hook/state-io.js +56 -12
  268. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  269. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  270. package/dist/scripts/notify-hook/team-dispatch.js +305 -167
  271. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  272. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  273. package/dist/scripts/notify-hook/team-leader-nudge.js +87 -15
  274. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  275. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  276. package/dist/scripts/notify-hook/tmux-injection.js +11 -2
  277. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  278. package/dist/scripts/notify-hook.js +26 -16
  279. package/dist/scripts/notify-hook.js.map +1 -1
  280. package/dist/scripts/run-provider-advisor.js +20 -2
  281. package/dist/scripts/run-provider-advisor.js.map +1 -1
  282. package/dist/scripts/smoke-packed-install.d.ts +1 -8
  283. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  284. package/dist/scripts/smoke-packed-install.js +12 -68
  285. package/dist/scripts/smoke-packed-install.js.map +1 -1
  286. package/dist/state/__tests__/operations.test.js +113 -0
  287. package/dist/state/__tests__/operations.test.js.map +1 -1
  288. package/dist/state/__tests__/skill-active.test.js +35 -0
  289. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  290. package/dist/state/__tests__/workflow-transition.test.d.ts +2 -0
  291. package/dist/state/__tests__/workflow-transition.test.d.ts.map +1 -0
  292. package/dist/state/__tests__/workflow-transition.test.js +56 -0
  293. package/dist/state/__tests__/workflow-transition.test.js.map +1 -0
  294. package/dist/state/operations.d.ts +1 -1
  295. package/dist/state/operations.d.ts.map +1 -1
  296. package/dist/state/operations.js +88 -2
  297. package/dist/state/operations.js.map +1 -1
  298. package/dist/state/skill-active.d.ts +2 -2
  299. package/dist/state/skill-active.d.ts.map +1 -1
  300. package/dist/state/skill-active.js +119 -33
  301. package/dist/state/skill-active.js.map +1 -1
  302. package/dist/state/workflow-transition-reconcile.d.ts +15 -0
  303. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -0
  304. package/dist/state/workflow-transition-reconcile.js +100 -0
  305. package/dist/state/workflow-transition-reconcile.js.map +1 -0
  306. package/dist/state/workflow-transition.d.ts +22 -0
  307. package/dist/state/workflow-transition.d.ts.map +1 -0
  308. package/dist/state/workflow-transition.js +188 -0
  309. package/dist/state/workflow-transition.js.map +1 -0
  310. package/dist/team/__tests__/api-interop.test.js +90 -0
  311. package/dist/team/__tests__/api-interop.test.js.map +1 -1
  312. package/dist/team/__tests__/current-task-baseline.test.d.ts +2 -0
  313. package/dist/team/__tests__/current-task-baseline.test.d.ts.map +1 -0
  314. package/dist/team/__tests__/current-task-baseline.test.js +87 -0
  315. package/dist/team/__tests__/current-task-baseline.test.js.map +1 -0
  316. package/dist/team/__tests__/hardening-e2e.test.js +17 -0
  317. package/dist/team/__tests__/hardening-e2e.test.js.map +1 -1
  318. package/dist/team/__tests__/runtime.test.js +673 -65
  319. package/dist/team/__tests__/runtime.test.js.map +1 -1
  320. package/dist/team/__tests__/shutdown-fallback.test.js +11 -1
  321. package/dist/team/__tests__/shutdown-fallback.test.js.map +1 -1
  322. package/dist/team/__tests__/tmux-session.test.js +447 -4
  323. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  324. package/dist/team/api-interop.d.ts.map +1 -1
  325. package/dist/team/api-interop.js +10 -1
  326. package/dist/team/api-interop.js.map +1 -1
  327. package/dist/team/current-task-baseline.d.ts +32 -0
  328. package/dist/team/current-task-baseline.d.ts.map +1 -0
  329. package/dist/team/current-task-baseline.js +85 -0
  330. package/dist/team/current-task-baseline.js.map +1 -0
  331. package/dist/team/delivery-log.d.ts +1 -1
  332. package/dist/team/delivery-log.d.ts.map +1 -1
  333. package/dist/team/delivery-log.js.map +1 -1
  334. package/dist/team/leader-activity.d.ts +1 -0
  335. package/dist/team/leader-activity.d.ts.map +1 -1
  336. package/dist/team/leader-activity.js +4 -2
  337. package/dist/team/leader-activity.js.map +1 -1
  338. package/dist/team/progress-evidence.d.ts +2 -0
  339. package/dist/team/progress-evidence.d.ts.map +1 -0
  340. package/dist/team/progress-evidence.js +77 -0
  341. package/dist/team/progress-evidence.js.map +1 -0
  342. package/dist/team/runtime.d.ts.map +1 -1
  343. package/dist/team/runtime.js +269 -64
  344. package/dist/team/runtime.js.map +1 -1
  345. package/dist/team/scaling.d.ts.map +1 -1
  346. package/dist/team/scaling.js +1 -1
  347. package/dist/team/scaling.js.map +1 -1
  348. package/dist/team/state.d.ts.map +1 -1
  349. package/dist/team/state.js +2 -13
  350. package/dist/team/state.js.map +1 -1
  351. package/dist/team/tmux-session.d.ts +12 -3
  352. package/dist/team/tmux-session.d.ts.map +1 -1
  353. package/dist/team/tmux-session.js +174 -20
  354. package/dist/team/tmux-session.js.map +1 -1
  355. package/dist/team/worktree.d.ts +6 -1
  356. package/dist/team/worktree.d.ts.map +1 -1
  357. package/dist/team/worktree.js +28 -4
  358. package/dist/team/worktree.js.map +1 -1
  359. package/dist/utils/__tests__/agents-md.test.js +21 -1
  360. package/dist/utils/__tests__/agents-md.test.js.map +1 -1
  361. package/dist/utils/__tests__/repo-deps.test.d.ts +2 -0
  362. package/dist/utils/__tests__/repo-deps.test.d.ts.map +1 -0
  363. package/dist/utils/__tests__/repo-deps.test.js +71 -0
  364. package/dist/utils/__tests__/repo-deps.test.js.map +1 -0
  365. package/dist/utils/agents-md.d.ts +1 -0
  366. package/dist/utils/agents-md.d.ts.map +1 -1
  367. package/dist/utils/agents-md.js +7 -3
  368. package/dist/utils/agents-md.js.map +1 -1
  369. package/dist/utils/paths.d.ts +4 -0
  370. package/dist/utils/paths.d.ts.map +1 -1
  371. package/dist/utils/paths.js +20 -0
  372. package/dist/utils/paths.js.map +1 -1
  373. package/dist/utils/repo-deps.d.ts +20 -0
  374. package/dist/utils/repo-deps.d.ts.map +1 -0
  375. package/dist/utils/repo-deps.js +78 -0
  376. package/dist/utils/repo-deps.js.map +1 -0
  377. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.d.ts +2 -0
  378. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.d.ts.map +1 -0
  379. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +54 -0
  380. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -0
  381. package/dist/wiki/__tests__/cjk-tokenize.test.d.ts +12 -0
  382. package/dist/wiki/__tests__/cjk-tokenize.test.d.ts.map +1 -0
  383. package/dist/wiki/__tests__/cjk-tokenize.test.js +139 -0
  384. package/dist/wiki/__tests__/cjk-tokenize.test.js.map +1 -0
  385. package/dist/wiki/__tests__/crlf-parse.test.d.ts +2 -0
  386. package/dist/wiki/__tests__/crlf-parse.test.d.ts.map +1 -0
  387. package/dist/wiki/__tests__/crlf-parse.test.js +24 -0
  388. package/dist/wiki/__tests__/crlf-parse.test.js.map +1 -0
  389. package/dist/wiki/__tests__/escape-newline.test.d.ts +2 -0
  390. package/dist/wiki/__tests__/escape-newline.test.d.ts.map +1 -0
  391. package/dist/wiki/__tests__/escape-newline.test.js +45 -0
  392. package/dist/wiki/__tests__/escape-newline.test.js.map +1 -0
  393. package/dist/wiki/__tests__/ingest.test.d.ts +5 -0
  394. package/dist/wiki/__tests__/ingest.test.d.ts.map +1 -0
  395. package/dist/wiki/__tests__/ingest.test.js +181 -0
  396. package/dist/wiki/__tests__/ingest.test.js.map +1 -0
  397. package/dist/wiki/__tests__/lint.test.d.ts +5 -0
  398. package/dist/wiki/__tests__/lint.test.d.ts.map +1 -0
  399. package/dist/wiki/__tests__/lint.test.js +163 -0
  400. package/dist/wiki/__tests__/lint.test.js.map +1 -0
  401. package/dist/wiki/__tests__/query.test.d.ts +5 -0
  402. package/dist/wiki/__tests__/query.test.d.ts.map +1 -0
  403. package/dist/wiki/__tests__/query.test.js +141 -0
  404. package/dist/wiki/__tests__/query.test.js.map +1 -0
  405. package/dist/wiki/__tests__/reserved-file-guard.test.d.ts +2 -0
  406. package/dist/wiki/__tests__/reserved-file-guard.test.d.ts.map +1 -0
  407. package/dist/wiki/__tests__/reserved-file-guard.test.js +44 -0
  408. package/dist/wiki/__tests__/reserved-file-guard.test.js.map +1 -0
  409. package/dist/wiki/__tests__/session-hooks.test.d.ts +5 -0
  410. package/dist/wiki/__tests__/session-hooks.test.d.ts.map +1 -0
  411. package/dist/wiki/__tests__/session-hooks.test.js +36 -0
  412. package/dist/wiki/__tests__/session-hooks.test.js.map +1 -0
  413. package/dist/wiki/__tests__/slug-nonascii.test.d.ts +2 -0
  414. package/dist/wiki/__tests__/slug-nonascii.test.d.ts.map +1 -0
  415. package/dist/wiki/__tests__/slug-nonascii.test.js +24 -0
  416. package/dist/wiki/__tests__/slug-nonascii.test.js.map +1 -0
  417. package/dist/wiki/__tests__/storage.test.d.ts +5 -0
  418. package/dist/wiki/__tests__/storage.test.d.ts.map +1 -0
  419. package/dist/wiki/__tests__/storage.test.js +278 -0
  420. package/dist/wiki/__tests__/storage.test.js.map +1 -0
  421. package/dist/wiki/__tests__/test-helpers.d.ts +31 -0
  422. package/dist/wiki/__tests__/test-helpers.d.ts.map +1 -0
  423. package/dist/wiki/__tests__/test-helpers.js +108 -0
  424. package/dist/wiki/__tests__/test-helpers.js.map +1 -0
  425. package/dist/wiki/index.d.ts +14 -0
  426. package/dist/wiki/index.d.ts.map +1 -0
  427. package/dist/wiki/index.js +17 -0
  428. package/dist/wiki/index.js.map +1 -0
  429. package/dist/wiki/ingest.d.ts +20 -0
  430. package/dist/wiki/ingest.d.ts.map +1 -0
  431. package/dist/wiki/ingest.js +115 -0
  432. package/dist/wiki/ingest.js.map +1 -0
  433. package/dist/wiki/lifecycle.d.ts +20 -0
  434. package/dist/wiki/lifecycle.d.ts.map +1 -0
  435. package/dist/wiki/lifecycle.js +212 -0
  436. package/dist/wiki/lifecycle.js.map +1 -0
  437. package/dist/wiki/lint.d.ts +25 -0
  438. package/dist/wiki/lint.d.ts.map +1 -0
  439. package/dist/wiki/lint.js +166 -0
  440. package/dist/wiki/lint.js.map +1 -0
  441. package/dist/wiki/query.d.ts +36 -0
  442. package/dist/wiki/query.d.ts.map +1 -0
  443. package/dist/wiki/query.js +138 -0
  444. package/dist/wiki/query.js.map +1 -0
  445. package/dist/wiki/storage.d.ts +33 -0
  446. package/dist/wiki/storage.d.ts.map +1 -0
  447. package/dist/wiki/storage.js +321 -0
  448. package/dist/wiki/storage.js.map +1 -0
  449. package/dist/wiki/types.d.ts +83 -0
  450. package/dist/wiki/types.d.ts.map +1 -0
  451. package/dist/wiki/types.js +15 -0
  452. package/dist/wiki/types.js.map +1 -0
  453. package/package.json +3 -1
  454. package/skills/configure-notifications/SKILL.md +1 -0
  455. package/skills/doctor/SKILL.md +11 -0
  456. package/skills/omx-setup/SKILL.md +1 -1
  457. package/skills/wiki/SKILL.md +57 -0
  458. package/src/scripts/__tests__/codex-native-hook.test.ts +920 -56
  459. package/src/scripts/__tests__/hook-derived-watcher.test.ts +111 -0
  460. package/src/scripts/codex-native-hook.ts +377 -83
  461. package/src/scripts/hook-derived-watcher.ts +43 -1
  462. package/src/scripts/notify-fallback-watcher.ts +99 -20
  463. package/src/scripts/notify-hook/active-team.ts +54 -0
  464. package/src/scripts/notify-hook/auto-nudge.ts +132 -79
  465. package/src/scripts/notify-hook/managed-tmux.ts +22 -4
  466. package/src/scripts/notify-hook/operational-events.ts +21 -0
  467. package/src/scripts/notify-hook/ralph-session-resume.ts +3 -2
  468. package/src/scripts/notify-hook/state-io.ts +89 -12
  469. package/src/scripts/notify-hook/team-dispatch.ts +326 -168
  470. package/src/scripts/notify-hook/team-leader-nudge.ts +91 -14
  471. package/src/scripts/notify-hook/tmux-injection.ts +11 -2
  472. package/src/scripts/notify-hook.ts +36 -22
  473. package/src/scripts/run-provider-advisor.ts +20 -2
  474. package/src/scripts/smoke-packed-install.ts +16 -83
  475. package/templates/AGENTS.md +3 -4
@@ -3,8 +3,8 @@ 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, 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
- 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';
6
+ import { sanitizeTeamName, isTmuxAvailable, hasCurrentTmuxClientContext, createTeamSession, buildWorkerProcessLaunchSpec, resolveTeamWorkerCli, resolveTeamWorkerCliPlan, resolveTeamWorkerLaunchMode, waitForWorkerReady, dismissTrustPromptIfPresent, sleepFractionalSeconds, sendToWorker, sendToWorkerStdin, isWorkerAlive, isWorkerPaneOpen, getWorkerPanePid, killWorkerByPaneIdAsync, restoreStandaloneHudPane, teardownWorkerPanes, unregisterResizeHook, destroyTeamSession, listPaneIds, listTeamSessions, resolveSharedSessionShutdownTopology, } 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, 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, teamWriteWorkerStatus as writeWorkerStatus, } from './team-ops.js';
8
8
  import { queueInboxInstruction, queueDirectMailboxMessage, queueBroadcastMailboxMessage, waitForDispatchReceipt, } from './mcp-comm.js';
9
9
  import { appendTeamDeliveryLogForCwd } from './delivery-log.js';
10
10
  import { generateWorkerOverlay, writeTeamWorkerInstructionsFile, removeTeamWorkerInstructionsFile, writeWorkerWorktreeRootAgentsFile, removeWorkerWorktreeRootAgentsFile, generateInitialInbox, generateTaskAssignmentInbox, generateShutdownInbox, buildTriggerDirective, buildMailboxTriggerDirective, buildLeaderMailboxTriggerDirective, writeWorkerRoleInstructionsFile, } from './worker-bootstrap.js';
@@ -18,6 +18,7 @@ import { inferPhaseTargetFromTaskCounts, reconcilePhaseStateForMonitor } from '.
18
18
  import { getTeamTmuxSessions } from '../notifications/tmux.js';
19
19
  import { hasStructuredVerificationEvidence } from '../verification/verifier.js';
20
20
  import { buildRebalanceDecisions } from './rebalance-policy.js';
21
+ import { getStatePath } from '../mcp/state-paths.js';
21
22
  import { readModeState, updateModeState } from '../modes/base.js';
22
23
  import { appendTeamCommitHygieneEntries, buildTeamCommitHygieneContext, writeTeamCommitHygieneContext, } from './commit-hygiene.js';
23
24
  import { assertCleanLeaderWorkspaceForWorkerWorktrees, ensureWorktree, isGitRepository, isWorktreeDirty, planWorktreeTarget, removeWorktreeForce, rollbackProvisionedWorktrees, } from './worktree.js';
@@ -51,6 +52,40 @@ async function syncRootTeamModeStateOnTerminalPhase(teamName, phase, cwd) {
51
52
  // Best-effort compatibility sync only.
52
53
  }
53
54
  }
55
+ function matchesTeamModeStateForShutdown(state, teamName) {
56
+ const stateTeamName = typeof state?.team_name === 'string' ? state.team_name.trim() : '';
57
+ return !stateTeamName || stateTeamName === teamName;
58
+ }
59
+ async function syncExactTeamModeStateOnShutdown(teamName, cwd, sessionId) {
60
+ const path = getStatePath('team', cwd, sessionId);
61
+ if (!existsSync(path))
62
+ return;
63
+ try {
64
+ const parsed = JSON.parse(await readFile(path, 'utf-8'));
65
+ if (!matchesTeamModeStateForShutdown(parsed, teamName))
66
+ return;
67
+ const next = {
68
+ ...parsed,
69
+ active: false,
70
+ current_phase: 'cancelled',
71
+ completed_at: typeof parsed.completed_at === 'string' && parsed.completed_at.trim().length > 0
72
+ ? parsed.completed_at
73
+ : new Date().toISOString(),
74
+ team_name: teamName,
75
+ };
76
+ await writeFile(path, JSON.stringify(next, null, 2));
77
+ }
78
+ catch {
79
+ // Best-effort compatibility sync only.
80
+ }
81
+ }
82
+ async function syncTeamModeStateOnShutdown(teamName, cwd, leaderSessionId) {
83
+ await syncExactTeamModeStateOnShutdown(teamName, cwd);
84
+ const normalizedLeaderSessionId = typeof leaderSessionId === 'string' ? leaderSessionId.trim() : '';
85
+ if (normalizedLeaderSessionId) {
86
+ await syncExactTeamModeStateOnShutdown(teamName, cwd, normalizedLeaderSessionId);
87
+ }
88
+ }
54
89
  async function assertTeamStartupIsNonDestructive(teamName, cwd, leaderSessionId) {
55
90
  const activeTeams = await findActiveTeams(cwd, leaderSessionId);
56
91
  if (activeTeams.length > 0) {
@@ -86,10 +121,10 @@ export function applyCreatedInteractiveSessionToConfig(config, createdSession, w
86
121
  }
87
122
  }
88
123
  function collectShutdownPaneIds(params) {
89
- const { config, livePaneIds = [], restoredStandaloneHudPaneId = null } = params;
124
+ const { config, livePaneIds = [], restoredStandaloneHudPaneId = null, leaderPaneId = config.leader_pane_id, hudPaneId = config.hud_pane_id, } = params;
90
125
  const excludedPaneIds = new Set([
91
- config.leader_pane_id,
92
- config.hud_pane_id,
126
+ leaderPaneId,
127
+ hudPaneId,
93
128
  restoredStandaloneHudPaneId,
94
129
  ].filter((paneId) => typeof paneId === 'string' && paneId.trim().startsWith('%')));
95
130
  const paneIds = new Set();
@@ -862,14 +897,24 @@ const TEAM_LEADER_CWD_ENV = 'OMX_TEAM_LEADER_CWD';
862
897
  const WORKTREE_TRIGGER_STATE_ROOT = '$OMX_TEAM_STATE_ROOT';
863
898
  const STARTUP_EVIDENCE_TIMEOUT_MS = 2_000;
864
899
  const STARTUP_EVIDENCE_POLL_MS = 100;
900
+ const STARTUP_EVIDENCE_LAUNCH_TIMEOUT_MS = 5_000;
865
901
  const promptWorkerRegistry = new Map();
866
902
  const previousModelInstructionsFileByTeam = new Map();
867
903
  const PROMPT_WORKER_SIGTERM_WAIT_MS = 3_000;
868
904
  const PROMPT_WORKER_SIGKILL_WAIT_MS = 2_000;
869
905
  const PROMPT_WORKER_EXIT_POLL_MS = 100;
906
+ const PROMPT_MODE_CODEX_UNSUPPORTED_REASON = 'prompt_mode_codex_requires_tty';
907
+ // Test-only escape hatch for fake prompt workers that intentionally do not require a real TTY.
908
+ const PROMPT_MODE_CODEX_TEST_ALLOW_ENV = 'OMX_TEST_ALLOW_NONTTY_CODEX_PROMPT';
870
909
  function resolveInstructionStateRoot(worktreePath) {
871
910
  return worktreePath ? WORKTREE_TRIGGER_STATE_ROOT : undefined;
872
911
  }
912
+ function assertPromptModeWorkerCliSupported(workerCliPlan) {
913
+ if (workerCliPlan.some((workerCli) => workerCli === 'codex')
914
+ && process.env[PROMPT_MODE_CODEX_TEST_ALLOW_ENV] !== '1') {
915
+ throw new Error(`${PROMPT_MODE_CODEX_UNSUPPORTED_REASON}: Codex prompt workers require a terminal; use interactive team mode or set OMX_TEAM_WORKER_CLI=claude/gemini for prompt-mode teammates.`);
916
+ }
917
+ }
873
918
  function resolveWorkerReadyTimeoutMs(env) {
874
919
  const raw = env.OMX_TEAM_READY_TIMEOUT_MS;
875
920
  const parsed = Number.parseInt(String(raw ?? ''), 10);
@@ -877,6 +922,12 @@ function resolveWorkerReadyTimeoutMs(env) {
877
922
  return parsed;
878
923
  return 45_000;
879
924
  }
925
+ function resolveWorkerStartupEvidenceTimeoutMs(env, workerReadyTimeoutMs) {
926
+ const raw = Number.parseInt(String(env.OMX_TEAM_STARTUP_EVIDENCE_TIMEOUT_MS ?? ''), 10);
927
+ if (Number.isFinite(raw) && raw >= 500)
928
+ return raw;
929
+ return Math.max(STARTUP_EVIDENCE_TIMEOUT_MS, Math.min(workerReadyTimeoutMs, STARTUP_EVIDENCE_LAUNCH_TIMEOUT_MS));
930
+ }
880
931
  function parseTeamWorkerContext(raw) {
881
932
  if (typeof raw !== 'string' || raw.trim() === '')
882
933
  return null;
@@ -954,6 +1005,30 @@ export async function waitForClaudeStartupEvidence(params) {
954
1005
  function shouldSkipWorkerReadyWait(env) {
955
1006
  return env.OMX_TEAM_SKIP_READY_WAIT === '1';
956
1007
  }
1008
+ function isRecoverableInteractiveStartupReason(reason) {
1009
+ const normalized = reason.trim().toLowerCase();
1010
+ return normalized.includes('startup_no_evidence')
1011
+ || normalized.includes('fallback_attempted_but_unconfirmed')
1012
+ || normalized.includes('ready_prompt_timeout');
1013
+ }
1014
+ async function recordRecoverableStartupIssue(params) {
1015
+ const { teamName, workerName, taskIds, reason, cwd } = params;
1016
+ const updatedAt = new Date().toISOString();
1017
+ await writeWorkerStatus(teamName, workerName, {
1018
+ state: 'unknown',
1019
+ current_task_id: taskIds[0],
1020
+ reason,
1021
+ updated_at: updatedAt,
1022
+ }, cwd).catch(() => { });
1023
+ await appendTeamEvent(teamName, {
1024
+ type: 'worker_state_changed',
1025
+ worker: workerName,
1026
+ state: 'unknown',
1027
+ prev_state: 'unknown',
1028
+ task_id: taskIds[0],
1029
+ reason,
1030
+ }, cwd).catch(() => { });
1031
+ }
957
1032
  function setTeamModelInstructionsFile(teamName, filePath) {
958
1033
  if (!previousModelInstructionsFileByTeam.has(teamName)) {
959
1034
  previousModelInstructionsFileByTeam.set(teamName, process.env[MODEL_INSTRUCTIONS_FILE_ENV]);
@@ -1258,8 +1333,8 @@ function isPromptWorkerAlive(config, worker) {
1258
1333
  }
1259
1334
  export { TEAM_LOW_COMPLEXITY_DEFAULT_MODEL };
1260
1335
  export { resolveCanonicalTeamStateRoot };
1261
- function spawnPromptWorker(teamName, workerName, workerIndex, workerCwd, launchArgs, workerEnv, workerCli, initialPrompt) {
1262
- const processSpec = buildWorkerProcessLaunchSpec(teamName, workerIndex, launchArgs, workerCwd, workerEnv, workerCli, initialPrompt);
1336
+ function spawnPromptWorker(teamName, workerName, workerIndex, workerCwd, launchArgs, workerEnv, workerCli, initialPrompt, workerRole) {
1337
+ const processSpec = buildWorkerProcessLaunchSpec(teamName, workerIndex, launchArgs, workerCwd, workerEnv, workerCli, initialPrompt, workerRole);
1263
1338
  const child = spawn(processSpec.command, processSpec.args, {
1264
1339
  cwd: workerCwd,
1265
1340
  detached: process.platform !== 'win32',
@@ -1402,7 +1477,11 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
1402
1477
  fallbackModel: resolveAgentDefaultModel(agentType, process.env.CODEX_HOME),
1403
1478
  });
1404
1479
  const workerCliPlan = resolveTeamWorkerCliPlan(workerCount, sharedWorkerLaunchArgs, process.env);
1480
+ if (workerLaunchMode === 'prompt') {
1481
+ assertPromptModeWorkerCliSupported(workerCliPlan);
1482
+ }
1405
1483
  const workerReadyTimeoutMs = resolveWorkerReadyTimeoutMs(process.env);
1484
+ const workerStartupEvidenceTimeoutMs = resolveWorkerStartupEvidenceTimeoutMs(process.env, workerReadyTimeoutMs);
1406
1485
  const skipWorkerReadyWait = shouldSkipWorkerReadyWait(process.env);
1407
1486
  try {
1408
1487
  // 3. Init state directory + config
@@ -1518,9 +1597,53 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
1518
1597
  initialPrompt: plan.initialPrompt,
1519
1598
  launchArgs: plan.workerLaunchArgs,
1520
1599
  workerCli: plan.workerCli,
1600
+ workerRole: plan.workerRole,
1521
1601
  };
1522
1602
  });
1523
1603
  const workerPaneIds = Array.from({ length: workerCount }, () => undefined);
1604
+ const materializeWorkerStartupState = async (bootstrapPlan, workerIndex, paneId) => {
1605
+ const workerWorkspace = bootstrapPlan.workerWorkspace;
1606
+ const identity = {
1607
+ name: bootstrapPlan.workerName,
1608
+ index: workerIndex,
1609
+ role: bootstrapPlan.workerRole,
1610
+ worker_cli: bootstrapPlan.workerCli,
1611
+ assigned_tasks: bootstrapPlan.workerTasks.map((task) => task.id),
1612
+ working_dir: workerWorkspace.cwd,
1613
+ worktree_repo_root: workerWorkspace.worktreeRepoRoot,
1614
+ worktree_path: workerWorkspace.worktreePath,
1615
+ worktree_branch: workerWorkspace.worktreeBranch,
1616
+ worktree_detached: workerWorkspace.worktreeDetached,
1617
+ worktree_created: workerWorkspace.worktreeCreated,
1618
+ team_state_root: teamStateRoot,
1619
+ };
1620
+ if (workerLaunchMode === 'interactive') {
1621
+ const panePid = getWorkerPanePid(sessionName, workerIndex, paneId);
1622
+ if (panePid)
1623
+ identity.pid = panePid;
1624
+ }
1625
+ else if (config?.workers[workerIndex - 1]?.pid) {
1626
+ identity.pid = config.workers[workerIndex - 1].pid;
1627
+ }
1628
+ if (paneId)
1629
+ identity.pane_id = paneId;
1630
+ if (config?.workers[workerIndex - 1]) {
1631
+ config.workers[workerIndex - 1].pid = identity.pid;
1632
+ config.workers[workerIndex - 1].pane_id = paneId;
1633
+ config.workers[workerIndex - 1].role = bootstrapPlan.workerRole;
1634
+ config.workers[workerIndex - 1].worker_cli = bootstrapPlan.workerCli;
1635
+ config.workers[workerIndex - 1].assigned_tasks = bootstrapPlan.workerTasks.map((task) => task.id);
1636
+ config.workers[workerIndex - 1].working_dir = workerWorkspace.cwd;
1637
+ config.workers[workerIndex - 1].worktree_repo_root = workerWorkspace.worktreeRepoRoot;
1638
+ config.workers[workerIndex - 1].worktree_path = workerWorkspace.worktreePath;
1639
+ config.workers[workerIndex - 1].worktree_branch = workerWorkspace.worktreeBranch;
1640
+ config.workers[workerIndex - 1].worktree_detached = workerWorkspace.worktreeDetached;
1641
+ config.workers[workerIndex - 1].worktree_created = workerWorkspace.worktreeCreated;
1642
+ config.workers[workerIndex - 1].team_state_root = teamStateRoot;
1643
+ }
1644
+ await writeWorkerIdentity(sanitized, bootstrapPlan.workerName, identity, leaderCwd);
1645
+ await writeWorkerInbox(sanitized, bootstrapPlan.workerName, bootstrapPlan.inbox, leaderCwd);
1646
+ };
1524
1647
  // 6. Create worker runtime (interactive tmux panes or prompt-mode child processes)
1525
1648
  if (workerLaunchMode === 'interactive') {
1526
1649
  const createdSession = createTeamSession(sanitized, workerCount, leaderCwd, sharedWorkerLaunchArgs, workerStartups);
@@ -1539,12 +1662,21 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
1539
1662
  for (let i = 1; i <= workerCount; i++) {
1540
1663
  const startup = workerStartups[i - 1] || {};
1541
1664
  const workerName = `worker-${i}`;
1542
- const child = spawnPromptWorker(sanitized, workerName, i, startup.cwd || leaderCwd, startup.launchArgs || sharedWorkerLaunchArgs, startup.env || {}, startup.workerCli || workerCliPlan[i - 1], startup.initialPrompt);
1665
+ const child = spawnPromptWorker(sanitized, workerName, i, startup.cwd || leaderCwd, startup.launchArgs || sharedWorkerLaunchArgs, startup.env || {}, startup.workerCli || workerCliPlan[i - 1], startup.initialPrompt, startup.workerRole);
1543
1666
  if (config.workers[i - 1]) {
1544
1667
  config.workers[i - 1].pid = child.pid;
1545
1668
  }
1546
1669
  }
1547
1670
  }
1671
+ // Materialize durable startup state for every worker before any per-worker
1672
+ // readiness wait or startup-evidence gate can block later workers.
1673
+ for (let i = 1; i <= workerCount; i++) {
1674
+ const bootstrapPlan = workerBootstrapPlans[i - 1];
1675
+ if (!bootstrapPlan) {
1676
+ throw new Error(`missing bootstrap plan for worker-${i}`);
1677
+ }
1678
+ await materializeWorkerStartupState(bootstrapPlan, i, workerPaneIds[i - 1]);
1679
+ }
1548
1680
  await saveTeamConfig(config, leaderCwd);
1549
1681
  // 7. Wait for all workers to be ready (interactive mode), then bootstrap them
1550
1682
  const manifest = await readTeamManifestV2(sanitized, leaderCwd);
@@ -1554,64 +1686,33 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
1554
1686
  if (!bootstrapPlan) {
1555
1687
  throw new Error(`missing bootstrap plan for worker-${i}`);
1556
1688
  }
1557
- const { workerName, paneId, workerTasks, workerRole, inbox, trigger, triggerIntent, initialPrompt } = {
1689
+ const { workerName, paneId, workerTasks, inbox, trigger, triggerIntent, initialPrompt } = {
1558
1690
  workerName: bootstrapPlan.workerName,
1559
1691
  paneId: workerPaneIds[i - 1],
1560
1692
  workerTasks: bootstrapPlan.workerTasks,
1561
- workerRole: bootstrapPlan.workerRole,
1562
1693
  inbox: bootstrapPlan.inbox,
1563
1694
  trigger: bootstrapPlan.trigger,
1564
1695
  triggerIntent: bootstrapPlan.triggerIntent,
1565
1696
  initialPrompt: bootstrapPlan.initialPrompt,
1566
1697
  };
1567
- const workerWorkspace = bootstrapPlan.workerWorkspace;
1568
1698
  if (workerTasks.map(t => t.role).filter(Boolean).length > 0 && new Set(workerTasks.map(t => t.role).filter(Boolean)).size > 1) {
1569
1699
  console.log(`[omx:team] ${workerName}: mixed task roles [${[...new Set(workerTasks.map(t => t.role).filter(Boolean))].join(', ')}], falling back to ${agentType}`);
1570
1700
  }
1571
- // Write worker identity
1572
- const identity = {
1573
- name: workerName,
1574
- index: i,
1575
- role: workerRole,
1576
- worker_cli: workerCliPlan[i - 1],
1577
- assigned_tasks: workerTasks.map(t => t.id),
1578
- working_dir: workerWorkspace.cwd,
1579
- worktree_repo_root: workerWorkspace.worktreeRepoRoot,
1580
- worktree_path: workerWorkspace.worktreePath,
1581
- worktree_branch: workerWorkspace.worktreeBranch,
1582
- worktree_detached: workerWorkspace.worktreeDetached,
1583
- worktree_created: workerWorkspace.worktreeCreated,
1584
- team_state_root: teamStateRoot,
1585
- };
1586
- // Get pane PID and store it (interactive mode) or process PID (prompt mode)
1587
- if (workerLaunchMode === 'interactive') {
1588
- const panePid = getWorkerPanePid(sessionName, i, paneId);
1589
- if (panePid)
1590
- identity.pid = panePid;
1591
- }
1592
- else if (config.workers[i - 1]?.pid) {
1593
- identity.pid = config.workers[i - 1].pid;
1594
- }
1595
- if (paneId)
1596
- identity.pane_id = paneId;
1597
- if (config.workers[i - 1]) {
1598
- config.workers[i - 1].pid = identity.pid;
1599
- config.workers[i - 1].pane_id = paneId;
1600
- config.workers[i - 1].role = workerRole;
1601
- config.workers[i - 1].worker_cli = workerCliPlan[i - 1];
1602
- config.workers[i - 1].working_dir = workerWorkspace.cwd;
1603
- config.workers[i - 1].worktree_repo_root = workerWorkspace.worktreeRepoRoot;
1604
- config.workers[i - 1].worktree_path = workerWorkspace.worktreePath;
1605
- config.workers[i - 1].worktree_branch = workerWorkspace.worktreeBranch;
1606
- config.workers[i - 1].worktree_detached = workerWorkspace.worktreeDetached;
1607
- config.workers[i - 1].worktree_created = workerWorkspace.worktreeCreated;
1608
- config.workers[i - 1].team_state_root = teamStateRoot;
1609
- }
1610
- await writeWorkerIdentity(sanitized, workerName, identity, leaderCwd);
1611
1701
  // Wait for worker readiness
1612
1702
  if (workerLaunchMode === 'interactive' && !skipWorkerReadyWait && !initialPrompt) {
1613
1703
  const ready = waitForWorkerReady(sessionName, i, workerReadyTimeoutMs, paneId);
1614
1704
  if (!ready) {
1705
+ const workerAlive = isWorkerPaneOpen(sessionName, i, paneId);
1706
+ if (workerAlive) {
1707
+ await recordRecoverableStartupIssue({
1708
+ teamName: sanitized,
1709
+ workerName,
1710
+ taskIds: workerTasks.map((task) => task.id),
1711
+ reason: 'ready_prompt_timeout',
1712
+ cwd: leaderCwd,
1713
+ });
1714
+ continue;
1715
+ }
1615
1716
  throw new Error(`Worker ${workerName} did not become ready in tmux session ${sessionName}`);
1616
1717
  }
1617
1718
  }
@@ -1639,6 +1740,7 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
1639
1740
  dispatchPolicy,
1640
1741
  inboxCorrelationKey: `startup:${workerName}`,
1641
1742
  requireWorkerStartupEvidence: true,
1743
+ startupEvidenceTimeoutMs: workerStartupEvidenceTimeoutMs,
1642
1744
  });
1643
1745
  if (dispatchOutcome.ok)
1644
1746
  break;
@@ -1659,7 +1761,22 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
1659
1761
  }
1660
1762
  }
1661
1763
  if (!dispatchOutcome.ok) {
1662
- throw new Error(`worker_notify_failed:${workerName}`);
1764
+ const workerAlive = workerLaunchMode === 'prompt'
1765
+ ? isPromptWorkerAlive(config, config.workers[i - 1])
1766
+ : isWorkerPaneOpen(sessionName, i, paneId);
1767
+ if (workerLaunchMode === 'interactive'
1768
+ && workerAlive
1769
+ && isRecoverableInteractiveStartupReason(dispatchOutcome.reason)) {
1770
+ await recordRecoverableStartupIssue({
1771
+ teamName: sanitized,
1772
+ workerName,
1773
+ taskIds: workerTasks.map((task) => task.id),
1774
+ reason: dispatchOutcome.reason,
1775
+ cwd: leaderCwd,
1776
+ });
1777
+ continue;
1778
+ }
1779
+ throw new Error(`worker_notify_failed:${workerName}:${dispatchOutcome.reason}`);
1663
1780
  }
1664
1781
  }
1665
1782
  await saveTeamConfig(config, leaderCwd);
@@ -2118,10 +2235,14 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
2118
2235
  process.stderr.write(`[team/runtime] operation failed: ${err}\n`);
2119
2236
  }
2120
2237
  await cleanupTeamState(sanitized, cwd);
2238
+ await syncTeamModeStateOnShutdown(sanitized, cwd);
2121
2239
  restoreTeamModelInstructionsFile(sanitized);
2122
2240
  return { commitHygieneArtifacts: null };
2123
2241
  }
2124
2242
  const manifest = await readTeamManifestV2(sanitized, cwd);
2243
+ const leaderSessionId = typeof manifest?.leader?.session_id === 'string'
2244
+ ? manifest.leader.session_id.trim()
2245
+ : '';
2125
2246
  const governance = resolveGovernancePolicy(manifest?.governance, manifest?.policy);
2126
2247
  if (!force) {
2127
2248
  const classification = await classifyShutdown({
@@ -2232,8 +2353,20 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
2232
2353
  const leaderPaneId = config.leader_pane_id;
2233
2354
  const hudPaneId = config.hud_pane_id;
2234
2355
  if (config.worker_launch_mode === 'interactive') {
2235
- const livePaneIds = listPaneIds(sessionName);
2236
- let shutdownPaneIds = collectShutdownPaneIds({ config, livePaneIds });
2356
+ const sharedSessionTopology = sessionName.includes(':')
2357
+ ? resolveSharedSessionShutdownTopology(sessionName, leaderPaneId)
2358
+ : null;
2359
+ const effectiveLeaderPaneId = sharedSessionTopology?.leaderPaneId ?? leaderPaneId;
2360
+ const effectiveHudPaneId = sharedSessionTopology?.hudPaneIds.find((paneId) => paneId === hudPaneId)
2361
+ ?? sharedSessionTopology?.hudPaneIds[0]
2362
+ ?? hudPaneId;
2363
+ const livePaneIds = sharedSessionTopology?.livePaneIds ?? listPaneIds(sessionName);
2364
+ let shutdownPaneIds = collectShutdownPaneIds({
2365
+ config,
2366
+ livePaneIds,
2367
+ leaderPaneId: effectiveLeaderPaneId,
2368
+ hudPaneId: effectiveHudPaneId,
2369
+ });
2237
2370
  if (shouldPrekillInteractiveShutdownProcessTrees(sessionName)) {
2238
2371
  const workerPanePids = shutdownPaneIds
2239
2372
  .map((paneId) => getWorkerPanePid(sessionName, 1, paneId))
@@ -2261,10 +2394,10 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
2261
2394
  console.warn(`[team shutdown] ${sanitized}: ${resizeHookWarning}; continuing teardown`);
2262
2395
  }
2263
2396
  let restoredHudPaneId = null;
2264
- if (hudPaneId) {
2265
- await killWorkerByPaneIdAsync(hudPaneId, leaderPaneId ?? undefined);
2397
+ if (effectiveHudPaneId) {
2398
+ await killWorkerByPaneIdAsync(effectiveHudPaneId, effectiveLeaderPaneId ?? undefined);
2266
2399
  if (sessionName.includes(':')) {
2267
- restoredHudPaneId = restoreStandaloneHudPane(leaderPaneId, cwd);
2400
+ restoredHudPaneId = restoreStandaloneHudPane(effectiveLeaderPaneId, cwd);
2268
2401
  if (!restoredHudPaneId) {
2269
2402
  console.warn(`[team shutdown] ${sanitized}: failed to restore standalone HUD pane`);
2270
2403
  }
@@ -2274,10 +2407,12 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
2274
2407
  config,
2275
2408
  livePaneIds: listPaneIds(sessionName),
2276
2409
  restoredStandaloneHudPaneId: restoredHudPaneId,
2410
+ leaderPaneId: effectiveLeaderPaneId,
2411
+ hudPaneId: effectiveHudPaneId,
2277
2412
  });
2278
2413
  await teardownWorkerPanes(shutdownPaneIds, {
2279
- leaderPaneId,
2280
- hudPaneId: restoredHudPaneId ?? hudPaneId,
2414
+ leaderPaneId: effectiveLeaderPaneId,
2415
+ hudPaneId: restoredHudPaneId ?? effectiveHudPaneId,
2281
2416
  });
2282
2417
  // 4. Destroy tmux session
2283
2418
  if (!sessionName.includes(':')) {
@@ -2376,12 +2511,17 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
2376
2511
  }
2377
2512
  }
2378
2513
  // 7. Cleanup state
2514
+ let teamStateCleaned = false;
2379
2515
  try {
2380
2516
  await cleanupTeamState(sanitized, cwd);
2517
+ teamStateCleaned = true;
2381
2518
  }
2382
2519
  catch (err) {
2383
2520
  cleanupErrors.push(`cleanupTeamState: ${String(err)}`);
2384
2521
  }
2522
+ if (teamStateCleaned) {
2523
+ await syncTeamModeStateOnShutdown(sanitized, cwd, leaderSessionId);
2524
+ }
2385
2525
  if (cleanupErrors.length > 0) {
2386
2526
  throw new Error(cleanupErrors.join(' | '));
2387
2527
  }
@@ -2652,7 +2792,7 @@ async function markDispatchRequestLeaderPaneMissingDeferred(params) {
2652
2792
  }, cwd).catch(() => { });
2653
2793
  }
2654
2794
  async function dispatchCriticalInboxInstruction(params) {
2655
- const { teamName, config, workerName, workerIndex, paneId, workerCli, inbox, triggerMessage, intent, cwd, dispatchPolicy, inboxCorrelationKey, requireWorkerStartupEvidence, } = params;
2795
+ const { teamName, config, workerName, workerIndex, paneId, workerCli, inbox, triggerMessage, intent, cwd, dispatchPolicy, inboxCorrelationKey, requireWorkerStartupEvidence, startupEvidenceTimeoutMs, } = params;
2656
2796
  if (config.worker_launch_mode === 'prompt') {
2657
2797
  return await queueInboxInstruction({
2658
2798
  teamName,
@@ -2720,6 +2860,7 @@ async function dispatchCriticalInboxInstruction(params) {
2720
2860
  workerName,
2721
2861
  workerCli,
2722
2862
  cwd,
2863
+ timeoutMs: startupEvidenceTimeoutMs,
2723
2864
  });
2724
2865
  if (startupEvidence !== 'none') {
2725
2866
  return {
@@ -2733,7 +2874,27 @@ async function dispatchCriticalInboxInstruction(params) {
2733
2874
  if (receipt?.status === 'failed') {
2734
2875
  const fallback = await notifyWorkerOutcome(config, workerIndex, triggerMessage, paneId);
2735
2876
  if (fallback.ok) {
2736
- await transitionDispatchRequest(teamName, queued.request_id, 'pending', 'failed', { last_reason: `fallback_confirmed_after_failed_receipt:${fallback.reason}` }, cwd).catch(() => { });
2877
+ const fallbackStartupEvidence = await waitForRequiredStartupEvidenceAfterDirectFallback({
2878
+ requireWorkerStartupEvidence,
2879
+ workerCli,
2880
+ teamName,
2881
+ workerName,
2882
+ cwd,
2883
+ timeoutMs: startupEvidenceTimeoutMs,
2884
+ });
2885
+ if (requiresObservedStartupEvidence && fallbackStartupEvidence === 'none') {
2886
+ await transitionDispatchRequest(teamName, queued.request_id, 'failed', 'failed', { last_reason: `${workerCli}_startup_no_evidence_after_fallback:${fallback.reason}` }, cwd).catch(() => { });
2887
+ return {
2888
+ ok: false,
2889
+ transport: fallback.transport,
2890
+ reason: `${workerCli}_startup_no_evidence_after_fallback:${fallback.reason}`,
2891
+ request_id: queued.request_id,
2892
+ };
2893
+ }
2894
+ const current = await readDispatchRequest(teamName, queued.request_id, cwd);
2895
+ if (current) {
2896
+ await transitionDispatchRequest(teamName, queued.request_id, current.status, 'failed', { last_reason: `fallback_confirmed_after_failed_receipt:${fallback.reason}` }, cwd).catch(() => { });
2897
+ }
2737
2898
  return {
2738
2899
  ok: true,
2739
2900
  transport: fallback.transport,
@@ -2757,9 +2918,32 @@ async function dispatchCriticalInboxInstruction(params) {
2757
2918
  ? `${startupFallbackLabel}_fallback_failed:${fallback.reason}`
2758
2919
  : `fallback_attempted_but_unconfirmed:${fallback.reason}`;
2759
2920
  if (fallback.ok) {
2921
+ const fallbackStartupEvidence = await waitForRequiredStartupEvidenceAfterDirectFallback({
2922
+ requireWorkerStartupEvidence,
2923
+ workerCli,
2924
+ teamName,
2925
+ workerName,
2926
+ cwd,
2927
+ timeoutMs: startupEvidenceTimeoutMs,
2928
+ });
2929
+ if (requiresObservedStartupEvidence && fallbackStartupEvidence === 'none') {
2930
+ const current = await readDispatchRequest(teamName, queued.request_id, cwd);
2931
+ if (current && current.status !== 'failed') {
2932
+ await transitionDispatchRequest(teamName, queued.request_id, current.status, 'failed', { last_reason: `${workerCli}_startup_no_evidence_after_fallback:${fallback.reason}` }, cwd).catch(() => { });
2933
+ }
2934
+ return {
2935
+ ok: false,
2936
+ transport: fallback.transport,
2937
+ reason: `${workerCli}_startup_no_evidence_after_fallback:${fallback.reason}`,
2938
+ request_id: queued.request_id,
2939
+ };
2940
+ }
2760
2941
  const marked = await markDispatchRequestNotified(teamName, queued.request_id, { last_reason: `fallback_confirmed:${fallback.reason}` }, cwd);
2761
2942
  if (!marked) {
2762
- await transitionDispatchRequest(teamName, queued.request_id, 'pending', 'failed', { last_reason: `fallback_confirmed_after_failed_receipt:${fallback.reason}` }, cwd).catch(() => { });
2943
+ const current = await readDispatchRequest(teamName, queued.request_id, cwd);
2944
+ if (current) {
2945
+ await transitionDispatchRequest(teamName, queued.request_id, current.status, 'failed', { last_reason: `fallback_confirmed_after_failed_receipt:${fallback.reason}` }, cwd).catch(() => { });
2946
+ }
2763
2947
  }
2764
2948
  return {
2765
2949
  ok: true,
@@ -2781,6 +2965,21 @@ async function dispatchCriticalInboxInstruction(params) {
2781
2965
  request_id: queued.request_id,
2782
2966
  };
2783
2967
  }
2968
+ async function waitForRequiredStartupEvidenceAfterDirectFallback(params) {
2969
+ const { requireWorkerStartupEvidence, workerCli, teamName, workerName, cwd, timeoutMs, } = params;
2970
+ const requiresObservedStartupEvidence = requireWorkerStartupEvidence === true
2971
+ && (workerCli === 'claude' || workerCli === 'codex');
2972
+ if (!requiresObservedStartupEvidence || !workerCli) {
2973
+ return 'none';
2974
+ }
2975
+ return await waitForWorkerStartupEvidence({
2976
+ teamName,
2977
+ workerName,
2978
+ workerCli,
2979
+ cwd,
2980
+ timeoutMs,
2981
+ });
2982
+ }
2784
2983
  async function finalizeHookPreferredMailboxDispatch(params) {
2785
2984
  const { teamName, requestId, workerName, workerIndex, paneId, messageId, triggerMessage, intent, config, dispatchPolicy, cwd, fallbackNotify, } = params;
2786
2985
  const receipt = await waitForDispatchReceipt(teamName, requestId, cwd, {
@@ -2801,7 +3000,10 @@ async function finalizeHookPreferredMailboxDispatch(params) {
2801
3000
  if (receipt?.status === 'failed') {
2802
3001
  if (fallback.ok) {
2803
3002
  await markMessageNotified(teamName, workerName, messageId, cwd).catch(() => false);
2804
- await transitionDispatchRequest(teamName, requestId, 'failed', 'failed', { message_id: messageId, last_reason: `fallback_confirmed_after_failed_receipt:${fallback.reason}` }, cwd).catch(() => null);
3003
+ const current = await readDispatchRequest(teamName, requestId, cwd);
3004
+ if (current) {
3005
+ await transitionDispatchRequest(teamName, requestId, current.status, 'failed', { message_id: messageId, last_reason: `fallback_confirmed_after_failed_receipt:${fallback.reason}` }, cwd).catch(() => null);
3006
+ }
2805
3007
  const outcome = {
2806
3008
  ok: true,
2807
3009
  transport: fallback.transport,
@@ -2844,7 +3046,10 @@ async function finalizeHookPreferredMailboxDispatch(params) {
2844
3046
  await markMessageNotified(teamName, workerName, messageId, cwd).catch(() => false);
2845
3047
  const marked = await markDispatchRequestNotified(teamName, requestId, { message_id: messageId, last_reason: `fallback_confirmed:${fallback.reason}` }, cwd);
2846
3048
  if (!marked) {
2847
- await transitionDispatchRequest(teamName, requestId, 'failed', 'failed', { message_id: messageId, last_reason: `fallback_confirmed_after_failed_receipt:${fallback.reason}` }, cwd).catch(() => { });
3049
+ const current = await readDispatchRequest(teamName, requestId, cwd);
3050
+ if (current) {
3051
+ await transitionDispatchRequest(teamName, requestId, current.status, 'failed', { message_id: messageId, last_reason: `fallback_confirmed_after_failed_receipt:${fallback.reason}` }, cwd).catch(() => { });
3052
+ }
2848
3053
  }
2849
3054
  const outcome = {
2850
3055
  ok: true,