oh-my-codex 0.11.13 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (377) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/README.md +34 -17
  4. package/crates/omx-runtime/src/main.rs +6 -2
  5. package/dist/agents/native-config.js +1 -1
  6. package/dist/agents/native-config.js.map +1 -1
  7. package/dist/cli/__tests__/autoresearch-guided.test.js +74 -2
  8. package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
  9. package/dist/cli/__tests__/cleanup.test.js +37 -30
  10. package/dist/cli/__tests__/cleanup.test.js.map +1 -1
  11. package/dist/cli/__tests__/error-handling-warnings.test.js +3 -1
  12. package/dist/cli/__tests__/error-handling-warnings.test.js.map +1 -1
  13. package/dist/cli/__tests__/index.test.js +276 -5
  14. package/dist/cli/__tests__/index.test.js.map +1 -1
  15. package/dist/cli/__tests__/launch-fallback.test.js +95 -1
  16. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  17. package/dist/cli/__tests__/setup-refresh.test.js +49 -9
  18. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  19. package/dist/cli/__tests__/setup-scope.test.js +9 -0
  20. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  21. package/dist/cli/__tests__/team.test.js +136 -11
  22. package/dist/cli/__tests__/team.test.js.map +1 -1
  23. package/dist/cli/__tests__/uninstall.test.js +10 -0
  24. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  25. package/dist/cli/__tests__/windows-popup-loop-contract.test.js +1 -0
  26. package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
  27. package/dist/cli/autoresearch-guided.d.ts.map +1 -1
  28. package/dist/cli/autoresearch-guided.js +2 -1
  29. package/dist/cli/autoresearch-guided.js.map +1 -1
  30. package/dist/cli/autoresearch.d.ts.map +1 -1
  31. package/dist/cli/autoresearch.js +2 -1
  32. package/dist/cli/autoresearch.js.map +1 -1
  33. package/dist/cli/cleanup.d.ts.map +1 -1
  34. package/dist/cli/cleanup.js +10 -5
  35. package/dist/cli/cleanup.js.map +1 -1
  36. package/dist/cli/index.d.ts +21 -1
  37. package/dist/cli/index.d.ts.map +1 -1
  38. package/dist/cli/index.js +298 -36
  39. package/dist/cli/index.js.map +1 -1
  40. package/dist/cli/omx.js +2 -0
  41. package/dist/cli/omx.js.map +1 -1
  42. package/dist/cli/setup.d.ts +1 -0
  43. package/dist/cli/setup.d.ts.map +1 -1
  44. package/dist/cli/setup.js +41 -7
  45. package/dist/cli/setup.js.map +1 -1
  46. package/dist/cli/team.d.ts.map +1 -1
  47. package/dist/cli/team.js +16 -557
  48. package/dist/cli/team.js.map +1 -1
  49. package/dist/cli/uninstall.d.ts.map +1 -1
  50. package/dist/cli/uninstall.js +34 -9
  51. package/dist/cli/uninstall.js.map +1 -1
  52. package/dist/config/__tests__/generator-idempotent.test.js +79 -2
  53. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  54. package/dist/config/__tests__/generator-notify.test.js +2 -0
  55. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  56. package/dist/config/codex-hooks.d.ts +11 -0
  57. package/dist/config/codex-hooks.d.ts.map +1 -0
  58. package/dist/config/codex-hooks.js +50 -0
  59. package/dist/config/codex-hooks.js.map +1 -0
  60. package/dist/config/generator.d.ts +5 -3
  61. package/dist/config/generator.d.ts.map +1 -1
  62. package/dist/config/generator.js +24 -14
  63. package/dist/config/generator.js.map +1 -1
  64. package/dist/hooks/__tests__/debugger-log-recency-contract.test.d.ts +2 -0
  65. package/dist/hooks/__tests__/debugger-log-recency-contract.test.d.ts.map +1 -0
  66. package/dist/hooks/__tests__/debugger-log-recency-contract.test.js +20 -0
  67. package/dist/hooks/__tests__/debugger-log-recency-contract.test.js.map +1 -0
  68. package/dist/hooks/__tests__/keyword-detector.test.js +132 -0
  69. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  70. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +292 -4
  71. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  72. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +86 -0
  73. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  74. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +40 -0
  75. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  76. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.d.ts +2 -0
  77. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.d.ts.map +1 -0
  78. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +54 -0
  79. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -0
  80. package/dist/hooks/__tests__/notify-hook-modules.test.js +31 -0
  81. package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
  82. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +51 -0
  83. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
  84. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.d.ts +2 -0
  85. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.d.ts.map +1 -0
  86. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js +136 -0
  87. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js.map +1 -0
  88. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +120 -0
  89. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  90. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +145 -20
  91. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  92. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +116 -0
  93. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  94. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +86 -0
  95. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  96. package/dist/hooks/__tests__/pre-context-gate-skills.test.js +1 -0
  97. package/dist/hooks/__tests__/pre-context-gate-skills.test.js.map +1 -1
  98. package/dist/hooks/extensibility/__tests__/runtime.test.js +49 -0
  99. package/dist/hooks/extensibility/__tests__/runtime.test.js.map +1 -1
  100. package/dist/hooks/extensibility/runtime.d.ts.map +1 -1
  101. package/dist/hooks/extensibility/runtime.js +10 -0
  102. package/dist/hooks/extensibility/runtime.js.map +1 -1
  103. package/dist/hooks/extensibility/types.d.ts +1 -1
  104. package/dist/hooks/extensibility/types.d.ts.map +1 -1
  105. package/dist/hooks/keyword-detector.d.ts +2 -0
  106. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  107. package/dist/hooks/keyword-detector.js +76 -4
  108. package/dist/hooks/keyword-detector.js.map +1 -1
  109. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  110. package/dist/hooks/prompt-guidance-contract.js +12 -8
  111. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  112. package/dist/hooks/session.d.ts +5 -1
  113. package/dist/hooks/session.d.ts.map +1 -1
  114. package/dist/hooks/session.js +10 -6
  115. package/dist/hooks/session.js.map +1 -1
  116. package/dist/hud/index.d.ts.map +1 -1
  117. package/dist/hud/index.js +6 -1
  118. package/dist/hud/index.js.map +1 -1
  119. package/dist/mcp/__tests__/bootstrap.test.js +0 -3
  120. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  121. package/dist/mcp/__tests__/code-intel-server.test.js +27 -1
  122. package/dist/mcp/__tests__/code-intel-server.test.js.map +1 -1
  123. package/dist/mcp/__tests__/server-lifecycle.test.js +0 -5
  124. package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
  125. package/dist/mcp/bootstrap.d.ts +1 -1
  126. package/dist/mcp/bootstrap.d.ts.map +1 -1
  127. package/dist/mcp/bootstrap.js +0 -1
  128. package/dist/mcp/bootstrap.js.map +1 -1
  129. package/dist/mcp/code-intel-server.d.ts +20 -0
  130. package/dist/mcp/code-intel-server.d.ts.map +1 -1
  131. package/dist/mcp/code-intel-server.js +6 -5
  132. package/dist/mcp/code-intel-server.js.map +1 -1
  133. package/dist/notifications/__tests__/idle-cooldown.test.js +24 -1
  134. package/dist/notifications/__tests__/idle-cooldown.test.js.map +1 -1
  135. package/dist/notifications/__tests__/reply-listener.test.js +20 -1
  136. package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
  137. package/dist/notifications/__tests__/tmux.test.js +41 -0
  138. package/dist/notifications/__tests__/tmux.test.js.map +1 -1
  139. package/dist/notifications/idle-cooldown.d.ts +13 -0
  140. package/dist/notifications/idle-cooldown.d.ts.map +1 -1
  141. package/dist/notifications/idle-cooldown.js +50 -16
  142. package/dist/notifications/idle-cooldown.js.map +1 -1
  143. package/dist/notifications/reply-listener.d.ts.map +1 -1
  144. package/dist/notifications/reply-listener.js +2 -0
  145. package/dist/notifications/reply-listener.js.map +1 -1
  146. package/dist/notifications/tmux.d.ts.map +1 -1
  147. package/dist/notifications/tmux.js +4 -0
  148. package/dist/notifications/tmux.js.map +1 -1
  149. package/dist/scripts/__tests__/codex-native-hook.test.d.ts +2 -0
  150. package/dist/scripts/__tests__/codex-native-hook.test.d.ts.map +1 -0
  151. package/dist/scripts/__tests__/codex-native-hook.test.js +1050 -0
  152. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -0
  153. package/dist/scripts/codex-native-hook.d.ts +22 -0
  154. package/dist/scripts/codex-native-hook.d.ts.map +1 -0
  155. package/dist/scripts/codex-native-hook.js +792 -0
  156. package/dist/scripts/codex-native-hook.js.map +1 -0
  157. package/dist/scripts/codex-native-pre-post.d.ts +26 -0
  158. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -0
  159. package/dist/scripts/codex-native-pre-post.js +118 -0
  160. package/dist/scripts/codex-native-pre-post.js.map +1 -0
  161. package/dist/scripts/notify-fallback-watcher.js +322 -21
  162. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  163. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  164. package/dist/scripts/notify-hook/auto-nudge.js +5 -6
  165. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  166. package/dist/scripts/notify-hook/log.d.ts +2 -2
  167. package/dist/scripts/notify-hook/log.d.ts.map +1 -1
  168. package/dist/scripts/notify-hook/log.js +10 -2
  169. package/dist/scripts/notify-hook/log.js.map +1 -1
  170. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  171. package/dist/scripts/notify-hook/managed-tmux.js +2 -0
  172. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  173. package/dist/scripts/notify-hook/orchestration-intent.d.ts +18 -0
  174. package/dist/scripts/notify-hook/orchestration-intent.d.ts.map +1 -0
  175. package/dist/scripts/notify-hook/orchestration-intent.js +72 -0
  176. package/dist/scripts/notify-hook/orchestration-intent.js.map +1 -0
  177. package/dist/scripts/notify-hook/process-runner.js.map +1 -1
  178. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
  179. package/dist/scripts/notify-hook/ralph-session-resume.js +7 -0
  180. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  181. package/dist/scripts/notify-hook/team-dispatch.d.ts +15 -6
  182. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  183. package/dist/scripts/notify-hook/team-dispatch.js +125 -6
  184. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  185. package/dist/scripts/notify-hook/team-leader-nudge.d.ts +3 -2
  186. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  187. package/dist/scripts/notify-hook/team-leader-nudge.js +165 -37
  188. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  189. package/dist/scripts/notify-hook/team-tmux-guard.d.ts +4 -1
  190. package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
  191. package/dist/scripts/notify-hook/team-tmux-guard.js +33 -44
  192. package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
  193. package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
  194. package/dist/scripts/notify-hook/team-worker.js +68 -5
  195. package/dist/scripts/notify-hook/team-worker.js.map +1 -1
  196. package/dist/scripts/notify-hook/utils.d.ts +1 -1
  197. package/dist/scripts/notify-hook/utils.d.ts.map +1 -1
  198. package/dist/scripts/notify-hook/utils.js.map +1 -1
  199. package/dist/scripts/notify-hook.js +55 -32
  200. package/dist/scripts/notify-hook.js.map +1 -1
  201. package/dist/team/__tests__/api-interop.test.js +344 -18
  202. package/dist/team/__tests__/api-interop.test.js.map +1 -1
  203. package/dist/team/__tests__/delivery-e2e-smoke.test.d.ts +2 -0
  204. package/dist/team/__tests__/delivery-e2e-smoke.test.d.ts.map +1 -0
  205. package/dist/team/__tests__/delivery-e2e-smoke.test.js +671 -0
  206. package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -0
  207. package/dist/team/__tests__/mcp-comm.test.js +5 -0
  208. package/dist/team/__tests__/mcp-comm.test.js.map +1 -1
  209. package/dist/team/__tests__/runtime.test.js +543 -15
  210. package/dist/team/__tests__/runtime.test.js.map +1 -1
  211. package/dist/team/__tests__/state.test.js +133 -8
  212. package/dist/team/__tests__/state.test.js.map +1 -1
  213. package/dist/team/__tests__/team-ops-contract.test.js +4 -0
  214. package/dist/team/__tests__/team-ops-contract.test.js.map +1 -1
  215. package/dist/team/__tests__/tmux-session.test.js +160 -0
  216. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  217. package/dist/team/__tests__/worker-bootstrap.test.js +19 -1
  218. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  219. package/dist/team/api-interop.d.ts.map +1 -1
  220. package/dist/team/api-interop.js +95 -23
  221. package/dist/team/api-interop.js.map +1 -1
  222. package/dist/team/contracts.d.ts +11 -1
  223. package/dist/team/contracts.d.ts.map +1 -1
  224. package/dist/team/contracts.js +29 -0
  225. package/dist/team/contracts.js.map +1 -1
  226. package/dist/team/delivery-log.d.ts +14 -0
  227. package/dist/team/delivery-log.d.ts.map +1 -0
  228. package/dist/team/delivery-log.js +35 -0
  229. package/dist/team/delivery-log.js.map +1 -0
  230. package/dist/team/idle-nudge.d.ts +2 -2
  231. package/dist/team/idle-nudge.js +2 -2
  232. package/dist/team/mcp-comm.d.ts +4 -0
  233. package/dist/team/mcp-comm.d.ts.map +1 -1
  234. package/dist/team/mcp-comm.js +84 -1
  235. package/dist/team/mcp-comm.js.map +1 -1
  236. package/dist/team/pane-status.d.ts +149 -0
  237. package/dist/team/pane-status.d.ts.map +1 -0
  238. package/dist/team/pane-status.js +558 -0
  239. package/dist/team/pane-status.js.map +1 -0
  240. package/dist/team/reminder-intents.d.ts +11 -0
  241. package/dist/team/reminder-intents.d.ts.map +1 -0
  242. package/dist/team/reminder-intents.js +40 -0
  243. package/dist/team/reminder-intents.js.map +1 -0
  244. package/dist/team/runtime-cli.d.ts +1 -1
  245. package/dist/team/runtime-cli.js +2 -2
  246. package/dist/team/runtime-cli.js.map +1 -1
  247. package/dist/team/runtime.d.ts +2 -1
  248. package/dist/team/runtime.d.ts.map +1 -1
  249. package/dist/team/runtime.js +409 -191
  250. package/dist/team/runtime.js.map +1 -1
  251. package/dist/team/scaling.d.ts.map +1 -1
  252. package/dist/team/scaling.js +6 -5
  253. package/dist/team/scaling.js.map +1 -1
  254. package/dist/team/state/dispatch.d.ts +4 -1
  255. package/dist/team/state/dispatch.d.ts.map +1 -1
  256. package/dist/team/state/dispatch.js +59 -18
  257. package/dist/team/state/dispatch.js.map +1 -1
  258. package/dist/team/state/mailbox.d.ts.map +1 -1
  259. package/dist/team/state/mailbox.js +45 -2
  260. package/dist/team/state/mailbox.js.map +1 -1
  261. package/dist/team/state/monitor.d.ts +2 -1
  262. package/dist/team/state/monitor.d.ts.map +1 -1
  263. package/dist/team/state/monitor.js +30 -1
  264. package/dist/team/state/monitor.js.map +1 -1
  265. package/dist/team/state/types.d.ts +5 -2
  266. package/dist/team/state/types.d.ts.map +1 -1
  267. package/dist/team/state/types.js.map +1 -1
  268. package/dist/team/state.d.ts +30 -3
  269. package/dist/team/state.d.ts.map +1 -1
  270. package/dist/team/state.js +170 -2
  271. package/dist/team/state.js.map +1 -1
  272. package/dist/team/team-ops.d.ts +5 -1
  273. package/dist/team/team-ops.d.ts.map +1 -1
  274. package/dist/team/team-ops.js +4 -0
  275. package/dist/team/team-ops.js.map +1 -1
  276. package/dist/team/tmux-session.d.ts +2 -0
  277. package/dist/team/tmux-session.d.ts.map +1 -1
  278. package/dist/team/tmux-session.js +19 -3
  279. package/dist/team/tmux-session.js.map +1 -1
  280. package/dist/team/worker-bootstrap.d.ts +4 -0
  281. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  282. package/dist/team/worker-bootstrap.js +33 -6
  283. package/dist/team/worker-bootstrap.js.map +1 -1
  284. package/dist/utils/__tests__/paths.test.js +63 -1
  285. package/dist/utils/__tests__/paths.test.js.map +1 -1
  286. package/dist/utils/__tests__/platform-command.test.js +50 -4
  287. package/dist/utils/__tests__/platform-command.test.js.map +1 -1
  288. package/dist/utils/paths.d.ts +12 -0
  289. package/dist/utils/paths.d.ts.map +1 -1
  290. package/dist/utils/paths.js +44 -2
  291. package/dist/utils/paths.js.map +1 -1
  292. package/dist/utils/platform-command.d.ts.map +1 -1
  293. package/dist/utils/platform-command.js +13 -5
  294. package/dist/utils/platform-command.js.map +1 -1
  295. package/dist/utils/sleep.d.ts.map +1 -1
  296. package/dist/utils/sleep.js +10 -1
  297. package/dist/utils/sleep.js.map +1 -1
  298. package/package.json +1 -1
  299. package/prompts/analyst.md +2 -2
  300. package/prompts/api-reviewer.md +2 -2
  301. package/prompts/architect.md +2 -2
  302. package/prompts/build-fixer.md +2 -2
  303. package/prompts/code-reviewer.md +2 -2
  304. package/prompts/code-simplifier.md +1 -1
  305. package/prompts/critic.md +2 -2
  306. package/prompts/debugger.md +3 -2
  307. package/prompts/dependency-expert.md +2 -2
  308. package/prompts/designer.md +2 -2
  309. package/prompts/executor.md +3 -2
  310. package/prompts/explore.md +2 -2
  311. package/prompts/git-master.md +2 -2
  312. package/prompts/information-architect.md +15 -102
  313. package/prompts/performance-reviewer.md +2 -2
  314. package/prompts/planner.md +3 -2
  315. package/prompts/product-analyst.md +2 -2
  316. package/prompts/product-manager.md +2 -2
  317. package/prompts/qa-tester.md +2 -2
  318. package/prompts/quality-reviewer.md +2 -2
  319. package/prompts/quality-strategist.md +2 -2
  320. package/prompts/researcher.md +2 -2
  321. package/prompts/security-reviewer.md +2 -2
  322. package/prompts/sisyphus-lite.md +2 -2
  323. package/prompts/style-reviewer.md +2 -2
  324. package/prompts/team-executor.md +2 -2
  325. package/prompts/test-engineer.md +2 -2
  326. package/prompts/ux-researcher.md +2 -2
  327. package/prompts/verifier.md +3 -2
  328. package/prompts/vision.md +2 -2
  329. package/prompts/writer.md +2 -2
  330. package/skills/team/SKILL.md +18 -33
  331. package/src/scripts/__tests__/codex-native-hook.test.ts +1346 -0
  332. package/src/scripts/codex-native-hook.ts +983 -0
  333. package/src/scripts/codex-native-pre-post.ts +161 -0
  334. package/src/scripts/notify-fallback-watcher.ts +378 -29
  335. package/src/scripts/notify-hook/auto-nudge.ts +5 -10
  336. package/src/scripts/notify-hook/log.ts +18 -4
  337. package/src/scripts/notify-hook/managed-tmux.ts +1 -0
  338. package/src/scripts/notify-hook/orchestration-intent.ts +82 -0
  339. package/src/scripts/notify-hook/process-runner.ts +4 -4
  340. package/src/scripts/notify-hook/ralph-session-resume.ts +9 -0
  341. package/src/scripts/notify-hook/team-dispatch.ts +134 -6
  342. package/src/scripts/notify-hook/team-leader-nudge.ts +183 -37
  343. package/src/scripts/notify-hook/team-tmux-guard.ts +35 -43
  344. package/src/scripts/notify-hook/team-worker.ts +73 -4
  345. package/src/scripts/notify-hook/utils.ts +1 -1
  346. package/src/scripts/notify-hook.ts +64 -32
  347. package/templates/AGENTS.md +21 -11
  348. package/README.de.md +0 -263
  349. package/README.el.md +0 -223
  350. package/README.es.md +0 -263
  351. package/README.fr.md +0 -263
  352. package/README.it.md +0 -263
  353. package/README.ja.md +0 -264
  354. package/README.ko.md +0 -264
  355. package/README.pl.md +0 -216
  356. package/README.pt.md +0 -263
  357. package/README.ru.md +0 -263
  358. package/README.tr.md +0 -263
  359. package/README.vi.md +0 -223
  360. package/README.zh-TW.md +0 -293
  361. package/README.zh.md +0 -264
  362. package/dist/mcp/__tests__/team-server-cleanup.test.d.ts +0 -2
  363. package/dist/mcp/__tests__/team-server-cleanup.test.d.ts.map +0 -1
  364. package/dist/mcp/__tests__/team-server-cleanup.test.js +0 -219
  365. package/dist/mcp/__tests__/team-server-cleanup.test.js.map +0 -1
  366. package/dist/mcp/__tests__/team-server-runtime-deps.test.d.ts +0 -2
  367. package/dist/mcp/__tests__/team-server-runtime-deps.test.d.ts.map +0 -1
  368. package/dist/mcp/__tests__/team-server-runtime-deps.test.js +0 -13
  369. package/dist/mcp/__tests__/team-server-runtime-deps.test.js.map +0 -1
  370. package/dist/mcp/__tests__/team-server-wait.test.d.ts +0 -2
  371. package/dist/mcp/__tests__/team-server-wait.test.d.ts.map +0 -1
  372. package/dist/mcp/__tests__/team-server-wait.test.js +0 -155
  373. package/dist/mcp/__tests__/team-server-wait.test.js.map +0 -1
  374. package/dist/mcp/team-server.d.ts +0 -24
  375. package/dist/mcp/team-server.d.ts.map +0 -1
  376. package/dist/mcp/team-server.js +0 -482
  377. package/dist/mcp/team-server.js.map +0 -1
