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";
@@ -22,6 +22,7 @@ import {
22
22
  } from "../codex-native-hook.js";
23
23
  import { writeSessionStart } from "../../hooks/session.js";
24
24
  import { resetTriageConfigCache } from "../../hooks/triage-config.js";
25
+ import { executeStateOperation } from "../../state/operations.js";
25
26
 
26
27
  function nativeHookScriptPath(): string {
27
28
  return join(process.cwd(), "dist", "scripts", "codex-native-hook.js");
@@ -264,6 +265,49 @@ describe("codex native hook dispatch", () => {
264
265
  );
265
266
  });
266
267
 
268
+ it("emits parseable no-op JSON stdout for inactive Stop CLI runs", async () => {
269
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-stop-noop-json-"));
270
+ try {
271
+ const stdout = runNativeHookCli({
272
+ hook_event_name: "Stop",
273
+ cwd,
274
+ session_id: "sess-cli-stop-noop-json",
275
+ thread_id: "thread-cli-stop-noop-json",
276
+ turn_id: "turn-cli-stop-noop-json",
277
+ }, { cwd });
278
+ const output = parseSingleJsonStdout(stdout);
279
+
280
+ assert.deepEqual(output, {});
281
+ } finally {
282
+ await rm(cwd, { recursive: true, force: true });
283
+ }
284
+ });
285
+
286
+ it("does not crash Stop hook dispatch when the exec follow-up queue is malformed", async () => {
287
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-stop-exec-followup-corrupt-"));
288
+ try {
289
+ const session = await writeSessionStart(cwd, "sess-exec-followup-corrupt");
290
+ const queuePath = join(cwd, ".omx", "state", "sessions", session.session_id, "exec-followups.json");
291
+ await mkdir(dirname(queuePath), { recursive: true });
292
+ await writeFile(queuePath, '{"version":1,"records":[', "utf-8");
293
+
294
+ const result = await dispatchCodexNativeHook({
295
+ hook_event_name: "Stop",
296
+ cwd,
297
+ session_id: session.session_id,
298
+ });
299
+
300
+ assert.equal(result.hookEventName, "Stop");
301
+ assert.equal(result.outputJson, null);
302
+ const queueDirEntries = await readdir(dirname(queuePath));
303
+ assert.ok(queueDirEntries.some((entry) => entry.startsWith("exec-followups.json.corrupt-")));
304
+ const auditPath = join(cwd, ".omx", "logs", `exec-followups-${new Date().toISOString().slice(0, 10)}.jsonl`);
305
+ assert.match(await readFile(auditPath, "utf-8"), /exec_followup_queue_corrupt_recovered/);
306
+ } finally {
307
+ await rm(cwd, { recursive: true, force: true });
308
+ }
309
+ });
310
+
267
311
  it("emits exactly one parseable JSON object for active Stop CLI continuation", async () => {
268
312
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-stop-json-"));
269
313
  try {
@@ -440,6 +484,164 @@ describe("codex native hook dispatch", () => {
440
484
  }
441
485
  });
442
486
 
487
+ it("keeps subagent SessionStart from replacing the canonical leader session", async () => {
488
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-session-start-"));
489
+ try {
490
+ const stateDir = join(cwd, ".omx", "state");
491
+ const canonicalSessionId = "omx-leader-session";
492
+ const leaderNativeSessionId = "codex-leader-thread";
493
+ const childNativeSessionId = "codex-child-thread";
494
+ await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true });
495
+ await writeSessionStart(cwd, canonicalSessionId, {
496
+ nativeSessionId: leaderNativeSessionId,
497
+ });
498
+ await writeJson(join(stateDir, "sessions", canonicalSessionId, "ralph-state.json"), {
499
+ active: true,
500
+ mode: "ralph",
501
+ current_phase: "executing",
502
+ iteration: 1,
503
+ max_iterations: 5,
504
+ });
505
+ const transcriptPath = join(cwd, "subagent-rollout.jsonl");
506
+ await writeFile(
507
+ transcriptPath,
508
+ `${JSON.stringify({
509
+ type: "session_meta",
510
+ payload: {
511
+ id: childNativeSessionId,
512
+ source: {
513
+ subagent: {
514
+ thread_spawn: {
515
+ parent_thread_id: leaderNativeSessionId,
516
+ depth: 1,
517
+ agent_nickname: "Hegel",
518
+ agent_role: "critic",
519
+ },
520
+ },
521
+ },
522
+ agent_nickname: "Hegel",
523
+ agent_role: "critic",
524
+ },
525
+ })}\n`,
526
+ );
527
+
528
+ const result = await dispatchCodexNativeHook(
529
+ {
530
+ hook_event_name: "SessionStart",
531
+ cwd,
532
+ session_id: childNativeSessionId,
533
+ transcript_path: transcriptPath,
534
+ },
535
+ { cwd, sessionOwnerPid: process.pid },
536
+ );
537
+
538
+ const sessionState = JSON.parse(
539
+ await readFile(join(stateDir, "session.json"), "utf-8"),
540
+ ) as { session_id?: string; native_session_id?: string };
541
+ assert.equal(sessionState.session_id, canonicalSessionId);
542
+ assert.equal(sessionState.native_session_id, leaderNativeSessionId);
543
+ assert.equal(
544
+ existsSync(join(stateDir, "sessions", childNativeSessionId, "ralph-state.json")),
545
+ false,
546
+ );
547
+ assert.ok(result.outputJson);
548
+
549
+ const leaderRalph = JSON.parse(
550
+ await readFile(join(stateDir, "sessions", canonicalSessionId, "ralph-state.json"), "utf-8"),
551
+ ) as { active?: boolean; current_phase?: string };
552
+ assert.equal(leaderRalph.active, true);
553
+ assert.equal(leaderRalph.current_phase, "executing");
554
+
555
+ const tracking = JSON.parse(
556
+ await readFile(join(stateDir, "subagent-tracking.json"), "utf-8"),
557
+ ) as {
558
+ sessions?: Record<string, {
559
+ leader_thread_id?: string;
560
+ threads?: Record<string, { kind?: string; mode?: string }>;
561
+ }>;
562
+ };
563
+ assert.equal(tracking.sessions?.[canonicalSessionId]?.leader_thread_id, leaderNativeSessionId);
564
+ assert.equal(tracking.sessions?.[canonicalSessionId]?.threads?.[childNativeSessionId]?.kind, "subagent");
565
+ assert.equal(tracking.sessions?.[canonicalSessionId]?.threads?.[childNativeSessionId]?.mode, "critic");
566
+ assert.equal(tracking.sessions?.[leaderNativeSessionId]?.leader_thread_id, leaderNativeSessionId);
567
+ assert.equal(tracking.sessions?.[leaderNativeSessionId]?.threads?.[childNativeSessionId]?.kind, "subagent");
568
+ assert.equal(tracking.sessions?.[leaderNativeSessionId]?.threads?.[childNativeSessionId]?.mode, "critic");
569
+ } finally {
570
+ await rm(cwd, { recursive: true, force: true });
571
+ }
572
+ });
573
+
574
+ it("does not attach a subagent SessionStart to an unrelated canonical leader", async () => {
575
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-session-start-mismatch-"));
576
+ try {
577
+ const stateDir = join(cwd, ".omx", "state");
578
+ const canonicalSessionId = "omx-leader-session-a";
579
+ const leaderNativeSessionId = "codex-leader-thread-a";
580
+ const unrelatedParentNativeSessionId = "codex-leader-thread-b";
581
+ const childNativeSessionId = "codex-child-thread-b";
582
+ await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true });
583
+ await writeSessionStart(cwd, canonicalSessionId, {
584
+ nativeSessionId: leaderNativeSessionId,
585
+ });
586
+ await writeJson(join(stateDir, "sessions", canonicalSessionId, "ralph-state.json"), {
587
+ active: true,
588
+ mode: "ralph",
589
+ current_phase: "executing",
590
+ iteration: 1,
591
+ max_iterations: 5,
592
+ });
593
+ const transcriptPath = join(cwd, "unrelated-subagent-rollout.jsonl");
594
+ await writeFile(
595
+ transcriptPath,
596
+ `${JSON.stringify({
597
+ type: "session_meta",
598
+ payload: {
599
+ id: childNativeSessionId,
600
+ source: {
601
+ subagent: {
602
+ thread_spawn: {
603
+ parent_thread_id: unrelatedParentNativeSessionId,
604
+ depth: 1,
605
+ agent_nickname: "Spinoza",
606
+ agent_role: "critic",
607
+ },
608
+ },
609
+ },
610
+ agent_nickname: "Spinoza",
611
+ agent_role: "critic",
612
+ },
613
+ })}\n`,
614
+ );
615
+
616
+ const result = await dispatchCodexNativeHook(
617
+ {
618
+ hook_event_name: "SessionStart",
619
+ cwd,
620
+ session_id: childNativeSessionId,
621
+ transcript_path: transcriptPath,
622
+ },
623
+ { cwd, sessionOwnerPid: process.pid },
624
+ );
625
+
626
+ const sessionState = JSON.parse(
627
+ await readFile(join(stateDir, "session.json"), "utf-8"),
628
+ ) as { session_id?: string; native_session_id?: string };
629
+ assert.equal(sessionState.session_id, canonicalSessionId);
630
+ assert.equal(sessionState.native_session_id, leaderNativeSessionId);
631
+ assert.equal(existsSync(join(stateDir, "subagent-tracking.json")), false);
632
+ assert.equal(existsSync(join(stateDir, "sessions", childNativeSessionId)), false);
633
+ assert.equal(result.outputJson, null);
634
+
635
+ const leaderRalph = JSON.parse(
636
+ await readFile(join(stateDir, "sessions", canonicalSessionId, "ralph-state.json"), "utf-8"),
637
+ ) as { active?: boolean; current_phase?: string };
638
+ assert.equal(leaderRalph.active, true);
639
+ assert.equal(leaderRalph.current_phase, "executing");
640
+ } finally {
641
+ await rm(cwd, { recursive: true, force: true });
642
+ }
643
+ });
644
+
443
645
  it("describes attached tmux runtime in SessionStart context when TMUX is present", async () => {
444
646
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-start-tmux-"));
445
647
  process.env.TMUX = "/tmp/tmux-attached";
@@ -463,7 +665,7 @@ describe("codex native hook dispatch", () => {
463
665
  assert.match(additionalContext, /\[Execution environment\]/);
464
666
  assert.match(additionalContext, /attached tmux runtime/);
465
667
  assert.match(additionalContext, /omx team, omx hud, and omx quest(?:ion) are directly usable in this session/);
466
- assert.match(additionalContext, /visible renderer available from the current pane/);
668
+ assert.match(additionalContext, /visible temporary renderer available from the current pane; primary success JSON is answers\[\]/);
467
669
  } finally {
468
670
  await rm(cwd, { recursive: true, force: true });
469
671
  }
@@ -2218,7 +2420,7 @@ esac
2218
2420
  }
2219
2421
  });
