oh-my-codex 0.14.4 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (614) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/Cargo.lock +6 -6
  3. package/Cargo.toml +2 -1
  4. package/README.md +40 -7
  5. package/crates/omx-explore/Cargo.toml +1 -0
  6. package/crates/omx-explore/src/main.rs +246 -24
  7. package/crates/omx-mux/Cargo.toml +1 -0
  8. package/crates/omx-runtime/Cargo.toml +1 -0
  9. package/crates/omx-runtime-core/Cargo.toml +1 -0
  10. package/crates/omx-runtime-core/src/dispatch.rs +2 -1
  11. package/crates/omx-runtime-core/src/engine.rs +2 -2
  12. package/crates/omx-runtime-core/src/mailbox.rs +2 -1
  13. package/crates/omx-sparkshell/Cargo.toml +1 -0
  14. package/crates/omx-sparkshell/src/codex_bridge.rs +1 -0
  15. package/crates/omx-sparkshell/src/prompt.rs +1 -0
  16. package/crates/omx-sparkshell/src/threshold.rs +1 -0
  17. package/dist/agents/__tests__/native-config.test.js +98 -4
  18. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  19. package/dist/agents/native-config.d.ts +4 -0
  20. package/dist/agents/native-config.d.ts.map +1 -1
  21. package/dist/agents/native-config.js +19 -3
  22. package/dist/agents/native-config.js.map +1 -1
  23. package/dist/agents/policy.d.ts +10 -0
  24. package/dist/agents/policy.d.ts.map +1 -0
  25. package/dist/agents/policy.js +61 -0
  26. package/dist/agents/policy.js.map +1 -0
  27. package/dist/catalog/__tests__/generator.test.js +2 -0
  28. package/dist/catalog/__tests__/generator.test.js.map +1 -1
  29. package/dist/catalog/__tests__/plugin-bundle-ssot.test.d.ts +2 -0
  30. package/dist/catalog/__tests__/plugin-bundle-ssot.test.d.ts.map +1 -0
  31. package/dist/catalog/__tests__/plugin-bundle-ssot.test.js +72 -0
  32. package/dist/catalog/__tests__/plugin-bundle-ssot.test.js.map +1 -0
  33. package/dist/catalog/installable.d.ts +5 -0
  34. package/dist/catalog/installable.d.ts.map +1 -0
  35. package/dist/catalog/installable.js +13 -0
  36. package/dist/catalog/installable.js.map +1 -0
  37. package/dist/catalog/skill-mirror.d.ts +20 -0
  38. package/dist/catalog/skill-mirror.d.ts.map +1 -0
  39. package/dist/catalog/skill-mirror.js +104 -0
  40. package/dist/catalog/skill-mirror.js.map +1 -0
  41. package/dist/cli/__tests__/ask.test.js +4 -1
  42. package/dist/cli/__tests__/ask.test.js.map +1 -1
  43. package/dist/cli/__tests__/codex-plugin-layout.test.d.ts +2 -0
  44. package/dist/cli/__tests__/codex-plugin-layout.test.d.ts.map +1 -0
  45. package/dist/cli/__tests__/codex-plugin-layout.test.js +210 -0
  46. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -0
  47. package/dist/cli/__tests__/doctor-team.test.js +46 -1
  48. package/dist/cli/__tests__/doctor-team.test.js.map +1 -1
  49. package/dist/cli/__tests__/doctor-warning-copy.test.js +225 -111
  50. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  51. package/dist/cli/__tests__/exec.test.js +96 -1
  52. package/dist/cli/__tests__/exec.test.js.map +1 -1
  53. package/dist/cli/__tests__/explore.test.js +26 -9
  54. package/dist/cli/__tests__/explore.test.js.map +1 -1
  55. package/dist/cli/__tests__/index.test.js +301 -7
  56. package/dist/cli/__tests__/index.test.js.map +1 -1
  57. package/dist/cli/__tests__/launch-fallback.test.js +358 -4
  58. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  59. package/dist/cli/__tests__/list.test.d.ts +2 -0
  60. package/dist/cli/__tests__/list.test.d.ts.map +1 -0
  61. package/dist/cli/__tests__/list.test.js +38 -0
  62. package/dist/cli/__tests__/list.test.js.map +1 -0
  63. package/dist/cli/__tests__/mcp-parity.test.js +86 -0
  64. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  65. package/dist/cli/__tests__/mcp-serve.test.d.ts +2 -0
  66. package/dist/cli/__tests__/mcp-serve.test.d.ts.map +1 -0
  67. package/dist/cli/__tests__/mcp-serve.test.js +38 -0
  68. package/dist/cli/__tests__/mcp-serve.test.js.map +1 -0
  69. package/dist/cli/__tests__/nested-help-routing.test.js +1 -0
  70. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  71. package/dist/cli/__tests__/package-bin-contract.test.js +58 -3
  72. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  73. package/dist/cli/__tests__/question.test.js +209 -5
  74. package/dist/cli/__tests__/question.test.js.map +1 -1
  75. package/dist/cli/__tests__/setup-agents-overwrite.test.js +146 -3
  76. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  77. package/dist/cli/__tests__/setup-install-mode.test.d.ts +2 -0
  78. package/dist/cli/__tests__/setup-install-mode.test.d.ts.map +1 -0
  79. package/dist/cli/__tests__/setup-install-mode.test.js +873 -0
  80. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -0
  81. package/dist/cli/__tests__/setup-prompts-overwrite.test.js +83 -22
  82. package/dist/cli/__tests__/setup-prompts-overwrite.test.js.map +1 -1
  83. package/dist/cli/__tests__/setup-refresh.test.js +120 -2
  84. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  85. package/dist/cli/__tests__/setup-skills-overwrite.test.js +32 -13
  86. package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
  87. package/dist/cli/__tests__/sidecar.test.d.ts +2 -0
  88. package/dist/cli/__tests__/sidecar.test.d.ts.map +1 -0
  89. package/dist/cli/__tests__/sidecar.test.js +24 -0
  90. package/dist/cli/__tests__/sidecar.test.js.map +1 -0
  91. package/dist/cli/__tests__/team.test.js +90 -42
  92. package/dist/cli/__tests__/team.test.js.map +1 -1
  93. package/dist/cli/__tests__/version-sync-contract.test.js +11 -0
  94. package/dist/cli/__tests__/version-sync-contract.test.js.map +1 -1
  95. package/dist/cli/codex-home.d.ts +4 -5
  96. package/dist/cli/codex-home.d.ts.map +1 -1
  97. package/dist/cli/codex-home.js +9 -37
  98. package/dist/cli/codex-home.js.map +1 -1
  99. package/dist/cli/doctor.d.ts +1 -1
  100. package/dist/cli/doctor.d.ts.map +1 -1
  101. package/dist/cli/doctor.js +509 -278
  102. package/dist/cli/doctor.js.map +1 -1
  103. package/dist/cli/index.d.ts +13 -5
  104. package/dist/cli/index.d.ts.map +1 -1
  105. package/dist/cli/index.js +351 -60
  106. package/dist/cli/index.js.map +1 -1
  107. package/dist/cli/list.d.ts +2 -0
  108. package/dist/cli/list.d.ts.map +1 -0
  109. package/dist/cli/list.js +40 -0
  110. package/dist/cli/list.js.map +1 -0
  111. package/dist/cli/mcp-serve.d.ts +12 -0
  112. package/dist/cli/mcp-serve.d.ts.map +1 -0
  113. package/dist/cli/mcp-serve.js +63 -0
  114. package/dist/cli/mcp-serve.js.map +1 -0
  115. package/dist/cli/plugin-marketplace.d.ts +13 -0
  116. package/dist/cli/plugin-marketplace.d.ts.map +1 -0
  117. package/dist/cli/plugin-marketplace.js +77 -0
  118. package/dist/cli/plugin-marketplace.js.map +1 -0
  119. package/dist/cli/question.d.ts.map +1 -1
  120. package/dist/cli/question.js +27 -2
  121. package/dist/cli/question.js.map +1 -1
  122. package/dist/cli/setup-preferences.d.ts +20 -0
  123. package/dist/cli/setup-preferences.d.ts.map +1 -0
  124. package/dist/cli/setup-preferences.js +71 -0
  125. package/dist/cli/setup-preferences.js.map +1 -0
  126. package/dist/cli/setup.d.ts +12 -3
  127. package/dist/cli/setup.d.ts.map +1 -1
  128. package/dist/cli/setup.js +1080 -254
  129. package/dist/cli/setup.js.map +1 -1
  130. package/dist/cli/team.d.ts +1 -0
  131. package/dist/cli/team.d.ts.map +1 -1
  132. package/dist/cli/team.js +22 -3
  133. package/dist/cli/team.js.map +1 -1
  134. package/dist/cli/uninstall.d.ts.map +1 -1
  135. package/dist/cli/uninstall.js +2 -8
  136. package/dist/cli/uninstall.js.map +1 -1
  137. package/dist/config/__tests__/generator-idempotent.test.js +100 -3
  138. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  139. package/dist/config/__tests__/generator-notify.test.js +6 -5
  140. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  141. package/dist/config/__tests__/generator-status-line-presets.test.d.ts +2 -0
  142. package/dist/config/__tests__/generator-status-line-presets.test.d.ts.map +1 -0
  143. package/dist/config/__tests__/generator-status-line-presets.test.js +203 -0
  144. package/dist/config/__tests__/generator-status-line-presets.test.js.map +1 -0
  145. package/dist/config/__tests__/models.test.js +35 -2
  146. package/dist/config/__tests__/models.test.js.map +1 -1
  147. package/dist/config/codex-hooks.d.ts.map +1 -1
  148. package/dist/config/codex-hooks.js +2 -7
  149. package/dist/config/codex-hooks.js.map +1 -1
  150. package/dist/config/generator.d.ts +10 -0
  151. package/dist/config/generator.d.ts.map +1 -1
  152. package/dist/config/generator.js +259 -76
  153. package/dist/config/generator.js.map +1 -1
  154. package/dist/config/models.d.ts +13 -3
  155. package/dist/config/models.d.ts.map +1 -1
  156. package/dist/config/models.js +25 -5
  157. package/dist/config/models.js.map +1 -1
  158. package/dist/config/omx-first-party-mcp.d.ts +18 -0
  159. package/dist/config/omx-first-party-mcp.d.ts.map +1 -0
  160. package/dist/config/omx-first-party-mcp.js +76 -0
  161. package/dist/config/omx-first-party-mcp.js.map +1 -0
  162. package/dist/document-refresh/__tests__/enforcer.test.d.ts +2 -0
  163. package/dist/document-refresh/__tests__/enforcer.test.d.ts.map +1 -0
  164. package/dist/document-refresh/__tests__/enforcer.test.js +128 -0
  165. package/dist/document-refresh/__tests__/enforcer.test.js.map +1 -0
  166. package/dist/document-refresh/config.d.ts +9 -0
  167. package/dist/document-refresh/config.d.ts.map +1 -0
  168. package/dist/document-refresh/config.js +70 -0
  169. package/dist/document-refresh/config.js.map +1 -0
  170. package/dist/document-refresh/enforcer.d.ts +43 -0
  171. package/dist/document-refresh/enforcer.d.ts.map +1 -0
  172. package/dist/document-refresh/enforcer.js +329 -0
  173. package/dist/document-refresh/enforcer.js.map +1 -0
  174. package/dist/exec/followup.d.ts +44 -0
  175. package/dist/exec/followup.d.ts.map +1 -0
  176. package/dist/exec/followup.js +349 -0
  177. package/dist/exec/followup.js.map +1 -0
  178. package/dist/hooks/__tests__/codebase-map.test.js +63 -1
  179. package/dist/hooks/__tests__/codebase-map.test.js.map +1 -1
  180. package/dist/hooks/__tests__/deep-interview-contract.test.js +10 -7
  181. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  182. package/dist/hooks/__tests__/keyword-detector.test.js +89 -0
  183. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  184. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +84 -1
  185. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  186. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +23 -2
  187. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  188. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +43 -0
  189. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  190. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +17 -0
  191. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
  192. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +53 -0
  193. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  194. package/dist/hooks/__tests__/notify-hook-regression-205.test.js +4 -4
  195. package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
  196. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +103 -0
  197. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  198. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +27 -13
  199. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  200. package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.d.ts +2 -0
  201. package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.d.ts.map +1 -0
  202. package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.js +35 -0
  203. package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.js.map +1 -0
  204. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +70 -3
  205. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  206. package/dist/hooks/__tests__/pre-context-gate-skills.test.js +5 -0
  207. package/dist/hooks/__tests__/pre-context-gate-skills.test.js.map +1 -1
  208. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js +3 -2
  209. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js.map +1 -1
  210. package/dist/hooks/__tests__/prompt-refactor-contract.test.d.ts +2 -0
  211. package/dist/hooks/__tests__/prompt-refactor-contract.test.d.ts.map +1 -0
  212. package/dist/hooks/__tests__/prompt-refactor-contract.test.js +22 -0
  213. package/dist/hooks/__tests__/prompt-refactor-contract.test.js.map +1 -0
  214. package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.d.ts +2 -0
  215. package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.d.ts.map +1 -0
  216. package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.js +28 -0
  217. package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.js.map +1 -0
  218. package/dist/hooks/__tests__/triage-heuristic.test.js +59 -4
  219. package/dist/hooks/__tests__/triage-heuristic.test.js.map +1 -1
  220. package/dist/hooks/__tests__/visual-ralph-skill.test.d.ts +2 -0
  221. package/dist/hooks/__tests__/visual-ralph-skill.test.d.ts.map +1 -0
  222. package/dist/hooks/__tests__/visual-ralph-skill.test.js +44 -0
  223. package/dist/hooks/__tests__/visual-ralph-skill.test.js.map +1 -0
  224. package/dist/hooks/codebase-map.d.ts.map +1 -1
  225. package/dist/hooks/codebase-map.js +83 -6
  226. package/dist/hooks/codebase-map.js.map +1 -1
  227. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  228. package/dist/hooks/keyword-detector.js +19 -14
  229. package/dist/hooks/keyword-detector.js.map +1 -1
  230. package/dist/hooks/prompt-guidance-contract.d.ts +6 -0
  231. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  232. package/dist/hooks/prompt-guidance-contract.js +89 -0
  233. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  234. package/dist/hooks/session.d.ts +2 -0
  235. package/dist/hooks/session.d.ts.map +1 -1
  236. package/dist/hooks/session.js +6 -0
  237. package/dist/hooks/session.js.map +1 -1
  238. package/dist/hooks/triage-heuristic.d.ts +2 -2
  239. package/dist/hooks/triage-heuristic.d.ts.map +1 -1
  240. package/dist/hooks/triage-heuristic.js +97 -20
  241. package/dist/hooks/triage-heuristic.js.map +1 -1
  242. package/dist/hooks/triage-state.d.ts +1 -1
  243. package/dist/hooks/triage-state.d.ts.map +1 -1
  244. package/dist/hooks/triage-state.js +2 -1
  245. package/dist/hooks/triage-state.js.map +1 -1
  246. package/dist/hud/__tests__/index.test.js +4 -4
  247. package/dist/hud/__tests__/index.test.js.map +1 -1
  248. package/dist/hud/__tests__/state.test.js +4 -0
  249. package/dist/hud/__tests__/state.test.js.map +1 -1
  250. package/dist/hud/__tests__/types.test.js +27 -0
  251. package/dist/hud/__tests__/types.test.js.map +1 -1
  252. package/dist/hud/state.d.ts.map +1 -1
  253. package/dist/hud/state.js +8 -0
  254. package/dist/hud/state.js.map +1 -1
  255. package/dist/hud/types.d.ts +9 -0
  256. package/dist/hud/types.d.ts.map +1 -1
  257. package/dist/hud/types.js +3 -0
  258. package/dist/hud/types.js.map +1 -1
  259. package/dist/mcp/__tests__/bootstrap.test.js +45 -5
  260. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  261. package/dist/mcp/__tests__/server-lifecycle.test.js +50 -7
  262. package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
  263. package/dist/mcp/__tests__/state-server.test.js +67 -9
  264. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  265. package/dist/mcp/bootstrap.d.ts +12 -1
  266. package/dist/mcp/bootstrap.d.ts.map +1 -1
  267. package/dist/mcp/bootstrap.js +87 -29
  268. package/dist/mcp/bootstrap.js.map +1 -1
  269. package/dist/mcp/state-server.d.ts +5 -11
  270. package/dist/mcp/state-server.d.ts.map +1 -1
  271. package/dist/mcp/state-server.js +16 -432
  272. package/dist/mcp/state-server.js.map +1 -1
  273. package/dist/planning/__tests__/artifacts.test.js +152 -1
  274. package/dist/planning/__tests__/artifacts.test.js.map +1 -1
  275. package/dist/planning/artifacts.d.ts +9 -0
  276. package/dist/planning/artifacts.d.ts.map +1 -1
  277. package/dist/planning/artifacts.js +60 -1
  278. package/dist/planning/artifacts.js.map +1 -1
  279. package/dist/question/__tests__/deep-interview.test.js +1 -1
  280. package/dist/question/__tests__/deep-interview.test.js.map +1 -1
  281. package/dist/question/__tests__/renderer.test.js +70 -0
  282. package/dist/question/__tests__/renderer.test.js.map +1 -1
  283. package/dist/question/__tests__/ui.test.js +49 -0
  284. package/dist/question/__tests__/ui.test.js.map +1 -1
  285. package/dist/question/renderer.d.ts +14 -1
  286. package/dist/question/renderer.d.ts.map +1 -1
  287. package/dist/question/renderer.js +109 -10
  288. package/dist/question/renderer.js.map +1 -1
  289. package/dist/question/state.d.ts +2 -0
  290. package/dist/question/state.d.ts.map +1 -1
  291. package/dist/question/state.js +4 -0
  292. package/dist/question/state.js.map +1 -1
  293. package/dist/question/types.d.ts +2 -1
  294. package/dist/question/types.d.ts.map +1 -1
  295. package/dist/question/types.js.map +1 -1
  296. package/dist/question/ui.d.ts.map +1 -1
  297. package/dist/question/ui.js +28 -8
  298. package/dist/question/ui.js.map +1 -1
  299. package/dist/runtime/__tests__/bridge.test.js +143 -1
  300. package/dist/runtime/__tests__/bridge.test.js.map +1 -1
  301. package/dist/runtime/bridge.d.ts +21 -0
  302. package/dist/runtime/bridge.d.ts.map +1 -1
  303. package/dist/runtime/bridge.js +54 -4
  304. package/dist/runtime/bridge.js.map +1 -1
  305. package/dist/scripts/__tests__/codex-native-hook.test.js +1904 -230
  306. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  307. package/dist/scripts/__tests__/generate-release-body.test.js +90 -1
  308. package/dist/scripts/__tests__/generate-release-body.test.js.map +1 -1
  309. package/dist/scripts/__tests__/hook-derived-watcher.test.js +110 -2
  310. package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -1
  311. package/dist/scripts/__tests__/postinstall.test.js +7 -93
  312. package/dist/scripts/__tests__/postinstall.test.js.map +1 -1
  313. package/dist/scripts/__tests__/prompt-inventory.test.d.ts +2 -0
  314. package/dist/scripts/__tests__/prompt-inventory.test.d.ts.map +1 -0
  315. package/dist/scripts/__tests__/prompt-inventory.test.js +56 -0
  316. package/dist/scripts/__tests__/prompt-inventory.test.js.map +1 -0
  317. package/dist/scripts/__tests__/run-test-files.test.d.ts +2 -0
  318. package/dist/scripts/__tests__/run-test-files.test.d.ts.map +1 -0
  319. package/dist/scripts/__tests__/run-test-files.test.js +62 -0
  320. package/dist/scripts/__tests__/run-test-files.test.js.map +1 -0
  321. package/dist/scripts/__tests__/smoke-packed-install.test.js +13 -1
  322. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  323. package/dist/scripts/__tests__/verify-native-agents.test.d.ts +2 -0
  324. package/dist/scripts/__tests__/verify-native-agents.test.d.ts.map +1 -0
  325. package/dist/scripts/__tests__/verify-native-agents.test.js +166 -0
  326. package/dist/scripts/__tests__/verify-native-agents.test.js.map +1 -0
  327. package/dist/scripts/codex-execution-surface.d.ts +16 -0
  328. package/dist/scripts/codex-execution-surface.d.ts.map +1 -0
  329. package/dist/scripts/codex-execution-surface.js +42 -0
  330. package/dist/scripts/codex-execution-surface.js.map +1 -0
  331. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  332. package/dist/scripts/codex-native-hook.js +418 -76
  333. package/dist/scripts/codex-native-hook.js.map +1 -1
  334. package/dist/scripts/codex-native-pre-post.d.ts +8 -0
  335. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  336. package/dist/scripts/codex-native-pre-post.js +133 -19
  337. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  338. package/dist/scripts/generate-release-body.d.ts +1 -0
  339. package/dist/scripts/generate-release-body.d.ts.map +1 -1
  340. package/dist/scripts/generate-release-body.js +32 -3
  341. package/dist/scripts/generate-release-body.js.map +1 -1
  342. package/dist/scripts/hook-derived-watcher.js +46 -7
  343. package/dist/scripts/hook-derived-watcher.js.map +1 -1
  344. package/dist/scripts/notify-fallback-watcher.js +59 -9
  345. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  346. package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.d.ts +2 -0
  347. package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.d.ts.map +1 -0
  348. package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.js +153 -0
  349. package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.js.map +1 -0
  350. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  351. package/dist/scripts/notify-hook/managed-tmux.js +18 -1
  352. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  353. package/dist/scripts/notify-hook/process-runner.d.ts.map +1 -1
  354. package/dist/scripts/notify-hook/process-runner.js +7 -3
  355. package/dist/scripts/notify-hook/process-runner.js.map +1 -1
  356. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  357. package/dist/scripts/notify-hook/team-dispatch.js +96 -11
  358. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  359. package/dist/scripts/notify-hook/team-tmux-guard.js +3 -3
  360. package/dist/scripts/notify-hook/team-worker-posttooluse.d.ts +34 -0
  361. package/dist/scripts/notify-hook/team-worker-posttooluse.d.ts.map +1 -0
  362. package/dist/scripts/notify-hook/team-worker-posttooluse.js +434 -0
  363. package/dist/scripts/notify-hook/team-worker-posttooluse.js.map +1 -0
  364. package/dist/scripts/notify-hook/team-worker.d.ts +1 -1
  365. package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
  366. package/dist/scripts/notify-hook/team-worker.js +3 -43
  367. package/dist/scripts/notify-hook/team-worker.js.map +1 -1
  368. package/dist/scripts/notify-hook.js +36 -5
  369. package/dist/scripts/notify-hook.js.map +1 -1
  370. package/dist/scripts/postinstall.d.ts +1 -5
  371. package/dist/scripts/postinstall.d.ts.map +1 -1
  372. package/dist/scripts/postinstall.js +3 -42
  373. package/dist/scripts/postinstall.js.map +1 -1
  374. package/dist/scripts/prompt-inventory.d.ts +29 -0
  375. package/dist/scripts/prompt-inventory.d.ts.map +1 -0
  376. package/dist/scripts/prompt-inventory.js +178 -0
  377. package/dist/scripts/prompt-inventory.js.map +1 -0
  378. package/dist/scripts/run-test-files.js +32 -2
  379. package/dist/scripts/run-test-files.js.map +1 -1
  380. package/dist/scripts/smoke-packed-install.d.ts +3 -0
  381. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  382. package/dist/scripts/smoke-packed-install.js +9 -1
  383. package/dist/scripts/smoke-packed-install.js.map +1 -1
  384. package/dist/scripts/sync-plugin-mirror.d.ts +13 -0
  385. package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -0
  386. package/dist/scripts/sync-plugin-mirror.js +242 -0
  387. package/dist/scripts/sync-plugin-mirror.js.map +1 -0
  388. package/dist/scripts/verify-native-agents.d.ts +16 -0
  389. package/dist/scripts/verify-native-agents.d.ts.map +1 -0
  390. package/dist/scripts/verify-native-agents.js +188 -0
  391. package/dist/scripts/verify-native-agents.js.map +1 -0
  392. package/dist/session-history/search.d.ts.map +1 -1
  393. package/dist/session-history/search.js +7 -2
  394. package/dist/session-history/search.js.map +1 -1
  395. package/dist/sidecar/__tests__/boundary.test.d.ts +2 -0
  396. package/dist/sidecar/__tests__/boundary.test.d.ts.map +1 -0
  397. package/dist/sidecar/__tests__/boundary.test.js +48 -0
  398. package/dist/sidecar/__tests__/boundary.test.js.map +1 -0
  399. package/dist/sidecar/__tests__/collector.test.d.ts +2 -0
  400. package/dist/sidecar/__tests__/collector.test.d.ts.map +1 -0
  401. package/dist/sidecar/__tests__/collector.test.js +162 -0
  402. package/dist/sidecar/__tests__/collector.test.js.map +1 -0
  403. package/dist/sidecar/__tests__/render.test.d.ts +2 -0
  404. package/dist/sidecar/__tests__/render.test.d.ts.map +1 -0
  405. package/dist/sidecar/__tests__/render.test.js +67 -0
  406. package/dist/sidecar/__tests__/render.test.js.map +1 -0
  407. package/dist/sidecar/__tests__/tmux.test.d.ts +2 -0
  408. package/dist/sidecar/__tests__/tmux.test.d.ts.map +1 -0
  409. package/dist/sidecar/__tests__/tmux.test.js +30 -0
  410. package/dist/sidecar/__tests__/tmux.test.js.map +1 -0
  411. package/dist/sidecar/__tests__/watch.test.d.ts +2 -0
  412. package/dist/sidecar/__tests__/watch.test.d.ts.map +1 -0
  413. package/dist/sidecar/__tests__/watch.test.js +42 -0
  414. package/dist/sidecar/__tests__/watch.test.js.map +1 -0
  415. package/dist/sidecar/collector.d.ts +4 -0
  416. package/dist/sidecar/collector.d.ts.map +1 -0
  417. package/dist/sidecar/collector.js +377 -0
  418. package/dist/sidecar/collector.js.map +1 -0
  419. package/dist/sidecar/index.d.ts +25 -0
  420. package/dist/sidecar/index.d.ts.map +1 -0
  421. package/dist/sidecar/index.js +165 -0
  422. package/dist/sidecar/index.js.map +1 -0
  423. package/dist/sidecar/render.d.ts +3 -0
  424. package/dist/sidecar/render.d.ts.map +1 -0
  425. package/dist/sidecar/render.js +72 -0
  426. package/dist/sidecar/render.js.map +1 -0
  427. package/dist/sidecar/tmux.d.ts +13 -0
  428. package/dist/sidecar/tmux.d.ts.map +1 -0
  429. package/dist/sidecar/tmux.js +44 -0
  430. package/dist/sidecar/tmux.js.map +1 -0
  431. package/dist/sidecar/types.d.ts +125 -0
  432. package/dist/sidecar/types.d.ts.map +1 -0
  433. package/dist/sidecar/types.js +2 -0
  434. package/dist/sidecar/types.js.map +1 -0
  435. package/dist/state/__tests__/operations.test.js +50 -22
  436. package/dist/state/__tests__/operations.test.js.map +1 -1
  437. package/dist/state/operations.d.ts +1 -1
  438. package/dist/state/operations.d.ts.map +1 -1
  439. package/dist/state/operations.js +19 -7
  440. package/dist/state/operations.js.map +1 -1
  441. package/dist/team/__tests__/commit-hygiene.test.d.ts +2 -0
  442. package/dist/team/__tests__/commit-hygiene.test.d.ts.map +1 -0
  443. package/dist/team/__tests__/commit-hygiene.test.js +93 -0
  444. package/dist/team/__tests__/commit-hygiene.test.js.map +1 -0
  445. package/dist/team/__tests__/delegation-policy.test.d.ts +2 -0
  446. package/dist/team/__tests__/delegation-policy.test.d.ts.map +1 -0
  447. package/dist/team/__tests__/delegation-policy.test.js +69 -0
  448. package/dist/team/__tests__/delegation-policy.test.js.map +1 -0
  449. package/dist/team/__tests__/events.test.js +54 -4
  450. package/dist/team/__tests__/events.test.js.map +1 -1
  451. package/dist/team/__tests__/hook-primary-e2e-contract.test.d.ts +2 -0
  452. package/dist/team/__tests__/hook-primary-e2e-contract.test.d.ts.map +1 -0
  453. package/dist/team/__tests__/hook-primary-e2e-contract.test.js +78 -0
  454. package/dist/team/__tests__/hook-primary-e2e-contract.test.js.map +1 -0
  455. package/dist/team/__tests__/model-contract.test.js +18 -2
  456. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  457. package/dist/team/__tests__/repo-aware-decomposition.test.d.ts +2 -0
  458. package/dist/team/__tests__/repo-aware-decomposition.test.d.ts.map +1 -0
  459. package/dist/team/__tests__/repo-aware-decomposition.test.js +95 -0
  460. package/dist/team/__tests__/repo-aware-decomposition.test.js.map +1 -0
  461. package/dist/team/__tests__/runtime.test.js +677 -17
  462. package/dist/team/__tests__/runtime.test.js.map +1 -1
  463. package/dist/team/__tests__/state-root.test.js +177 -1
  464. package/dist/team/__tests__/state-root.test.js.map +1 -1
  465. package/dist/team/__tests__/state.test.js +110 -0
  466. package/dist/team/__tests__/state.test.js.map +1 -1
  467. package/dist/team/__tests__/tmux-session.test.js +301 -2
  468. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  469. package/dist/team/__tests__/worker-bootstrap.test.js +94 -0
  470. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  471. package/dist/team/commit-hygiene.d.ts +22 -3
  472. package/dist/team/commit-hygiene.d.ts.map +1 -1
  473. package/dist/team/commit-hygiene.js +134 -2
  474. package/dist/team/commit-hygiene.js.map +1 -1
  475. package/dist/team/contracts.d.ts +1 -1
  476. package/dist/team/contracts.d.ts.map +1 -1
  477. package/dist/team/contracts.js +2 -0
  478. package/dist/team/contracts.js.map +1 -1
  479. package/dist/team/dag-schema.d.ts +38 -0
  480. package/dist/team/dag-schema.d.ts.map +1 -0
  481. package/dist/team/dag-schema.js +221 -0
  482. package/dist/team/dag-schema.js.map +1 -0
  483. package/dist/team/delegation-policy.d.ts +3 -0
  484. package/dist/team/delegation-policy.d.ts.map +1 -0
  485. package/dist/team/delegation-policy.js +82 -0
  486. package/dist/team/delegation-policy.js.map +1 -0
  487. package/dist/team/model-contract.d.ts +3 -1
  488. package/dist/team/model-contract.d.ts.map +1 -1
  489. package/dist/team/model-contract.js +44 -5
  490. package/dist/team/model-contract.js.map +1 -1
  491. package/dist/team/repo-aware-decomposition.d.ts +60 -0
  492. package/dist/team/repo-aware-decomposition.d.ts.map +1 -0
  493. package/dist/team/repo-aware-decomposition.js +229 -0
  494. package/dist/team/repo-aware-decomposition.js.map +1 -0
  495. package/dist/team/runtime.d.ts +34 -0
  496. package/dist/team/runtime.d.ts.map +1 -1
  497. package/dist/team/runtime.js +191 -50
  498. package/dist/team/runtime.js.map +1 -1
  499. package/dist/team/state/tasks.d.ts.map +1 -1
  500. package/dist/team/state/tasks.js +33 -0
  501. package/dist/team/state/tasks.js.map +1 -1
  502. package/dist/team/state/types.d.ts +23 -1
  503. package/dist/team/state/types.d.ts.map +1 -1
  504. package/dist/team/state/types.js.map +1 -1
  505. package/dist/team/state-root.d.ts +35 -0
  506. package/dist/team/state-root.d.ts.map +1 -1
  507. package/dist/team/state-root.js +281 -1
  508. package/dist/team/state-root.js.map +1 -1
  509. package/dist/team/state.d.ts +27 -1
  510. package/dist/team/state.d.ts.map +1 -1
  511. package/dist/team/state.js +6 -0
  512. package/dist/team/state.js.map +1 -1
  513. package/dist/team/tmux-session.d.ts +1 -0
  514. package/dist/team/tmux-session.d.ts.map +1 -1
  515. package/dist/team/tmux-session.js +83 -6
  516. package/dist/team/tmux-session.js.map +1 -1
  517. package/dist/team/worker-bootstrap.d.ts +3 -0
  518. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  519. package/dist/team/worker-bootstrap.js +77 -4
  520. package/dist/team/worker-bootstrap.js.map +1 -1
  521. package/dist/utils/__tests__/agents-model-table.test.js +8 -0
  522. package/dist/utils/__tests__/agents-model-table.test.js.map +1 -1
  523. package/dist/utils/__tests__/paths.test.js +42 -9
  524. package/dist/utils/__tests__/paths.test.js.map +1 -1
  525. package/dist/utils/agents-md.d.ts +3 -0
  526. package/dist/utils/agents-md.d.ts.map +1 -1
  527. package/dist/utils/agents-md.js +25 -0
  528. package/dist/utils/agents-md.js.map +1 -1
  529. package/dist/utils/agents-model-table.d.ts.map +1 -1
  530. package/dist/utils/agents-model-table.js +2 -3
  531. package/dist/utils/agents-model-table.js.map +1 -1
  532. package/package.json +14 -7
  533. package/plugins/oh-my-codex/.app.json +3 -0
  534. package/plugins/oh-my-codex/.codex-plugin/plugin.json +30 -0
  535. package/plugins/oh-my-codex/.mcp.json +44 -0
  536. package/plugins/oh-my-codex/skills/ai-slop-cleaner/SKILL.md +114 -0
  537. package/plugins/oh-my-codex/skills/analyze/SKILL.md +148 -0
  538. package/plugins/oh-my-codex/skills/ask-claude/SKILL.md +61 -0
  539. package/plugins/oh-my-codex/skills/ask-gemini/SKILL.md +61 -0
  540. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +231 -0
  541. package/plugins/oh-my-codex/skills/autoresearch/SKILL.md +68 -0
  542. package/plugins/oh-my-codex/skills/cancel/SKILL.md +399 -0
  543. package/plugins/oh-my-codex/skills/code-review/SKILL.md +290 -0
  544. package/plugins/oh-my-codex/skills/configure-notifications/SKILL.md +287 -0
  545. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +464 -0
  546. package/plugins/oh-my-codex/skills/doctor/SKILL.md +239 -0
  547. package/plugins/oh-my-codex/skills/help/SKILL.md +202 -0
  548. package/plugins/oh-my-codex/skills/hud/SKILL.md +98 -0
  549. package/plugins/oh-my-codex/skills/note/SKILL.md +62 -0
  550. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +135 -0
  551. package/plugins/oh-my-codex/skills/pipeline/SKILL.md +86 -0
  552. package/plugins/oh-my-codex/skills/plan/SKILL.md +276 -0
  553. package/plugins/oh-my-codex/skills/ralph/SKILL.md +269 -0
  554. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +162 -0
  555. package/plugins/oh-my-codex/skills/security-review/SKILL.md +300 -0
  556. package/plugins/oh-my-codex/skills/skill/SKILL.md +835 -0
  557. package/plugins/oh-my-codex/skills/team/SKILL.md +510 -0
  558. package/plugins/oh-my-codex/skills/trace/SKILL.md +33 -0
  559. package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +143 -0
  560. package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +175 -0
  561. package/plugins/oh-my-codex/skills/visual-ralph/SKILL.md +153 -0
  562. package/plugins/oh-my-codex/skills/visual-verdict/SKILL.md +76 -0
  563. package/plugins/oh-my-codex/skills/wiki/SKILL.md +57 -0
  564. package/plugins/oh-my-codex/skills/worker/SKILL.md +106 -0
  565. package/prompts/critic.md +35 -83
  566. package/prompts/executor.md +32 -109
  567. package/prompts/explore-harness.md +1 -1
  568. package/prompts/explore.md +37 -90
  569. package/prompts/planner.md +28 -58
  570. package/prompts/researcher.md +46 -78
  571. package/prompts/verifier.md +23 -31
  572. package/skills/autopilot/SKILL.md +1 -4
  573. package/skills/deep-interview/SKILL.md +8 -5
  574. package/skills/doctor/SKILL.md +45 -17
  575. package/skills/help/SKILL.md +2 -2
  576. package/skills/omx-setup/SKILL.md +52 -9
  577. package/skills/plan/SKILL.md +1 -4
  578. package/skills/ralph/SKILL.md +3 -5
  579. package/skills/ralplan/SKILL.md +2 -6
  580. package/skills/team/SKILL.md +2 -5
  581. package/skills/ultraqa/SKILL.md +1 -4
  582. package/skills/ultrawork/SKILL.md +2 -3
  583. package/skills/visual-ralph/SKILL.md +153 -0
  584. package/skills/web-clone/SKILL.md +12 -354
  585. package/src/scripts/__tests__/codex-native-hook.test.ts +2306 -224
  586. package/src/scripts/__tests__/generate-release-body.test.ts +109 -0
  587. package/src/scripts/__tests__/hook-derived-watcher.test.ts +132 -2
  588. package/src/scripts/__tests__/postinstall.test.ts +7 -104
  589. package/src/scripts/__tests__/prompt-inventory.test.ts +64 -0
  590. package/src/scripts/__tests__/run-test-files.test.ts +72 -0
  591. package/src/scripts/__tests__/smoke-packed-install.test.ts +15 -0
  592. package/src/scripts/__tests__/verify-native-agents.test.ts +204 -0
  593. package/src/scripts/codex-execution-surface.ts +73 -0
  594. package/src/scripts/codex-native-hook.ts +579 -82
  595. package/src/scripts/codex-native-pre-post.ts +157 -19
  596. package/src/scripts/generate-release-body.ts +34 -2
  597. package/src/scripts/hook-derived-watcher.ts +51 -8
  598. package/src/scripts/notify-fallback-watcher.ts +65 -9
  599. package/src/scripts/notify-hook/__tests__/team-worker-posttooluse.test.ts +180 -0
  600. package/src/scripts/notify-hook/managed-tmux.ts +22 -4
  601. package/src/scripts/notify-hook/process-runner.ts +7 -3
  602. package/src/scripts/notify-hook/team-dispatch.ts +103 -11
  603. package/src/scripts/notify-hook/team-tmux-guard.ts +3 -3
  604. package/src/scripts/notify-hook/team-worker-posttooluse.ts +536 -0
  605. package/src/scripts/notify-hook/team-worker.ts +4 -48
  606. package/src/scripts/notify-hook.ts +36 -5
  607. package/src/scripts/postinstall.ts +4 -58
  608. package/src/scripts/prompt-inventory.ts +218 -0
  609. package/src/scripts/run-test-files.ts +41 -2
  610. package/src/scripts/smoke-packed-install.ts +10 -1
  611. package/src/scripts/sync-plugin-mirror.ts +360 -0
  612. package/src/scripts/verify-native-agents.ts +245 -0
  613. package/templates/AGENTS.md +26 -91
  614. package/templates/catalog-manifest.json +16 -1