@@ -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, updateWorkerHeartbeat, writeAtomic, readTask, readMonitorSnapshot, claimTask, transitionTaskStatus, writeWorkerStatus, } from '../state.js';
10
- import { monitorTeam, shutdownTeam, resumeTeam, startTeam, assignTask, sendWorkerMessage, resolveWorkerLaunchArgsFromEnv, waitForWorkerStartupEvidence, waitForClaudeStartupEvidence, } from '../runtime.js';
9
+ import { initTeamState, createTask, readTeamConfig, saveTeamConfig, listMailboxMessages, listDispatchRequests, transitionDispatchRequest, updateWorkerHeartbeat, writeAtomic, readTask, readMonitorSnapshot, claimTask, transitionTaskStatus, writeWorkerStatus, } from '../state.js';
10
+ import { monitorTeam, shutdownTeam, resumeTeam, startTeam, assignTask, sendWorkerMessage, applyCreatedInteractiveSessionToConfig, resolveWorkerLaunchArgsFromEnv, waitForWorkerStartupEvidence, waitForClaudeStartupEvidence, } from '../runtime.js';
11
11
  import { resolveTeamLowComplexityDefaultModel } from '../model-contract.js';
12
12
  import { readTeamEvents } from '../state/events.js';
13
13
  async function initRepo() {
@@ -28,6 +28,15 @@ async function addWorktree(repo, branchName, pathPrefix) {
28
28
  function expectedLowComplexityModel(codexHomeOverride) {
29
29
  return resolveTeamLowComplexityDefaultModel(codexHomeOverride);
30
30
  }
31
+ async function readTeamDeliveryLog(cwd) {
32
+ const path = join(cwd, '.omx', 'logs', `team-delivery-${new Date().toISOString().slice(0, 10)}.jsonl`);
33
+ const raw = await readFile(path, 'utf-8').catch(() => '');
34
+ return raw
35
+ .split('\n')
36
+ .map((line) => line.trim())
37
+ .filter(Boolean)
38
+ .map((line) => JSON.parse(line));
39
+ }
31
40
  function withEmptyPath(fn) {
32
41
  const prev = process.env.PATH;
33
42
  process.env.PATH = '';
@@ -437,8 +446,9 @@ sleep 5
437
446
  runtime = null;
438
447
  }
439
448
  finally {
440
- if (runtime) {
441
- await shutdownTeam(runtime.teamName, cwd, { force: true }).catch(() => { });
449
+ const runtimeToShutdown = runtime;
450
+ if (runtimeToShutdown) {
451
+ await shutdownTeam(runtimeToShutdown.teamName, cwd, { force: true }).catch(() => { });
442
452
  }
443
453
  if (typeof prevPath === 'string')
444
454
  process.env.PATH = prevPath;
@@ -486,6 +496,292 @@ sleep 5
486
496
  await rm(cwd, { recursive: true, force: true });
487
497
  }
488
498
  });
499
+ it('startTeam accepts native Windows tmux clients even when TMUX env vars are absent', async () => {
500
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-win32-no-env-'));
501
+ const prevTmux = process.env.TMUX;
502
+ const prevTmuxPane = process.env.TMUX_PANE;
503
+ const prevLaunchMode = process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
504
+ const prevWorkerCli = process.env.OMX_TEAM_WORKER_CLI;
505
+ const prevSkipReadyWait = process.env.OMX_TEAM_SKIP_READY_WAIT;
506
+ const prevMsystem = process.env.MSYSTEM;
507
+ const prevOstype = process.env.OSTYPE;
508
+ const prevWsl = process.env.WSL_DISTRO_NAME;
509
+ const prevWslInterop = process.env.WSL_INTEROP;
510
+ const origPlatform = Object.getOwnPropertyDescriptor(process, 'platform');
511
+ let runtime = null;
512
+ let teamNameForCleanup = null;
513
+ try {
514
+ await withMockTmuxFixture({
515
+ dirPrefix: 'omx-runtime-win32-no-env-',
516
+ tmuxScript: (tmuxLogPath) => `#!/bin/sh
517
+ set -eu
518
+ printf '%s\\n' "$*" >> "${tmuxLogPath}"
519
+ case "\${1:-}" in
520
+ -V)
521
+ echo "tmux 3.4"
522
+ exit 0
523
+ ;;
524
+ display-message)
525
+ case "$*" in
526
+ *"#{window_width}"*)
527
+ echo "120"
528
+ ;;
529
+ *)
530
+ echo "leader:0 %1"
531
+ ;;
532
+ esac
533
+ exit 0
534
+ ;;
535
+ list-panes)
536
+ printf "%%1\\tnode\\t'codex'\\n"
537
+ exit 0
538
+ ;;
539
+ split-window)
540
+ case "$*" in
541
+ *" -h "*)
542
+ echo "%2"
543
+ ;;
544
+ *)
545
+ echo "%3"
546
+ ;;
547
+ esac
548
+ exit 0
549
+ ;;
550
+ resize-pane|select-layout|set-window-option|select-pane|kill-pane|set-hook|run-shell)
551
+ exit 0
552
+ ;;
553
+ *)
554
+ exit 0
555
+ ;;
556
+ esac
557
+ `,
558
+ binaries: [{
559
+ name: 'gemini',
560
+ content: '#!/bin/sh\nexit 0\n',
561
+ }],
562
+ }, async ({ tmuxLogPath }) => {
563
+ delete process.env.TMUX;
564
+ delete process.env.TMUX_PANE;
565
+ process.env.OMX_TEAM_WORKER_LAUNCH_MODE = 'interactive';
566
+ process.env.OMX_TEAM_WORKER_CLI = 'gemini';
567
+ process.env.OMX_TEAM_SKIP_READY_WAIT = '1';
568
+ delete process.env.MSYSTEM;
569
+ delete process.env.OSTYPE;
570
+ delete process.env.WSL_DISTRO_NAME;
571
+ delete process.env.WSL_INTEROP;
572
+ Object.defineProperty(process, 'platform', { value: 'win32', configurable: true });
573
+ runtime = await withoutTeamWorkerEnv(() => startTeam('team-win32-no-env', 'native windows current-client detection', 'executor', 1, [{ subject: 's', description: 'd', owner: 'worker-1' }], cwd));
574
+ teamNameForCleanup = runtime.teamName;
575
+ assert.equal(runtime.config.tmux_session, 'leader:0');
576
+ assert.equal(runtime.config.leader_pane_id, '%1');
577
+ assert.equal(runtime.config.hud_pane_id, '%3');
578
+ const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
579
+ assert.match(tmuxLog, /display-message -p #S:#I #{pane_id}/);
580
+ assert.match(tmuxLog, new RegExp(`resize-pane -t %3 -y ${HUD_TMUX_TEAM_HEIGHT_LINES}`));
581
+ if (teamNameForCleanup) {
582
+ await shutdownTeam(teamNameForCleanup, cwd, { force: true });
583
+ }
584
+ runtime = null;
585
+ });
586
+ }
587
+ finally {
588
+ if (teamNameForCleanup) {
589
+ await shutdownTeam(teamNameForCleanup, cwd, { force: true }).catch(() => { });
590
+ }
591
+ if (origPlatform)
592
+ Object.defineProperty(process, 'platform', origPlatform);
593
+ if (typeof prevTmux === 'string')
594
+ process.env.TMUX = prevTmux;
595
+ else
596
+ delete process.env.TMUX;
597
+ if (typeof prevTmuxPane === 'string')
598
+ process.env.TMUX_PANE = prevTmuxPane;
599
+ else
600
+ delete process.env.TMUX_PANE;
601
+ if (typeof prevLaunchMode === 'string')
602
+ process.env.OMX_TEAM_WORKER_LAUNCH_MODE = prevLaunchMode;
603
+ else
604
+ delete process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
605
+ if (typeof prevWorkerCli === 'string')
606
+ process.env.OMX_TEAM_WORKER_CLI = prevWorkerCli;
607
+ else
608
+ delete process.env.OMX_TEAM_WORKER_CLI;
609
+ if (typeof prevSkipReadyWait === 'string')
610
+ process.env.OMX_TEAM_SKIP_READY_WAIT = prevSkipReadyWait;
611
+ else
612
+ delete process.env.OMX_TEAM_SKIP_READY_WAIT;
613
+ if (typeof prevMsystem === 'string')
614
+ process.env.MSYSTEM = prevMsystem;
615
+ else
616
+ delete process.env.MSYSTEM;
617
+ if (typeof prevOstype === 'string')
618
+ process.env.OSTYPE = prevOstype;
619
+ else
620
+ delete process.env.OSTYPE;
621
+ if (typeof prevWsl === 'string')
622
+ process.env.WSL_DISTRO_NAME = prevWsl;
623
+ else
624
+ delete process.env.WSL_DISTRO_NAME;
625
+ if (typeof prevWslInterop === 'string')
626
+ process.env.WSL_INTEROP = prevWslInterop;
627
+ else
628
+ delete process.env.WSL_INTEROP;
629
+ await rm(cwd, { recursive: true, force: true });
630
+ }
631
+ });
632
+ it('applyCreatedInteractiveSessionToConfig persists worker pane ids before readiness waits', async () => {
633
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-pane-persist-race-'));
634
+ try {
635
+ const config = await initTeamState('team-pane-persist-race', 'persist pane ids before readiness wait', 'executor', 2, cwd);
636
+ const workerPaneIds = Array.from({ length: 2 }, () => undefined);
637
+ applyCreatedInteractiveSessionToConfig(config, {
638
+ name: 'leader:0',
639
+ workerCount: 2,
640
+ cwd,
641
+ workerPaneIds: ['%2', '%3'],
642
+ leaderPaneId: '%1',
643
+ hudPaneId: '%4',
644
+ resizeHookName: 'resize-hook',
645
+ resizeHookTarget: 'leader:0',
646
+ }, workerPaneIds);
647
+ assert.equal(config.tmux_session, 'leader:0');
648
+ assert.equal(config.leader_pane_id, '%1');
649
+ assert.equal(config.hud_pane_id, '%4');
650
+ assert.deepEqual(workerPaneIds, ['%2', '%3']);
651
+ assert.equal(config.workers[0]?.pane_id, '%2');
652
+ assert.equal(config.workers[1]?.pane_id, '%3');
653
+ }
654
+ finally {
655
+ await rm(cwd, { recursive: true, force: true });
656
+ }
657
+ });
658
+ it('startTeam captures interactive worker pid from the resolved pane id', async () => {
659
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-pane-pid-'));
660
+ const prevTmux = process.env.TMUX;
661
+ const prevTmuxPane = process.env.TMUX_PANE;
662
+ const prevLaunchMode = process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
663
+ const prevWorkerCli = process.env.OMX_TEAM_WORKER_CLI;
664
+ const prevSkipReadyWait = process.env.OMX_TEAM_SKIP_READY_WAIT;
665
+ let runtime = null;
666
+ try {
667
+ await withMockTmuxFixture({
668
+ dirPrefix: 'omx-runtime-pane-pid-bin-',
669
+ tmuxScript: (tmuxLogPath) => `#!/bin/sh
670
+ set -eu
671
+ printf '%s\n' "$*" >> "${tmuxLogPath}"
672
+ case "\${1:-}" in
673
+ -V)
674
+ echo "tmux 3.4"
675
+ exit 0
676
+ ;;
677
+ display-message)
678
+ case "$*" in
679
+ *"#{window_width}"*)
680
+ echo "120"
681
+ ;;
682
+ *)
683
+ echo "leader:0 %1"
684
+ ;;
685
+ esac
686
+ exit 0
687
+ ;;
688
+ list-panes)
689
+ case "$*" in
690
+ *"pane_current_command"*)
691
+ printf "%%1\tnode\t'codex'\n"
692
+ ;;
693
+ *"#{pane_dead} #{pane_pid}"*)
694
+ echo "1 999999"
695
+ ;;
696
+ *"-t %2"*"#{pane_pid}"*)
697
+ echo "2222"
698
+ ;;
699
+ *"-t %3"*"#{pane_pid}"*)
700
+ echo "3333"
701
+ ;;
702
+ *"#{pane_pid}"*)
703
+ echo "1111"
704
+ ;;
705
+ *)
706
+ exit 0
707
+ ;;
708
+ esac
709
+ exit 0
710
+ ;;
711
+ split-window)
712
+ case "$*" in
713
+ *" -h "*)
714
+ echo "%2"
715
+ ;;
716
+ *)
717
+ echo "%3"
718
+ ;;
719
+ esac
720
+ exit 0
721
+ ;;
722
+ set-hook|run-shell|select-layout|set-window-option|select-pane|send-keys|kill-pane|kill-session)
723
+ exit 0
724
+ ;;
725
+ *)
726
+ exit 0
727
+ ;;
728
+ esac
729
+ `,
730
+ binaries: [{ name: 'codex', content: '#!/bin/sh\nexit 0\n' }],
731
+ }, async () => {
732
+ delete process.env.TMUX;
733
+ process.env.TMUX_PANE = '%1';
734
+ process.env.OMX_TEAM_WORKER_LAUNCH_MODE = 'interactive';
735
+ process.env.OMX_TEAM_WORKER_CLI = 'codex';
736
+ process.env.OMX_TEAM_SKIP_READY_WAIT = '1';
737
+ runtime = await withoutTeamWorkerEnv(() => startTeam('team-pane-pid', 'interactive pane pid capture', 'executor', 1, [{ subject: 's', description: 'd', owner: 'worker-1' }], cwd));
738
+ assert.equal(runtime.config.workers[0]?.pane_id, '%2');
739
+ assert.equal(runtime.config.workers[0]?.pid, 2222);
740
+ const identityPath = join(cwd, '.omx', 'state', 'team', runtime.teamName, 'workers', 'worker-1', 'identity.json');
741
+ const identity = JSON.parse(await readFile(identityPath, 'utf-8'));
742
+ assert.equal(identity.pane_id, '%2');
743
+ assert.equal(identity.pid, 2222);
744
+ });
745
+ }
746
+ finally {
747
+ const runtimeToShutdown = runtime;
748
+ if (runtimeToShutdown) {
749
+ await shutdownTeam(runtimeToShutdown.teamName, cwd, { force: true }).catch(() => { });
750
+ }
751
+ if (typeof prevTmux === 'string')
752
+ process.env.TMUX = prevTmux;
753
+ else
754
+ delete process.env.TMUX;
755
+ if (typeof prevTmuxPane === 'string')
756
+ process.env.TMUX_PANE = prevTmuxPane;
757
+ else
758
+ delete process.env.TMUX_PANE;
759
+ if (typeof prevLaunchMode === 'string')
760
+ process.env.OMX_TEAM_WORKER_LAUNCH_MODE = prevLaunchMode;
761
+ else
762
+ delete process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
763
+ if (typeof prevWorkerCli === 'string')
764
+ process.env.OMX_TEAM_WORKER_CLI = prevWorkerCli;
765
+ else
766
+ delete process.env.OMX_TEAM_WORKER_CLI;
767
+ if (typeof prevSkipReadyWait === 'string')
768
+ process.env.OMX_TEAM_SKIP_READY_WAIT = prevSkipReadyWait;
769
+ else
770
+ delete process.env.OMX_TEAM_SKIP_READY_WAIT;
771
+ await rm(cwd, { recursive: true, force: true });
772
+ }
773
+ });
774
+ it('startTeam saves interactive pane ids before readiness waits in source order', async () => {
775
+ const source = await readFile(join(process.cwd(), 'src', 'team', 'runtime.ts'), 'utf-8');
776
+ const applyIndex = source.indexOf('applyCreatedInteractiveSessionToConfig(config, createdSession, workerPaneIds);');
777
+ const saveIndex = source.indexOf('await saveTeamConfig(config, leaderCwd);', applyIndex);
778
+ const readyIndex = source.indexOf('const ready = waitForWorkerReady(sessionName, i, workerReadyTimeoutMs, paneId);', saveIndex);
779
+ assert.notEqual(applyIndex, -1);
780
+ assert.notEqual(saveIndex, -1);
781
+ assert.notEqual(readyIndex, -1);
782
+ assert.equal(applyIndex < saveIndex, true);
783
+ assert.equal(saveIndex < readyIndex, true);
784
+ });
489
785
  it('startTeam rejects dirty leader workspace before provisioning worker worktrees', async () => {
490
786
  const repo = await initRepo();
491
787
  const prevLaunchMode = process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
@@ -536,21 +832,25 @@ sleep 5
536
832
  runtime = await withoutTeamWorkerEnv(() => startTeam('team-gemini-prompt', 'gemini prompt-mode team bootstrap', 'explore', 1, [{ subject: 's', description: 'd', owner: 'worker-1' }], cwd));
537
833
  assert.equal(runtime.config.worker_launch_mode, 'prompt');
538
834
  assert.equal((runtime.config.workers[0]?.pid ?? 0) > 0, true);
835
+ const expectedArgv = [
836
+ '--approval-mode',
837
+ 'yolo',
838
+ '-i',
839
+ 'Read .omx/state/team/team-gemini-prompt/workers/worker-1/inbox.md, start work now, report concrete progress, then continue assigned work or next feasible task.',
840
+ ];
539
841
  let argv = null;
540
842
  for (let attempt = 0; attempt < 50; attempt += 1) {
541
843
  if (existsSync(capturePath)) {
542
- argv = (await readFile(capturePath, 'utf-8')).trim().split('\n');
543
- break;
844
+ const captured = (await readFile(capturePath, 'utf-8')).trim().split('\n').filter(Boolean);
845
+ if (captured.length >= expectedArgv.length) {
846
+ argv = captured;
847
+ break;
848
+ }
544
849
  }
545
850
  await new Promise((resolve) => setTimeout(resolve, 100));
546
851
  }
547
852
  assert.ok(argv, 'gemini argv capture file should be written');
548
- assert.deepEqual(argv, [
549
- '--approval-mode',
550
- 'yolo',
551
- '-i',
552
- 'Read .omx/state/team/team-gemini-prompt/workers/worker-1/inbox.md, start work now, report concrete progress, then continue assigned work or next feasible task.',
553
- ]);
853
+ assert.deepEqual(argv, expectedArgv);
554
854
  await shutdownTeam(runtime.teamName, cwd, { force: true });
555
855
  runtime = null;
556
856
  }
@@ -1010,9 +1310,9 @@ exit 0
1010
1310
  });
1011
1311
  }
