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
@@ -0,0 +1,1050 @@
1
+ import assert from "node:assert/strict";
2
+ import { execFileSync } from "node:child_process";
3
+ import { existsSync } from "node:fs";
4
+ import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
5
+ import { tmpdir } from "node:os";
6
+ import { dirname, join } from "node:path";
7
+ import { describe, it } from "node:test";
8
+ import { buildManagedCodexHooksConfig } from "../../config/codex-hooks.js";
9
+ import { initTeamState } from "../../team/state.js";
10
+ import { dispatchCodexNativeHook, mapCodexHookEventToOmxEvent, resolveSessionOwnerPidFromAncestry, } from "../codex-native-hook.js";
11
+ async function writeJson(path, value) {
12
+ await mkdir(dirname(path), { recursive: true }).catch(() => { });
13
+ await writeFile(path, JSON.stringify(value, null, 2));
14
+ }
15
+ const TEAM_STOP_COMMIT_GUIDANCE = " If system-generated worker auto-checkpoint commits exist, rewrite them into Lore-format final commits before merge/finalization.";
16
+ describe("codex native hook config", () => {
17
+ it("builds the expected managed hooks.json shape", () => {
18
+ const config = buildManagedCodexHooksConfig("/tmp/omx");
19
+ assert.deepEqual(Object.keys(config.hooks), [
20
+ "SessionStart",
21
+ "PreToolUse",
22
+ "PostToolUse",
23
+ "UserPromptSubmit",
24
+ "Stop",
25
+ ]);
26
+ const preToolUse = config.hooks.PreToolUse[0];
27
+ assert.equal(preToolUse.matcher, "Bash");
28
+ assert.match(String(preToolUse.hooks?.[0]?.command || ""), /codex-native-hook\.js"?$/);
29
+ const stop = config.hooks.Stop[0];
30
+ assert.equal(stop.hooks?.[0]?.timeout, 30);
31
+ });
32
+ });
33
+ describe("codex native hook dispatch", () => {
34
+ it("maps Codex events onto OMX logical surfaces", () => {
35
+ assert.equal(mapCodexHookEventToOmxEvent("SessionStart"), "session-start");
36
+ assert.equal(mapCodexHookEventToOmxEvent("UserPromptSubmit"), "keyword-detector");
37
+ assert.equal(mapCodexHookEventToOmxEvent("PreToolUse"), "pre-tool-use");
38
+ assert.equal(mapCodexHookEventToOmxEvent("PostToolUse"), "post-tool-use");
39
+ assert.equal(mapCodexHookEventToOmxEvent("Stop"), "stop");
40
+ });
41
+ it("writes SessionStart state against the long-lived session owner pid", async () => {
42
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-start-"));
43
+ try {
44
+ const result = await dispatchCodexNativeHook({
45
+ hook_event_name: "SessionStart",
46
+ cwd,
47
+ session_id: "sess-start-1",
48
+ }, {
49
+ cwd,
50
+ sessionOwnerPid: 43210,
51
+ });
52
+ assert.equal(result.omxEventName, "session-start");
53
+ assert.deepEqual(result.outputJson, {
54
+ hookSpecificOutput: {
55
+ hookEventName: "SessionStart",
56
+ additionalContext: "OMX native SessionStart detected. Load workspace conventions from AGENTS.md, restore relevant .omx runtime/project memory context, and continue from existing mode state before making changes.",
57
+ },
58
+ });
59
+ const sessionState = JSON.parse(await readFile(join(cwd, ".omx", "state", "session.json"), "utf-8"));
60
+ assert.equal(sessionState.session_id, "sess-start-1");
61
+ assert.equal(sessionState.pid, 43210);
62
+ }
63
+ finally {
64
+ await rm(cwd, { recursive: true, force: true });
65
+ }
66
+ });
67
+ it("appends .omx/ to repo-root .gitignore during SessionStart when missing", async () => {
68
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-gitignore-"));
69
+ try {
70
+ await writeFile(join(cwd, ".gitignore"), "node_modules/\n");
71
+ execFileSync("git", ["init"], { cwd, stdio: "pipe" });
72
+ const result = await dispatchCodexNativeHook({
73
+ hook_event_name: "SessionStart",
74
+ cwd,
75
+ session_id: "sess-gitignore-1",
76
+ }, { cwd, sessionOwnerPid: 43210 });
77
+ assert.equal(result.omxEventName, "session-start");
78
+ const gitignore = await readFile(join(cwd, ".gitignore"), "utf-8");
79
+ assert.match(gitignore, /^node_modules\/\n\.omx\/\n$/);
80
+ assert.match(JSON.stringify(result.outputJson), /Added \.omx\/ to .*\.gitignore/);
81
+ }
82
+ finally {
83
+ await rm(cwd, { recursive: true, force: true });
84
+ }
85
+ });
86
+ it("includes persisted project-memory summary in SessionStart context", async () => {
87
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-memory-"));
88
+ try {
89
+ await writeJson(join(cwd, ".omx", "project-memory.json"), {
90
+ techStack: "TypeScript + Node.js",
91
+ build: "npm test",
92
+ conventions: "small diffs, verify before claim",
93
+ directives: [
94
+ { directive: "Keep native Stop bounded to real continuation decisions.", priority: "high" },
95
+ ],
96
+ notes: [
97
+ { category: "env", content: "Requires LOCAL_API_BASE for smoke tests", timestamp: new Date().toISOString() },
98
+ ],
99
+ });
100
+ const result = await dispatchCodexNativeHook({
101
+ hook_event_name: "SessionStart",
102
+ cwd,
103
+ session_id: "sess-memory-1",
104
+ }, { cwd, sessionOwnerPid: 43210 });
105
+ const serialized = JSON.stringify(result.outputJson);
106
+ assert.match(serialized, /\[Project memory\]/);
107
+ assert.match(serialized, /TypeScript \+ Node\.js/);
108
+ assert.match(serialized, /small diffs, verify before claim/);
109
+ assert.match(serialized, /Keep native Stop bounded to real continuation decisions\./);
110
+ assert.match(serialized, /Requires LOCAL_API_BASE for smoke tests/);
111
+ }
112
+ finally {
113
+ await rm(cwd, { recursive: true, force: true });
114
+ }
115
+ });
116
+ it("resolves the Codex owner from ancestry without mistaking codex-native-hook wrappers for Codex", () => {
117
+ const commands = new Map([
118
+ [2100, 'sh -c node "/repo/dist/scripts/codex-native-hook.js"'],
119
+ [1100, 'node /usr/local/bin/codex.js'],
120
+ [900, 'bash'],
121
+ ]);
122
+ const parents = new Map([
123
+ [2100, 1100],
124
+ [1100, 900],
125
+ [900, 1],
126
+ ]);
127
+ const resolved = resolveSessionOwnerPidFromAncestry(2100, {
128
+ readParentPid: (pid) => parents.get(pid) ?? null,
129
+ readProcessCommand: (pid) => commands.get(pid) ?? "",
130
+ });
131
+ assert.equal(resolved, 1100);
132
+ });
133
+ it("records keyword activation from UserPromptSubmit payloads", async () => {
134
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-"));
135
+ try {
136
+ await mkdir(join(cwd, ".omx", "state"), { recursive: true });
137
+ const result = await dispatchCodexNativeHook({
138
+ hook_event_name: "UserPromptSubmit",
139
+ cwd,
140
+ session_id: "sess-1",
141
+ thread_id: "thread-1",
142
+ turn_id: "turn-1",
143
+ prompt: "$ralplan implement issue #1307",
144
+ }, { cwd });
145
+ assert.equal(result.omxEventName, "keyword-detector");
146
+ assert.equal(result.skillState?.skill, "ralplan");
147
+ assert.ok(result.outputJson, "UserPromptSubmit should emit developer context");
148
+ assert.match(JSON.stringify(result.outputJson), /skill: ralplan activated and initial state initialized at \.omx\/state\/sessions\/sess-1\/ralplan-state\.json; write subsequent updates via omx_state MCP\./);
149
+ const statePath = join(cwd, ".omx", "state", "skill-active-state.json");
150
+ assert.equal(existsSync(statePath), true);
151
+ const state = JSON.parse(await readFile(statePath, "utf-8"));
152
+ assert.equal(state.skill, "ralplan");
153
+ assert.equal(state.active, true);
154
+ assert.equal(state.initialized_mode, "ralplan");
155
+ assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", "sess-1", "ralplan-state.json")), true);
156
+ }
157
+ finally {
158
+ await rm(cwd, { recursive: true, force: true });
159
+ }
160
+ });
161
+ it("returns a destructive-command caution on PreToolUse for rm -rf dist", async () => {
162
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-danger-"));
163
+ try {
164
+ const result = await dispatchCodexNativeHook({
165
+ hook_event_name: "PreToolUse",
166
+ cwd,
167
+ tool_name: "Bash",
168
+ tool_use_id: "tool-danger",
169
+ tool_input: { command: "rm -rf dist" },
170
+ }, { cwd });
171
+ assert.equal(result.omxEventName, "pre-tool-use");
172
+ assert.deepEqual(result.outputJson, {
173
+ hookSpecificOutput: {
174
+ hookEventName: "PreToolUse",
175
+ },
176
+ systemMessage: "Destructive Bash command detected (`rm -rf dist`). Confirm the target and expected side effects before running it.",
177
+ });
178
+ }
179
+ finally {
180
+ await rm(cwd, { recursive: true, force: true });
181
+ }
182
+ });
183
+ it("stays silent on PreToolUse for neutral pwd", async () => {
184
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-neutral-"));
185
+ try {
186
+ const result = await dispatchCodexNativeHook({
187
+ hook_event_name: "PreToolUse",
188
+ cwd,
189
+ tool_name: "Bash",
190
+ tool_use_id: "tool-neutral",
191
+ tool_input: { command: "pwd" },
192
+ }, { cwd });
193
+ assert.equal(result.omxEventName, "pre-tool-use");
194
+ assert.equal(result.outputJson, null);
195
+ }
196
+ finally {
197
+ await rm(cwd, { recursive: true, force: true });
198
+ }
199
+ });
200
+ it("returns PostToolUse remediation guidance for command-not-found output", async () => {
201
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-failure-"));
202
+ try {
203
+ const result = await dispatchCodexNativeHook({
204
+ hook_event_name: "PostToolUse",
205
+ cwd,
206
+ tool_name: "Bash",
207
+ tool_use_id: "tool-fail",
208
+ tool_input: { command: "foo --version" },
209
+ tool_response: "{\"exit_code\":127,\"stdout\":\"\",\"stderr\":\"bash: foo: command not found\"}",
210
+ }, { cwd });
211
+ assert.equal(result.omxEventName, "post-tool-use");
212
+ assert.deepEqual(result.outputJson, {
213
+ decision: "block",
214
+ reason: "The Bash output indicates a command/setup failure that should be fixed before retrying.",
215
+ hookSpecificOutput: {
216
+ hookEventName: "PostToolUse",
217
+ additionalContext: "Bash reported `command not found`, `permission denied`, or a missing file/path. Verify the command, dependency installation, PATH, file permissions, and referenced paths before retrying.",
218
+ },
219
+ });
220
+ }
221
+ finally {
222
+ await rm(cwd, { recursive: true, force: true });
223
+ }
224
+ });
225
+ it("treats stderr-only informative non-zero output as reviewable instead of a generic failure", async () => {
226
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-informative-stderr-"));
227
+ try {
228
+ const result = await dispatchCodexNativeHook({
229
+ hook_event_name: "PostToolUse",
230
+ cwd,
231
+ tool_name: "Bash",
232
+ tool_use_id: "tool-useful-stderr",
233
+ tool_input: { command: "gh pr checks" },
234
+ tool_response: "{\"exit_code\":8,\"stdout\":\"\",\"stderr\":\"build pending\\nlint pass\"}",
235
+ }, { cwd });
236
+ assert.equal(result.omxEventName, "post-tool-use");
237
+ assert.deepEqual(result.outputJson, {
238
+ decision: "block",
239
+ reason: "The Bash command returned a non-zero exit code but produced useful output that should be reviewed before retrying.",
240
+ hookSpecificOutput: {
241
+ hookEventName: "PostToolUse",
242
+ additionalContext: "The Bash output appears informative despite the non-zero exit code. Review and report the output before retrying instead of assuming the command simply failed.",
243
+ },
244
+ });
245
+ }
246
+ finally {
247
+ await rm(cwd, { recursive: true, force: true });
248
+ }
249
+ });
250
+ it("treats non-zero gh pr checks style output as informative instead of a generic failure", async () => {
251
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-informative-"));
252
+ try {
253
+ const result = await dispatchCodexNativeHook({
254
+ hook_event_name: "PostToolUse",
255
+ cwd,
256
+ tool_name: "Bash",
257
+ tool_use_id: "tool-useful",
258
+ tool_input: { command: "gh pr checks" },
259
+ tool_response: "{\"exit_code\":8,\"stdout\":\"build\\tpending\\t2m\\nlint\\tpass\\t18s\",\"stderr\":\"\"}",
260
+ }, { cwd });
261
+ assert.equal(result.omxEventName, "post-tool-use");
262
+ assert.deepEqual(result.outputJson, {
263
+ decision: "block",
264
+ reason: "The Bash command returned a non-zero exit code but produced useful output that should be reviewed before retrying.",
265
+ hookSpecificOutput: {
266
+ hookEventName: "PostToolUse",
267
+ additionalContext: "The Bash output appears informative despite the non-zero exit code. Review and report the output before retrying instead of assuming the command simply failed.",
268
+ },
269
+ });
270
+ }
271
+ finally {
272
+ await rm(cwd, { recursive: true, force: true });
273
+ }
274
+ });
275
+ it("stays silent on neutral successful PostToolUse output", async () => {
276
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-neutral-"));
277
+ try {
278
+ const result = await dispatchCodexNativeHook({
279
+ hook_event_name: "PostToolUse",
280
+ cwd,
281
+ tool_name: "Bash",
282
+ tool_use_id: "tool-ok",
283
+ tool_input: { command: "pwd" },
284
+ tool_response: "{\"exit_code\":0,\"stdout\":\"/repo\",\"stderr\":\"\"}",
285
+ }, { cwd });
286
+ assert.equal(result.omxEventName, "post-tool-use");
287
+ assert.equal(result.outputJson, null);
288
+ }
289
+ finally {
290
+ await rm(cwd, { recursive: true, force: true });
291
+ }
292
+ });
293
+ it("returns Stop continuation output while Autopilot is active", async () => {
294
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-autopilot-"));
295
+ try {
296
+ const stateDir = join(cwd, ".omx", "state");
297
+ await mkdir(stateDir, { recursive: true });
298
+ await writeJson(join(stateDir, "autopilot-state.json"), {
299
+ active: true,
300
+ current_phase: "execution",
301
+ });
302
+ const result = await dispatchCodexNativeHook({
303
+ hook_event_name: "Stop",
304
+ cwd,
305
+ session_id: "sess-stop-autopilot",
306
+ }, { cwd });
307
+ assert.equal(result.omxEventName, "stop");
308
+ assert.deepEqual(result.outputJson, {
309
+ decision: "block",
310
+ reason: "OMX autopilot is still active (phase: execution); continue the task and gather fresh verification evidence before stopping.",
311
+ stopReason: "autopilot_execution",
312
+ systemMessage: "OMX autopilot is still active (phase: execution).",
313
+ });
314
+ }
315
+ finally {
316
+ await rm(cwd, { recursive: true, force: true });
317
+ }
318
+ });
319
+ it("returns Stop continuation output while Ultrawork is active", async () => {
320
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ultrawork-"));
321
+ try {
322
+ const stateDir = join(cwd, ".omx", "state");
323
+ await mkdir(stateDir, { recursive: true });
324
+ await writeJson(join(stateDir, "ultrawork-state.json"), {
325
+ active: true,
326
+ current_phase: "executing",
327
+ });
328
+ const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: "sess-stop-ultrawork" }, { cwd });
329
+ assert.deepEqual(result.outputJson, {
330
+ decision: "block",
331
+ reason: "OMX ultrawork is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.",
332
+ stopReason: "ultrawork_executing",
333
+ systemMessage: "OMX ultrawork is still active (phase: executing).",
334
+ });
335
+ }
336
+ finally {
337
+ await rm(cwd, { recursive: true, force: true });
338
+ }
339
+ });
340
+ it("returns Stop continuation output while UltraQA is active", async () => {
341
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ultraqa-"));
342
+ try {
343
+ const stateDir = join(cwd, ".omx", "state");
344
+ await mkdir(stateDir, { recursive: true });
345
+ await writeJson(join(stateDir, "ultraqa-state.json"), {
346
+ active: true,
347
+ current_phase: "diagnose",
348
+ });
349
+ const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: "sess-stop-ultraqa" }, { cwd });
350
+ assert.deepEqual(result.outputJson, {
351
+ decision: "block",
352
+ reason: "OMX ultraqa is still active (phase: diagnose); continue the task and gather fresh verification evidence before stopping.",
353
+ stopReason: "ultraqa_diagnose",
354
+ systemMessage: "OMX ultraqa is still active (phase: diagnose).",
355
+ });
356
+ }
357
+ finally {
358
+ await rm(cwd, { recursive: true, force: true });
359
+ }
360
+ });
361
+ it("returns Stop continuation output while team phase is non-terminal", async () => {
362
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-"));
363
+ try {
364
+ const stateDir = join(cwd, ".omx", "state");
365
+ await mkdir(stateDir, { recursive: true });
366
+ await writeJson(join(stateDir, "team-state.json"), {
367
+ active: true,
368
+ current_phase: "team-exec",
369
+ team_name: "review-team",
370
+ });
371
+ await writeJson(join(stateDir, "team", "review-team", "phase.json"), {
372
+ current_phase: "team-verify",
373
+ max_fix_attempts: 3,
374
+ current_fix_attempt: 0,
375
+ transitions: [],
376
+ updated_at: new Date().toISOString(),
377
+ });
378
+ const result = await dispatchCodexNativeHook({
379
+ hook_event_name: "Stop",
380
+ cwd,
381
+ session_id: "sess-stop-team",
382
+ }, { cwd });
383
+ assert.equal(result.omxEventName, "stop");
384
+ assert.deepEqual(result.outputJson, {
385
+ decision: "block",
386
+ reason: `OMX team pipeline is still active (review-team) at phase team-verify; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`,
387
+ stopReason: "team_team-verify",
388
+ systemMessage: "OMX team pipeline is still active at phase team-verify.",
389
+ });
390
+ }
391
+ finally {
392
+ await rm(cwd, { recursive: true, force: true });
393
+ }
394
+ });
395
+ it("blocks Stop for a team worker with a non-terminal assigned task via native worker context", async () => {
396
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-"));
397
+ const prevTeamWorker = process.env.OMX_TEAM_WORKER;
398
+ const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
399
+ const prevLeaderCwd = process.env.OMX_TEAM_LEADER_CWD;
400
+ try {
401
+ await initTeamState("worker-stop-team", "worker stop fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-worker" });
402
+ const workerDir = join(cwd, ".omx", "state", "team", "worker-stop-team", "workers", "worker-1");
403
+ await writeJson(join(workerDir, "status.json"), {
404
+ state: "idle",
405
+ current_task_id: "1",
406
+ updated_at: new Date().toISOString(),
407
+ });
408
+ await writeJson(join(cwd, ".omx", "state", "team", "worker-stop-team", "tasks", "task-1.json"), {
409
+ id: "1",
410
+ subject: "hook task",
411
+ description: "finish hook task",
412
+ status: "in_progress",
413
+ owner: "worker-1",
414
+ created_at: new Date().toISOString(),
415
+ });
416
+ process.env.OMX_TEAM_WORKER = "worker-stop-team/worker-1";
417
+ process.env.OMX_TEAM_STATE_ROOT = join(cwd, ".omx", "state");
418
+ process.env.OMX_TEAM_LEADER_CWD = cwd;
419
+ const result = await dispatchCodexNativeHook({
420
+ hook_event_name: "Stop",
421
+ cwd: join(cwd, ".omx", "team", "worker-stop-team", "worktrees", "worker-1"),
422
+ session_id: "sess-stop-team-worker",
423
+ }, { cwd: join(cwd, ".omx", "team", "worker-stop-team", "worktrees", "worker-1") });
424
+ assert.deepEqual(result.outputJson, {
425
+ decision: "block",
426
+ reason: "OMX team worker worker-1 is still assigned non-terminal task 1 (in_progress); continue the current assigned task or report a concrete blocker before stopping.",
427
+ stopReason: "team_worker_worker-1_1_in_progress",
428
+ systemMessage: "OMX team worker worker-1 is still assigned task 1 (in_progress).",
429
+ });
430
+ }
431
+ finally {
432
+ if (typeof prevTeamWorker === "string")
433
+ process.env.OMX_TEAM_WORKER = prevTeamWorker;
434
+ else
435
+ delete process.env.OMX_TEAM_WORKER;
436
+ if (typeof prevTeamStateRoot === "string")
437
+ process.env.OMX_TEAM_STATE_ROOT = prevTeamStateRoot;
438
+ else
439
+ delete process.env.OMX_TEAM_STATE_ROOT;
440
+ if (typeof prevLeaderCwd === "string")
441
+ process.env.OMX_TEAM_LEADER_CWD = prevLeaderCwd;
442
+ else
443
+ delete process.env.OMX_TEAM_LEADER_CWD;
444
+ await rm(cwd, { recursive: true, force: true });
445
+ }
446
+ });
447
+ it("does not block Stop for a team worker when assigned task is terminal", async () => {
448
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-terminal-"));
449
+ const prevTeamWorker = process.env.OMX_TEAM_WORKER;
450
+ const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
451
+ try {
452
+ await initTeamState("worker-stop-team-terminal", "worker stop terminal fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-worker-terminal" });
453
+ const workerDir = join(cwd, ".omx", "state", "team", "worker-stop-team-terminal", "workers", "worker-1");
454
+ await writeJson(join(workerDir, "status.json"), {
455
+ state: "done",
456
+ current_task_id: "1",
457
+ updated_at: new Date().toISOString(),
458
+ });
459
+ await writeJson(join(cwd, ".omx", "state", "team", "worker-stop-team-terminal", "tasks", "task-1.json"), {
460
+ id: "1",
461
+ subject: "hook task",
462
+ description: "finish hook task",
463
+ status: "completed",
464
+ owner: "worker-1",
465
+ created_at: new Date().toISOString(),
466
+ });
467
+ process.env.OMX_TEAM_WORKER = "worker-stop-team-terminal/worker-1";
468
+ process.env.OMX_TEAM_STATE_ROOT = join(cwd, ".omx", "state");
469
+ const result = await dispatchCodexNativeHook({
470
+ hook_event_name: "Stop",
471
+ cwd,
472
+ session_id: "sess-stop-team-worker-terminal",
473
+ }, { cwd });
474
+ assert.equal(result.outputJson, null);
475
+ }
476
+ finally {
477
+ if (typeof prevTeamWorker === "string")
478
+ process.env.OMX_TEAM_WORKER = prevTeamWorker;
479
+ else
480
+ delete process.env.OMX_TEAM_WORKER;
481
+ if (typeof prevTeamStateRoot === "string")
482
+ process.env.OMX_TEAM_STATE_ROOT = prevTeamStateRoot;
483
+ else
484
+ delete process.env.OMX_TEAM_STATE_ROOT;
485
+ await rm(cwd, { recursive: true, force: true });
486
+ }
487
+ });
488
+ it("returns Stop continuation output from canonical team state when coarse mode state is missing", async () => {
489
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-canonical-"));
490
+ try {
491
+ await initTeamState("canonical-team", "canonical stop fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-canonical" });
492
+ const result = await dispatchCodexNativeHook({
493
+ hook_event_name: "Stop",
494
+ cwd,
495
+ session_id: "sess-stop-team-canonical",
496
+ }, { cwd });
497
+ assert.equal(result.omxEventName, "stop");
498
+ assert.deepEqual(result.outputJson, {
499
+ decision: "block",
500
+ reason: `OMX team pipeline is still active (canonical-team) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`,
501
+ stopReason: "team_team-exec",
502
+ systemMessage: "OMX team pipeline is still active at phase team-exec.",
503
+ });
504
+ }
505
+ finally {
506
+ await rm(cwd, { recursive: true, force: true });
507
+ }
508
+ });
509
+ it("re-fires canonical-team Stop output for a later fresh Stop reply when coarse mode state is missing", async () => {
510
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-canonical-refire-"));
511
+ try {
512
+ await initTeamState("canonical-team-refire", "canonical stop fallback refire", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-canonical-refire" });
513
+ await dispatchCodexNativeHook({
514
+ hook_event_name: "Stop",
515
+ cwd,
516
+ session_id: "sess-stop-team-canonical-refire",
517
+ thread_id: "thread-stop-team-canonical-refire",
518
+ turn_id: "turn-stop-team-canonical-refire-1",
519
+ }, { cwd });
520
+ const result = await dispatchCodexNativeHook({
521
+ hook_event_name: "Stop",
522
+ cwd,
523
+ session_id: "sess-stop-team-canonical-refire",
524
+ thread_id: "thread-stop-team-canonical-refire",
525
+ turn_id: "turn-stop-team-canonical-refire-2",
526
+ stop_hook_active: true,
527
+ }, { cwd });
528
+ assert.equal(result.omxEventName, "stop");
529
+ assert.deepEqual(result.outputJson, {
530
+ decision: "block",
531
+ reason: `OMX team pipeline is still active (canonical-team-refire) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`,
532
+ stopReason: "team_team-exec",
533
+ systemMessage: "OMX team pipeline is still active at phase team-exec.",
534
+ });
535
+ }
536
+ finally {
537
+ await rm(cwd, { recursive: true, force: true });
538
+ }
539
+ });
540
+ it("does not block Stop from canonical team state alone when the canonical phase is terminal", async () => {
541
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-terminal-"));
542
+ try {
543
+ await initTeamState("terminal-team", "terminal stop fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-terminal" });
544
+ await writeJson(join(cwd, ".omx", "state", "team", "terminal-team", "phase.json"), {
545
+ current_phase: "complete",
546
+ max_fix_attempts: 3,
547
+ current_fix_attempt: 0,
548
+ transitions: [],
549
+ updated_at: new Date().toISOString(),
550
+ });
551
+ const result = await dispatchCodexNativeHook({
552
+ hook_event_name: "Stop",
553
+ cwd,
554
+ session_id: "sess-stop-team-terminal",
555
+ }, { cwd });
556
+ assert.equal(result.omxEventName, "stop");
557
+ assert.equal(result.outputJson, null);
558
+ }
559
+ finally {
560
+ await rm(cwd, { recursive: true, force: true });
561
+ }
562
+ });
563
+ it("returns Stop continuation output from canonical team state when manifest session ownership is missing", async () => {
564
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-legacy-"));
565
+ try {
566
+ await initTeamState("legacy-team", "legacy stop fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-legacy" });
567
+ const manifestPath = join(cwd, ".omx", "state", "team", "legacy-team", "manifest.v2.json");
568
+ const manifest = JSON.parse(await readFile(manifestPath, "utf-8"));
569
+ await writeJson(manifestPath, {
570
+ ...manifest,
571
+ leader: {
572
+ ...manifest.leader,
573
+ session_id: "",
574
+ },
575
+ });
576
+ const result = await dispatchCodexNativeHook({
577
+ hook_event_name: "Stop",
578
+ cwd,
579
+ session_id: "sess-stop-team-legacy",
580
+ }, { cwd });
581
+ assert.equal(result.omxEventName, "stop");
582
+ assert.deepEqual(result.outputJson, {
583
+ decision: "block",
584
+ reason: `OMX team pipeline is still active (legacy-team) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`,
585
+ stopReason: "team_team-exec",
586
+ systemMessage: "OMX team pipeline is still active at phase team-exec.",
587
+ });
588
+ }
589
+ finally {
590
+ await rm(cwd, { recursive: true, force: true });
591
+ }
592
+ });
593
+ it("reads canonical Stop fallback team state from OMX_TEAM_STATE_ROOT when configured", async () => {
594
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-root-"));
595
+ const sharedRoot = join(cwd, "shared-root");
596
+ const priorTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
597
+ try {
598
+ process.env.OMX_TEAM_STATE_ROOT = "shared-root";
599
+ await initTeamState("canonical-root-team", "canonical stop root fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-root", OMX_TEAM_STATE_ROOT: "shared-root" });
600
+ const result = await dispatchCodexNativeHook({
601
+ hook_event_name: "Stop",
602
+ cwd,
603
+ session_id: "sess-stop-team-root",
604
+ }, { cwd });
605
+ assert.equal(result.omxEventName, "stop");
606
+ assert.deepEqual(result.outputJson, {
607
+ decision: "block",
608
+ reason: `OMX team pipeline is still active (canonical-root-team) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`,
609
+ stopReason: "team_team-exec",
610
+ systemMessage: "OMX team pipeline is still active at phase team-exec.",
611
+ });
612
+ assert.equal(existsSync(join(sharedRoot, "team", "canonical-root-team", "phase.json")), true);
613
+ }
614
+ finally {
615
+ if (typeof priorTeamStateRoot === "string")
616
+ process.env.OMX_TEAM_STATE_ROOT = priorTeamStateRoot;
617
+ else
618
+ delete process.env.OMX_TEAM_STATE_ROOT;
619
+ await rm(cwd, { recursive: true, force: true });
620
+ }
621
+ });
622
+ it("returns Stop continuation output from canonical team state rooted via OMX_TEAM_STATE_ROOT", async () => {
623
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-env-root-"));
624
+ const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
625
+ try {
626
+ process.env.OMX_TEAM_STATE_ROOT = "shared-team-state";
627
+ await initTeamState("env-root-team", "env root stop fallback", "executor", 1, cwd, undefined, {
628
+ ...process.env,
629
+ OMX_SESSION_ID: "sess-stop-team-env-root",
630
+ OMX_TEAM_STATE_ROOT: "shared-team-state",
631
+ });
632
+ const result = await dispatchCodexNativeHook({
633
+ hook_event_name: "Stop",
634
+ cwd,
635
+ session_id: "sess-stop-team-env-root",
636
+ }, { cwd });
637
+ assert.equal(result.omxEventName, "stop");
638
+ assert.deepEqual(result.outputJson, {
639
+ decision: "block",
640
+ reason: `OMX team pipeline is still active (env-root-team) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`,
641
+ stopReason: "team_team-exec",
642
+ systemMessage: "OMX team pipeline is still active at phase team-exec.",
643
+ });
644
+ }
645
+ finally {
646
+ if (typeof previousTeamStateRoot === "string")
647
+ process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot;
648
+ else
649
+ delete process.env.OMX_TEAM_STATE_ROOT;
650
+ await rm(cwd, { recursive: true, force: true });
651
+ }
652
+ });
653
+ it("returns Stop continuation output for active ralplan skill without active subagents", async () => {
654
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-skill-"));
655
+ try {
656
+ const stateDir = join(cwd, ".omx", "state");
657
+ await mkdir(join(stateDir, "sessions", "sess-stop-skill"), { recursive: true });
658
+ await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-skill" });
659
+ await writeJson(join(stateDir, "sessions", "sess-stop-skill", "skill-active-state.json"), {
660
+ active: true,
661
+ skill: "ralplan",
662
+ phase: "planning",
663
+ });
664
+ const result = await dispatchCodexNativeHook({
665
+ hook_event_name: "Stop",
666
+ cwd,
667
+ session_id: "sess-stop-skill",
668
+ }, { cwd });
669
+ assert.equal(result.omxEventName, "stop");
670
+ assert.deepEqual(result.outputJson, {
671
+ decision: "block",
672
+ reason: "OMX skill ralplan is still active (phase: planning); continue until the current ralplan workflow reaches a terminal state.",
673
+ stopReason: "skill_ralplan_planning",
674
+ systemMessage: "OMX skill ralplan is still active (phase: planning).",
675
+ });
676
+ }
677
+ finally {
678
+ await rm(cwd, { recursive: true, force: true });
679
+ }
680
+ });
681
+ it("does not block on active ralplan skill when subagents are still active", async () => {
682
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-skill-subagent-"));
683
+ try {
684
+ const stateDir = join(cwd, ".omx", "state");
685
+ await mkdir(join(stateDir, "sessions", "sess-stop-skill-subagent"), { recursive: true });
686
+ await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-skill-subagent" });
687
+ await writeJson(join(stateDir, "sessions", "sess-stop-skill-subagent", "skill-active-state.json"), {
688
+ active: true,
689
+ skill: "ralplan",
690
+ phase: "planning",
691
+ });
692
+ await writeJson(join(stateDir, "subagent-tracking.json"), {
693
+ schemaVersion: 1,
694
+ sessions: {
695
+ "sess-stop-skill-subagent": {
696
+ session_id: "sess-stop-skill-subagent",
697
+ leader_thread_id: "leader-1",
698
+ updated_at: new Date().toISOString(),
699
+ threads: {
700
+ "leader-1": {
701
+ thread_id: "leader-1",
702
+ kind: "leader",
703
+ first_seen_at: new Date().toISOString(),
704
+ last_seen_at: new Date().toISOString(),
705
+ turn_count: 1,
706
+ },
707
+ "sub-1": {
708
+ thread_id: "sub-1",
709
+ kind: "subagent",
710
+ first_seen_at: new Date().toISOString(),
711
+ last_seen_at: new Date().toISOString(),
712
+ turn_count: 1,
713
+ },
714
+ },
715
+ },
716
+ },
717
+ });
718
+ const result = await dispatchCodexNativeHook({
719
+ hook_event_name: "Stop",
720
+ cwd,
721
+ session_id: "sess-stop-skill-subagent",
722
+ }, { cwd });
723
+ assert.equal(result.omxEventName, "stop");
724
+ assert.equal(result.outputJson, null);
725
+ }
726
+ finally {
727
+ await rm(cwd, { recursive: true, force: true });
728
+ }
729
+ });
730
+ it("returns Stop continuation output for active deep-interview skill without active subagents", async () => {
731
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-deep-interview-"));
732
+ try {
733
+ const stateDir = join(cwd, ".omx", "state");
734
+ await mkdir(join(stateDir, "sessions", "sess-stop-deep-interview"), { recursive: true });
735
+ await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-deep-interview" });
736
+ await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview", "skill-active-state.json"), {
737
+ active: true,
738
+ skill: "deep-interview",
739
+ phase: "planning",
740
+ });
741
+ const result = await dispatchCodexNativeHook({
742
+ hook_event_name: "Stop",
743
+ cwd,
744
+ session_id: "sess-stop-deep-interview",
745
+ }, { cwd });
746
+ assert.deepEqual(result.outputJson, {
747
+ decision: "block",
748
+ reason: "OMX skill deep-interview is still active (phase: planning); continue until the current deep-interview workflow reaches a terminal state.",
749
+ stopReason: "skill_deep-interview_planning",
750
+ systemMessage: "OMX skill deep-interview is still active (phase: planning).",
751
+ });
752
+ }
753
+ finally {
754
+ await rm(cwd, { recursive: true, force: true });
755
+ }
756
+ });
757
+ it("ignores root skill-active fallback from a different thread when evaluating Stop", async () => {
758
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-foreign-thread-"));
759
+ try {
760
+ const stateDir = join(cwd, ".omx", "state");
761
+ await mkdir(stateDir, { recursive: true });
762
+ await writeJson(join(stateDir, "skill-active-state.json"), {
763
+ active: true,
764
+ skill: "deep-interview",
765
+ phase: "planning",
766
+ session_id: "",
767
+ thread_id: "other-thread",
768
+ });
769
+ const result = await dispatchCodexNativeHook({
770
+ hook_event_name: "Stop",
771
+ cwd,
772
+ session_id: "sess-stop-main",
773
+ thread_id: "main-thread",
774
+ }, { cwd });
775
+ assert.equal(result.outputJson, null);
776
+ }
777
+ finally {
778
+ await rm(cwd, { recursive: true, force: true });
779
+ }
780
+ });
781
+ it("returns Stop continuation output while Ralph is active", async () => {
782
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-"));
783
+ try {
784
+ const stateDir = join(cwd, ".omx", "state");
785
+ await mkdir(stateDir, { recursive: true });
786
+ await writeFile(join(stateDir, "ralph-state.json"), JSON.stringify({
787
+ active: true,
788
+ current_phase: "executing",
789
+ }));
790
+ const result = await dispatchCodexNativeHook({
791
+ hook_event_name: "Stop",
792
+ cwd,
793
+ session_id: "sess-stop",
794
+ }, { cwd });
795
+ assert.equal(result.omxEventName, "stop");
796
+ assert.deepEqual(result.outputJson, {
797
+ decision: "block",
798
+ reason: "OMX Ralph is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.",
799
+ stopReason: "ralph_executing",
800
+ systemMessage: "OMX Ralph is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.",
801
+ });
802
+ }
803
+ finally {
804
+ await rm(cwd, { recursive: true, force: true });
805
+ }
806
+ });
807
+ it("does not re-block Ralph when Stop already continued once", async () => {
808
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-once-"));
809
+ try {
810
+ const stateDir = join(cwd, ".omx", "state");
811
+ await mkdir(stateDir, { recursive: true });
812
+ await writeFile(join(stateDir, "ralph-state.json"), JSON.stringify({
813
+ active: true,
814
+ current_phase: "executing",
815
+ }));
816
+ const result = await dispatchCodexNativeHook({
817
+ hook_event_name: "Stop",
818
+ cwd,
819
+ session_id: "sess-stop-ralph-once",
820
+ stop_hook_active: true,
821
+ }, { cwd });
822
+ assert.equal(result.omxEventName, "stop");
823
+ assert.equal(result.outputJson, null);
824
+ }
825
+ finally {
826
+ await rm(cwd, { recursive: true, force: true });
827
+ }
828
+ });
829
+ it("returns Stop continuation output for native auto-nudge stall prompts", async () => {
830
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-"));
831
+ try {
832
+ const stateDir = join(cwd, ".omx", "state");
833
+ await mkdir(stateDir, { recursive: true });
834
+ const result = await dispatchCodexNativeHook({
835
+ hook_event_name: "Stop",
836
+ cwd,
837
+ session_id: "sess-stop-auto",
838
+ last_assistant_message: "Would you like me to keep going and finish the cleanup?",
839
+ }, { cwd });
840
+ assert.equal(result.omxEventName, "stop");
841
+ assert.deepEqual(result.outputJson, {
842
+ decision: "block",
843
+ reason: "yes, proceed",
844
+ stopReason: "auto_nudge",
845
+ systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.",
846
+ });
847
+ }
848
+ finally {
849
+ await rm(cwd, { recursive: true, force: true });
850
+ }
851
+ });
852
+ it("suppresses duplicate native auto-nudge replays for the same Stop reply", async () => {
853
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-once-"));
854
+ try {
855
+ const stateDir = join(cwd, ".omx", "state");
856
+ await mkdir(stateDir, { recursive: true });
857
+ await dispatchCodexNativeHook({
858
+ hook_event_name: "Stop",
859
+ cwd,
860
+ session_id: "sess-stop-auto-once",
861
+ thread_id: "thread-stop-auto",
862
+ turn_id: "turn-stop-auto-1",
863
+ last_assistant_message: "Would you like me to continue?",
864
+ }, { cwd });
865
+ const result = await dispatchCodexNativeHook({
866
+ hook_event_name: "Stop",
867
+ cwd,
868
+ session_id: "sess-stop-auto-once",
869
+ thread_id: "thread-stop-auto",
870
+ turn_id: "turn-stop-auto-1",
871
+ stop_hook_active: true,
872
+ last_assistant_message: "Would you like me to continue?",
873
+ }, { cwd });
874
+ assert.equal(result.omxEventName, "stop");
875
+ assert.equal(result.outputJson, null);
876
+ }
877
+ finally {
878
+ await rm(cwd, { recursive: true, force: true });
879
+ }
880
+ });
881
+ it("re-fires native auto-nudge for a later fresh Stop reply even when stop_hook_active is true", async () => {
882
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-refire-"));
883
+ try {
884
+ const stateDir = join(cwd, ".omx", "state");
885
+ await mkdir(stateDir, { recursive: true });
886
+ await dispatchCodexNativeHook({
887
+ hook_event_name: "Stop",
888
+ cwd,
889
+ session_id: "sess-stop-auto-refire",
890
+ thread_id: "thread-stop-auto-refire",
891
+ turn_id: "turn-stop-auto-refire-1",
892
+ last_assistant_message: "Would you like me to continue?",
893
+ }, { cwd });
894
+ const result = await dispatchCodexNativeHook({
895
+ hook_event_name: "Stop",
896
+ cwd,
897
+ session_id: "sess-stop-auto-refire",
898
+ thread_id: "thread-stop-auto-refire",
899
+ turn_id: "turn-stop-auto-refire-2",
900
+ stop_hook_active: true,
901
+ last_assistant_message: "If you want, I can keep going and finish the cleanup.",
902
+ }, { cwd });
903
+ assert.equal(result.omxEventName, "stop");
904
+ assert.deepEqual(result.outputJson, {
905
+ decision: "block",
906
+ reason: "yes, proceed",
907
+ stopReason: "auto_nudge",
908
+ systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.",
909
+ });
910
+ }
911
+ finally {
912
+ await rm(cwd, { recursive: true, force: true });
913
+ }
914
+ });
915
+ it("suppresses native auto-nudge when only the deep-interview input lock is active", async () => {
916
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-deep-interview-lock-"));
917
+ try {
918
+ const stateDir = join(cwd, ".omx", "state");
919
+ await mkdir(join(stateDir, "sessions", "sess-stop-auto-lock"), { recursive: true });
920
+ await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-auto-lock" });
921
+ await writeJson(join(stateDir, "sessions", "sess-stop-auto-lock", "skill-active-state.json"), {
922
+ version: 1,
923
+ active: true,
924
+ skill: "deep-interview",
925
+ phase: "planning",
926
+ input_lock: {
927
+ active: true,
928
+ scope: "deep-interview-auto-approval",
929
+ blocked_inputs: ["yes", "proceed"],
930
+ message: "Deep interview is active; auto-approval shortcuts are blocked until the interview finishes.",
931
+ },
932
+ });
933
+ const result = await dispatchCodexNativeHook({
934
+ hook_event_name: "Stop",
935
+ cwd,
936
+ session_id: "sess-stop-auto-lock",
937
+ thread_id: "thread-stop-auto-lock",
938
+ turn_id: "turn-stop-auto-lock-1",
939
+ last_assistant_message: "Would you like me to continue with the next step?",
940
+ }, { cwd });
941
+ assert.equal(result.omxEventName, "stop");
942
+ assert.deepEqual(result.outputJson, {
943
+ decision: "block",
944
+ reason: "OMX skill deep-interview is still active (phase: planning); continue until the current deep-interview workflow reaches a terminal state.",
945
+ stopReason: "skill_deep-interview_planning",
946
+ systemMessage: "OMX skill deep-interview is still active (phase: planning).",
947
+ });
948
+ }
949
+ finally {
950
+ await rm(cwd, { recursive: true, force: true });
951
+ }
952
+ });
953
+ it("suppresses native auto-nudge re-fire while session-scoped deep-interview state is still active", async () => {
954
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-deep-interview-state-"));
955
+ try {
956
+ const stateDir = join(cwd, ".omx", "state");
957
+ await mkdir(join(stateDir, "sessions", "sess-stop-auto-interview"), { recursive: true });
958
+ await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-auto-interview" });
959
+ await writeJson(join(stateDir, "sessions", "sess-stop-auto-interview", "deep-interview-state.json"), {
960
+ active: true,
961
+ mode: "deep-interview",
962
+ current_phase: "intent-first",
963
+ });
964
+ const result = await dispatchCodexNativeHook({
965
+ hook_event_name: "Stop",
966
+ cwd,
967
+ session_id: "sess-stop-auto-interview",
968
+ thread_id: "thread-stop-auto-interview",
969
+ turn_id: "turn-stop-auto-interview-2",
970
+ stop_hook_active: true,
971
+ last_assistant_message: "If you want, I can keep going from here.",
972
+ }, { cwd });
973
+ assert.equal(result.omxEventName, "stop");
974
+ assert.equal(result.outputJson, null);
975
+ }
976
+ finally {
977
+ await rm(cwd, { recursive: true, force: true });
978
+ }
979
+ });
980
+ it("suppresses native auto-nudge when deep-interview mode state is active without a scoped skill state", async () => {
981
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-deep-interview-mode-"));
982
+ try {
983
+ const stateDir = join(cwd, ".omx", "state");
984
+ await mkdir(stateDir, { recursive: true });
985
+ await writeJson(join(stateDir, "deep-interview-state.json"), {
986
+ active: true,
987
+ mode: "deep-interview",
988
+ current_phase: "intent-first",
989
+ });
990
+ const result = await dispatchCodexNativeHook({
991
+ hook_event_name: "Stop",
992
+ cwd,
993
+ session_id: "sess-stop-auto-mode",
994
+ thread_id: "thread-stop-auto-mode",
995
+ turn_id: "turn-stop-auto-mode-1",
996
+ last_assistant_message: "Would you like me to continue with the next step?",
997
+ }, { cwd });
998
+ assert.equal(result.omxEventName, "stop");
999
+ assert.equal(result.outputJson, null);
1000
+ }
1001
+ finally {
1002
+ await rm(cwd, { recursive: true, force: true });
1003
+ }
1004
+ });
1005
+ it("re-fires team Stop output for a later fresh Stop reply while the team is still active", async () => {
1006
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-refire-"));
1007
+ try {
1008
+ const stateDir = join(cwd, ".omx", "state");
1009
+ await mkdir(stateDir, { recursive: true });
1010
+ await writeJson(join(stateDir, "team-state.json"), {
1011
+ active: true,
1012
+ current_phase: "team-exec",
1013
+ team_name: "review-team",
1014
+ });
1015
+ await writeJson(join(stateDir, "team", "review-team", "phase.json"), {
1016
+ current_phase: "team-verify",
1017
+ max_fix_attempts: 3,
1018
+ current_fix_attempt: 0,
1019
+ transitions: [],
1020
+ updated_at: new Date().toISOString(),
1021
+ });
1022
+ await dispatchCodexNativeHook({
1023
+ hook_event_name: "Stop",
1024
+ cwd,
1025
+ session_id: "sess-stop-team-refire",
1026
+ thread_id: "thread-stop-team-refire",
1027
+ turn_id: "turn-stop-team-refire-1",
1028
+ }, { cwd });
1029
+ const result = await dispatchCodexNativeHook({
1030
+ hook_event_name: "Stop",
1031
+ cwd,
1032
+ session_id: "sess-stop-team-refire",
1033
+ thread_id: "thread-stop-team-refire",
1034
+ turn_id: "turn-stop-team-refire-2",
1035
+ stop_hook_active: true,
1036
+ }, { cwd });
1037
+ assert.equal(result.omxEventName, "stop");
1038
+ assert.deepEqual(result.outputJson, {
1039
+ decision: "block",
1040
+ reason: `OMX team pipeline is still active (review-team) at phase team-verify; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`,
1041
+ stopReason: "team_team-verify",
1042
+ systemMessage: "OMX team pipeline is still active at phase team-verify.",
1043
+ });
1044
+ }
1045
+ finally {
1046
+ await rm(cwd, { recursive: true, force: true });
1047
+ }
1048
+ });
1049
+ });
1050
+ //# sourceMappingURL=codex-native-hook.test.js.map