@@ -6,8 +6,8 @@ import { join } from 'path';
6
6
  import { tmpdir } from 'os';
7
7
  import { existsSync } from 'fs';
8
8
  import { HUD_TMUX_TEAM_HEIGHT_LINES } from '../../hud/constants.js';
9
- import { initTeamState, createTask, readTeamConfig, saveTeamConfig, listMailboxMessages, listDispatchRequests, transitionDispatchRequest, updateWorkerHeartbeat, writeAtomic, readTask, readMonitorSnapshot, claimTask, transitionTaskStatus, readWorkerStatus, writeWorkerStatus, } from '../state.js';
10
- import { monitorTeam, shutdownTeam, resumeTeam, startTeam, assignTask, sendWorkerMessage, applyCreatedInteractiveSessionToConfig, resolveWorkerLaunchArgsFromEnv, shouldPrekillInteractiveShutdownProcessTrees, waitForWorkerStartupEvidence, waitForClaudeStartupEvidence, } from '../runtime.js';
9
+ import { initTeamState, createTask, writeWorkerInbox, readTeamConfig, saveTeamConfig, listMailboxMessages, listDispatchRequests, transitionDispatchRequest, updateWorkerHeartbeat, writeAtomic, readTask, readMonitorSnapshot, claimTask, transitionTaskStatus, readWorkerStatus, writeWorkerStatus, } from '../state.js';
10
+ import { monitorTeam, shutdownTeam, resumeTeam, startTeam, assignTask, sendWorkerMessage, applyCreatedInteractiveSessionToConfig, resolveWorkerLaunchArgsFromEnv, shouldPrekillInteractiveShutdownProcessTrees, waitForWorkerStartupEvidence, waitForClaudeStartupEvidence, cleanupTeamWorkerLaunchOrphanedMcpProcesses, settleStartupAttemptResults, } from '../runtime.js';
11
11
  import { resolveAgentReasoningEffort, resolveTeamLowComplexityDefaultModel } from '../model-contract.js';