1012
1312
  finally {
1013
- const activeRuntime = runtime;
1014
- if (activeRuntime) {
1015
- await shutdownTeam(activeRuntime.teamName, cwd, { force: true }).catch(() => { });
1313
+ const runtimeToShutdown = runtime;
1314
+ if (runtimeToShutdown) {
1315
+ await shutdownTeam(runtimeToShutdown.teamName, cwd, { force: true }).catch(() => { });
1016
1316
  }
1017
1317
  if (typeof previousTmux === 'string')
1018
1318
  process.env.TMUX = previousTmux;
@@ -1825,6 +2125,72 @@ process.on('SIGTERM', () => process.exit(0));
1825
2125
  await rm(repo, { recursive: true, force: true });
1826
2126
  }
1827
2127
  });
2128
+ it('monitorTeam does not emit INTEGRATED when merge reports success but leader HEAD never advances', async () => {
2129
+ const repo = await initRepo();
2130
+ let workerPath = '';
2131
+ const fakeBinDir = await mkdtemp(join(tmpdir(), 'omx-runtime-fake-git-'));
2132
+ const previousPath = process.env.PATH;
2133
+ const previousFakeMode = process.env.OMX_FAKE_GIT_SUCCESS_NOOP;
2134
+ try {
2135
+ workerPath = await addWorktree(repo, 'wk1-merge-noadvance-branch', 'omx-runtime-wk1-merge-noadvance-');
2136
+ await writeFile(join(workerPath, 'feature.txt'), 'new feature\n', 'utf-8');
2137
+ execFileSync('git', ['add', 'feature.txt'], { cwd: workerPath, stdio: 'ignore' });
2138
+ execFileSync('git', ['commit', '-m', 'worker feature'], { cwd: workerPath, stdio: 'ignore' });
2139
+ await initTeamState('team-merge-noadvance', 'merge no advance test', 'executor', 1, repo);
2140
+ const cfg = await readTeamConfig('team-merge-noadvance', repo);
2141
+ assert.ok(cfg);
2142
+ if (!cfg)
2143
+ throw new Error('missing config');
2144
+ cfg.leader_pane_id = '';
2145
+ cfg.workers[0] = {
2146
+ ...cfg.workers[0],
2147
+ assigned_tasks: ['1'],
2148
+ worktree_repo_root: repo,
2149
+ worktree_path: workerPath,
2150
+ worktree_branch: 'wk1-merge-noadvance-branch',
2151
+ worktree_detached: false,
2152
+ worktree_created: false,
2153
+ };
2154
+ await saveTeamConfig(cfg, repo);
2155
+ const realGit = execFileSync('bash', ['-lc', 'command -v git'], { encoding: 'utf-8' }).trim();
2156
+ await writeFile(join(fakeBinDir, 'git'), `#!/usr/bin/env bash
2157
+ set -euo pipefail
2158
+ if [[ "\${OMX_FAKE_GIT_SUCCESS_NOOP:-}" == "merge" && "\${1:-}" == "merge" ]]; then
2159
+ exit 0
2160
+ fi
2161
+ exec "${realGit}" "$@"
2162
+ `, { mode: 0o755 });
2163
+ process.env.PATH = `${fakeBinDir}:${previousPath ?? ''}`;
2164
+ process.env.OMX_FAKE_GIT_SUCCESS_NOOP = 'merge';
2165
+ const leaderHeadBefore = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: repo, encoding: 'utf-8' }).trim();
2166
+ await monitorTeam('team-merge-noadvance', repo);
2167
+ const leaderHeadAfter = execFileSync(realGit, ['rev-parse', 'HEAD'], { cwd: repo, encoding: 'utf-8' }).trim();
2168
+ assert.equal(leaderHeadAfter, leaderHeadBefore, 'leader HEAD should stay unchanged in regression setup');
2169
+ const snapshot = await readMonitorSnapshot('team-merge-noadvance', repo);
2170
+ assert.equal(snapshot?.integrationByWorker?.['worker-1']?.status, 'integration_failed');
2171
+ assert.equal(snapshot?.integrationByWorker?.['worker-1']?.last_integrated_head, undefined);
2172
+ const leaderMailbox = await listMailboxMessages('team-merge-noadvance', 'leader-fixed', repo);
2173
+ assert.equal(leaderMailbox.some((message) => /INTEGRATED:/.test(message.body)), false);
2174
+ assert.equal(leaderMailbox.some((message) => /INTEGRATION FAILED:/.test(message.body)), true);
2175
+ const events = await readTeamEvents('team-merge-noadvance', repo, { wakeableOnly: false });
2176
+ assert.equal(events.some((event) => event.type === 'worker_integration_failed'), true);
2177
+ }
2178
+ finally {
2179
+ if (typeof previousPath === 'string')
2180
+ process.env.PATH = previousPath;
2181
+ else
2182
+ delete process.env.PATH;
2183
+ if (typeof previousFakeMode === 'string')
2184
+ process.env.OMX_FAKE_GIT_SUCCESS_NOOP = previousFakeMode;
2185
+ else
2186
+ delete process.env.OMX_FAKE_GIT_SUCCESS_NOOP;
2187
+ await rm(fakeBinDir, { recursive: true, force: true });
2188
+ if (workerPath) {
2189
+ await rm(workerPath, { recursive: true, force: true });
2190
+ }
2191
+ await rm(repo, { recursive: true, force: true });
2192
+ }
2193
+ });
1828
2194
  it('monitorTeam uses cherry-pick for diverged worker (hybrid cherry-pick path)', async () => {
1829
2195
  const repo = await initRepo();
1830
2196
  let workerPath = '';
@@ -2443,6 +2809,92 @@ esac
2443
2809
  await rm(cwd, { recursive: true, force: true });
2444
2810
  }
2445
2811
  });