2220
2422
 
2221
- it("blocks PreToolUse git commit when the inline message is not Lore-compliant", async () => {
2423
+ it("blocks PreToolUse git commit with supported response shape when the inline message is not Lore-compliant", async () => {
2222
2424
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-invalid-"));
2223
2425
  try {
2224
2426
  const result = await dispatchCodexNativeHook(
@@ -2239,13 +2441,6 @@ esac
2239
2441
  "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
2240
2442
  hookSpecificOutput: {
2241
2443
  hookEventName: "PreToolUse",
2242
- additionalContext: [
2243
- "Lore-format git commit enforcement triggered.",
2244
- "- Add a blank line after the subject before the narrative body.",
2245
- "- Add a narrative body paragraph explaining the decision context.",
2246
- "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.",
2247
- "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
2248
- ].join("\n"),
2249
2444
  },
2250
2445
  systemMessage: [
2251
2446
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -2255,6 +2450,9 @@ esac
2255
2450
  "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
2256
2451
  ].join("\n"),
2257
2452
  });
2453
+ const hookSpecificOutput = (result.outputJson as { hookSpecificOutput?: Record<string, unknown> })
2454
+ .hookSpecificOutput ?? {};
2455
+ assert.equal("additionalContext" in hookSpecificOutput, false);
2258
2456
  } finally {
2259
2457
  await rm(cwd, { recursive: true, force: true });
2260
2458
  }
@@ -2344,13 +2542,6 @@ esac
2344
2542
  "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
2345
2543
  hookSpecificOutput: {
2346
2544
  hookEventName: "PreToolUse",
2347
- additionalContext: [
2348
- "Lore-format git commit enforcement triggered.",
2349
- "- Add a blank line after the subject before the narrative body.",
2350
- "- Add a narrative body paragraph explaining the decision context.",
2351
- "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.",
2352
- "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
2353
- ].join("\n"),
2354
2545
  },
2355
2546
  systemMessage: [
2356
2547
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -2386,13 +2577,6 @@ esac
2386
2577
  "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
2387
2578
  hookSpecificOutput: {
2388
2579
  hookEventName: "PreToolUse",
2389
- additionalContext: [
2390
- "Lore-format git commit enforcement triggered.",
2391
- "- Add a blank line after the subject before the narrative body.",
2392
- "- Add a narrative body paragraph explaining the decision context.",
2393
- "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.",
2394
- "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
2395
- ].join("\n"),
2396
2580
  },
2397
2581
  systemMessage: [
2398
2582
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -2428,13 +2612,6 @@ esac
2428
2612
  "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
2429
2613
  hookSpecificOutput: {
2430
2614
  hookEventName: "PreToolUse",
2431
- additionalContext: [
2432
- "Lore-format git commit enforcement triggered.",
2433
- "- Add a blank line after the subject before the narrative body.",
2434
- "- Add a narrative body paragraph explaining the decision context.",
2435
- "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.",
2436
- "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
2437
- ].join("\n"),
2438
2615
  },
2439
2616
  systemMessage: [
2440
2617
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -2470,13 +2647,6 @@ esac
2470
2647
  "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
2471
2648
  hookSpecificOutput: {
2472
2649
  hookEventName: "PreToolUse",
2473
- additionalContext: [
2474
- "Lore-format git commit enforcement triggered.",
2475
- "- Add a blank line after the subject before the narrative body.",
2476
- "- Add a narrative body paragraph explaining the decision context.",
2477
- "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.",
2478
- "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
2479
- ].join("\n"),
2480
2650
  },
2481
2651
  systemMessage: [
2482
2652
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -2512,13 +2682,6 @@ esac
2512
2682
  "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
2513
2683
  hookSpecificOutput: {
2514
2684
  hookEventName: "PreToolUse",
2515
- additionalContext: [
2516
- "Lore-format git commit enforcement triggered.",
2517
- "- Add a blank line after the subject before the narrative body.",
2518
- "- Add a narrative body paragraph explaining the decision context.",
2519
- "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.",
2520
- "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
2521
- ].join("\n"),
2522
2685
  },
2523
2686
  systemMessage: [
2524
2687
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -2554,13 +2717,6 @@ esac
2554
2717
  "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
2555
2718
  hookSpecificOutput: {
2556
2719
  hookEventName: "PreToolUse",
2557
- additionalContext: [
2558
- "Lore-format git commit enforcement triggered.",
2559
- "- Add a blank line after the subject before the narrative body.",
2560
- "- Add a narrative body paragraph explaining the decision context.",
2561
- "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.",
2562
- "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
2563
- ].join("\n"),
2564
2720
  },
2565
2721
  systemMessage: [
2566
2722
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -2596,13 +2752,6 @@ esac
2596
2752
  "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
2597
2753
  hookSpecificOutput: {
2598
2754
  hookEventName: "PreToolUse",
2599
- additionalContext: [
2600
- "Lore-format git commit enforcement triggered.",
2601
- "- Add a blank line after the subject before the narrative body.",
2602
- "- Add a narrative body paragraph explaining the decision context.",
2603
- "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.",
2604
- "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
2605
- ].join("\n"),
2606
2755
  },
2607
2756
  systemMessage: [
2608
2757
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -2638,13 +2787,6 @@ esac
2638
2787
  "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
2639
2788
  hookSpecificOutput: {
2640
2789
  hookEventName: "PreToolUse",
2641
- additionalContext: [
2642
- "Lore-format git commit enforcement triggered.",
2643
- "- Add a blank line after the subject before the narrative body.",
2644
- "- Add a narrative body paragraph explaining the decision context.",
2645
- "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.",
2646
- "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
2647
- ].join("\n"),
2648
2790
  },
2649
2791
  systemMessage: [
2650
2792
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -2680,13 +2822,6 @@ esac
2680
2822
  "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
2681
2823
  hookSpecificOutput: {
2682
2824
  hookEventName: "PreToolUse",
2683
- additionalContext: [
2684
- "Lore-format git commit enforcement triggered.",
2685
- "- Add a blank line after the subject before the narrative body.",
2686
- "- Add a narrative body paragraph explaining the decision context.",
2687
- "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.",
2688
- "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
2689
- ].join("\n"),
2690
2825
  },
2691
2826
  systemMessage: [
2692
2827
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -2722,10 +2857,6 @@ esac
2722
2857
  "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
2723
2858
  hookSpecificOutput: {
2724
2859
  hookEventName: "PreToolUse",
2725
- additionalContext: [
2726
- "Lore-format git commit enforcement triggered.",
2727
- "- 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.",
2728
- ].join("\n"),
2729
2860
  },
2730
2861
  systemMessage: [
2731
2862
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -2766,10 +2897,6 @@ esac
2766
2897
  "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.",
2767
2898
  hookSpecificOutput: {
2768
2899
  hookEventName: "PreToolUse",
2769
- additionalContext: [
2770
- "Lore-format git commit enforcement triggered.",
2771
- "- Add the required co-author trailer: `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
2772
- ].join("\n"),
2773
2900
  },
2774
2901
  systemMessage: [
2775
2902
  "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX <omx@oh-my-codex.dev>`.",
@@ -3042,6 +3169,28 @@ esac
3042
3169
  }
3043
3170
  });
3044
3171
 
3172
+ it("stays silent when Bash stdout only contains failure-like source text", async () => {
3173
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-failure-source-text-"));
3174
+ try {
3175
+ const result = await dispatchCodexNativeHook(
3176
+ {
3177
+ hook_event_name: "PostToolUse",
3178
+ cwd,
3179
+ tool_name: "Bash",
3180
+ tool_use_id: "tool-source-text",
3181
+ tool_input: { command: "sed -n '1,40p' hook-source.ts" },
3182
+ tool_response: "const text = 'bash: foo: command not found';\nconst detail = 'permission denied';",
3183
+ },
3184
+ { cwd },
3185
+ );
3186
+
3187
+ assert.equal(result.omxEventName, "post-tool-use");
3188
+ assert.equal(result.outputJson, null);
3189
+ } finally {
3190
+ await rm(cwd, { recursive: true, force: true });
3191
+ }
3192
+ });
3193
+
3045
3194
  it("stays silent for rc-zero build logs that mention missing grep paths", async () => {
3046
3195
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-build-log-"));
3047
3196
  try {
@@ -3068,6 +3217,32 @@ esac
3068
3217
  }
3069
3218
  });
3070
3219
 
3220
+ it("does not treat Bash output containing MCP transport text as MCP transport death", async () => {
3221
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-mcp-source-text-"));
3222
+ try {
3223
+ const result = await dispatchCodexNativeHook(
3224
+ {
3225
+ hook_event_name: "PostToolUse",
3226
+ cwd,
3227
+ tool_name: "Bash",
3228
+ tool_use_id: "tool-mcp-source-text",
3229
+ tool_input: { command: "sed -n '580,620p' codex-native-pre-post.ts" },
3230
+ tool_response: JSON.stringify({
3231
+ exit_code: 0,
3232
+ stdout: "reason: 'MCP transport closed before response over stdio pipe closed'",
3233
+ stderr: "",
3234
+ }),
3235
+ },
3236
+ { cwd },
3237
+ );
3238
+
3239
+ assert.equal(result.omxEventName, "post-tool-use");
3240
+ assert.equal(result.outputJson, null);
3241
+ } finally {
3242
+ await rm(cwd, { recursive: true, force: true });
3243
+ }
3244
+ });
3245
+
3071
3246
  it("stays silent when successful output includes prior hook context text", async () => {
3072
3247
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-recursive-context-"));
3073
3248
  try {
@@ -3095,6 +3270,54 @@ esac
3095
3270
  }
3096
3271
  });
3097
3272
 
3273
+ it("stays silent when successful Bash output quotes MCP transport warnings", async () => {
3274
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-bash-mcp-quote-"));
3275
+ try {
3276
+ const result = await dispatchCodexNativeHook(
3277
+ {
3278
+ hook_event_name: "PostToolUse",
3279
+ cwd,
3280
+ tool_name: "Bash",
3281
+ tool_use_id: "tool-bash-mcp-quote",
3282
+ tool_input: { command: "cat diagnostic-log.txt" },
3283
+ tool_response: JSON.stringify({
3284
+ exit_code: 0,
3285
+ stdout: "diagnostic log quoted: MCP transport closed; stdio pipe closed before response",
3286
+ stderr: "",
3287
+ }),
3288
+ },
3289
+ { cwd },
3290
+ );
3291
+
3292
+ assert.equal(result.omxEventName, "post-tool-use");
3293
+ assert.equal(result.outputJson, null);
3294
+ } finally {
3295
+ await rm(cwd, { recursive: true, force: true });
3296
+ }
3297
+ });
3298
+
3299
+ it("stays silent when Bash hard-failure text has no parsed exit code", async () => {
3300
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-bash-unparsed-failure-"));
3301
+ try {
3302
+ const result = await dispatchCodexNativeHook(
3303
+ {
3304
+ hook_event_name: "PostToolUse",
3305
+ cwd,
3306
+ tool_name: "Bash",
3307
+ tool_use_id: "tool-bash-unparsed-failure",
3308
+ tool_input: { command: "cat captured-output.txt" },
3309
+ tool_response: "captured transcript says: bash: foo: command not found",
3310
+ },
3311
+ { cwd },
3312
+ );
3313
+
3314
+ assert.equal(result.omxEventName, "post-tool-use");
3315
+ assert.equal(result.outputJson, null);
3316
+ } finally {
3317
+ await rm(cwd, { recursive: true, force: true });
3318
+ }
3319
+ });
3320
+
3098
3321
  it("returns PostToolUse MCP transport fallback guidance for clear MCP transport death", async () => {
3099
3322
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-mcp-transport-"));
3100
3323
  try {
@@ -3711,7 +3934,17 @@ esac
3711
3934
  undefined,
3712
3935
  { ...process.env, OMX_SESSION_ID: "sess-stop-team-worker" },
3713
3936
  );
3937
+ const workerCwd = join(cwd, ".omx", "team", "worker-stop-team", "worktrees", "worker-1");
3714
3938
  const workerDir = join(cwd, ".omx", "state", "team", "worker-stop-team", "workers", "worker-1");
3939
+ await mkdir(workerCwd, { recursive: true });
3940
+ await writeJson(join(workerDir, "identity.json"), {
3941
+ name: "worker-1",
3942
+ index: 1,
3943
+ role: "executor",
3944
+ assigned_tasks: ["1"],
3945
+ worktree_path: workerCwd,
3946
+ team_state_root: join(cwd, ".omx", "state"),
3947
+ });
3715
3948
  await writeJson(join(workerDir, "status.json"), {
3716
3949
  state: "idle",
3717
3950
  current_task_id: "1",
@@ -3733,10 +3966,10 @@ esac
3733
3966
  const result = await dispatchCodexNativeHook(
3734
3967
  {
3735
3968
  hook_event_name: "Stop",
3736
- cwd: join(cwd, ".omx", "team", "worker-stop-team", "worktrees", "worker-1"),
3969
+ cwd: workerCwd,
3737
3970
  session_id: "sess-stop-team-worker",
3738
3971
  },
3739
- { cwd: join(cwd, ".omx", "team", "worker-stop-team", "worktrees", "worker-1") },
3972
+ { cwd: workerCwd },
3740
3973
  );
3741
3974
 
3742
3975
  assert.deepEqual(result.outputJson, {
@@ -3772,6 +4005,16 @@ esac
3772
4005
  const stateDir = join(cwd, ".omx", "state");
3773
4006
  const workerDir = join(stateDir, "team", "worker-repeat-team", "workers", "worker-1");
3774
4007
  const taskPath = join(stateDir, "team", "worker-repeat-team", "tasks", "task-1.json");
4008
+ const workerCwd = join(cwd, ".omx", "team", "worker-repeat-team", "worktrees", "worker-1");
4009
+ await mkdir(workerCwd, { recursive: true });
4010
+ await writeJson(join(workerDir, "identity.json"), {
4011
+ name: "worker-1",
4012
+ index: 1,
4013
+ role: "executor",
4014
+ assigned_tasks: ["1"],
4015
+ worktree_path: workerCwd,
4016
+ team_state_root: stateDir,
4017
+ });
3775
4018
  await writeJson(join(workerDir, "status.json"), {
3776
4019
  state: "idle",
3777
4020
  current_task_id: "1",
@@ -3790,7 +4033,6 @@ esac
3790
4033
  process.env.OMX_TEAM_STATE_ROOT = stateDir;
3791
4034
  process.env.OMX_TEAM_LEADER_CWD = cwd;
3792
4035
 
3793
- const workerCwd = join(cwd, ".omx", "team", "worker-repeat-team", "worktrees", "worker-1");
3794
4036
  const basePayload = {
3795
4037
  hook_event_name: "Stop",
3796
4038
  cwd: workerCwd,
@@ -4032,6 +4274,51 @@ esac
4032
4274
  }
4033
4275
  });
4034
4276
 
4277
+ it("honors terminal team run-state before later canonical-team Stop fallback", async () => {
4278
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-terminal-run-state-canonical-"));
4279
+ try {
4280
+ const stateDir = join(cwd, ".omx", "state");
4281
+ const sessionId = "sess-stop-team-terminal-run-state";
4282
+ await initTeamState(
4283
+ "terminal-run-state-team",
4284
+ "terminal team stop canonical fallback regression",
4285
+ "executor",
4286
+ 1,
4287
+ cwd,
4288
+ undefined,
4289
+ { ...process.env, OMX_SESSION_ID: sessionId },
4290
+ );
4291
+ await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
4292
+ await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd });
4293
+ await writeJson(join(stateDir, "sessions", sessionId, "run-state.json"), {
4294
+ version: 1,
4295
+ mode: "team",
4296
+ active: false,
4297
+ outcome: "finish",
4298
+ lifecycle_outcome: "finished",
4299
+ current_phase: "complete",
4300
+ completed_at: "2026-04-27T12:00:00.000Z",
4301
+ updated_at: "2026-04-27T12:00:00.000Z",
4302
+ });
4303
+
4304
+ const result = await dispatchCodexNativeHook(
4305
+ {
4306
+ hook_event_name: "Stop",
4307
+ cwd,
4308
+ session_id: sessionId,
4309
+ thread_id: "thread-stop-team-terminal-run-state",
4310
+ turn_id: "turn-stop-team-terminal-run-state-1",
4311
+ },
4312
+ { cwd },
4313
+ );
4314
+
4315
+ assert.equal(result.omxEventName, "stop");
4316
+ assert.equal(result.outputJson, null);
4317
+ } finally {
4318
+ await rm(cwd, { recursive: true, force: true });
4319
+ }
4320
+ });
4321
+
4035
4322
  it("re-fires canonical-team Stop output for a later fresh Stop reply when coarse mode state is missing", async () => {
4036
4323
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-canonical-refire-"));
4037
4324
  try {
@@ -4628,7 +4915,7 @@ esac
4628
4915
  "Deep interview is still active (phase: intent-first) and has a pending structured question obligation; use `omx question` before stopping.",
4629
4916
  stopReason: "deep_interview_question_required",
4630
4917
  systemMessage:
4631
- "OMX deep-interview is still active (phase: intent-first) and requires a structured question via omx question before stopping.",
4918
+ "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.",
4632
4919
  });
4633
4920
  } finally {
4634
4921
  await rm(cwd, { recursive: true, force: true });
@@ -4684,7 +4971,7 @@ esac
4684
4971
  "Deep interview is still active (phase: intent-first) and has a pending structured question obligation; use `omx question` before stopping.",
4685
4972
  stopReason: "deep_interview_question_required",
4686
4973
  systemMessage:
4687
- "OMX deep-interview is still active (phase: intent-first) and requires a structured question via omx question before stopping.",
4974
+ "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.",
4688
4975
  });
4689
4976
  } finally {
4690
4977
  await rm(cwd, { recursive: true, force: true });
@@ -4812,7 +5099,7 @@ esac
4812
5099
  "Deep interview is still active (phase: intent-first) and has a pending structured question obligation; use `omx question` before stopping.",
4813
5100
  stopReason: "deep_interview_question_required",
4814
5101
  systemMessage:
4815
- "OMX deep-interview is still active (phase: intent-first) and requires a structured question via omx question before stopping.",
5102
+ "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.",
4816
5103
  };
4817
5104
 
4818
5105
  const first = await dispatchCodexNativeHook(payload, { cwd });
@@ -4958,7 +5245,7 @@ esac
4958
5245
  "Deep interview is still active (phase: intent-first) and has a pending structured question obligation; use `omx question` before stopping.",
4959
5246
  stopReason: "deep_interview_question_required",
4960
5247
  systemMessage:
4961
- "OMX deep-interview is still active (phase: intent-first) and requires a structured question via omx question before stopping.",
5248
+ "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.",
4962
5249
  });
4963
5250
  } finally {
4964
5251
  await rm(cwd, { recursive: true, force: true });
@@ -5136,10 +5423,10 @@ esac
5136
5423
  assert.deepEqual(result.outputJson, {
5137
5424
  decision: "block",
5138
5425
  reason:
5139
- "OMX Ralph is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.",
5426
+ "OMX Ralph is still active (phase: executing; state: .omx/state/ralph-state.json); continue the task and gather fresh verification evidence before stopping.",
5140
5427
  stopReason: "ralph_executing",
5141
5428
  systemMessage:
5142
- "OMX Ralph is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.",
5429
+ "OMX Ralph is still active (phase: executing; state: .omx/state/ralph-state.json); continue the task and gather fresh verification evidence before stopping.",
5143
5430
  });
5144
5431
  } finally {
5145
5432
  await rm(cwd, { recursive: true, force: true });
@@ -5171,10 +5458,10 @@ esac
5171
5458
  assert.deepEqual(result.outputJson, {
5172
5459
  decision: "block",
5173
5460
  reason:
5174
- "OMX Ralph is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.",
5461
+ "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.",
5175
5462
  stopReason: "ralph_executing",
5176
5463
  systemMessage:
5177
- "OMX Ralph is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.",
5464
+ "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.",
5178
5465
  });
5179
5466
  } finally {
5180
5467
  await rm(cwd, { recursive: true, force: true });
@@ -5259,6 +5546,87 @@ esac
5259
5546
  }
5260
5547
  });
5261
5548
 
5549
+ it("does not hard-block Stop on stale session-scoped Ralph starting state after visible active modes are cleared", async () => {
5550
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-cleared-stale-ralph-"));
5551
+ try {
5552
+ const stateDir = join(cwd, ".omx", "state");
5553
+ const sessionId = "sess-cleared-ralph";
5554
+ await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
5555
+ await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), {
5556
+ active: true,
5557
+ mode: "ralph",
5558
+ current_phase: "starting",
5559
+ session_id: sessionId,
5560
+ });
5561
+ await writeJson(join(stateDir, "skill-active-state.json"), {
5562
+ active: false,
5563
+ skill: "ralph",
5564
+ active_skills: [],
5565
+ });
5566
+
5567
+ const listActive = await executeStateOperation("state_list_active", {
5568
+ workingDirectory: cwd,
5569
+ });
5570
+ assert.deepEqual(listActive.payload, { active_modes: [] });
5571
+
5572
+ const result = await dispatchCodexNativeHook(
5573
+ {
5574
+ hook_event_name: "Stop",
5575
+ cwd,
5576
+ session_id: sessionId,
5577
+ },
5578
+ { cwd },
5579
+ );
5580
+
5581
+ assert.equal(result.omxEventName, "stop");
5582
+ assert.equal(result.outputJson, null);
5583
+ } finally {
5584
+ await rm(cwd, { recursive: true, force: true });
5585
+ }
5586
+ });
5587
+
5588
+ it("blocks Stop on visible active session-scoped Ralph starting state and reports its path", async () => {
5589
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-visible-starting-ralph-"));
5590
+ try {
5591
+ const stateDir = join(cwd, ".omx", "state");
5592
+ const sessionId = "sess-visible-ralph";
5593
+ await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
5594
+ await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), {
5595
+ active: true,
5596
+ mode: "ralph",
5597
+ current_phase: "starting",
5598
+ session_id: sessionId,
5599
+ });
5600
+ await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
5601
+ active: true,
5602
+ skill: "ralph",
5603
+ phase: "starting",
5604
+ active_skills: [{ skill: "ralph", phase: "starting", active: true, session_id: sessionId }],
5605
+ });
5606
+
5607
+ const result = await dispatchCodexNativeHook(
5608
+ {
5609
+ hook_event_name: "Stop",
5610
+ cwd,
5611
+ session_id: sessionId,
5612
+ },
5613
+ { cwd },
5614
+ );
5615
+
5616
+ assert.equal(result.omxEventName, "stop");
5617
+ assert.deepEqual(result.outputJson, {
5618
+ decision: "block",
5619
+ reason:
5620
+ "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.",
5621
+ stopReason: "ralph_starting",
5622
+ systemMessage:
5623
+ "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.",
5624
+ });
5625
+ } finally {
5626
+ await rm(cwd, { recursive: true, force: true });
5627
+ }
5628
+ });
5629
+
5262
5630
  it("does not block Stop from another session-scoped Ralph state when an explicit session_id has no active Ralph state", async () => {
5263
5631
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-explicit-session-ralph-"));
5264
5632
  try {
@@ -5286,6 +5654,46 @@ esac
5286
5654
  }
5287
5655
  });
5288
5656
 
5657
+ it("prefers canonical run-state terminal lifecycle before stale session Ralph state during Stop", async () => {
5658
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-canonical-run-state-ralph-"));
5659
+ try {
5660
+ const stateDir = join(cwd, ".omx", "state");
5661
+ const sessionId = "sess-canonical-run-state-ralph";
5662
+ await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
5663
+ await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd });
5664
+ await writeJson(join(stateDir, "sessions", sessionId, "run-state.json"), {
5665
+ version: 1,
5666
+ mode: "ralph",
5667
+ active: false,
5668
+ outcome: "finish",
5669
+ lifecycle_outcome: "finished",
5670
+ current_phase: "complete",
5671
+ completed_at: "2026-04-27T12:00:00.000Z",
5672
+ updated_at: "2026-04-27T12:00:00.000Z",
5673
+ });
5674
+ await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), {
5675
+ active: true,
5676
+ mode: "ralph",
5677
+ current_phase: "verifying",
5678
+ session_id: sessionId,
5679
+ });
5680
+
5681
+ const result = await dispatchCodexNativeHook(
5682
+ {
5683
+ hook_event_name: "Stop",
5684
+ cwd,
5685
+ session_id: sessionId,
5686
+ },
5687
+ { cwd },
5688
+ );
5689
+
5690
+ assert.equal(result.omxEventName, "stop");
5691
+ assert.equal(result.outputJson, null);
5692
+ } finally {
5693
+ await rm(cwd, { recursive: true, force: true });
5694
+ }
5695
+ });
5696
+
5289
5697
  it("does not block Stop from root Ralph fallback when the current session has no scoped Ralph state", async () => {
5290
5698
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-root-fallback-ralph-"));
5291
5699
  try {
@@ -5399,10 +5807,10 @@ esac
5399
5807
  const expected = {
5400
5808
  decision: "block",
5401
5809
  reason:
5402
- "OMX Ralph is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.",
5810
+ "OMX Ralph is still active (phase: executing; state: .omx/state/ralph-state.json); continue the task and gather fresh verification evidence before stopping.",
5403
5811
  stopReason: "ralph_executing",
5404
5812
  systemMessage:
5405
- "OMX Ralph is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.",
5813
+ "OMX Ralph is still active (phase: executing; state: .omx/state/ralph-state.json); continue the task and gather fresh verification evidence before stopping.",
5406
5814
  };
5407
5815
 
5408
5816
  const first = await dispatchCodexNativeHook(payload, { cwd });