12
12
  import { readTeamEvents } from '../state/events.js';
13
13
  import { sanitizeTeamName } from '../tmux-session.js';
@@ -56,6 +56,21 @@ async function readTeamDeliveryLog(cwd) {
56
56
  .filter(Boolean)
57
57
  .map((line) => JSON.parse(line));
58
58
  }
59
+ async function markPendingInboxDispatchesDelivered(teamName, cwd, opts = {}) {
60
+ const requests = await listDispatchRequests(teamName, cwd, { kind: 'inbox' }).catch(() => []);
61
+ for (const request of requests) {
62
+ if (request.status !== 'pending')
63
+ continue;
64
+ if (opts.toWorker && request.to_worker !== opts.toWorker)
65
+ continue;
66
+ await opts.beforeDeliver?.();
67
+ const notified = await transitionDispatchRequest(teamName, request.request_id, 'pending', 'notified', { last_reason: opts.lastReason ?? 'test_delivered_receipt' }, cwd).catch(() => null);
68
+ if (!notified)
69
+ continue;
70
+ await transitionDispatchRequest(teamName, request.request_id, 'notified', 'delivered', { last_reason: opts.lastReason ?? 'test_delivered_receipt' }, cwd).catch(() => { });
71
+ await opts.afterDeliver?.();
72
+ }
73
+ }
59
74
  function withEmptyPath(fn) {
60
75
  const prev = process.env.PATH;
61
76
  process.env.PATH = '';
@@ -517,6 +532,30 @@ describe('runtime', () => {
517
532
  await rm(cwd, { recursive: true, force: true });
518
533
  }
519
534
  });