2812
+ it('shutdownTeam reconciles persisted worker panes with live tmux panes before teardown', async () => {
2813
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-pane-reconcile-'));
2814
+ try {
2815
+ await withMockTmuxFixture({
2816
+ dirPrefix: 'omx-runtime-shutdown-pane-reconcile-bin-',
2817
+ tmuxScript: (tmuxLogPath) => `#!/bin/sh
2818
+ set -eu
2819
+ printf '%s\\n' "$*" >> "${tmuxLogPath}"
2820
+ restored_marker="${tmuxLogPath}.restored"
2821
+ case "$1" in
2822
+ -V)
2823
+ echo "tmux 3.4"
2824
+ exit 0
2825
+ ;;
2826
+ list-panes)
2827
+ case "$*" in
2828
+ *"-t leader:0 -F #{pane_dead} #{pane_pid}"*)
2829
+ exit 1
2830
+ ;;
2831
+ *"-t %13 -F #{pane_pid}"*)
2832
+ echo "1013"
2833
+ exit 0
2834
+ ;;
2835
+ *"-t %14 -F #{pane_pid}"*)
2836
+ echo "1014"
2837
+ exit 0
2838
+ ;;
2839
+ *"-t leader:0 -F #{pane_id}"*"#{pane_current_command}"*)
2840
+ printf "%%11\\tzsh\\tzsh\\n%%12\\tnode\\tnode /tmp/bin/omx.js hud --watch\\n%%13\\tcodex\\tcodex\\n%%14\\tcodex\\tcodex\\n"
2841
+ if [ -f "$restored_marker" ]; then
2842
+ printf "%%44\\tnode\\tnode /tmp/bin/omx.js hud --watch\\n"
2843
+ fi
2844
+ exit 0
2845
+ ;;
2846
+ *)
2847
+ exit 1
2848
+ ;;
2849
+ esac
2850
+ ;;
2851
+ split-window)
2852
+ : > "$restored_marker"
2853
+ printf '%%44\\n'
2854
+ exit 0
2855
+ ;;
2856
+ kill-pane)
2857
+ if [ "\${3:-}" = "%999" ]; then
2858
+ echo "missing pane" >&2
2859
+ exit 1
2860
+ fi
2861
+ exit 0
2862
+ ;;
2863
+ kill-session|select-pane|run-shell)
2864
+ exit 0
2865
+ ;;
2866
+ *)
2867
+ exit 0
2868
+ ;;
2869
+ esac
2870
+ `,
2871
+ }, async ({ tmuxLogPath }) => {
2872
+ await initTeamState('team-shutdown-pane-reconcile', 'shutdown pane reconcile test', 'executor', 2, cwd);
2873
+ const config = await readTeamConfig('team-shutdown-pane-reconcile', cwd);
2874
+ assert.ok(config);
2875
+ if (!config)
2876
+ return;
2877
+ config.tmux_session = 'leader:0';
2878
+ config.leader_pane_id = '%11';
2879
+ config.hud_pane_id = '%12';
2880
+ config.workers[0].pane_id = '';
2881
+ config.workers[1].pane_id = '%999';
2882
+ await saveTeamConfig(config, cwd);
2883
+ await shutdownTeam('team-shutdown-pane-reconcile', cwd, { force: true });
2884
+ const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
2885
+ assert.doesNotMatch(tmuxLog, /kill-pane -t %11/);
2886
+ assert.match(tmuxLog, /kill-pane -t %12/);
2887
+ assert.match(tmuxLog, /kill-pane -t %13/);
2888
+ assert.match(tmuxLog, /kill-pane -t %14/);
2889
+ assert.match(tmuxLog, /kill-pane -t %999/);
2890
+ assert.match(tmuxLog, new RegExp(`split-window -v -l ${HUD_TMUX_TEAM_HEIGHT_LINES} -t %11 -d -P -F #\{pane_id\}`));
2891
+ assert.doesNotMatch(tmuxLog, /kill-pane -t %44/);
2892
+ });
2893
+ }
2894
+ finally {
2895
+ await rm(cwd, { recursive: true, force: true });
2896
+ }
2897
+ });
2446
2898
  it('shutdownTeam restores a standalone HUD pane after tearing down the team HUD', async () => {
2447
2899
  const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-restore-hud-'));
2448
2900
  try {
@@ -2922,6 +3374,7 @@ esac
2922
3374
  if (!cfg)
2923
3375
  throw new Error('missing team config');
2924
3376
  cfg.leader_pane_id = '%55';
3377
+ cfg.team_state_root = '/tmp/custom-team-state-root';
2925
3378
  await saveTeamConfig(cfg, cwd);
2926
3379
  const manifestPath = teamStateTestPath(cwd, 'team', 'team-leader-inject', 'manifest.v2.json');
2927
3380
  const manifest = JSON.parse(await readFile(manifestPath, 'utf-8'));
@@ -2937,6 +3390,74 @@ esac
2937
3390
  const latest = requests[requests.length - 1];
2938
3391
  assert.equal(latest?.status, 'notified');
2939
3392
  assert.equal(latest?.last_reason, 'fallback_confirmed:leader_mailbox_notified');
3393
+ assert.match(latest?.trigger_message ?? '', /Read \/tmp\/custom-team-state-root\/team\/team-leader-inject\/mailbox\/leader-fixed\.json; new msg from worker-1\./);
3394
+ const deliveryLog = await readTeamDeliveryLog(cwd);
3395
+ const runtimeEntries = deliveryLog.filter((entry) => entry.event === 'dispatch_result'
3396
+ && entry.source === 'team.runtime'
3397
+ && entry.to_worker === 'leader-fixed'
3398
+ && entry.transport === 'mailbox'
3399
+ && entry.result === 'confirmed'
3400
+ && typeof entry.reason === 'string'
3401
+ && String(entry.reason).includes('leader_mailbox_notified'));
3402
+ assert.equal(runtimeEntries.length, 1, 'leader hook-preferred confirmation should emit exactly one runtime dispatch_result entry');
3403
+ });
3404
+ }
3405
+ finally {
3406
+ await rm(cwd, { recursive: true, force: true });
3407
+ }
3408
+ });
3409
+ it('sendWorkerMessage keeps failed hook receipts failed when fallback mailbox persistence confirms delivery', async () => {
3410
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-leader-failed-receipt-'));
3411
+ try {
3412
+ await withMockTmuxFixture({
3413
+ dirPrefix: 'omx-runtime-leader-failed-receipt-bin-',
3414
+ tmuxScript: (tmuxLogPath) => `#!/bin/sh
3415
+ set -eu
3416
+ printf '%s\n' "$*" >> "${tmuxLogPath}"
3417
+ case "\${1:-}" in
3418
+ send-keys)
3419
+ exit 0
3420
+ ;;
3421
+ *)
3422
+ exit 0
3423
+ ;;
3424
+ esac
3425
+ `,
3426
+ }, async () => {
3427
+ await initTeamState('team-leader-failed-receipt', 'leader failed receipt fallback test', 'executor', 1, cwd);
3428
+ const cfg = await readTeamConfig('team-leader-failed-receipt', cwd);
3429
+ assert.ok(cfg);
3430
+ if (!cfg)
3431
+ throw new Error('missing team config');
3432
+ cfg.leader_pane_id = '%55';
3433
+ await saveTeamConfig(cfg, cwd);
3434
+ const manifestPath = teamStateTestPath(cwd, 'team', 'team-leader-failed-receipt', 'manifest.v2.json');
3435
+ const manifest = JSON.parse(await readFile(manifestPath, 'utf-8'));
3436
+ manifest.policy = { ...(manifest.policy || {}), dispatch_ack_timeout_ms: 250 };
3437
+ await writeFile(manifestPath, JSON.stringify(manifest, null, 2));
3438
+ const sendPromise = sendWorkerMessage('team-leader-failed-receipt', 'worker-1', 'leader-fixed', 'hello failed receipt', cwd);
3439
+ const deadline = Date.now() + 2_000;
3440
+ let requestId = null;
3441
+ while (Date.now() < deadline && !requestId) {
3442
+ const requests = await listDispatchRequests('team-leader-failed-receipt', cwd, { kind: 'mailbox', to_worker: 'leader-fixed' });
3443
+ requestId = requests[requests.length - 1]?.request_id ?? null;
3444
+ if (!requestId)
3445
+ await new Promise((resolve) => setTimeout(resolve, 20));
3446
+ }
3447
+ assert.ok(requestId, 'expected mailbox dispatch request to be queued');
3448
+ if (!requestId)
3449
+ throw new Error('missing request id');
3450
+ await transitionDispatchRequest('team-leader-failed-receipt', requestId, 'pending', 'failed', { last_reason: 'hook_failed:test_receipt' }, cwd);
3451
+ const outcome = await sendPromise;
3452
+ assert.equal(outcome.ok, true);
3453
+ assert.equal(outcome.reason, 'fallback_confirmed_after_failed_receipt:leader_mailbox_notified');
3454
+ const requests = await listDispatchRequests('team-leader-failed-receipt', cwd, { kind: 'mailbox', to_worker: 'leader-fixed' });
3455
+ const latest = requests[requests.length - 1];
3456
+ assert.equal(latest?.request_id, requestId);
3457
+ assert.equal(latest?.status, 'failed');
3458
+ assert.equal(latest?.last_reason, 'fallback_confirmed_after_failed_receipt:leader_mailbox_notified');
3459
+ const mailbox = await listMailboxMessages('team-leader-failed-receipt', 'leader-fixed', cwd);
3460
+ assert.ok(mailbox[0]?.notified_at, 'fallback mailbox persistence should still mark notified_at');
2940
3461
  });
2941
3462
  }
2942
3463
  finally {
@@ -2963,6 +3484,13 @@ esac
2963
3484
  const pending = requests.find((r) => r.status === 'pending' && r.to_worker === 'leader-fixed');
2964
3485
  assert.ok(pending, 'expected a pending leader-fixed dispatch request');
2965
3486
  assert.equal(pending?.last_reason, 'leader_pane_missing_deferred');
3487
+ const deliveryLog = await readTeamDeliveryLog(cwd);
3488
+ const runtimeEntries = deliveryLog.filter((entry) => entry.event === 'dispatch_result'
3489
+ && entry.source === 'team.runtime'
3490
+ && entry.to_worker === 'leader-fixed'
3491
+ && entry.transport === 'mailbox'
3492
+ && entry.reason === 'leader_pane_missing_mailbox_persisted');
3493
+ assert.equal(runtimeEntries.length, 1, 'leader missing-pane fallback should emit exactly one runtime dispatch_result entry');
2966
3494
  }
2967
3495
  finally {
2968
3496
  await rm(cwd, { recursive: true, force: true });