oh-my-codex 0.15.0 → 0.15.2

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 (533) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/README.md +36 -5
  4. package/crates/omx-explore/src/main.rs +222 -12
  5. package/dist/agents/__tests__/native-config.test.js +40 -0
  6. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  7. package/dist/agents/native-config.d.ts +1 -0
  8. package/dist/agents/native-config.d.ts.map +1 -1
  9. package/dist/agents/native-config.js +6 -1
  10. package/dist/agents/native-config.js.map +1 -1
  11. package/dist/agents/policy.d.ts +1 -0
  12. package/dist/agents/policy.d.ts.map +1 -1
  13. package/dist/agents/policy.js +4 -0
  14. package/dist/agents/policy.js.map +1 -1
  15. package/dist/cli/__tests__/autoresearch-guided.test.js +37 -13
  16. package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
  17. package/dist/cli/__tests__/codex-plugin-layout.test.js +1 -1
  18. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  19. package/dist/cli/__tests__/doctor-team.test.js +46 -1
  20. package/dist/cli/__tests__/doctor-team.test.js.map +1 -1
  21. package/dist/cli/__tests__/doctor-warning-copy.test.js +225 -111
  22. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  23. package/dist/cli/__tests__/exec.test.js +96 -1
  24. package/dist/cli/__tests__/exec.test.js.map +1 -1
  25. package/dist/cli/__tests__/explore.test.js +15 -2
  26. package/dist/cli/__tests__/explore.test.js.map +1 -1
  27. package/dist/cli/__tests__/index.test.js +292 -3
  28. package/dist/cli/__tests__/index.test.js.map +1 -1
  29. package/dist/cli/__tests__/launch-fallback.test.js +223 -0
  30. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  31. package/dist/cli/__tests__/mcp-parity.test.js +86 -0
  32. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  33. package/dist/cli/__tests__/package-bin-contract.test.js +23 -0
  34. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  35. package/dist/cli/__tests__/question.test.js +76 -11
  36. package/dist/cli/__tests__/question.test.js.map +1 -1
  37. package/dist/cli/__tests__/setup-agents-overwrite.test.js +140 -1
  38. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  39. package/dist/cli/__tests__/setup-install-mode.test.js +310 -4
  40. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  41. package/dist/cli/__tests__/setup-prompts-overwrite.test.js +78 -19
  42. package/dist/cli/__tests__/setup-prompts-overwrite.test.js.map +1 -1
  43. package/dist/cli/__tests__/setup-refresh.test.js +79 -2
  44. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  45. package/dist/cli/__tests__/sidecar.test.d.ts +2 -0
  46. package/dist/cli/__tests__/sidecar.test.d.ts.map +1 -0
  47. package/dist/cli/__tests__/sidecar.test.js +24 -0
  48. package/dist/cli/__tests__/sidecar.test.js.map +1 -0
  49. package/dist/cli/__tests__/team.test.js +54 -7
  50. package/dist/cli/__tests__/team.test.js.map +1 -1
  51. package/dist/cli/autoresearch-guided.d.ts.map +1 -1
  52. package/dist/cli/autoresearch-guided.js +12 -4
  53. package/dist/cli/autoresearch-guided.js.map +1 -1
  54. package/dist/cli/codex-home.d.ts +4 -6
  55. package/dist/cli/codex-home.d.ts.map +1 -1
  56. package/dist/cli/codex-home.js +9 -41
  57. package/dist/cli/codex-home.js.map +1 -1
  58. package/dist/cli/doctor.d.ts +1 -1
  59. package/dist/cli/doctor.d.ts.map +1 -1
  60. package/dist/cli/doctor.js +509 -279
  61. package/dist/cli/doctor.js.map +1 -1
  62. package/dist/cli/index.d.ts +6 -4
  63. package/dist/cli/index.d.ts.map +1 -1
  64. package/dist/cli/index.js +284 -25
  65. package/dist/cli/index.js.map +1 -1
  66. package/dist/cli/omx.js +3 -1
  67. package/dist/cli/omx.js.map +1 -1
  68. package/dist/cli/plugin-marketplace.d.ts +13 -0
  69. package/dist/cli/plugin-marketplace.d.ts.map +1 -0
  70. package/dist/cli/plugin-marketplace.js +77 -0
  71. package/dist/cli/plugin-marketplace.js.map +1 -0
  72. package/dist/cli/question.d.ts +1 -1
  73. package/dist/cli/question.d.ts.map +1 -1
  74. package/dist/cli/question.js +26 -12
  75. package/dist/cli/question.js.map +1 -1
  76. package/dist/cli/setup-preferences.d.ts +20 -0
  77. package/dist/cli/setup-preferences.d.ts.map +1 -0
  78. package/dist/cli/setup-preferences.js +71 -0
  79. package/dist/cli/setup-preferences.js.map +1 -0
  80. package/dist/cli/setup.d.ts +7 -5
  81. package/dist/cli/setup.d.ts.map +1 -1
  82. package/dist/cli/setup.js +271 -152
  83. package/dist/cli/setup.js.map +1 -1
  84. package/dist/cli/team.d.ts +1 -0
  85. package/dist/cli/team.d.ts.map +1 -1
  86. package/dist/cli/team.js +70 -15
  87. package/dist/cli/team.js.map +1 -1
  88. package/dist/config/__tests__/generator-idempotent.test.js +100 -3
  89. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  90. package/dist/config/__tests__/generator-notify.test.js +6 -5
  91. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  92. package/dist/config/__tests__/generator-status-line-presets.test.d.ts +2 -0
  93. package/dist/config/__tests__/generator-status-line-presets.test.d.ts.map +1 -0
  94. package/dist/config/__tests__/generator-status-line-presets.test.js +203 -0
  95. package/dist/config/__tests__/generator-status-line-presets.test.js.map +1 -0
  96. package/dist/config/__tests__/models.test.js +23 -1
  97. package/dist/config/__tests__/models.test.js.map +1 -1
  98. package/dist/config/generator.d.ts +9 -1
  99. package/dist/config/generator.d.ts.map +1 -1
  100. package/dist/config/generator.js +184 -16
  101. package/dist/config/generator.js.map +1 -1
  102. package/dist/config/models.d.ts +5 -1
  103. package/dist/config/models.d.ts.map +1 -1
  104. package/dist/config/models.js +12 -2
  105. package/dist/config/models.js.map +1 -1
  106. package/dist/exec/followup.d.ts +44 -0
  107. package/dist/exec/followup.d.ts.map +1 -0
  108. package/dist/exec/followup.js +349 -0
  109. package/dist/exec/followup.js.map +1 -0
  110. package/dist/hooks/__tests__/autopilot-skill-contract.test.d.ts +2 -0
  111. package/dist/hooks/__tests__/autopilot-skill-contract.test.d.ts.map +1 -0
  112. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +37 -0
  113. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -0
  114. package/dist/hooks/__tests__/codebase-map.test.js +63 -1
  115. package/dist/hooks/__tests__/codebase-map.test.js.map +1 -1
  116. package/dist/hooks/__tests__/consensus-execution-handoff.test.d.ts +1 -1
  117. package/dist/hooks/__tests__/consensus-execution-handoff.test.js +5 -5
  118. package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
  119. package/dist/hooks/__tests__/deep-interview-contract.test.js +12 -9
  120. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  121. package/dist/hooks/__tests__/keyword-detector.test.js +25 -18
  122. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  123. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +23 -2
  124. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  125. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +45 -2
  126. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  127. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +17 -0
  128. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
  129. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +121 -0
  130. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  131. package/dist/hooks/__tests__/notify-hook-regression-205.test.js +4 -4
  132. package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
  133. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +103 -0
  134. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  135. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +2 -2
  136. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  137. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +27 -13
  138. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  139. package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.d.ts +2 -0
  140. package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.d.ts.map +1 -0
  141. package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.js +35 -0
  142. package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.js.map +1 -0
  143. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +215 -0
  144. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  145. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +70 -3
  146. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  147. package/dist/hooks/__tests__/pre-context-gate-skills.test.js +5 -0
  148. package/dist/hooks/__tests__/pre-context-gate-skills.test.js.map +1 -1
  149. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js +3 -2
  150. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js.map +1 -1
  151. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js +9 -0
  152. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js.map +1 -1
  153. package/dist/hooks/__tests__/prompt-refactor-contract.test.d.ts +2 -0
  154. package/dist/hooks/__tests__/prompt-refactor-contract.test.d.ts.map +1 -0
  155. package/dist/hooks/__tests__/prompt-refactor-contract.test.js +22 -0
  156. package/dist/hooks/__tests__/prompt-refactor-contract.test.js.map +1 -0
  157. package/dist/hooks/codebase-map.d.ts.map +1 -1
  158. package/dist/hooks/codebase-map.js +83 -6
  159. package/dist/hooks/codebase-map.js.map +1 -1
  160. package/dist/hooks/keyword-detector.d.ts +1 -1
  161. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  162. package/dist/hooks/keyword-detector.js +35 -4
  163. package/dist/hooks/keyword-detector.js.map +1 -1
  164. package/dist/hooks/prompt-guidance-contract.d.ts +6 -0
  165. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  166. package/dist/hooks/prompt-guidance-contract.js +117 -13
  167. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  168. package/dist/hooks/session.d.ts +2 -0
  169. package/dist/hooks/session.d.ts.map +1 -1
  170. package/dist/hooks/session.js +6 -0
  171. package/dist/hooks/session.js.map +1 -1
  172. package/dist/hud/__tests__/index.test.js +4 -4
  173. package/dist/hud/__tests__/index.test.js.map +1 -1
  174. package/dist/hud/__tests__/state.test.js +4 -0
  175. package/dist/hud/__tests__/state.test.js.map +1 -1
  176. package/dist/hud/__tests__/types.test.js +27 -0
  177. package/dist/hud/__tests__/types.test.js.map +1 -1
  178. package/dist/hud/state.d.ts.map +1 -1
  179. package/dist/hud/state.js +8 -0
  180. package/dist/hud/state.js.map +1 -1
  181. package/dist/hud/types.d.ts +9 -0
  182. package/dist/hud/types.d.ts.map +1 -1
  183. package/dist/hud/types.js +3 -0
  184. package/dist/hud/types.js.map +1 -1
  185. package/dist/mcp/__tests__/bootstrap.test.js +23 -5
  186. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  187. package/dist/mcp/__tests__/server-lifecycle.test.js +50 -7
  188. package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
  189. package/dist/mcp/__tests__/state-server.test.js +70 -12
  190. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  191. package/dist/mcp/bootstrap.d.ts +10 -1
  192. package/dist/mcp/bootstrap.d.ts.map +1 -1
  193. package/dist/mcp/bootstrap.js +71 -26
  194. package/dist/mcp/bootstrap.js.map +1 -1
  195. package/dist/mcp/state-server.d.ts +5 -11
  196. package/dist/mcp/state-server.d.ts.map +1 -1
  197. package/dist/mcp/state-server.js +16 -432
  198. package/dist/mcp/state-server.js.map +1 -1
  199. package/dist/modes/__tests__/base-autoresearch-contract.test.js +1 -1
  200. package/dist/modes/__tests__/base-autoresearch-contract.test.js.map +1 -1
  201. package/dist/pipeline/__tests__/orchestrator.test.js +89 -5
  202. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  203. package/dist/pipeline/__tests__/stages.test.js +98 -1
  204. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  205. package/dist/pipeline/index.d.ts +5 -3
  206. package/dist/pipeline/index.d.ts.map +1 -1
  207. package/dist/pipeline/index.js +4 -3
  208. package/dist/pipeline/index.js.map +1 -1
  209. package/dist/pipeline/orchestrator.d.ts +7 -6
  210. package/dist/pipeline/orchestrator.d.ts.map +1 -1
  211. package/dist/pipeline/orchestrator.js +90 -11
  212. package/dist/pipeline/orchestrator.js.map +1 -1
  213. package/dist/pipeline/review-verdict.d.ts +3 -0
  214. package/dist/pipeline/review-verdict.d.ts.map +1 -0
  215. package/dist/pipeline/review-verdict.js +14 -0
  216. package/dist/pipeline/review-verdict.js.map +1 -0
  217. package/dist/pipeline/stages/code-review.d.ts +33 -0
  218. package/dist/pipeline/stages/code-review.d.ts.map +1 -0
  219. package/dist/pipeline/stages/code-review.js +51 -0
  220. package/dist/pipeline/stages/code-review.js.map +1 -0
  221. package/dist/pipeline/stages/ralph-verify.d.ts +12 -2
  222. package/dist/pipeline/stages/ralph-verify.d.ts.map +1 -1
  223. package/dist/pipeline/stages/ralph-verify.js +24 -6
  224. package/dist/pipeline/stages/ralph-verify.js.map +1 -1
  225. package/dist/pipeline/stages/ralplan.d.ts +1 -1
  226. package/dist/pipeline/stages/ralplan.d.ts.map +1 -1
  227. package/dist/pipeline/stages/ralplan.js +21 -1
  228. package/dist/pipeline/stages/ralplan.js.map +1 -1
  229. package/dist/pipeline/types.d.ts +14 -7
  230. package/dist/pipeline/types.d.ts.map +1 -1
  231. package/dist/pipeline/types.js +2 -2
  232. package/dist/planning/__tests__/artifacts.test.js +152 -1
  233. package/dist/planning/__tests__/artifacts.test.js.map +1 -1
  234. package/dist/planning/artifacts.d.ts +9 -0
  235. package/dist/planning/artifacts.d.ts.map +1 -1
  236. package/dist/planning/artifacts.js +60 -1
  237. package/dist/planning/artifacts.js.map +1 -1
  238. package/dist/question/__tests__/client.test.js +23 -3
  239. package/dist/question/__tests__/client.test.js.map +1 -1
  240. package/dist/question/__tests__/renderer.test.js +148 -37
  241. package/dist/question/__tests__/renderer.test.js.map +1 -1
  242. package/dist/question/__tests__/types.test.js +21 -0
  243. package/dist/question/__tests__/types.test.js.map +1 -1
  244. package/dist/question/__tests__/ui.test.js +155 -7
  245. package/dist/question/__tests__/ui.test.js.map +1 -1
  246. package/dist/question/client.d.ts +14 -4
  247. package/dist/question/client.d.ts.map +1 -1
  248. package/dist/question/client.js.map +1 -1
  249. package/dist/question/renderer.d.ts +11 -1
  250. package/dist/question/renderer.d.ts.map +1 -1
  251. package/dist/question/renderer.js +102 -7
  252. package/dist/question/renderer.js.map +1 -1
  253. package/dist/question/state.d.ts +2 -2
  254. package/dist/question/state.d.ts.map +1 -1
  255. package/dist/question/state.js +26 -17
  256. package/dist/question/state.js.map +1 -1
  257. package/dist/question/types.d.ts +25 -1
  258. package/dist/question/types.d.ts.map +1 -1
  259. package/dist/question/types.js +48 -13
  260. package/dist/question/types.js.map +1 -1
  261. package/dist/question/ui.d.ts +15 -2
  262. package/dist/question/ui.d.ts.map +1 -1
  263. package/dist/question/ui.js +268 -162
  264. package/dist/question/ui.js.map +1 -1
  265. package/dist/scripts/__tests__/codex-native-hook.test.js +415 -94
  266. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  267. package/dist/scripts/__tests__/generate-release-body.test.js +36 -0
  268. package/dist/scripts/__tests__/generate-release-body.test.js.map +1 -1
  269. package/dist/scripts/__tests__/prompt-inventory.test.d.ts +2 -0
  270. package/dist/scripts/__tests__/prompt-inventory.test.d.ts.map +1 -0
  271. package/dist/scripts/__tests__/prompt-inventory.test.js +56 -0
  272. package/dist/scripts/__tests__/prompt-inventory.test.js.map +1 -0
  273. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  274. package/dist/scripts/codex-native-hook.js +232 -54
  275. package/dist/scripts/codex-native-hook.js.map +1 -1
  276. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  277. package/dist/scripts/codex-native-pre-post.js +12 -9
  278. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  279. package/dist/scripts/generate-release-body.d.ts.map +1 -1
  280. package/dist/scripts/generate-release-body.js +12 -3
  281. package/dist/scripts/generate-release-body.js.map +1 -1
  282. package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.d.ts +2 -0
  283. package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.d.ts.map +1 -0
  284. package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.js +153 -0
  285. package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.js.map +1 -0
  286. package/dist/scripts/notify-hook/managed-tmux.d.ts +4 -2
  287. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  288. package/dist/scripts/notify-hook/managed-tmux.js +188 -6
  289. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  290. package/dist/scripts/notify-hook/process-runner.d.ts.map +1 -1
  291. package/dist/scripts/notify-hook/process-runner.js +7 -3
  292. package/dist/scripts/notify-hook/process-runner.js.map +1 -1
  293. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  294. package/dist/scripts/notify-hook/team-dispatch.js +96 -11
  295. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  296. package/dist/scripts/notify-hook/team-tmux-guard.js +3 -3
  297. package/dist/scripts/notify-hook/team-worker-posttooluse.d.ts +34 -0
  298. package/dist/scripts/notify-hook/team-worker-posttooluse.d.ts.map +1 -0
  299. package/dist/scripts/notify-hook/team-worker-posttooluse.js +434 -0
  300. package/dist/scripts/notify-hook/team-worker-posttooluse.js.map +1 -0
  301. package/dist/scripts/notify-hook/team-worker.d.ts +1 -1
  302. package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
  303. package/dist/scripts/notify-hook/team-worker.js +3 -43
  304. package/dist/scripts/notify-hook/team-worker.js.map +1 -1
  305. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  306. package/dist/scripts/notify-hook/tmux-injection.js +25 -4
  307. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  308. package/dist/scripts/notify-hook.js +36 -5
  309. package/dist/scripts/notify-hook.js.map +1 -1
  310. package/dist/scripts/prompt-inventory.d.ts +29 -0
  311. package/dist/scripts/prompt-inventory.d.ts.map +1 -0
  312. package/dist/scripts/prompt-inventory.js +178 -0
  313. package/dist/scripts/prompt-inventory.js.map +1 -0
  314. package/dist/scripts/run-test-files.js +1 -0
  315. package/dist/scripts/run-test-files.js.map +1 -1
  316. package/dist/sidecar/__tests__/boundary.test.d.ts +2 -0
  317. package/dist/sidecar/__tests__/boundary.test.d.ts.map +1 -0
  318. package/dist/sidecar/__tests__/boundary.test.js +48 -0
  319. package/dist/sidecar/__tests__/boundary.test.js.map +1 -0
  320. package/dist/sidecar/__tests__/collector.test.d.ts +2 -0
  321. package/dist/sidecar/__tests__/collector.test.d.ts.map +1 -0
  322. package/dist/sidecar/__tests__/collector.test.js +162 -0
  323. package/dist/sidecar/__tests__/collector.test.js.map +1 -0
  324. package/dist/sidecar/__tests__/render.test.d.ts +2 -0
  325. package/dist/sidecar/__tests__/render.test.d.ts.map +1 -0
  326. package/dist/sidecar/__tests__/render.test.js +67 -0
  327. package/dist/sidecar/__tests__/render.test.js.map +1 -0
  328. package/dist/sidecar/__tests__/tmux.test.d.ts +2 -0
  329. package/dist/sidecar/__tests__/tmux.test.d.ts.map +1 -0
  330. package/dist/sidecar/__tests__/tmux.test.js +30 -0
  331. package/dist/sidecar/__tests__/tmux.test.js.map +1 -0
  332. package/dist/sidecar/__tests__/watch.test.d.ts +2 -0
  333. package/dist/sidecar/__tests__/watch.test.d.ts.map +1 -0
  334. package/dist/sidecar/__tests__/watch.test.js +42 -0
  335. package/dist/sidecar/__tests__/watch.test.js.map +1 -0
  336. package/dist/sidecar/collector.d.ts +4 -0
  337. package/dist/sidecar/collector.d.ts.map +1 -0
  338. package/dist/sidecar/collector.js +377 -0
  339. package/dist/sidecar/collector.js.map +1 -0
  340. package/dist/sidecar/index.d.ts +25 -0
  341. package/dist/sidecar/index.d.ts.map +1 -0
  342. package/dist/sidecar/index.js +165 -0
  343. package/dist/sidecar/index.js.map +1 -0
  344. package/dist/sidecar/render.d.ts +3 -0
  345. package/dist/sidecar/render.d.ts.map +1 -0
  346. package/dist/sidecar/render.js +72 -0
  347. package/dist/sidecar/render.js.map +1 -0
  348. package/dist/sidecar/tmux.d.ts +13 -0
  349. package/dist/sidecar/tmux.d.ts.map +1 -0
  350. package/dist/sidecar/tmux.js +44 -0
  351. package/dist/sidecar/tmux.js.map +1 -0
  352. package/dist/sidecar/types.d.ts +125 -0
  353. package/dist/sidecar/types.d.ts.map +1 -0
  354. package/dist/sidecar/types.js +2 -0
  355. package/dist/sidecar/types.js.map +1 -0
  356. package/dist/state/__tests__/operations.test.js +50 -22
  357. package/dist/state/__tests__/operations.test.js.map +1 -1
  358. package/dist/state/__tests__/workflow-transition.test.js +9 -1
  359. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  360. package/dist/state/operations.d.ts +1 -1
  361. package/dist/state/operations.d.ts.map +1 -1
  362. package/dist/state/operations.js +19 -7
  363. package/dist/state/operations.js.map +1 -1
  364. package/dist/state/workflow-transition.d.ts.map +1 -1
  365. package/dist/state/workflow-transition.js +1 -0
  366. package/dist/state/workflow-transition.js.map +1 -1
  367. package/dist/team/__tests__/commit-hygiene.test.d.ts +2 -0
  368. package/dist/team/__tests__/commit-hygiene.test.d.ts.map +1 -0
  369. package/dist/team/__tests__/commit-hygiene.test.js +93 -0
  370. package/dist/team/__tests__/commit-hygiene.test.js.map +1 -0
  371. package/dist/team/__tests__/delegation-policy.test.d.ts +2 -0
  372. package/dist/team/__tests__/delegation-policy.test.d.ts.map +1 -0
  373. package/dist/team/__tests__/delegation-policy.test.js +69 -0
  374. package/dist/team/__tests__/delegation-policy.test.js.map +1 -0
  375. package/dist/team/__tests__/events.test.js +54 -4
  376. package/dist/team/__tests__/events.test.js.map +1 -1
  377. package/dist/team/__tests__/hook-primary-e2e-contract.test.d.ts +2 -0
  378. package/dist/team/__tests__/hook-primary-e2e-contract.test.d.ts.map +1 -0
  379. package/dist/team/__tests__/hook-primary-e2e-contract.test.js +78 -0
  380. package/dist/team/__tests__/hook-primary-e2e-contract.test.js.map +1 -0
  381. package/dist/team/__tests__/model-contract.test.js +16 -0
  382. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  383. package/dist/team/__tests__/repo-aware-decomposition.test.d.ts +2 -0
  384. package/dist/team/__tests__/repo-aware-decomposition.test.d.ts.map +1 -0
  385. package/dist/team/__tests__/repo-aware-decomposition.test.js +95 -0
  386. package/dist/team/__tests__/repo-aware-decomposition.test.js.map +1 -0
  387. package/dist/team/__tests__/runtime.test.js +623 -14
  388. package/dist/team/__tests__/runtime.test.js.map +1 -1
  389. package/dist/team/__tests__/state-root.test.js +177 -1
  390. package/dist/team/__tests__/state-root.test.js.map +1 -1
  391. package/dist/team/__tests__/state.test.js +110 -0
  392. package/dist/team/__tests__/state.test.js.map +1 -1
  393. package/dist/team/__tests__/tmux-session.test.js +399 -2
  394. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  395. package/dist/team/__tests__/worker-bootstrap.test.js +94 -0
  396. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  397. package/dist/team/commit-hygiene.d.ts +22 -3
  398. package/dist/team/commit-hygiene.d.ts.map +1 -1
  399. package/dist/team/commit-hygiene.js +134 -2
  400. package/dist/team/commit-hygiene.js.map +1 -1
  401. package/dist/team/contracts.d.ts +1 -1
  402. package/dist/team/contracts.d.ts.map +1 -1
  403. package/dist/team/contracts.js +2 -0
  404. package/dist/team/contracts.js.map +1 -1
  405. package/dist/team/dag-schema.d.ts +38 -0
  406. package/dist/team/dag-schema.d.ts.map +1 -0
  407. package/dist/team/dag-schema.js +221 -0
  408. package/dist/team/dag-schema.js.map +1 -0
  409. package/dist/team/delegation-policy.d.ts +3 -0
  410. package/dist/team/delegation-policy.d.ts.map +1 -0
  411. package/dist/team/delegation-policy.js +82 -0
  412. package/dist/team/delegation-policy.js.map +1 -0
  413. package/dist/team/model-contract.d.ts +3 -1
  414. package/dist/team/model-contract.d.ts.map +1 -1
  415. package/dist/team/model-contract.js +44 -5
  416. package/dist/team/model-contract.js.map +1 -1
  417. package/dist/team/repo-aware-decomposition.d.ts +60 -0
  418. package/dist/team/repo-aware-decomposition.d.ts.map +1 -0
  419. package/dist/team/repo-aware-decomposition.js +229 -0
  420. package/dist/team/repo-aware-decomposition.js.map +1 -0
  421. package/dist/team/runtime.d.ts +27 -0
  422. package/dist/team/runtime.d.ts.map +1 -1
  423. package/dist/team/runtime.js +172 -52
  424. package/dist/team/runtime.js.map +1 -1
  425. package/dist/team/state/tasks.d.ts.map +1 -1
  426. package/dist/team/state/tasks.js +33 -0
  427. package/dist/team/state/tasks.js.map +1 -1
  428. package/dist/team/state/types.d.ts +23 -1
  429. package/dist/team/state/types.d.ts.map +1 -1
  430. package/dist/team/state/types.js.map +1 -1
  431. package/dist/team/state-root.d.ts +35 -0
  432. package/dist/team/state-root.d.ts.map +1 -1
  433. package/dist/team/state-root.js +281 -1
  434. package/dist/team/state-root.js.map +1 -1
  435. package/dist/team/state.d.ts +27 -1
  436. package/dist/team/state.d.ts.map +1 -1
  437. package/dist/team/state.js +6 -0
  438. package/dist/team/state.js.map +1 -1
  439. package/dist/team/tmux-session.d.ts +1 -0
  440. package/dist/team/tmux-session.d.ts.map +1 -1
  441. package/dist/team/tmux-session.js +105 -6
  442. package/dist/team/tmux-session.js.map +1 -1
  443. package/dist/team/worker-bootstrap.d.ts +3 -0
  444. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  445. package/dist/team/worker-bootstrap.js +77 -4
  446. package/dist/team/worker-bootstrap.js.map +1 -1
  447. package/dist/utils/agents-md.d.ts +3 -0
  448. package/dist/utils/agents-md.d.ts.map +1 -1
  449. package/dist/utils/agents-md.js +25 -0
  450. package/dist/utils/agents-md.js.map +1 -1
  451. package/package.json +3 -2
  452. package/plugins/oh-my-codex/.codex-plugin/plugin.json +2 -2
  453. package/plugins/oh-my-codex/skills/ai-slop-cleaner/SKILL.md +1 -1
  454. package/plugins/oh-my-codex/skills/analyze/SKILL.md +1 -1
  455. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +134 -205
  456. package/plugins/oh-my-codex/skills/code-review/SKILL.md +4 -4
  457. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +14 -7
  458. package/plugins/oh-my-codex/skills/doctor/SKILL.md +1 -1
  459. package/plugins/oh-my-codex/skills/help/SKILL.md +1 -1
  460. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +41 -10
  461. package/plugins/oh-my-codex/skills/plan/SKILL.md +12 -14
  462. package/plugins/oh-my-codex/skills/ralph/SKILL.md +2 -4
  463. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +5 -9
  464. package/plugins/oh-my-codex/skills/security-review/SKILL.md +4 -4
  465. package/plugins/oh-my-codex/skills/team/SKILL.md +2 -5
  466. package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +2 -5
  467. package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +2 -3
  468. package/prompts/analyst.md +2 -2
  469. package/prompts/api-reviewer.md +2 -2
  470. package/prompts/architect.md +2 -2
  471. package/prompts/build-fixer.md +2 -2
  472. package/prompts/code-reviewer.md +15 -5
  473. package/prompts/code-simplifier.md +1 -1
  474. package/prompts/critic.md +35 -83
  475. package/prompts/debugger.md +2 -2
  476. package/prompts/dependency-expert.md +2 -2
  477. package/prompts/designer.md +2 -2
  478. package/prompts/executor.md +40 -114
  479. package/prompts/explore-harness.md +1 -1
  480. package/prompts/explore.md +37 -90
  481. package/prompts/git-master.md +2 -2
  482. package/prompts/information-architect.md +1 -1
  483. package/prompts/performance-reviewer.md +2 -2
  484. package/prompts/planner.md +35 -62
  485. package/prompts/product-analyst.md +2 -2
  486. package/prompts/product-manager.md +2 -2
  487. package/prompts/qa-tester.md +2 -2
  488. package/prompts/quality-reviewer.md +2 -2
  489. package/prompts/quality-strategist.md +2 -2
  490. package/prompts/researcher.md +46 -78
  491. package/prompts/security-reviewer.md +2 -2
  492. package/prompts/sisyphus-lite.md +2 -2
  493. package/prompts/style-reviewer.md +2 -2
  494. package/prompts/team-executor.md +1 -1
  495. package/prompts/test-engineer.md +2 -2
  496. package/prompts/ux-researcher.md +2 -2
  497. package/prompts/verifier.md +29 -34
  498. package/prompts/vision.md +2 -2
  499. package/prompts/writer.md +2 -2
  500. package/skills/ai-slop-cleaner/SKILL.md +1 -1
  501. package/skills/analyze/SKILL.md +1 -1
  502. package/skills/autopilot/SKILL.md +134 -205
  503. package/skills/build-fix/SKILL.md +4 -4
  504. package/skills/code-review/SKILL.md +4 -4
  505. package/skills/deep-interview/SKILL.md +14 -7
  506. package/skills/doctor/SKILL.md +1 -1
  507. package/skills/help/SKILL.md +1 -1
  508. package/skills/omx-setup/SKILL.md +41 -10
  509. package/skills/plan/SKILL.md +12 -14
  510. package/skills/ralph/SKILL.md +2 -4
  511. package/skills/ralplan/SKILL.md +5 -9
  512. package/skills/security-review/SKILL.md +4 -4
  513. package/skills/team/SKILL.md +2 -5
  514. package/skills/ultraqa/SKILL.md +2 -5
  515. package/skills/ultrawork/SKILL.md +2 -3
  516. package/src/scripts/__tests__/codex-native-hook.test.ts +502 -94
  517. package/src/scripts/__tests__/generate-release-body.test.ts +41 -0
  518. package/src/scripts/__tests__/prompt-inventory.test.ts +64 -0
  519. package/src/scripts/codex-native-hook.ts +293 -61
  520. package/src/scripts/codex-native-pre-post.ts +10 -8
  521. package/src/scripts/generate-release-body.ts +13 -2
  522. package/src/scripts/notify-hook/__tests__/team-worker-posttooluse.test.ts +180 -0
  523. package/src/scripts/notify-hook/managed-tmux.ts +196 -9
  524. package/src/scripts/notify-hook/process-runner.ts +7 -3
  525. package/src/scripts/notify-hook/team-dispatch.ts +103 -11
  526. package/src/scripts/notify-hook/team-tmux-guard.ts +3 -3
  527. package/src/scripts/notify-hook/team-worker-posttooluse.ts +536 -0
  528. package/src/scripts/notify-hook/team-worker.ts +4 -48
  529. package/src/scripts/notify-hook/tmux-injection.ts +24 -6
  530. package/src/scripts/notify-hook.ts +36 -5
  531. package/src/scripts/prompt-inventory.ts +218 -0
  532. package/src/scripts/run-test-files.ts +1 -0
  533. package/templates/AGENTS.md +34 -95