535
+ it('cleanupTeamWorkerLaunchOrphanedMcpProcesses keeps ps and cleanup failures non-fatal', async () => {
536
+ const warnings = [];
537
+ let calls = 0;
538
+ await cleanupTeamWorkerLaunchOrphanedMcpProcesses({
539
+ cleanup: async () => {
540
+ calls += 1;
541
+ throw new Error('ps unavailable');
542
+ },
543
+ writeWarning: (message) => warnings.push(message),
544
+ });
545
+ await cleanupTeamWorkerLaunchOrphanedMcpProcesses({
546
+ cleanup: async () => ({
547
+ dryRun: false,
548
+ candidates: [],
549
+ terminatedCount: 0,
550
+ forceKilledCount: 0,
551
+ failedPids: [1234],
552
+ }),
553
+ writeWarning: (message) => warnings.push(message),
554
+ });
555
+ assert.equal(calls, 1);
556
+ assert.match(warnings.join('\n'), /ps unavailable.*continuing worker launch/);
557
+ assert.match(warnings.join('\n'), /Failed to reap 1 orphaned OMX MCP process/);
558
+ });
520
559
  it('waitForWorkerStartupEvidence treats blocked worker status as settled progress even without a claimed task id', async () => {
521
560
  const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-codex-blocked-startup-'));
522
561
  try {
@@ -785,10 +824,6 @@ sleep 5
785
824
  process.env.OMX_TEAM_WORKER_LAUNCH_MODE = prevLaunchMode;
786
825
  else
787
826
  delete process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
788
- if (typeof prevWorkerCli === 'string')
789
- process.env.OMX_TEAM_WORKER_CLI = prevWorkerCli;
790
- else
791
- delete process.env.OMX_TEAM_WORKER_CLI;
792
827
  await rm(cwd, { recursive: true, force: true });
793
828
  }
794
829
  });
@@ -804,6 +839,7 @@ sleep 5
804
839
  process.env.OMX_TEAM_WORKER_LAUNCH_MODE = prevLaunchMode;
805
840
  else
806
841
  delete process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
842
+ delete process.env.OMX_TEAM_WORKER_CLI;
807
843
  await rm(cwd, { recursive: true, force: true });
808
844
  }
809
845
  });
@@ -932,7 +968,7 @@ esac
932
968
  delete process.env.WSL_DISTRO_NAME;
933
969
  delete process.env.WSL_INTEROP;
934
970
  Object.defineProperty(process, 'platform', { value: 'win32', configurable: true });
935
- runtime = await withoutTeamWorkerEnv(() => startTeam('team-win32-no-env', 'native windows current-client detection', 'executor', 1, [{ subject: 's', description: 'd', owner: 'worker-1' }], cwd));
971
+ const runtime = await withoutTeamWorkerEnv(() => startTeam('team-win32-no-env', 'native windows current-client detection', 'executor', 1, [{ subject: 's', description: 'd', owner: 'worker-1' }], cwd));
936
972
  teamNameForCleanup = runtime.teamName;
937
973
  assert.equal(runtime.config.tmux_session, 'leader:0');
938
974
  assert.equal(runtime.config.leader_pane_id, '%1');
@@ -943,7 +979,6 @@ esac
943
979
  if (teamNameForCleanup) {
944
980
  await shutdownTeam(teamNameForCleanup, cwd, { force: true });
945
981
  }
946
- runtime = null;
947
982
  });
948
983
  }
949
984
  finally {
@@ -1017,6 +1052,115 @@ esac
1017
1052
  await rm(cwd, { recursive: true, force: true });
1018
1053
  }
1019
1054
  });
1055
+ it('startTeam runs worker MCP orphan cleanup before interactive tmux worker spawn', async () => {
1056
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-interactive-mcp-cleanup-'));
1057
+ const prevTmux = process.env.TMUX;
1058
+ const prevTmuxPane = process.env.TMUX_PANE;
1059
+ const prevLaunchMode = process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
1060
+ const prevWorkerCli = process.env.OMX_TEAM_WORKER_CLI;
1061
+ const prevSkipReadyWait = process.env.OMX_TEAM_SKIP_READY_WAIT;
1062
+ let runtime = null;
1063
+ try {
1064
+ await withMockTmuxFixture({
1065
+ dirPrefix: 'omx-runtime-interactive-mcp-cleanup-bin-',
1066
+ tmuxScript: (tmuxLogPath) => `#!/bin/sh
1067
+ set -eu
1068
+ printf '%s\n' "$*" >> "${tmuxLogPath}"
1069
+ case "\${1:-}" in
1070
+ -V)
1071
+ echo "tmux 3.4"
1072
+ exit 0
1073
+ ;;
1074
+ display-message)
1075
+ case "$*" in
1076
+ *"#{window_width}"*) echo "120" ;;
1077
+ *) echo "leader:0 %1" ;;
1078
+ esac
1079
+ exit 0
1080
+ ;;
1081
+ list-panes)
1082
+ case "$*" in
1083
+ *"pane_current_command"*) printf "%%1\tnode\t'codex'\n" ;;
1084
+ *"#{pane_dead} #{pane_pid}"*) echo "1 999999" ;;
1085
+ *"-t %2"*"#{pane_pid}"*) echo "2222" ;;
1086
+ *"#{pane_pid}"*) echo "1111" ;;
1087
+ *) exit 0 ;;
1088
+ esac
1089
+ exit 0
1090
+ ;;
1091
+ split-window)
1092
+ case "$*" in
1093
+ *" -h "*)
1094
+ mkdir -p "${cwd}/.omx/state/team/team-interactive-cleanup/workers/worker-1"
1095
+ cat > "${cwd}/.omx/state/team/team-interactive-cleanup/workers/worker-1/status.json" <<'EOF'
1096
+ {
1097
+ "state": "working",
1098
+ "current_task_id": "1",
1099
+ "updated_at": "2026-04-23T00:00:00.000Z"
1100
+ }
1101
+ EOF
1102
+ echo "%2"
1103
+ ;;
1104
+ *) echo "%3" ;;
1105
+ esac
1106
+ exit 0
1107
+ ;;
1108
+ set-hook|run-shell|select-layout|set-window-option|select-pane|send-keys|kill-pane|kill-session)
1109
+ exit 0
1110
+ ;;
1111
+ *)
1112
+ exit 0
1113
+ ;;
1114
+ esac
1115
+ `,
1116
+ binaries: [{ name: 'codex', content: '#!/bin/sh\nexit 0\n' }],
1117
+ }, async ({ tmuxLogPath }) => {
1118
+ delete process.env.TMUX;
1119
+ process.env.TMUX_PANE = '%1';
1120
+ process.env.OMX_TEAM_WORKER_LAUNCH_MODE = 'interactive';
1121
+ process.env.OMX_TEAM_WORKER_CLI = 'codex';
1122
+ process.env.OMX_TEAM_SKIP_READY_WAIT = '1';
1123
+ const events = [];
1124
+ runtime = await withoutTeamWorkerEnv(() => startTeam('team-interactive-cleanup', 'interactive cleanup before worker spawn', 'executor', 1, [{ subject: 's', description: 'd', owner: 'worker-1' }], cwd, {
1125
+ cleanupLaunchOrphanedMcpProcesses: async () => {
1126
+ events.push('cleanup');
1127
+ return { dryRun: false, candidates: [], terminatedCount: 0, forceKilledCount: 0, failedPids: [] };
1128
+ },
1129
+ }));
1130
+ const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
1131
+ assert.match(tmuxLog, /split-window/);
1132
+ assert.deepEqual(events, ['cleanup']);
1133
+ assert.equal(runtime.config.workers[0]?.pane_id, '%2');
1134
+ });
1135
+ }
1136
+ finally {
1137
+ const runtimeToShutdown = runtime;
1138
+ if (runtimeToShutdown) {
1139
+ await shutdownTeam(runtimeToShutdown.teamName, cwd, { force: true }).catch(() => { });
1140
+ }
1141
+ if (typeof prevTmux === 'string')
1142
+ process.env.TMUX = prevTmux;
1143
+ else
1144
+ delete process.env.TMUX;
1145
+ if (typeof prevTmuxPane === 'string')
1146
+ process.env.TMUX_PANE = prevTmuxPane;
1147
+ else
1148
+ delete process.env.TMUX_PANE;
1149
+ if (typeof prevLaunchMode === 'string')
1150
+ process.env.OMX_TEAM_WORKER_LAUNCH_MODE = prevLaunchMode;
1151
+ else
1152
+ delete process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
1153
+ if (typeof prevWorkerCli === 'string')
1154
+ process.env.OMX_TEAM_WORKER_CLI = prevWorkerCli;
1155
+ else
1156
+ delete process.env.OMX_TEAM_WORKER_CLI;
1157
+ if (typeof prevSkipReadyWait === 'string')
1158
+ process.env.OMX_TEAM_SKIP_READY_WAIT = prevSkipReadyWait;
1159
+ else
1160
+ delete process.env.OMX_TEAM_SKIP_READY_WAIT;
1161
+ await rm(cwd, { recursive: true, force: true });
1162
+ }
1163
+ });
1020
1164
  it('startTeam captures interactive worker pid from the resolved pane id', async () => {
1021
1165
  const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-pane-pid-'));
1022
1166
  const prevTmux = process.env.TMUX;
@@ -1141,11 +1285,11 @@ esac
1141
1285
  await rm(cwd, { recursive: true, force: true });
1142
1286
  }
1143
1287
  });