@@ -1,7 +1,7 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { execFileSync } from "node:child_process";
3
3
  import { existsSync } from "node:fs";
4
- import { chmod, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
4
+ import { chmod, mkdir, mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises";
5
5
  import { tmpdir } from "node:os";
6
6
  import { dirname, join } from "node:path";
7
7
  import { pathToFileURL } from "node:url";
@@ -12,6 +12,7 @@ import { initTeamState, readTeamLeaderAttention, readTeamPhase, writeTeamLeaderA
12
12
  import { dispatchCodexNativeHook, isCodexNativeHookMainModule, mapCodexHookEventToOmxEvent, resolveSessionOwnerPidFromAncestry, } from "../codex-native-hook.js";
13
13
  import { writeSessionStart } from "../../hooks/session.js";
14
14
  import { resetTriageConfigCache } from "../../hooks/triage-config.js";
15
+ import { executeStateOperation } from "../../state/operations.js";
15
16
  function nativeHookScriptPath() {
16
17
  return join(process.cwd(), "dist", "scripts", "codex-native-hook.js");
17
18
  }
@@ -163,6 +164,46 @@ describe("codex native hook dispatch", () => {
163
164
  assert.equal(output.hookSpecificOutput?.hookEventName, "Unknown");
164
165
  assert.match(String(output.hookSpecificOutput?.additionalContext ?? ""), /stdin JSON parsing failed inside codex-native-hook:/);
165
166
  });
167
+ it("emits parseable no-op JSON stdout for inactive Stop CLI runs", async () => {
168
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-stop-noop-json-"));
169
+ try {
170
+ const stdout = runNativeHookCli({
171
+ hook_event_name: "Stop",
172
+ cwd,
173
+ session_id: "sess-cli-stop-noop-json",
174
+ thread_id: "thread-cli-stop-noop-json",
175
+ turn_id: "turn-cli-stop-noop-json",
176
+ }, { cwd });
177
+ const output = parseSingleJsonStdout(stdout);
178
+ assert.deepEqual(output, {});
179
+ }
180
+ finally {
181
+ await rm(cwd, { recursive: true, force: true });
182
+ }
183
+ });
184
+ it("does not crash Stop hook dispatch when the exec follow-up queue is malformed", async () => {
185
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-stop-exec-followup-corrupt-"));
186
+ try {
187
+ const session = await writeSessionStart(cwd, "sess-exec-followup-corrupt");
188
+ const queuePath = join(cwd, ".omx", "state", "sessions", session.session_id, "exec-followups.json");
189
+ await mkdir(dirname(queuePath), { recursive: true });
190
+ await writeFile(queuePath, '{"version":1,"records":[', "utf-8");
191
+ const result = await dispatchCodexNativeHook({
192
+ hook_event_name: "Stop",
193
+ cwd,
194
+ session_id: session.session_id,
195
+ });
196
+ assert.equal(result.hookEventName, "Stop");
197
+ assert.equal(result.outputJson, null);
198
+ const queueDirEntries = await readdir(dirname(queuePath));
199
+ assert.ok(queueDirEntries.some((entry) => entry.startsWith("exec-followups.json.corrupt-")));
200
+ const auditPath = join(cwd, ".omx", "logs", `exec-followups-${new Date().toISOString().slice(0, 10)}.jsonl`);
201
+ assert.match(await readFile(auditPath, "utf-8"), /exec_followup_queue_corrupt_recovered/);
202
+ }
203
+ finally {
204
+ await rm(cwd, { recursive: true, force: true });
205
+ }
206
+ });
166
207
  it("emits exactly one parseable JSON object for active Stop CLI continuation", async () => {
167
208
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-stop-json-"));
168
209
  try {
@@ -309,6 +350,127 @@ describe("codex native hook dispatch", () => {
309
350
  await rm(cwd, { recursive: true, force: true });
310
351
  }
311
352
  });
353
+ it("keeps subagent SessionStart from replacing the canonical leader session", async () => {
354
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-session-start-"));
355
+ try {
356
+ const stateDir = join(cwd, ".omx", "state");
357
+ const canonicalSessionId = "omx-leader-session";
358
+ const leaderNativeSessionId = "codex-leader-thread";
359
+ const childNativeSessionId = "codex-child-thread";
360
+ await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true });
361
+ await writeSessionStart(cwd, canonicalSessionId, {
362
+ nativeSessionId: leaderNativeSessionId,
363
+ });
364
+ await writeJson(join(stateDir, "sessions", canonicalSessionId, "ralph-state.json"), {
365
+ active: true,
366
+ mode: "ralph",
367
+ current_phase: "executing",
368
+ iteration: 1,
369
+ max_iterations: 5,
370
+ });
371
+ const transcriptPath = join(cwd, "subagent-rollout.jsonl");
372
+ await writeFile(transcriptPath, `${JSON.stringify({
373
+ type: "session_meta",
374
+ payload: {
375
+ id: childNativeSessionId,
376
+ source: {
377
+ subagent: {
378
+ thread_spawn: {
379
+ parent_thread_id: leaderNativeSessionId,
380
+ depth: 1,
381
+ agent_nickname: "Hegel",
382
+ agent_role: "critic",
383
+ },
384
+ },
385
+ },
386
+ agent_nickname: "Hegel",
387
+ agent_role: "critic",
388
+ },
389
+ })}\n`);
390
+ const result = await dispatchCodexNativeHook({
391
+ hook_event_name: "SessionStart",
392
+ cwd,
393
+ session_id: childNativeSessionId,
394
+ transcript_path: transcriptPath,
395
+ }, { cwd, sessionOwnerPid: process.pid });
396
+ const sessionState = JSON.parse(await readFile(join(stateDir, "session.json"), "utf-8"));
397
+ assert.equal(sessionState.session_id, canonicalSessionId);
398
+ assert.equal(sessionState.native_session_id, leaderNativeSessionId);
399
+ assert.equal(existsSync(join(stateDir, "sessions", childNativeSessionId, "ralph-state.json")), false);
400
+ assert.ok(result.outputJson);
401
+ const leaderRalph = JSON.parse(await readFile(join(stateDir, "sessions", canonicalSessionId, "ralph-state.json"), "utf-8"));
402
+ assert.equal(leaderRalph.active, true);
403
+ assert.equal(leaderRalph.current_phase, "executing");
404
+ const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8"));
405
+ assert.equal(tracking.sessions?.[canonicalSessionId]?.leader_thread_id, leaderNativeSessionId);
406
+ assert.equal(tracking.sessions?.[canonicalSessionId]?.threads?.[childNativeSessionId]?.kind, "subagent");
407
+ assert.equal(tracking.sessions?.[canonicalSessionId]?.threads?.[childNativeSessionId]?.mode, "critic");
408
+ assert.equal(tracking.sessions?.[leaderNativeSessionId]?.leader_thread_id, leaderNativeSessionId);
409
+ assert.equal(tracking.sessions?.[leaderNativeSessionId]?.threads?.[childNativeSessionId]?.kind, "subagent");
410
+ assert.equal(tracking.sessions?.[leaderNativeSessionId]?.threads?.[childNativeSessionId]?.mode, "critic");
411
+ }
412
+ finally {
413
+ await rm(cwd, { recursive: true, force: true });
414
+ }
415
+ });
416
+ it("does not attach a subagent SessionStart to an unrelated canonical leader", async () => {
417
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-session-start-mismatch-"));
418
+ try {
419
+ const stateDir = join(cwd, ".omx", "state");
420
+ const canonicalSessionId = "omx-leader-session-a";
421
+ const leaderNativeSessionId = "codex-leader-thread-a";
422
+ const unrelatedParentNativeSessionId = "codex-leader-thread-b";
423
+ const childNativeSessionId = "codex-child-thread-b";
424
+ await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true });
425
+ await writeSessionStart(cwd, canonicalSessionId, {
426
+ nativeSessionId: leaderNativeSessionId,
427
+ });
428
+ await writeJson(join(stateDir, "sessions", canonicalSessionId, "ralph-state.json"), {
429
+ active: true,
430
+ mode: "ralph",
431
+ current_phase: "executing",
432
+ iteration: 1,
433
+ max_iterations: 5,
434
+ });
435
+ const transcriptPath = join(cwd, "unrelated-subagent-rollout.jsonl");
436
+ await writeFile(transcriptPath, `${JSON.stringify({
437
+ type: "session_meta",
438
+ payload: {
439
+ id: childNativeSessionId,
440
+ source: {
441
+ subagent: {
442
+ thread_spawn: {
443
+ parent_thread_id: unrelatedParentNativeSessionId,
444
+ depth: 1,
445
+ agent_nickname: "Spinoza",
446
+ agent_role: "critic",
447
+ },
448
+ },
449
+ },
450
+ agent_nickname: "Spinoza",
451
+ agent_role: "critic",
452
+ },
453
+ })}\n`);
454
+ const result = await dispatchCodexNativeHook({
455
+ hook_event_name: "SessionStart",
456
+ cwd,
457
+ session_id: childNativeSessionId,
458
+ transcript_path: transcriptPath,
459
+ }, { cwd, sessionOwnerPid: process.pid });
460
+ const sessionState = JSON.parse(await readFile(join(stateDir, "session.json"), "utf-8"));
461
+ assert.equal(sessionState.session_id, canonicalSessionId);
462
+ assert.equal(sessionState.native_session_id, leaderNativeSessionId);
463
+ assert.equal(existsSync(join(stateDir, "subagent-tracking.json")), false);
464
+ assert.equal(existsSync(join(stateDir, "sessions", childNativeSessionId)), false);
465
+ assert.equal(result.outputJson, null);
466
+ const leaderRalph = JSON.parse(await readFile(join(stateDir, "sessions", canonicalSessionId, "ralph-state.json"), "utf-8"));
467
+ assert.equal(leaderRalph.active, true);
468
+ assert.equal(leaderRalph.current_phase, "executing");
469
+ }
470
+ finally {
471
+ await rm(cwd, { recursive: true, force: true });
472
+ }
473
+ });
312
474
  it("describes attached tmux runtime in SessionStart context when TMUX is present", async () => {
313
475
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-start-tmux-"));
314
476
  process.env.TMUX = "/tmp/tmux-attached";
@@ -326,7 +488,7 @@ describe("codex native hook dispatch", () => {
326
488
  assert.match(additionalContext, /\[Execution environment\]/);
327
489
  assert.match(additionalContext, /attached tmux runtime/);
328
490
  assert.match(additionalContext, /omx team, omx hud, and omx quest(?:ion) are directly usable in this session/);
329
- assert.match(additionalContext, /visible renderer available from the current pane/);
491
+ assert.match(additionalContext, /visible temporary renderer available from the current pane; primary success JSON is answers\[\]/);
330
492
  }
331
493
  finally {
332
494
  await rm(cwd, { recursive: true, force: true });
@@ -1771,7 +1933,7 @@ esac
1771
1933
  await rm(cwd, { recursive: true, force: true });
1772
1934
  }
1773
1935
  });
1774
- it("blocks PreToolUse git commit when the inline message is not Lore-compliant", async () => {
1936
+ it("blocks PreToolUse git commit with supported response shape when the inline message is not Lore-compliant", async () => {
1775
1937
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-invalid-"));
1776
1938
  try {
1777
1939
  const result = await dispatchCodexNativeHook({
@@ -1787,13 +1949,6 @@ esac
1787
1949
  reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
1788
1950
  hookSpecificOutput: {
1789
1951
  hookEventName: "PreToolUse",
1790
- additionalContext: [
1791
- "Lore-format git commit enforcement triggered.",
1792
- "- Add a blank line after the subject before the narrative body.",
1793
- "- Add a narrative body paragraph explaining the decision context.",
1794
- "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.",
1795
- "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
1796
- ].join("\n"),
1797
1952
  },
1798
1953
  systemMessage: [
1799
1954
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -1803,6 +1958,9 @@ esac
1803
1958
  "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
1804
1959
  ].join("\n"),
1805
1960
  });
1961
+ const hookSpecificOutput = result.outputJson
1962
+ .hookSpecificOutput ?? {};
1963
+ assert.equal("additionalContext" in hookSpecificOutput, false);
1806
1964
  }
1807
1965
  finally {
1808
1966
  await rm(cwd, { recursive: true, force: true });
@@ -1875,13 +2033,6 @@ esac
1875
2033
  reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
1876
2034
  hookSpecificOutput: {
1877
2035
  hookEventName: "PreToolUse",
1878
- additionalContext: [
1879
- "Lore-format git commit enforcement triggered.",
1880
- "- Add a blank line after the subject before the narrative body.",
1881
- "- Add a narrative body paragraph explaining the decision context.",
1882
- "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.",
1883
- "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
1884
- ].join("\n"),
1885
2036
  },
1886
2037
  systemMessage: [
1887
2038
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -1912,13 +2063,6 @@ esac
1912
2063
  reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
1913
2064
  hookSpecificOutput: {
1914
2065
  hookEventName: "PreToolUse",
1915
- additionalContext: [
1916
- "Lore-format git commit enforcement triggered.",
1917
- "- Add a blank line after the subject before the narrative body.",
1918
- "- Add a narrative body paragraph explaining the decision context.",
1919
- "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.",
1920
- "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
1921
- ].join("\n"),
1922
2066
  },
1923
2067
  systemMessage: [
1924
2068
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -1949,13 +2093,6 @@ esac
1949
2093
  reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
1950
2094
  hookSpecificOutput: {
1951
2095
  hookEventName: "PreToolUse",
1952
- additionalContext: [
1953
- "Lore-format git commit enforcement triggered.",
1954
- "- Add a blank line after the subject before the narrative body.",
1955
- "- Add a narrative body paragraph explaining the decision context.",
1956
- "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.",
1957
- "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
1958
- ].join("\n"),
1959
2096
  },
1960
2097
  systemMessage: [
1961
2098
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -1986,13 +2123,6 @@ esac
1986
2123
  reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
1987
2124
  hookSpecificOutput: {
1988
2125
  hookEventName: "PreToolUse",
1989
- additionalContext: [
1990
- "Lore-format git commit enforcement triggered.",
1991
- "- Add a blank line after the subject before the narrative body.",
1992
- "- Add a narrative body paragraph explaining the decision context.",
1993
- "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.",
1994
- "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
1995
- ].join("\n"),
1996
2126
  },
1997
2127
  systemMessage: [
1998
2128
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -2023,13 +2153,6 @@ esac
2023
2153
  reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
2024
2154
  hookSpecificOutput: {
2025
2155
  hookEventName: "PreToolUse",
2026
- additionalContext: [
2027
- "Lore-format git commit enforcement triggered.",
2028
- "- Add a blank line after the subject before the narrative body.",
2029
- "- Add a narrative body paragraph explaining the decision context.",
2030
- "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.",
2031
- "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
2032
- ].join("\n"),
2033
2156
  },
2034
2157
  systemMessage: [
2035
2158
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -2060,13 +2183,6 @@ esac
2060
2183
  reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
2061
2184
  hookSpecificOutput: {
2062
2185
  hookEventName: "PreToolUse",
2063
- additionalContext: [
2064
- "Lore-format git commit enforcement triggered.",
2065
- "- Add a blank line after the subject before the narrative body.",
2066
- "- Add a narrative body paragraph explaining the decision context.",
2067
- "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.",
2068
- "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
2069
- ].join("\n"),
2070
2186
  },
2071
2187
  systemMessage: [
2072
2188
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -2097,13 +2213,6 @@ esac
2097
2213
  reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
2098
2214
  hookSpecificOutput: {
2099
2215
  hookEventName: "PreToolUse",
2100
- additionalContext: [
2101
- "Lore-format git commit enforcement triggered.",
2102
- "- Add a blank line after the subject before the narrative body.",
2103
- "- Add a narrative body paragraph explaining the decision context.",
2104
- "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.",
2105
- "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
2106
- ].join("\n"),
2107
2216
  },
2108
2217
  systemMessage: [
2109
2218
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -2134,13 +2243,6 @@ esac
2134
2243
  reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
2135
2244
  hookSpecificOutput: {
2136
2245
  hookEventName: "PreToolUse",
2137
- additionalContext: [
2138
- "Lore-format git commit enforcement triggered.",
2139
- "- Add a blank line after the subject before the narrative body.",
2140
- "- Add a narrative body paragraph explaining the decision context.",
2141
- "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.",
2142
- "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
2143
- ].join("\n"),
2144
2246
  },
2145
2247
  systemMessage: [
2146
2248
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -2171,13 +2273,6 @@ esac
2171
2273
  reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
2172
2274
  hookSpecificOutput: {
2173
2275
  hookEventName: "PreToolUse",
2174
- additionalContext: [
2175
- "Lore-format git commit enforcement triggered.",
2176
- "- Add a blank line after the subject before the narrative body.",
2177
- "- Add a narrative body paragraph explaining the decision context.",
2178
- "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.",
2179
- "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
2180
- ].join("\n"),
2181
2276
  },
2182
2277
  systemMessage: [
2183
2278
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -2208,10 +2303,6 @@ esac
2208
2303
  reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
2209
2304
  hookSpecificOutput: {
2210
2305
  hookEventName: "PreToolUse",
2211
- additionalContext: [
2212
- "Lore-format git commit enforcement triggered.",
2213
- "- Use inline `git commit -m ...` paragraphs for Lore-format commits in this path; file/editor/reuse/fixup message sources are not inspectable safely from pre-tool-use enforcement.",
2214
- ].join("\n"),
2215
2306
  },
2216
2307
  systemMessage: [
2217
2308
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -2247,10 +2338,6 @@ esac
2247
2338
  reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
2248
2339
  hookSpecificOutput: {
2249
2340
  hookEventName: "PreToolUse",
2250
- additionalContext: [
2251
- "Lore-format git commit enforcement triggered.",
2252
- "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
2253
- ].join("\n"),
2254
2341
  },
2255
2342
  systemMessage: [
2256
2343
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -2489,6 +2576,24 @@ esac
2489
2576
  await rm(cwd, { recursive: true, force: true });
2490
2577
  }
2491
2578
  });
2579
+ it("stays silent when Bash stdout only contains failure-like source text", async () => {
2580
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-failure-source-text-"));
2581
+ try {
2582
+ const result = await dispatchCodexNativeHook({
2583
+ hook_event_name: "PostToolUse",
2584
+ cwd,
2585
+ tool_name: "Bash",
2586
+ tool_use_id: "tool-source-text",
2587
+ tool_input: { command: "sed -n '1,40p' hook-source.ts" },
2588
+ tool_response: "const text = 'bash: foo: command not found';\nconst detail = 'permission denied';",
2589
+ }, { cwd });
2590
+ assert.equal(result.omxEventName, "post-tool-use");
2591
+ assert.equal(result.outputJson, null);
2592
+ }
2593
+ finally {
2594
+ await rm(cwd, { recursive: true, force: true });
2595
+ }
2596
+ });
2492
2597
  it("stays silent for rc-zero build logs that mention missing grep paths", async () => {
2493
2598
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-build-log-"));
2494
2599
  try {
@@ -2511,6 +2616,28 @@ esac
2511
2616
  await rm(cwd, { recursive: true, force: true });
2512
2617
  }
2513
2618
  });
2619
+ it("does not treat Bash output containing MCP transport text as MCP transport death", async () => {
2620
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-mcp-source-text-"));
2621
+ try {
2622
+ const result = await dispatchCodexNativeHook({
2623
+ hook_event_name: "PostToolUse",
2624
+ cwd,
2625
+ tool_name: "Bash",
2626
+ tool_use_id: "tool-mcp-source-text",
2627
+ tool_input: { command: "sed -n '580,620p' codex-native-pre-post.ts" },
2628
+ tool_response: JSON.stringify({
2629
+ exit_code: 0,
2630
+ stdout: "reason: 'MCP transport closed before response over stdio pipe closed'",
2631
+ stderr: "",
2632
+ }),
2633
+ }, { cwd });
2634
+ assert.equal(result.omxEventName, "post-tool-use");
2635
+ assert.equal(result.outputJson, null);
2636
+ }
2637
+ finally {
2638
+ await rm(cwd, { recursive: true, force: true });
2639
+ }
2640
+ });
2514
2641
  it("stays silent when successful output includes prior hook context text", async () => {
2515
2642
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-recursive-context-"));
2516
2643
  try {
@@ -2533,6 +2660,46 @@ esac
2533
2660
  await rm(cwd, { recursive: true, force: true });
2534
2661
  }
2535
2662
  });
2663
+ it("stays silent when successful Bash output quotes MCP transport warnings", async () => {
2664
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-bash-mcp-quote-"));
2665
+ try {
2666
+ const result = await dispatchCodexNativeHook({
2667
+ hook_event_name: "PostToolUse",
2668
+ cwd,
2669
+ tool_name: "Bash",
2670
+ tool_use_id: "tool-bash-mcp-quote",
2671
+ tool_input: { command: "cat diagnostic-log.txt" },
2672
+ tool_response: JSON.stringify({
2673
+ exit_code: 0,
2674
+ stdout: "diagnostic log quoted: MCP transport closed; stdio pipe closed before response",
2675
+ stderr: "",
2676
+ }),
2677
+ }, { cwd });
2678
+ assert.equal(result.omxEventName, "post-tool-use");
2679
+ assert.equal(result.outputJson, null);
2680
+ }
2681
+ finally {
2682
+ await rm(cwd, { recursive: true, force: true });
2683
+ }
2684
+ });
2685
+ it("stays silent when Bash hard-failure text has no parsed exit code", async () => {
2686
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-bash-unparsed-failure-"));
2687
+ try {
2688
+ const result = await dispatchCodexNativeHook({
2689
+ hook_event_name: "PostToolUse",
2690
+ cwd,
2691
+ tool_name: "Bash",
2692
+ tool_use_id: "tool-bash-unparsed-failure",
2693
+ tool_input: { command: "cat captured-output.txt" },
2694
+ tool_response: "captured transcript says: bash: foo: command not found",
2695
+ }, { cwd });
2696
+ assert.equal(result.omxEventName, "post-tool-use");
2697
+ assert.equal(result.outputJson, null);
2698
+ }
2699
+ finally {
2700
+ await rm(cwd, { recursive: true, force: true });
2701
+ }
2702
+ });
2536
2703
  it("returns PostToolUse MCP transport fallback guidance for clear MCP transport death", async () => {
2537
2704
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-mcp-transport-"));
2538
2705
  try {
@@ -2979,7 +3146,17 @@ esac
2979
3146
  const prevLeaderCwd = process.env.OMX_TEAM_LEADER_CWD;
2980
3147
  try {
2981
3148
  await initTeamState("worker-stop-team", "worker stop fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-worker" });
3149
+ const workerCwd = join(cwd, ".omx", "team", "worker-stop-team", "worktrees", "worker-1");
2982
3150
  const workerDir = join(cwd, ".omx", "state", "team", "worker-stop-team", "workers", "worker-1");
3151
+ await mkdir(workerCwd, { recursive: true });
3152
+ await writeJson(join(workerDir, "identity.json"), {
3153
+ name: "worker-1",
3154
+ index: 1,
3155
+ role: "executor",
3156
+ assigned_tasks: ["1"],
3157
+ worktree_path: workerCwd,
3158
+ team_state_root: join(cwd, ".omx", "state"),
3159
+ });
2983
3160
  await writeJson(join(workerDir, "status.json"), {
2984
3161
  state: "idle",
2985
3162
  current_task_id: "1",
@@ -2998,9 +3175,9 @@ esac
2998
3175
  process.env.OMX_TEAM_LEADER_CWD = cwd;
2999
3176
  const result = await dispatchCodexNativeHook({
3000
3177
  hook_event_name: "Stop",
3001
- cwd: join(cwd, ".omx", "team", "worker-stop-team", "worktrees", "worker-1"),
3178
+ cwd: workerCwd,
3002
3179
  session_id: "sess-stop-team-worker",
3003
- }, { cwd: join(cwd, ".omx", "team", "worker-stop-team", "worktrees", "worker-1") });
3180
+ }, { cwd: workerCwd });
3004
3181
  assert.deepEqual(result.outputJson, {
3005
3182
  decision: "block",
3006
3183
  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.",
@@ -3031,6 +3208,16 @@ esac
3031
3208
  const stateDir = join(cwd, ".omx", "state");
3032
3209
  const workerDir = join(stateDir, "team", "worker-repeat-team", "workers", "worker-1");
3033
3210
  const taskPath = join(stateDir, "team", "worker-repeat-team", "tasks", "task-1.json");
3211
+ const workerCwd = join(cwd, ".omx", "team", "worker-repeat-team", "worktrees", "worker-1");
3212
+ await mkdir(workerCwd, { recursive: true });
3213
+ await writeJson(join(workerDir, "identity.json"), {
3214
+ name: "worker-1",
3215
+ index: 1,
3216
+ role: "executor",
3217
+ assigned_tasks: ["1"],
3218
+ worktree_path: workerCwd,
3219
+ team_state_root: stateDir,
3220
+ });
3034
3221
  await writeJson(join(workerDir, "status.json"), {
3035
3222
  state: "idle",
3036
3223
  current_task_id: "1",
@@ -3047,7 +3234,6 @@ esac
3047
3234
  process.env.OMX_TEAM_WORKER = "worker-repeat-team/worker-1";
3048
3235
  process.env.OMX_TEAM_STATE_ROOT = stateDir;
3049
3236
  process.env.OMX_TEAM_LEADER_CWD = cwd;
3050
- const workerCwd = join(cwd, ".omx", "team", "worker-repeat-team", "worktrees", "worker-1");
3051
3237
  const basePayload = {
3052
3238
  hook_event_name: "Stop",
3053
3239
  cwd: workerCwd,
@@ -3207,6 +3393,38 @@ esac
3207
3393
  await rm(cwd, { recursive: true, force: true });
3208
3394
  }
3209
3395
  });
3396
+ it("honors terminal team run-state before later canonical-team Stop fallback", async () => {
3397
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-terminal-run-state-canonical-"));
3398
+ try {
3399
+ const stateDir = join(cwd, ".omx", "state");
3400
+ const sessionId = "sess-stop-team-terminal-run-state";
3401
+ await initTeamState("terminal-run-state-team", "terminal team stop canonical fallback regression", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: sessionId });
3402
+ await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
3403
+ await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd });
3404
+ await writeJson(join(stateDir, "sessions", sessionId, "run-state.json"), {
3405
+ version: 1,
3406
+ mode: "team",
3407
+ active: false,
3408
+ outcome: "finish",
3409
+ lifecycle_outcome: "finished",
3410
+ current_phase: "complete",
3411
+ completed_at: "2026-04-27T12:00:00.000Z",
3412
+ updated_at: "2026-04-27T12:00:00.000Z",
3413
+ });
3414
+ const result = await dispatchCodexNativeHook({
3415
+ hook_event_name: "Stop",
3416
+ cwd,
3417
+ session_id: sessionId,
3418
+ thread_id: "thread-stop-team-terminal-run-state",
3419
+ turn_id: "turn-stop-team-terminal-run-state-1",
3420
+ }, { cwd });
3421
+ assert.equal(result.omxEventName, "stop");
3422
+ assert.equal(result.outputJson, null);
3423
+ }
3424
+ finally {
3425
+ await rm(cwd, { recursive: true, force: true });
3426
+ }
3427
+ });
3210
3428
  it("re-fires canonical-team Stop output for a later fresh Stop reply when coarse mode state is missing", async () => {
3211
3429
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-canonical-refire-"));
3212
3430
  try {
@@ -3679,7 +3897,7 @@ esac
3679
3897
  decision: "block",
3680
3898
  reason: "Deep interview is still active (phase: intent-first) and has a pending structured question obligation; use `omx question` before stopping.",
3681
3899
  stopReason: "deep_interview_question_required",
3682
- systemMessage: "OMX deep-interview is still active (phase: intent-first) and requires a structured question via omx question before stopping.",
3900
+ systemMessage: "OMX deep-interview is still active (phase: intent-first) and requires a structured question via omx question before stopping; read the returned answers[] JSON before continuing.",
3683
3901
  });
3684
3902
  }
3685
3903
  finally {
@@ -3728,7 +3946,7 @@ esac
3728
3946
  decision: "block",
3729
3947
  reason: "Deep interview is still active (phase: intent-first) and has a pending structured question obligation; use `omx question` before stopping.",
3730
3948
  stopReason: "deep_interview_question_required",
3731
- systemMessage: "OMX deep-interview is still active (phase: intent-first) and requires a structured question via omx question before stopping.",
3949
+ systemMessage: "OMX deep-interview is still active (phase: intent-first) and requires a structured question via omx question before stopping; read the returned answers[] JSON before continuing.",
3732
3950
  });
3733
3951
  }
3734
3952
  finally {
@@ -3841,7 +4059,7 @@ esac
3841
4059
  decision: "block",
3842
4060
  reason: "Deep interview is still active (phase: intent-first) and has a pending structured question obligation; use `omx question` before stopping.",
3843
4061
  stopReason: "deep_interview_question_required",
3844
- systemMessage: "OMX deep-interview is still active (phase: intent-first) and requires a structured question via omx question before stopping.",
4062
+ systemMessage: "OMX deep-interview is still active (phase: intent-first) and requires a structured question via omx question before stopping; read the returned answers[] JSON before continuing.",
3845
4063
  };
3846
4064
  const first = await dispatchCodexNativeHook(payload, { cwd });
3847
4065
  const replay = await dispatchCodexNativeHook({ ...payload, stop_hook_active: true }, { cwd });
@@ -3968,7 +4186,7 @@ esac
3968
4186
  decision: "block",
3969
4187
  reason: "Deep interview is still active (phase: intent-first) and has a pending structured question obligation; use `omx question` before stopping.",
3970
4188
  stopReason: "deep_interview_question_required",
3971
- systemMessage: "OMX deep-interview is still active (phase: intent-first) and requires a structured question via omx question before stopping.",
4189
+ systemMessage: "OMX deep-interview is still active (phase: intent-first) and requires a structured question via omx question before stopping; read the returned answers[] JSON before continuing.",
3972
4190
  });
3973
4191
  }
3974
4192
  finally {
@@ -4114,9 +4332,9 @@ esac
4114
4332
  assert.equal(result.omxEventName, "stop");
4115
4333
  assert.deepEqual(result.outputJson, {
4116
4334
  decision: "block",
4117
- reason: "OMX Ralph is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.",
4335
+ reason: "OMX Ralph is still active (phase: executing; state: .omx/state/ralph-state.json); continue the task and gather fresh verification evidence before stopping.",
4118
4336
  stopReason: "ralph_executing",
4119
- systemMessage: "OMX Ralph is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.",
4337
+ systemMessage: "OMX Ralph is still active (phase: executing; state: .omx/state/ralph-state.json); continue the task and gather fresh verification evidence before stopping.",
4120
4338
  });
4121
4339
  }
4122
4340
  finally {
@@ -4142,9 +4360,9 @@ esac
4142
4360
  assert.equal(result.omxEventName, "stop");
4143
4361
  assert.deepEqual(result.outputJson, {
4144
4362
  decision: "block",
4145
- reason: "OMX Ralph is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.",
4363
+ reason: "OMX Ralph is still active (phase: executing; state: .omx/state/sessions/sess-live-ralph/ralph-state.json); continue the task and gather fresh verification evidence before stopping.",
4146
4364
  stopReason: "ralph_executing",
4147
- systemMessage: "OMX Ralph is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.",
4365
+ systemMessage: "OMX Ralph is still active (phase: executing; state: .omx/state/sessions/sess-live-ralph/ralph-state.json); continue the task and gather fresh verification evidence before stopping.",
4148
4366
  });
4149
4367
  }
4150
4368
  finally {
@@ -4219,6 +4437,74 @@ esac
4219
4437
  await rm(cwd, { recursive: true, force: true });
4220
4438
  }
4221
4439
  });
4440
+ it("does not hard-block Stop on stale session-scoped Ralph starting state after visible active modes are cleared", async () => {
4441
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-cleared-stale-ralph-"));
4442
+ try {
4443
+ const stateDir = join(cwd, ".omx", "state");
4444
+ const sessionId = "sess-cleared-ralph";
4445
+ await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
4446
+ await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), {
4447
+ active: true,
4448
+ mode: "ralph",
4449
+ current_phase: "starting",
4450
+ session_id: sessionId,
4451
+ });
4452
+ await writeJson(join(stateDir, "skill-active-state.json"), {
4453
+ active: false,
4454
+ skill: "ralph",
4455
+ active_skills: [],
4456
+ });
4457
+ const listActive = await executeStateOperation("state_list_active", {
4458
+ workingDirectory: cwd,
4459
+ });
4460
+ assert.deepEqual(listActive.payload, { active_modes: [] });
4461
+ const result = await dispatchCodexNativeHook({
4462
+ hook_event_name: "Stop",
4463
+ cwd,
4464
+ session_id: sessionId,
4465
+ }, { cwd });
4466
+ assert.equal(result.omxEventName, "stop");
4467
+ assert.equal(result.outputJson, null);
4468
+ }
4469
+ finally {
4470
+ await rm(cwd, { recursive: true, force: true });
4471
+ }
4472
+ });
4473
+ it("blocks Stop on visible active session-scoped Ralph starting state and reports its path", async () => {
4474
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-visible-starting-ralph-"));
4475
+ try {
4476
+ const stateDir = join(cwd, ".omx", "state");
4477
+ const sessionId = "sess-visible-ralph";
4478
+ await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
4479
+ await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), {
4480
+ active: true,
4481
+ mode: "ralph",
4482
+ current_phase: "starting",
4483
+ session_id: sessionId,
4484
+ });
4485
+ await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
4486
+ active: true,
4487
+ skill: "ralph",
4488
+ phase: "starting",
4489
+ active_skills: [{ skill: "ralph", phase: "starting", active: true, session_id: sessionId }],
4490
+ });
4491
+ const result = await dispatchCodexNativeHook({
4492
+ hook_event_name: "Stop",
4493
+ cwd,
4494
+ session_id: sessionId,
4495
+ }, { cwd });
4496
+ assert.equal(result.omxEventName, "stop");
4497
+ assert.deepEqual(result.outputJson, {
4498
+ decision: "block",
4499
+ reason: "OMX Ralph is still active (phase: starting; state: .omx/state/sessions/sess-visible-ralph/ralph-state.json); continue the task and gather fresh verification evidence before stopping.",
4500
+ stopReason: "ralph_starting",
4501
+ systemMessage: "OMX Ralph is still active (phase: starting; state: .omx/state/sessions/sess-visible-ralph/ralph-state.json); continue the task and gather fresh verification evidence before stopping.",
4502
+ });
4503
+ }
4504
+ finally {
4505
+ await rm(cwd, { recursive: true, force: true });
4506
+ }
4507
+ });
4222
4508
  it("does not block Stop from another session-scoped Ralph state when an explicit session_id has no active Ralph state", async () => {
4223
4509
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-explicit-session-ralph-"));
4224
4510
  try {
@@ -4241,6 +4527,41 @@ esac
4241
4527
  await rm(cwd, { recursive: true, force: true });
4242
4528
  }
4243
4529
  });
4530
+ it("prefers canonical run-state terminal lifecycle before stale session Ralph state during Stop", async () => {
4531
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-canonical-run-state-ralph-"));
4532
+ try {
4533
+ const stateDir = join(cwd, ".omx", "state");
4534
+ const sessionId = "sess-canonical-run-state-ralph";
4535
+ await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
4536
+ await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd });
4537
+ await writeJson(join(stateDir, "sessions", sessionId, "run-state.json"), {
4538
+ version: 1,
4539
+ mode: "ralph",
4540
+ active: false,
4541
+ outcome: "finish",
4542
+ lifecycle_outcome: "finished",
4543
+ current_phase: "complete",
4544
+ completed_at: "2026-04-27T12:00:00.000Z",
4545
+ updated_at: "2026-04-27T12:00:00.000Z",
4546
+ });
4547
+ await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), {
4548
+ active: true,
4549
+ mode: "ralph",
4550
+ current_phase: "verifying",
4551
+ session_id: sessionId,
4552
+ });
4553
+ const result = await dispatchCodexNativeHook({
4554
+ hook_event_name: "Stop",
4555
+ cwd,
4556
+ session_id: sessionId,
4557
+ }, { cwd });
4558
+ assert.equal(result.omxEventName, "stop");
4559
+ assert.equal(result.outputJson, null);
4560
+ }
4561
+ finally {
4562
+ await rm(cwd, { recursive: true, force: true });
4563
+ }
4564
+ });
4244
4565
  it("does not block Stop from root Ralph fallback when the current session has no scoped Ralph state", async () => {
4245
4566
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-root-fallback-ralph-"));
4246
4567
  try {
@@ -4334,9 +4655,9 @@ esac
4334
4655
  };
4335
4656
  const expected = {
4336
4657
  decision: "block",
4337
- reason: "OMX Ralph is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.",
4658
+ reason: "OMX Ralph is still active (phase: executing; state: .omx/state/ralph-state.json); continue the task and gather fresh verification evidence before stopping.",
4338
4659
  stopReason: "ralph_executing",
4339
- systemMessage: "OMX Ralph is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.",
4660
+ systemMessage: "OMX Ralph is still active (phase: executing; state: .omx/state/ralph-state.json); continue the task and gather fresh verification evidence before stopping.",
4340
4661
  };
4341
4662
  const first = await dispatchCodexNativeHook(payload, { cwd });
4342
4663
  const replay = await dispatchCodexNativeHook({