1144
- it('startTeam saves interactive pane ids before readiness waits in source order', async () => {
1288
+ it('startTeam saves interactive pane ids before concurrent readiness attempts', async () => {
1145
1289
  const source = await readFile(join(process.cwd(), 'src', 'team', 'runtime.ts'), 'utf-8');
1146
1290
  const applyMatch = source.match(/applyCreatedInteractiveSessionToConfig\(\s*config,\s*createdSession,\s*workerPaneIds\s*\);/m);
1147
1291
  const saveMatch = source.match(/await saveTeamConfig\(config, leaderCwd\);/m);
1148
- const readyMatch = source.match(/const ready = waitForWorkerReady\(/m);
1292
+ const readyMatch = source.match(/waitForWorkerReadyAsync\(/m);
1149
1293
  const applyIndex = applyMatch?.index ?? -1;
1150
1294
  const saveIndex = saveMatch?.index ?? -1;
1151
1295
  const readyIndex = readyMatch?.index ?? -1;
@@ -1155,6 +1299,147 @@ esac
1155
1299
  assert.equal(applyIndex < saveIndex, true);
1156
1300
  assert.equal(saveIndex < readyIndex, true);
1157
1301
  });
1302
+ it('startTeam starts worker-2 readiness before delayed worker-1 readiness settles', async () => {
1303
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-parallel-ready-'));
1304
+ const previousTmux = process.env.TMUX;
1305
+ const previousTmuxPane = process.env.TMUX_PANE;
1306
+ const previousLaunchMode = process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
1307
+ const previousWorkerCli = process.env.OMX_TEAM_WORKER_CLI;
1308
+ const previousReadyTimeout = process.env.OMX_TEAM_READY_TIMEOUT_MS;
1309
+ const previousStartupEvidenceTimeout = process.env.OMX_TEAM_STARTUP_EVIDENCE_TIMEOUT_MS;
1310
+ const previousStartupDispatchRetries = process.env.OMX_TEAM_STARTUP_DISPATCH_RETRIES;
1311
+ let receiptDeliverer = null;
1312
+ let runtimeTeamName = null;
1313
+ try {
1314
+ await withMockTmuxFixture({
1315
+ dirPrefix: 'omx-runtime-parallel-ready-bin-',
1316
+ tmuxScript: () => `#!/bin/sh
1317
+ set -eu
1318
+ order_file="${cwd}/ready-order.log"
1319
+ case "$1" in
1320
+ -V)
1321
+ echo "tmux 3.4"
1322
+ exit 0
1323
+ ;;
1324
+ display-message)
1325
+ case "$*" in
1326
+ *"#{window_width}"*) echo "120" ;;
1327
+ *) echo "leader:0 %1" ;;
1328
+ esac
1329
+ exit 0
1330
+ ;;
1331
+ list-panes)
1332
+ case "$*" in
1333
+ *"pane_current_command"*) printf "%%1\tnode\t'codex'\n" ;;
1334
+ *"#{pane_dead} #{pane_pid}"*) echo "0 4242" ;;
1335
+ *"-t %2"*"#{pane_pid}"*) echo "4242" ;;
1336
+ *"-t %3"*"#{pane_pid}"*) echo "4343" ;;
1337
+ *"#{pane_pid}"*) echo "4141" ;;
1338
+ *) exit 0 ;;
1339
+ esac
1340
+ exit 0
1341
+ ;;
1342
+ capture-pane)
1343
+ case "$*" in
1344
+ *"-t %2"*)
1345
+ printf '%s\n' w1-ready-start >> "$order_file"
1346
+ sleep 0.4
1347
+ printf '%s\n' w1-ready-done >> "$order_file"
1348
+ printf 'OpenAI Codex\nmodel: test\n› \n'
1349
+ ;;
1350
+ *"-t %3"*)
1351
+ printf '%s\n' w2-ready-start >> "$order_file"
1352
+ printf 'OpenAI Codex\nmodel: test\n› \n'
1353
+ ;;
1354
+ *)
1355
+ printf 'OpenAI Codex\nmodel: test\n› \n'
1356
+ ;;
1357
+ esac
1358
+ exit 0
1359
+ ;;
1360
+ split-window)
1361
+ count_file="${cwd}/split-window-count"
1362
+ count=0
1363
+ if [ -f "$count_file" ]; then count=$(cat "$count_file"); fi
1364
+ count=$((count + 1))
1365
+ printf '%s' "$count" > "$count_file"
1366
+ case "$count" in
1367
+ 1) echo "%2" ;;
1368
+ 2) echo "%3" ;;
1369
+ *) echo "%4" ;;
1370
+ esac
1371
+ exit 0
1372
+ ;;
1373
+ set-hook|run-shell|select-layout|set-window-option|select-pane|send-keys|kill-pane|kill-session|resize-pane)
1374
+ exit 0
1375
+ ;;
1376
+ *)
1377
+ exit 0
1378
+ ;;
1379
+ esac
1380
+ `,
1381
+ binaries: [{ name: 'codex', content: '#!/usr/bin/env node\nprocess.stdin.resume();\n' }],
1382
+ }, async () => {
1383
+ delete process.env.TMUX;
1384
+ process.env.TMUX_PANE = '%1';
1385
+ process.env.OMX_TEAM_WORKER_LAUNCH_MODE = 'interactive';
1386
+ process.env.OMX_TEAM_WORKER_CLI = 'codex';
1387
+ process.env.OMX_TEAM_READY_TIMEOUT_MS = '2000';
1388
+ process.env.OMX_TEAM_STARTUP_EVIDENCE_TIMEOUT_MS = '50';
1389
+ process.env.OMX_TEAM_STARTUP_DISPATCH_RETRIES = '1';
1390
+ receiptDeliverer = setInterval(() => {
1391
+ void (async () => {
1392
+ await markPendingInboxDispatchesDelivered('team-parallel-ready', cwd);
1393
+ })();
1394
+ }, 20);
1395
+ const runtime = await withoutTeamWorkerEnv(() => startTeam('team-parallel-ready', 'worker-2 readiness should not wait for worker-1 readiness settle', 'executor', 2, [
1396
+ { subject: 'w1', description: 'worker one', owner: 'worker-1' },
1397
+ { subject: 'w2', description: 'worker two', owner: 'worker-2' },
1398
+ ], cwd));
1399
+ runtimeTeamName = runtime.teamName;
1400
+ const order = (await readFile(join(cwd, 'ready-order.log'), 'utf-8')).trim().split('\n');
1401
+ assert.ok(order.includes('w1-ready-start'));
1402
+ assert.ok(order.includes('w2-ready-start'));
1403
+ assert.ok(order.includes('w1-ready-done'));
1404
+ assert.equal(order.indexOf('w2-ready-start') < order.indexOf('w1-ready-done'), true, `expected worker-2 readiness attempt before worker-1 readiness settled, got ${order.join(',')}`);
1405
+ });
1406
+ }
1407
+ finally {
1408
+ if (receiptDeliverer)
1409
+ clearInterval(receiptDeliverer);
1410
+ if (runtimeTeamName)
1411
+ await shutdownTeam(runtimeTeamName, cwd, { force: true }).catch(() => { });
1412
+ if (typeof previousTmux === 'string')
1413
+ process.env.TMUX = previousTmux;
1414
+ else
1415
+ delete process.env.TMUX;
1416
+ if (typeof previousTmuxPane === 'string')
1417
+ process.env.TMUX_PANE = previousTmuxPane;
1418
+ else
1419
+ delete process.env.TMUX_PANE;
1420
+ if (typeof previousLaunchMode === 'string')
1421
+ process.env.OMX_TEAM_WORKER_LAUNCH_MODE = previousLaunchMode;
1422
+ else
1423
+ delete process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
1424
+ if (typeof previousWorkerCli === 'string')
1425
+ process.env.OMX_TEAM_WORKER_CLI = previousWorkerCli;
1426
+ else
1427
+ delete process.env.OMX_TEAM_WORKER_CLI;
1428
+ if (typeof previousReadyTimeout === 'string')
1429
+ process.env.OMX_TEAM_READY_TIMEOUT_MS = previousReadyTimeout;
1430
+ else
1431
+ delete process.env.OMX_TEAM_READY_TIMEOUT_MS;
1432
+ if (typeof previousStartupEvidenceTimeout === 'string')
1433
+ process.env.OMX_TEAM_STARTUP_EVIDENCE_TIMEOUT_MS = previousStartupEvidenceTimeout;
1434
+ else
1435
+ delete process.env.OMX_TEAM_STARTUP_EVIDENCE_TIMEOUT_MS;
1436
+ if (typeof previousStartupDispatchRetries === 'string')
1437
+ process.env.OMX_TEAM_STARTUP_DISPATCH_RETRIES = previousStartupDispatchRetries;
1438
+ else
1439
+ delete process.env.OMX_TEAM_STARTUP_DISPATCH_RETRIES;
1440
+ await rm(cwd, { recursive: true, force: true });
1441
+ }
1442
+ });
1158
1443
  it('startTeam records recoverable startup issues per worker instead of failing launch early when panes stay alive', async () => {
1159
1444
  const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-no-startup-evidence-'));
1160
1445
  const previousTmux = process.env.TMUX;
@@ -1324,6 +1609,164 @@ process.on('SIGTERM', () => process.exit(0));
1324
1609
  await rm(cwd, { recursive: true, force: true });
1325
1610
  }
1326
1611
  });
1612
+ it('startTeam attempts worker-2 before rejecting lowest-index unrecoverable startup failure', async () => {
1613
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-parallel-dead-pane-'));
1614
+ const previousTmux = process.env.TMUX;
1615
+ const previousTmuxPane = process.env.TMUX_PANE;
1616
+ const previousLaunchMode = process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
1617
+ const previousWorkerCli = process.env.OMX_TEAM_WORKER_CLI;
1618
+ const previousReadyTimeout = process.env.OMX_TEAM_READY_TIMEOUT_MS;
1619
+ const previousStartupEvidenceTimeout = process.env.OMX_TEAM_STARTUP_EVIDENCE_TIMEOUT_MS;
1620
+ const previousStartupDispatchRetries = process.env.OMX_TEAM_STARTUP_DISPATCH_RETRIES;
1621
+ let receiptFailer = null;
1622
+ try {
1623
+ await withMockTmuxFixture({
1624
+ dirPrefix: 'omx-runtime-parallel-dead-pane-bin-',
1625
+ tmuxScript: () => `#!/bin/sh
1626
+ set -eu
1627
+ order_file="${cwd}/dead-pane-order.log"
1628
+ case "$1" in
1629
+ -V)
1630
+ echo "tmux 3.4"
1631
+ exit 0
1632
+ ;;
1633
+ display-message)
1634
+ case "$*" in
1635
+ *"#{window_width}"*) echo "120" ;;
1636
+ *) echo "leader:0 %1" ;;
1637
+ esac
1638
+ exit 0
1639
+ ;;
1640
+ list-panes)
1641
+ case "$*" in
1642
+ *"pane_current_command"*) printf "%%1\tnode\t'codex'\n" ;;
1643
+ *"-t %2"*"#{pane_dead} #{pane_pid}"*) echo "1 4242" ;;
1644
+ *"-t %3"*"#{pane_dead} #{pane_pid}"*) echo "0 4343" ;;
1645
+ *"#{pane_dead} #{pane_pid}"*) echo "0 4141" ;;
1646
+ *"-t %2"*"#{pane_pid}"*) echo "4242" ;;
1647
+ *"-t %3"*"#{pane_pid}"*) echo "4343" ;;
1648
+ *"#{pane_pid}"*) echo "4141" ;;
1649
+ *) exit 0 ;;
1650
+ esac
1651
+ exit 0
1652
+ ;;
1653
+ capture-pane)
1654
+ case "$*" in
1655
+ *"-t %2"*) printf '%s\n' w1-ready-start >> "$order_file" ;;
1656
+ *"-t %3"*) printf '%s\n' w2-ready-start >> "$order_file"; printf 'OpenAI Codex\nmodel: test\n› \n' ;;
1657
+ *) printf 'OpenAI Codex\nmodel: test\n› \n' ;;
1658
+ esac
1659
+ exit 0
1660
+ ;;
1661
+ split-window)
1662
+ count_file="${cwd}/split-window-count"
1663
+ count=0
1664
+ if [ -f "$count_file" ]; then count=$(cat "$count_file"); fi
1665
+ count=$((count + 1))
1666
+ printf '%s' "$count" > "$count_file"
1667
+ case "$count" in
1668
+ 1) echo "%2" ;;
1669
+ 2) echo "%3" ;;
1670
+ *) echo "%4" ;;
1671
+ esac
1672
+ exit 0
1673
+ ;;
1674
+ set-hook|run-shell|select-layout|set-window-option|select-pane|send-keys|kill-pane|kill-session|resize-pane)
1675
+ exit 0
1676
+ ;;
1677
+ *)
1678
+ exit 0
1679
+ ;;
1680
+ esac
1681
+ `,
1682
+ binaries: [{ name: 'codex', content: '#!/usr/bin/env node\nprocess.stdin.resume();\n' }],
1683
+ }, async () => {
1684
+ delete process.env.TMUX;
1685
+ process.env.TMUX_PANE = '%1';
1686
+ process.env.OMX_TEAM_WORKER_LAUNCH_MODE = 'interactive';
1687
+ process.env.OMX_TEAM_WORKER_CLI = 'codex';
1688
+ process.env.OMX_TEAM_READY_TIMEOUT_MS = '300';
1689
+ process.env.OMX_TEAM_STARTUP_EVIDENCE_TIMEOUT_MS = '50';
1690
+ process.env.OMX_TEAM_STARTUP_DISPATCH_RETRIES = '1';
1691
+ receiptFailer = setInterval(() => {
1692
+ void (async () => {
1693
+ const requests = await listDispatchRequests('team-parallel-dead-pane', cwd, { kind: 'inbox' }).catch(() => []);
1694
+ for (const request of requests) {
1695
+ if (request.status !== 'pending')
1696
+ continue;
1697
+ await transitionDispatchRequest('team-parallel-dead-pane', request.request_id, 'pending', 'failed', { last_reason: 'test_failed_receipt' }, cwd).catch(() => { });
1698
+ }
1699
+ })();
1700
+ }, 20);
1701
+ await assert.rejects(() => withoutTeamWorkerEnv(() => startTeam('team-parallel-dead-pane', 'worker-2 should be attempted despite worker-1 fatal readiness failure', 'executor', 2, [
1702
+ { subject: 'w1', description: 'worker one', owner: 'worker-1' },
1703
+ { subject: 'w2', description: 'worker two', owner: 'worker-2' },
1704
+ ], cwd)), /Worker worker-1 did not become ready/);
1705
+ const order = (await readFile(join(cwd, 'dead-pane-order.log'), 'utf-8')).trim().split('\n');
1706
+ assert.ok(order.includes('w1-ready-start'));
1707
+ assert.ok(order.includes('w2-ready-start'));
1708
+ });
1709
+ }
1710
+ finally {
1711
+ if (receiptFailer)
1712
+ clearInterval(receiptFailer);
1713
+ if (typeof previousTmux === 'string')
1714
+ process.env.TMUX = previousTmux;
1715
+ else
1716
+ delete process.env.TMUX;
1717
+ if (typeof previousTmuxPane === 'string')
1718
+ process.env.TMUX_PANE = previousTmuxPane;
1719
+ else
1720
+ delete process.env.TMUX_PANE;
1721
+ if (typeof previousLaunchMode === 'string')
1722
+ process.env.OMX_TEAM_WORKER_LAUNCH_MODE = previousLaunchMode;
1723
+ else
1724
+ delete process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
1725
+ if (typeof previousWorkerCli === 'string')
1726
+ process.env.OMX_TEAM_WORKER_CLI = previousWorkerCli;
1727
+ else
1728
+ delete process.env.OMX_TEAM_WORKER_CLI;
1729
+ if (typeof previousReadyTimeout === 'string')
1730
+ process.env.OMX_TEAM_READY_TIMEOUT_MS = previousReadyTimeout;
1731
+ else
1732
+ delete process.env.OMX_TEAM_READY_TIMEOUT_MS;
1733
+ if (typeof previousStartupEvidenceTimeout === 'string')
1734
+ process.env.OMX_TEAM_STARTUP_EVIDENCE_TIMEOUT_MS = previousStartupEvidenceTimeout;
1735
+ else
1736
+ delete process.env.OMX_TEAM_STARTUP_EVIDENCE_TIMEOUT_MS;
1737
+ if (typeof previousStartupDispatchRetries === 'string')
1738
+ process.env.OMX_TEAM_STARTUP_DISPATCH_RETRIES = previousStartupDispatchRetries;
1739
+ else
1740
+ delete process.env.OMX_TEAM_STARTUP_DISPATCH_RETRIES;
1741
+ await rm(cwd, { recursive: true, force: true });
1742
+ }
1743
+ });
1744
+ it('settleStartupAttemptResults waits for sibling startup attempt to settle after worker startup throw', async () => {
1745
+ let worker2Settled = false;
1746
+ const startupAttemptResults = await settleStartupAttemptResults([
1747
+ {
1748
+ workerIndex: 1,
1749
+ workerName: 'worker-1',
1750
+ attempt: Promise.reject(new Error('test_startup_attempt_throw:worker-1')),
1751
+ },
1752
+ {
1753
+ workerIndex: 2,
1754
+ workerName: 'worker-2',
1755
+ attempt: new Promise((resolve) => {
1756
+ setTimeout(() => {
1757
+ worker2Settled = true;
1758
+ resolve({ ok: true, workerIndex: 2, workerName: 'worker-2' });
1759
+ }, 100);
1760
+ }),
1761
+ },
1762
+ ]);
1763
+ assert.equal(worker2Settled, true, 'worker-2 startup attempt should settle before results return');
1764
+ const firstStartupError = startupAttemptResults
1765
+ .filter((result) => !result.ok)
1766
+ .sort((a, b) => a.workerIndex - b.workerIndex)[0];
1767
+ assert.equal(firstStartupError?.workerName, 'worker-1');
1768
+ assert.match(String(firstStartupError?.error), /test_startup_attempt_throw:worker-1/);
1769
+ });
1327
1770
  it('startTeam still fails startup when the worker pane is dead/unrecoverable', async () => {
1328
1771
  const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-dead-startup-pane-'));
1329
1772
  const previousTmux = process.env.TMUX;
@@ -1651,6 +2094,78 @@ process.on('SIGTERM', () => process.exit(0));
1651
2094
  await rm(repo, { recursive: true, force: true });
1652
2095
  }
1653
2096
  });
2097
+ it('startTeam runs worker MCP orphan cleanup before prompt worker spawn', async () => {
2098
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-prompt-mcp-cleanup-'));
2099
+ const binDir = join(cwd, 'bin');
2100
+ const fakeCodexPath = join(binDir, 'codex');
2101
+ const capturePath = join(cwd, 'prompt-cleanup-order.jsonl');
2102
+ await mkdir(binDir, { recursive: true });
2103
+ await writeFakePromptWorkerBinary(fakeCodexPath, `const capturePath = process.env.OMX_PROMPT_CLEANUP_CAPTURE_PATH;
2104
+ if (capturePath) require('fs').appendFileSync(capturePath, 'spawn' + String.fromCharCode(10));
2105
+ setTimeout(() => {}, 5000);`);
2106
+ const prevPath = process.env.PATH;
2107
+ const prevTmux = process.env.TMUX;
2108
+ const prevLaunchMode = process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
2109
+ const prevWorkerCli = process.env.OMX_TEAM_WORKER_CLI;
2110
+ const prevLaunchArgs = process.env.OMX_TEAM_WORKER_LAUNCH_ARGS;
2111
+ const prevCapture = process.env.OMX_PROMPT_CLEANUP_CAPTURE_PATH;
2112
+ const prevAllowNonTty = process.env.OMX_TEST_ALLOW_NONTTY_CODEX_PROMPT;
2113
+ let runtime = null;
2114
+ try {
2115
+ process.env.PATH = `${binDir}:${prevPath ?? ''}`;
2116
+ delete process.env.TMUX;
2117
+ process.env.OMX_TEAM_WORKER_LAUNCH_MODE = 'prompt';
2118
+ process.env.OMX_TEAM_WORKER_CLI = 'codex';
2119
+ process.env.OMX_TEAM_WORKER_LAUNCH_ARGS = `--config ${JSON.stringify(`model_instructions_file=\"${join(cwd, 'AGENTS.md')}\"`)}`;
2120
+ process.env.OMX_PROMPT_CLEANUP_CAPTURE_PATH = capturePath;
2121
+ process.env.OMX_TEST_ALLOW_NONTTY_CODEX_PROMPT = '1';
2122
+ const started = await withoutTeamWorkerEnv(() => startTeam('team-prompt-cleanup', 'prompt cleanup before worker spawn', 'executor', 1, [{ subject: 's', description: 'd', owner: 'worker-1' }], cwd, {
2123
+ cleanupLaunchOrphanedMcpProcesses: async () => {
2124
+ await writeFile(capturePath, 'cleanup\n', 'utf-8');
2125
+ return { dryRun: false, candidates: [], terminatedCount: 0, forceKilledCount: 0, failedPids: [] };
2126
+ },
2127
+ }));
2128
+ runtime = started;
2129
+ const order = await waitForFileText(capturePath, (content) => /spawn/.test(content));
2130
+ assert.equal(order, 'cleanup\nspawn\n');
2131
+ await shutdownTeam(runtime.teamName, cwd, { force: true });
2132
+ runtime = null;
2133
+ }
2134
+ finally {
2135
+ if (runtime) {
2136
+ await shutdownTeam(runtime.teamName, cwd, { force: true }).catch(() => { });
2137
+ }
2138
+ if (typeof prevPath === 'string')
2139
+ process.env.PATH = prevPath;
2140
+ else
2141
+ delete process.env.PATH;
2142
+ if (typeof prevTmux === 'string')
2143
+ process.env.TMUX = prevTmux;
2144
+ else
2145
+ delete process.env.TMUX;
2146
+ if (typeof prevLaunchMode === 'string')
2147
+ process.env.OMX_TEAM_WORKER_LAUNCH_MODE = prevLaunchMode;
2148
+ else
2149
+ delete process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
2150
+ if (typeof prevWorkerCli === 'string')
2151
+ process.env.OMX_TEAM_WORKER_CLI = prevWorkerCli;
2152
+ else
2153
+ delete process.env.OMX_TEAM_WORKER_CLI;
2154
+ if (typeof prevLaunchArgs === 'string')
2155
+ process.env.OMX_TEAM_WORKER_LAUNCH_ARGS = prevLaunchArgs;
2156
+ else
2157
+ delete process.env.OMX_TEAM_WORKER_LAUNCH_ARGS;
2158
+ if (typeof prevCapture === 'string')
2159
+ process.env.OMX_PROMPT_CLEANUP_CAPTURE_PATH = prevCapture;
2160
+ else
2161
+ delete process.env.OMX_PROMPT_CLEANUP_CAPTURE_PATH;
2162
+ if (typeof prevAllowNonTty === 'string')
2163
+ process.env.OMX_TEST_ALLOW_NONTTY_CODEX_PROMPT = prevAllowNonTty;
2164
+ else
2165
+ delete process.env.OMX_TEST_ALLOW_NONTTY_CODEX_PROMPT;
2166
+ await rm(cwd, { recursive: true, force: true });
2167
+ }
2168
+ });
1654
2169
  it('startTeam launches gemini workers with startup prompt and no default model passthrough', async () => {
1655
2170
  const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-gemini-'));
1656
2171
  const binDir = join(cwd, 'bin');
@@ -1801,11 +2316,13 @@ process.on('SIGTERM', () => process.exit(0));
1801
2316
  const prevLaunchMode = process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
1802
2317
  const prevWorkerCli = process.env.OMX_TEAM_WORKER_CLI;
1803
2318
  const prevCaptureDir = process.env.OMX_ARGV_CAPTURE_DIR;
2319
+ const prevStandardModel = process.env.OMX_DEFAULT_STANDARD_MODEL;
1804
2320
  process.env.PATH = `${binDir}:${prevPath ?? ''}`;
1805
2321
  delete process.env.TMUX;
1806
2322
  process.env.OMX_TEAM_WORKER_LAUNCH_MODE = 'prompt';
1807
2323
  process.env.OMX_TEAM_WORKER_CLI = 'codex';
1808
2324
  process.env.OMX_ARGV_CAPTURE_DIR = captureDir;
2325
+ delete process.env.OMX_DEFAULT_STANDARD_MODEL;
1809
2326
  let runtime = null;
1810
2327
  try {
1811
2328
  runtime = await withMockPromptModeCodexAllowed(() => withoutTeamWorkerEnv(() => startTeam('team-role-routing', 'heuristic routing handoff', 'executor', 2, [
@@ -1829,8 +2346,8 @@ process.on('SIGTERM', () => process.exit(0));
1829
2346
  assert.doesNotMatch(worker1Instructions, /exact gpt-5\.4-mini model/);
1830
2347
  assert.match(worker2Instructions, /You are operating as the \*\*writer\*\* role/);
1831
2348
  assert.match(worker2Instructions, /You are Writer\./);
1832
- assert.match(worker2Instructions, /exact gpt-5\.4-mini model/);
1833
- assert.match(worker2Instructions, /strict execution order: inspect -> plan -> act -> verify/);
2349
+ assert.doesNotMatch(worker2Instructions, /exact gpt-5\.4-mini model/);
2350
+ assert.match(worker2Instructions, /resolved_model: gpt-5\.5/);
1834
2351
  let worker1Args = null;
1835
2352
  let worker2Args = null;
1836
2353
  for (let attempt = 0; attempt < 50; attempt += 1) {
@@ -1852,7 +2369,7 @@ process.on('SIGTERM', () => process.exit(0));
1852
2369
  assert.match(worker1Joined, /--model gpt-5\.5/);
1853
2370
  assert.match(worker2Joined, /model_reasoning_effort="high"/);
1854
2371
  assert.match(worker2Joined, /model_instructions_file=.*worker-2\/AGENTS\.md/);
1855
- assert.match(worker2Joined, /--model gpt-5\.4-mini/);
2372
+ assert.match(worker2Joined, /--model gpt-5\.5/);
1856
2373
  await shutdownTeam(runtime.teamName, cwd, { force: true });
1857
2374
  runtime = null;
1858
2375
  }
@@ -1880,6 +2397,10 @@ process.on('SIGTERM', () => process.exit(0));
1880
2397
  process.env.OMX_ARGV_CAPTURE_DIR = prevCaptureDir;
1881
2398
  else
1882
2399
  delete process.env.OMX_ARGV_CAPTURE_DIR;
2400
+ if (typeof prevStandardModel === 'string')
2401
+ process.env.OMX_DEFAULT_STANDARD_MODEL = prevStandardModel;
2402
+ else
2403
+ delete process.env.OMX_DEFAULT_STANDARD_MODEL;
1883
2404
  await rm(cwd, { recursive: true, force: true });
1884
2405
  }
1885
2406
  });
@@ -1909,11 +2430,13 @@ process.on('SIGTERM', () => process.exit(0));
1909
2430
  const prevWorkerCli = process.env.OMX_TEAM_WORKER_CLI;
1910
2431
  const prevCaptureDir = process.env.OMX_ARGV_CAPTURE_DIR;
1911
2432
  const prevLaunchArgs = process.env.OMX_TEAM_WORKER_LAUNCH_ARGS;
2433
+ const prevStandardModel = process.env.OMX_DEFAULT_STANDARD_MODEL;
1912
2434
  process.env.PATH = `${binDir}:${prevPath ?? ''}`;
1913
2435
  delete process.env.TMUX;
1914
2436
  process.env.OMX_TEAM_WORKER_LAUNCH_MODE = 'prompt';
1915
2437
  process.env.OMX_TEAM_WORKER_CLI = 'codex';
1916
2438
  process.env.OMX_ARGV_CAPTURE_DIR = captureDir;
2439
+ delete process.env.OMX_DEFAULT_STANDARD_MODEL;
1917
2440
  process.env.OMX_TEAM_WORKER_LAUNCH_ARGS = '--model gpt-5.4-mini-tuned';
1918
2441
  let runtime = null;
1919
2442
  try {
@@ -1965,6 +2488,10 @@ process.on('SIGTERM', () => process.exit(0));
1965
2488
  process.env.OMX_ARGV_CAPTURE_DIR = prevCaptureDir;
1966
2489
  else
1967
2490
  delete process.env.OMX_ARGV_CAPTURE_DIR;
2491
+ if (typeof prevStandardModel === 'string')
2492
+ process.env.OMX_DEFAULT_STANDARD_MODEL = prevStandardModel;
2493
+ else
2494
+ delete process.env.OMX_DEFAULT_STANDARD_MODEL;
1968
2495
  if (typeof prevLaunchArgs === 'string')
1969
2496
  process.env.OMX_TEAM_WORKER_LAUNCH_ARGS = prevLaunchArgs;
1970
2497
  else
@@ -4046,8 +4573,19 @@ esac
4046
4573
  config.workers[0].pid = sleeperPid;
4047
4574
  config.workers[0].pane_id = null;
4048
4575
  await writeFile(configPath, JSON.stringify(config, null, 2));
4576
+ const manifestPath = join(cwd, '.omx', 'state', 'team', 'team-prompt-resume', 'manifest.v2.json');
4577
+ const manifest = JSON.parse(await readFile(manifestPath, 'utf-8'));
4578
+ manifest.policy.worker_launch_mode = 'prompt';
4579
+ manifest.tmux_session = 'prompt-team-prompt-resume';
4580
+ manifest.leader_pane_id = null;
4581
+ manifest.hud_pane_id = null;
4582
+ manifest.workers[0].pid = sleeperPid;
4583
+ manifest.workers[0].pane_id = null;
4584
+ await writeFile(manifestPath, JSON.stringify(manifest, null, 2));
4049
4585
  const runtime = await resumeTeam('team-prompt-resume', cwd);
4050
4586
  assert.equal(runtime, null);
4587
+ const events = await readTeamEvents('team-prompt-resume', cwd);
4588
+ assert.ok(events.some((event) => event.reason === `prompt_resume_unavailable:missing_handle:worker-1:${sleeperPid}`), 'resumeTeam should persist an explicit missing-handle diagnostic event');
4051
4589
  }
4052
4590
  finally {
4053
4591
  if (sleeperPid > 0) {
@@ -4148,6 +4686,119 @@ esac
4148
4686
  await rm(cwd, { recursive: true, force: true });
4149
4687
  }
4150
4688
  });
4689
+ it('startTeam persists synthesized delegation plans for broad tasks', async () => {
4690
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-'));
4691
+ const binDir = join(cwd, 'bin');
4692
+ const fakeCodexPath = join(binDir, 'codex');
4693
+ await mkdir(binDir, { recursive: true });
4694
+ await writeFakePromptWorkerBinary(fakeCodexPath, `setTimeout(() => {}, 5000);`);
4695
+ let runtime = null;
4696
+ try {
4697
+ runtime = await withPromptModeCodexEnv(binDir, {}, () => withoutTeamWorkerEnv(() => startTeam('team-delegation-persist', 'delegation persistence test', 'executor', 1, [{ subject: 'Investigate runtime assignment', description: 'Search runtime and debug assignTask behavior' }], cwd)));
4698
+ const task = await readTask('team-delegation-persist', '1', cwd);
4699
+ assert.equal(task?.delegation?.mode, 'auto');
4700
+ assert.equal(task?.delegation?.child_model, 'gpt-5.4-mini');
4701
+ assert.equal(task?.delegation?.required_parallel_probe, true);
4702
+ }
4703
+ finally {
4704
+ if (runtime) {
4705
+ await shutdownTeam(runtime.teamName, cwd, { force: true }).catch(() => { });
4706
+ }
4707
+ await rm(cwd, { recursive: true, force: true });
4708
+ }
4709
+ });
4710
+ it('startTeam remaps repo-aware DAG dependencies after concrete task IDs are created', async () => {
4711
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-'));
4712
+ const binDir = join(cwd, 'bin');
4713
+ const fakeCodexPath = join(binDir, 'codex');
4714
+ await mkdir(binDir, { recursive: true });
4715
+ await writeFakePromptWorkerBinary(fakeCodexPath, `setTimeout(() => {}, 5000);`);
4716
+ let runtime = null;
4717
+ try {
4718
+ runtime = await withPromptModeCodexEnv(binDir, {}, () => withoutTeamWorkerEnv(() => startTeam('team-dag-remap', 'repo-aware DAG remap test', 'executor', 2, [
4719
+ {
4720
+ subject: 'Implement runtime',
4721
+ description: 'Change runtime',
4722
+ owner: 'worker-1',
4723
+ role: 'executor',
4724
+ symbolic_id: 'impl',
4725
+ symbolic_depends_on: [],
4726
+ lane: 'implementation',
4727
+ filePaths: ['src/team/runtime.ts'],
4728
+ allocation_reason: 'balances current load',
4729
+ },
4730
+ {
4731
+ subject: 'Verify runtime',
4732
+ description: 'Test runtime',
4733
+ owner: 'worker-2',
4734
+ role: 'test-engineer',
4735
+ symbolic_id: 'verify',
4736
+ symbolic_depends_on: ['impl'],
4737
+ lane: 'verification',
4738
+ allocation_reason: 'keeps blocked work on a lighter lane',
4739
+ },
4740
+ ], cwd, {
4741
+ decompositionMetadata: {
4742
+ decomposition_source: 'dag_sidecar',
4743
+ worker_count_requested: 2,
4744
+ worker_count_effective: 2,
4745
+ worker_count_source: 'plan-suggested',
4746
+ ready_lane_count: 1,
4747
+ useful_lane_count: 2,
4748
+ allocation_reasons: {
4749
+ impl: 'balances current load',
4750
+ verify: 'keeps blocked work on a lighter lane',
4751
+ },
4752
+ node_dependencies: {
4753
+ impl: [],
4754
+ verify: ['impl'],
4755
+ },
4756
+ },
4757
+ })));
4758
+ const first = await readTask('team-dag-remap', '1', cwd);
4759
+ const second = await readTask('team-dag-remap', '2', cwd);
4760
+ assert.deepEqual(first?.depends_on, []);
4761
+ assert.deepEqual(first?.blocked_by, undefined);
4762
+ assert.deepEqual(second?.depends_on, ['1']);
4763
+ assert.deepEqual(second?.blocked_by, ['1']);
4764
+ const report = JSON.parse(await readFile(join(cwd, '.omx', 'state', 'team', 'team-dag-remap', 'decomposition-report.json'), 'utf-8'));
4765
+ assert.deepEqual(report.node_id_to_task_id, { impl: '1', verify: '2' });
4766
+ assert.deepEqual(report.task_hints?.['2']?.depends_on, ['1']);
4767
+ assert.deepEqual(report.task_hints?.['2']?.symbolic_depends_on, ['impl']);
4768
+ const inbox = await readFile(join(cwd, '.omx', 'state', 'team', 'team-dag-remap', 'workers', 'worker-2', 'inbox.md'), 'utf-8');
4769
+ assert.match(inbox, /Blocked by: 1/);
4770
+ assert.doesNotMatch(inbox, /Blocked by: impl/);
4771
+ assert.doesNotMatch(inbox, /Depends on: impl/);
4772
+ }
4773
+ finally {
4774
+ if (runtime) {
4775
+ await shutdownTeam(runtime.teamName, cwd, { force: true }).catch(() => { });
4776
+ }
4777
+ await rm(cwd, { recursive: true, force: true });
4778
+ }
4779
+ });
4780
+ it('assignTask synthesizes delegation before follow-up dispatch rollback', async () => {
4781
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-'));
4782
+ try {
4783
+ await initTeamState('team-assign-delegation', 'assignment delegation test', 'executor', 1, cwd);
4784
+ const config = await readTeamConfig('team-assign-delegation', cwd);
4785
+ assert.ok(config);
4786
+ config.worker_launch_mode = 'prompt';
4787
+ await saveTeamConfig(config, cwd);
4788
+ const task = await createTask('team-assign-delegation', { subject: 'Investigate follow-up assignment', description: 'Search repo and debug follow-up assignment behavior', status: 'pending' }, cwd);
4789
+ await writeWorkerInbox('team-assign-delegation', 'worker-1', 'existing inbox', cwd);
4790
+ await assert.rejects(() => assignTask('team-assign-delegation', 'worker-1', task.id, cwd), /worker_notify_failed/);
4791
+ const reread = await readTask('team-assign-delegation', task.id, cwd);
4792
+ assert.equal(reread?.delegation?.mode, 'auto');
4793
+ assert.equal(reread?.delegation?.child_model, 'gpt-5.4-mini');
4794
+ const inbox = await readFile(join(cwd, '.omx', 'state', 'team', 'team-assign-delegation', 'workers', 'worker-1', 'inbox.md'), 'utf-8');
4795
+ assert.match(inbox, /Assignment Cancelled/);
4796
+ assert.match(inbox, /worker_notify_failed/);
4797
+ }
4798
+ finally {
4799
+ await rm(cwd, { recursive: true, force: true });
4800
+ }
4801
+ });
4151
4802
  it('monitorTeam does not re-notify already-notified mailbox messages (issue #116)', async () => {
4152
4803
  // Regression: deliverPendingMailboxMessages used to re-notify every 15 s via shouldRetry.
4153
4804
  // After the fix it must NOT re-notify messages that already have notified_at set.
@@ -4498,7 +5149,7 @@ esac
4498
5149
  await rm(cwd, { recursive: true, force: true });
4499
5150
  }
4500
5151
  });
4501
- it('sendWorkerMessage transport_direct keeps leader-fixed request pending when leader_pane_id missing', async () => {
5152
+ it('sendWorkerMessage transport_direct fails fast for leader-fixed when leader_pane_id missing', async () => {
4502
5153
  const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-leader-direct-'));
4503
5154
  try {
4504
5155
  await initTeamState('team-leader-direct', 'leader direct transport test', 'executor', 1, cwd);
@@ -4512,14 +5163,23 @@ esac
4512
5163
  const manifest = JSON.parse(await readFile(manifestPath, 'utf-8'));
4513
5164
  manifest.policy = { ...(manifest.policy || {}), dispatch_mode: 'transport_direct' };
4514
5165
  await writeFile(manifestPath, JSON.stringify(manifest, null, 2));
4515
- await sendWorkerMessage('team-leader-direct', 'worker-1', 'leader-fixed', 'hello leader direct', cwd);
5166
+ await assert.rejects(sendWorkerMessage('team-leader-direct', 'worker-1', 'leader-fixed', 'hello leader direct', cwd), /mailbox_notify_failed:leader_pane_missing_transport_direct_failed/);
4516
5167
  const mailbox = await listMailboxMessages('team-leader-direct', 'leader-fixed', cwd);
4517
5168
  assert.ok(mailbox.length >= 1, `expected at least 1 mailbox message, got ${mailbox.length}`);
4518
5169
  const requests = await listDispatchRequests('team-leader-direct', cwd, { kind: 'mailbox', to_worker: 'leader-fixed' });
4519
5170
  assert.ok(requests.length >= 1, `expected at least 1 leader-fixed dispatch request, got ${requests.length}`);
4520
5171
  const latest = requests[requests.length - 1];
4521
- assert.equal(latest?.status, 'pending');
4522
- assert.equal(latest?.last_reason, 'leader_pane_missing_deferred');
5172
+ assert.equal(latest?.status, 'failed');
5173
+ assert.equal(latest?.last_reason, 'leader_pane_missing_transport_direct_failed');
5174
+ assert.ok(latest?.failed_at, 'missing leader pane should fail fast with failed_at evidence');
5175
+ const deliveryLog = await readTeamDeliveryLog(cwd);
5176
+ const runtimeEntries = deliveryLog.filter((entry) => entry.event === 'dispatch_result'
5177
+ && entry.source === 'team.runtime'
5178
+ && entry.to_worker === 'leader-fixed'
5179
+ && entry.transport === 'mailbox'
5180
+ && entry.result === 'failed'
5181
+ && entry.reason === 'leader_pane_missing_transport_direct_failed');
5182
+ assert.equal(runtimeEntries.length, 1, 'leader direct missing-pane failure should emit exactly one runtime dispatch_result entry');
4523
5183
  }
4524
5184
  finally {
4525
5185
  await rm(cwd, { recursive: true, force: true });