oh-my-codex 0.18.6 → 0.18.8

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 (444) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +59 -10
  4. package/crates/omx-sparkshell/tests/execution.rs +1 -1
  5. package/dist/agents/__tests__/definitions.test.js +11 -0
  6. package/dist/agents/__tests__/definitions.test.js.map +1 -1
  7. package/dist/agents/__tests__/native-config.test.js +56 -6
  8. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  9. package/dist/agents/definitions.d.ts +10 -0
  10. package/dist/agents/definitions.d.ts.map +1 -1
  11. package/dist/agents/definitions.js +5 -1
  12. package/dist/agents/definitions.js.map +1 -1
  13. package/dist/agents/native-config.d.ts +5 -1
  14. package/dist/agents/native-config.d.ts.map +1 -1
  15. package/dist/agents/native-config.js +19 -4
  16. package/dist/agents/native-config.js.map +1 -1
  17. package/dist/autopilot/__tests__/fsm.test.d.ts +2 -0
  18. package/dist/autopilot/__tests__/fsm.test.d.ts.map +1 -0
  19. package/dist/autopilot/__tests__/fsm.test.js +75 -0
  20. package/dist/autopilot/__tests__/fsm.test.js.map +1 -0
  21. package/dist/autopilot/__tests__/ralplan-gate.test.d.ts +2 -0
  22. package/dist/autopilot/__tests__/ralplan-gate.test.d.ts.map +1 -0
  23. package/dist/autopilot/__tests__/ralplan-gate.test.js +79 -0
  24. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -0
  25. package/dist/autopilot/deep-interview-gate.d.ts +18 -0
  26. package/dist/autopilot/deep-interview-gate.d.ts.map +1 -0
  27. package/dist/autopilot/deep-interview-gate.js +256 -0
  28. package/dist/autopilot/deep-interview-gate.js.map +1 -0
  29. package/dist/autopilot/fsm.d.ts +13 -0
  30. package/dist/autopilot/fsm.d.ts.map +1 -0
  31. package/dist/autopilot/fsm.js +70 -0
  32. package/dist/autopilot/fsm.js.map +1 -0
  33. package/dist/autopilot/ralplan-gate.d.ts +17 -0
  34. package/dist/autopilot/ralplan-gate.d.ts.map +1 -0
  35. package/dist/autopilot/ralplan-gate.js +61 -0
  36. package/dist/autopilot/ralplan-gate.js.map +1 -0
  37. package/dist/cli/__tests__/codex-plugin-layout.test.js +512 -1
  38. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  39. package/dist/cli/__tests__/doctor-warning-copy.test.js +39 -0
  40. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  41. package/dist/cli/__tests__/index.test.js +83 -7
  42. package/dist/cli/__tests__/index.test.js.map +1 -1
  43. package/dist/cli/__tests__/launch-fallback.test.js +175 -6
  44. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  45. package/dist/cli/__tests__/package-bin-contract.test.js +8 -4
  46. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  47. package/dist/cli/__tests__/question.test.js +100 -0
  48. package/dist/cli/__tests__/question.test.js.map +1 -1
  49. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +13 -0
  50. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  51. package/dist/cli/__tests__/ralph.test.js +14 -0
  52. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  53. package/dist/cli/__tests__/setup-install-mode.test.js +89 -0
  54. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  55. package/dist/cli/__tests__/setup-refresh.test.js +83 -0
  56. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  57. package/dist/cli/__tests__/state.test.js +21 -0
  58. package/dist/cli/__tests__/state.test.js.map +1 -1
  59. package/dist/cli/__tests__/team.test.js +2 -2
  60. package/dist/cli/__tests__/team.test.js.map +1 -1
  61. package/dist/cli/__tests__/update.test.js +110 -2
  62. package/dist/cli/__tests__/update.test.js.map +1 -1
  63. package/dist/cli/doctor.d.ts.map +1 -1
  64. package/dist/cli/doctor.js +8 -1
  65. package/dist/cli/doctor.js.map +1 -1
  66. package/dist/cli/index.d.ts +14 -3
  67. package/dist/cli/index.d.ts.map +1 -1
  68. package/dist/cli/index.js +298 -50
  69. package/dist/cli/index.js.map +1 -1
  70. package/dist/cli/plugin-marketplace.d.ts +14 -2
  71. package/dist/cli/plugin-marketplace.d.ts.map +1 -1
  72. package/dist/cli/plugin-marketplace.js +62 -15
  73. package/dist/cli/plugin-marketplace.js.map +1 -1
  74. package/dist/cli/question.d.ts.map +1 -1
  75. package/dist/cli/question.js +36 -5
  76. package/dist/cli/question.js.map +1 -1
  77. package/dist/cli/ralph.d.ts.map +1 -1
  78. package/dist/cli/ralph.js +3 -1
  79. package/dist/cli/ralph.js.map +1 -1
  80. package/dist/cli/setup-preferences.d.ts +2 -0
  81. package/dist/cli/setup-preferences.d.ts.map +1 -1
  82. package/dist/cli/setup-preferences.js +4 -0
  83. package/dist/cli/setup-preferences.js.map +1 -1
  84. package/dist/cli/setup.d.ts +3 -0
  85. package/dist/cli/setup.d.ts.map +1 -1
  86. package/dist/cli/setup.js +166 -27
  87. package/dist/cli/setup.js.map +1 -1
  88. package/dist/cli/state.d.ts.map +1 -1
  89. package/dist/cli/state.js +8 -1
  90. package/dist/cli/state.js.map +1 -1
  91. package/dist/cli/tmux-hook.d.ts.map +1 -1
  92. package/dist/cli/tmux-hook.js +16 -0
  93. package/dist/cli/tmux-hook.js.map +1 -1
  94. package/dist/cli/update.d.ts +2 -0
  95. package/dist/cli/update.d.ts.map +1 -1
  96. package/dist/cli/update.js +47 -3
  97. package/dist/cli/update.js.map +1 -1
  98. package/dist/config/__tests__/deep-interview.test.js +7 -6
  99. package/dist/config/__tests__/deep-interview.test.js.map +1 -1
  100. package/dist/config/__tests__/generator-notify.test.js +1 -0
  101. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  102. package/dist/config/deep-interview.d.ts.map +1 -1
  103. package/dist/config/deep-interview.js +14 -4
  104. package/dist/config/deep-interview.js.map +1 -1
  105. package/dist/config/generator.d.ts +2 -2
  106. package/dist/config/generator.d.ts.map +1 -1
  107. package/dist/config/generator.js +2 -2
  108. package/dist/config/generator.js.map +1 -1
  109. package/dist/config/team-mode.d.ts +12 -0
  110. package/dist/config/team-mode.d.ts.map +1 -0
  111. package/dist/config/team-mode.js +91 -0
  112. package/dist/config/team-mode.js.map +1 -0
  113. package/dist/hooks/__tests__/agents-overlay.test.js +88 -0
  114. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  115. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +8 -0
  116. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  117. package/dist/hooks/__tests__/code-review-skill-contract.test.js +8 -0
  118. package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
  119. package/dist/hooks/__tests__/deep-interview-contract.test.js +10 -0
  120. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  121. package/dist/hooks/__tests__/keyword-detector.test.js +1072 -14
  122. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  123. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +64 -1
  124. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  125. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +189 -0
  126. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  127. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +35 -2
  128. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  129. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +3 -3
  130. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  131. package/dist/hooks/__tests__/session.test.js +25 -0
  132. package/dist/hooks/__tests__/session.test.js.map +1 -1
  133. package/dist/hooks/__tests__/skill-guidance-contract.test.js +21 -0
  134. package/dist/hooks/__tests__/skill-guidance-contract.test.js.map +1 -1
  135. package/dist/hooks/agents-overlay.d.ts.map +1 -1
  136. package/dist/hooks/agents-overlay.js +36 -50
  137. package/dist/hooks/agents-overlay.js.map +1 -1
  138. package/dist/hooks/deep-interview-config-instruction.js +1 -1
  139. package/dist/hooks/deep-interview-config-instruction.js.map +1 -1
  140. package/dist/hooks/extensibility/__tests__/plugin-runner.test.js +31 -0
  141. package/dist/hooks/extensibility/__tests__/plugin-runner.test.js.map +1 -1
  142. package/dist/hooks/extensibility/plugin-runner.js +17 -21
  143. package/dist/hooks/extensibility/plugin-runner.js.map +1 -1
  144. package/dist/hooks/keyword-detector.d.ts +1 -0
  145. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  146. package/dist/hooks/keyword-detector.js +428 -32
  147. package/dist/hooks/keyword-detector.js.map +1 -1
  148. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  149. package/dist/hooks/keyword-registry.js +1 -0
  150. package/dist/hooks/keyword-registry.js.map +1 -1
  151. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  152. package/dist/hooks/prompt-guidance-contract.js +6 -0
  153. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  154. package/dist/hooks/session.d.ts +3 -0
  155. package/dist/hooks/session.d.ts.map +1 -1
  156. package/dist/hooks/session.js +13 -5
  157. package/dist/hooks/session.js.map +1 -1
  158. package/dist/hud/__tests__/authority.test.js +469 -31
  159. package/dist/hud/__tests__/authority.test.js.map +1 -1
  160. package/dist/hud/__tests__/hud-tmux-injection.test.js +2 -1
  161. package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
  162. package/dist/hud/__tests__/index.test.js +210 -2
  163. package/dist/hud/__tests__/index.test.js.map +1 -1
  164. package/dist/hud/__tests__/reconcile.test.js +588 -28
  165. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  166. package/dist/hud/__tests__/render.test.js +61 -0
  167. package/dist/hud/__tests__/render.test.js.map +1 -1
  168. package/dist/hud/__tests__/state.test.js +208 -0
  169. package/dist/hud/__tests__/state.test.js.map +1 -1
  170. package/dist/hud/__tests__/tmux.test.js +314 -22
  171. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  172. package/dist/hud/authority.d.ts +5 -0
  173. package/dist/hud/authority.d.ts.map +1 -1
  174. package/dist/hud/authority.js +337 -30
  175. package/dist/hud/authority.js.map +1 -1
  176. package/dist/hud/index.d.ts +20 -2
  177. package/dist/hud/index.d.ts.map +1 -1
  178. package/dist/hud/index.js +103 -26
  179. package/dist/hud/index.js.map +1 -1
  180. package/dist/hud/reconcile.d.ts +3 -3
  181. package/dist/hud/reconcile.d.ts.map +1 -1
  182. package/dist/hud/reconcile.js +129 -20
  183. package/dist/hud/reconcile.js.map +1 -1
  184. package/dist/hud/render.d.ts.map +1 -1
  185. package/dist/hud/render.js +35 -0
  186. package/dist/hud/render.js.map +1 -1
  187. package/dist/hud/state.d.ts.map +1 -1
  188. package/dist/hud/state.js +64 -50
  189. package/dist/hud/state.js.map +1 -1
  190. package/dist/hud/tmux.d.ts +26 -6
  191. package/dist/hud/tmux.d.ts.map +1 -1
  192. package/dist/hud/tmux.js +173 -38
  193. package/dist/hud/tmux.js.map +1 -1
  194. package/dist/hud/types.d.ts +11 -0
  195. package/dist/hud/types.d.ts.map +1 -1
  196. package/dist/hud/types.js.map +1 -1
  197. package/dist/mcp/__tests__/hermes-bridge.test.js +203 -7
  198. package/dist/mcp/__tests__/hermes-bridge.test.js.map +1 -1
  199. package/dist/mcp/__tests__/state-paths.test.js +71 -1
  200. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  201. package/dist/mcp/__tests__/state-server.test.js +13 -1
  202. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  203. package/dist/mcp/hermes-bridge.d.ts +12 -2
  204. package/dist/mcp/hermes-bridge.d.ts.map +1 -1
  205. package/dist/mcp/hermes-bridge.js +83 -9
  206. package/dist/mcp/hermes-bridge.js.map +1 -1
  207. package/dist/mcp/state-paths.d.ts +32 -0
  208. package/dist/mcp/state-paths.d.ts.map +1 -1
  209. package/dist/mcp/state-paths.js +113 -17
  210. package/dist/mcp/state-paths.js.map +1 -1
  211. package/dist/mcp/state-server.d.ts +4 -4
  212. package/dist/modes/__tests__/base-autoresearch-contract.test.js +7 -1
  213. package/dist/modes/__tests__/base-autoresearch-contract.test.js.map +1 -1
  214. package/dist/pipeline/__tests__/stages.test.js +130 -0
  215. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  216. package/dist/pipeline/orchestrator.js +1 -1
  217. package/dist/pipeline/orchestrator.js.map +1 -1
  218. package/dist/pipeline/stages/ralplan.d.ts +1 -0
  219. package/dist/pipeline/stages/ralplan.d.ts.map +1 -1
  220. package/dist/pipeline/stages/ralplan.js +14 -5
  221. package/dist/pipeline/stages/ralplan.js.map +1 -1
  222. package/dist/question/__tests__/deep-interview.test.js +160 -2
  223. package/dist/question/__tests__/deep-interview.test.js.map +1 -1
  224. package/dist/question/__tests__/policy.test.js +63 -3
  225. package/dist/question/__tests__/policy.test.js.map +1 -1
  226. package/dist/question/__tests__/renderer.test.js +191 -2
  227. package/dist/question/__tests__/renderer.test.js.map +1 -1
  228. package/dist/question/__tests__/state.test.js +94 -3
  229. package/dist/question/__tests__/state.test.js.map +1 -1
  230. package/dist/question/__tests__/ui.test.js +4 -0
  231. package/dist/question/__tests__/ui.test.js.map +1 -1
  232. package/dist/question/autopilot-wait.d.ts +12 -2
  233. package/dist/question/autopilot-wait.d.ts.map +1 -1
  234. package/dist/question/autopilot-wait.js +158 -47
  235. package/dist/question/autopilot-wait.js.map +1 -1
  236. package/dist/question/deep-interview.d.ts.map +1 -1
  237. package/dist/question/deep-interview.js +22 -6
  238. package/dist/question/deep-interview.js.map +1 -1
  239. package/dist/question/policy.d.ts.map +1 -1
  240. package/dist/question/policy.js +2 -5
  241. package/dist/question/policy.js.map +1 -1
  242. package/dist/question/renderer.d.ts +12 -0
  243. package/dist/question/renderer.d.ts.map +1 -1
  244. package/dist/question/renderer.js +87 -3
  245. package/dist/question/renderer.js.map +1 -1
  246. package/dist/question/state.d.ts +8 -1
  247. package/dist/question/state.d.ts.map +1 -1
  248. package/dist/question/state.js +54 -14
  249. package/dist/question/state.js.map +1 -1
  250. package/dist/question/types.d.ts +1 -1
  251. package/dist/question/types.d.ts.map +1 -1
  252. package/dist/question/ui.d.ts +1 -0
  253. package/dist/question/ui.d.ts.map +1 -1
  254. package/dist/question/ui.js +1 -0
  255. package/dist/question/ui.js.map +1 -1
  256. package/dist/ralplan/__tests__/runtime.test.js +191 -0
  257. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  258. package/dist/ralplan/consensus-gate.d.ts +9 -1
  259. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  260. package/dist/ralplan/consensus-gate.js +84 -2
  261. package/dist/ralplan/consensus-gate.js.map +1 -1
  262. package/dist/ralplan/runtime.d.ts +9 -0
  263. package/dist/ralplan/runtime.d.ts.map +1 -1
  264. package/dist/ralplan/runtime.js +32 -11
  265. package/dist/ralplan/runtime.js.map +1 -1
  266. package/dist/scripts/__tests__/codex-native-hook.test.js +2315 -280
  267. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  268. package/dist/scripts/__tests__/notify-state-io.test.js +72 -1
  269. package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
  270. package/dist/scripts/__tests__/notify-tmux-injection.test.d.ts +2 -0
  271. package/dist/scripts/__tests__/notify-tmux-injection.test.d.ts.map +1 -0
  272. package/dist/scripts/__tests__/notify-tmux-injection.test.js +57 -0
  273. package/dist/scripts/__tests__/notify-tmux-injection.test.js.map +1 -0
  274. package/dist/scripts/__tests__/run-test-files.test.js +74 -0
  275. package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
  276. package/dist/scripts/__tests__/verify-native-agents.test.js +65 -0
  277. package/dist/scripts/__tests__/verify-native-agents.test.js.map +1 -1
  278. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  279. package/dist/scripts/codex-native-hook.js +431 -56
  280. package/dist/scripts/codex-native-hook.js.map +1 -1
  281. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  282. package/dist/scripts/codex-native-pre-post.js +79 -1
  283. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  284. package/dist/scripts/eval/eval-parity-smoke.js +1 -1
  285. package/dist/scripts/eval/eval-parity-smoke.js.map +1 -1
  286. package/dist/scripts/hook-payload-guard.d.ts +9 -0
  287. package/dist/scripts/hook-payload-guard.d.ts.map +1 -0
  288. package/dist/scripts/hook-payload-guard.js +111 -0
  289. package/dist/scripts/hook-payload-guard.js.map +1 -0
  290. package/dist/scripts/notify-fallback-watcher.js +8 -1
  291. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  292. package/dist/scripts/notify-hook/__tests__/payload-guard.test.d.ts +2 -0
  293. package/dist/scripts/notify-hook/__tests__/payload-guard.test.d.ts.map +1 -0
  294. package/dist/scripts/notify-hook/__tests__/payload-guard.test.js +39 -0
  295. package/dist/scripts/notify-hook/__tests__/payload-guard.test.js.map +1 -0
  296. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  297. package/dist/scripts/notify-hook/auto-nudge.js +3 -1
  298. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  299. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
  300. package/dist/scripts/notify-hook/ralph-session-resume.js +3 -10
  301. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  302. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  303. package/dist/scripts/notify-hook/state-io.js +62 -38
  304. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  305. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  306. package/dist/scripts/notify-hook/team-leader-nudge.js +7 -0
  307. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  308. package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
  309. package/dist/scripts/notify-hook/team-worker-stop.js +234 -86
  310. package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
  311. package/dist/scripts/notify-hook/tmux-injection.d.ts +7 -0
  312. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  313. package/dist/scripts/notify-hook/tmux-injection.js +24 -18
  314. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  315. package/dist/scripts/notify-hook.js +86 -13
  316. package/dist/scripts/notify-hook.js.map +1 -1
  317. package/dist/scripts/run-test-files.js +193 -22
  318. package/dist/scripts/run-test-files.js.map +1 -1
  319. package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
  320. package/dist/scripts/sync-plugin-mirror.js +61 -3
  321. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  322. package/dist/scripts/verify-native-agents.d.ts.map +1 -1
  323. package/dist/scripts/verify-native-agents.js +58 -1
  324. package/dist/scripts/verify-native-agents.js.map +1 -1
  325. package/dist/state/__tests__/operations.test.js +1125 -1
  326. package/dist/state/__tests__/operations.test.js.map +1 -1
  327. package/dist/state/__tests__/skill-active.test.js +46 -1
  328. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  329. package/dist/state/__tests__/workflow-transition.test.js +98 -7
  330. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  331. package/dist/state/operations.d.ts.map +1 -1
  332. package/dist/state/operations.js +159 -2
  333. package/dist/state/operations.js.map +1 -1
  334. package/dist/state/skill-active.js +6 -8
  335. package/dist/state/skill-active.js.map +1 -1
  336. package/dist/state/workflow-transition-reconcile.d.ts +6 -0
  337. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  338. package/dist/state/workflow-transition-reconcile.js +38 -15
  339. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  340. package/dist/state/workflow-transition.d.ts.map +1 -1
  341. package/dist/state/workflow-transition.js +10 -3
  342. package/dist/state/workflow-transition.js.map +1 -1
  343. package/dist/subagents/__tests__/tracker.test.js +139 -0
  344. package/dist/subagents/__tests__/tracker.test.js.map +1 -1
  345. package/dist/subagents/tracker.d.ts +3 -0
  346. package/dist/subagents/tracker.d.ts.map +1 -1
  347. package/dist/subagents/tracker.js +41 -4
  348. package/dist/subagents/tracker.js.map +1 -1
  349. package/dist/team/__tests__/coordination-protocol.test.d.ts +2 -0
  350. package/dist/team/__tests__/coordination-protocol.test.d.ts.map +1 -0
  351. package/dist/team/__tests__/coordination-protocol.test.js +173 -0
  352. package/dist/team/__tests__/coordination-protocol.test.js.map +1 -0
  353. package/dist/team/__tests__/runtime.test.js +52 -3
  354. package/dist/team/__tests__/runtime.test.js.map +1 -1
  355. package/dist/team/__tests__/scaling.test.js +9 -4
  356. package/dist/team/__tests__/scaling.test.js.map +1 -1
  357. package/dist/team/__tests__/state.test.js +83 -0
  358. package/dist/team/__tests__/state.test.js.map +1 -1
  359. package/dist/team/__tests__/tmux-session.test.js +240 -2
  360. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  361. package/dist/team/__tests__/worker-bootstrap.test.js +84 -0
  362. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  363. package/dist/team/__tests__/worker-runtime-identity.test.js +4 -2
  364. package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
  365. package/dist/team/coordination-protocol.d.ts +14 -0
  366. package/dist/team/coordination-protocol.d.ts.map +1 -0
  367. package/dist/team/coordination-protocol.js +244 -0
  368. package/dist/team/coordination-protocol.js.map +1 -0
  369. package/dist/team/runtime.d.ts +1 -0
  370. package/dist/team/runtime.d.ts.map +1 -1
  371. package/dist/team/runtime.js +19 -3
  372. package/dist/team/runtime.js.map +1 -1
  373. package/dist/team/scaling.d.ts.map +1 -1
  374. package/dist/team/scaling.js +3 -2
  375. package/dist/team/scaling.js.map +1 -1
  376. package/dist/team/state/tasks.d.ts.map +1 -1
  377. package/dist/team/state/tasks.js +24 -0
  378. package/dist/team/state/tasks.js.map +1 -1
  379. package/dist/team/state/types.d.ts +21 -1
  380. package/dist/team/state/types.d.ts.map +1 -1
  381. package/dist/team/state/types.js.map +1 -1
  382. package/dist/team/state.d.ts +17 -1
  383. package/dist/team/state.d.ts.map +1 -1
  384. package/dist/team/state.js +12 -5
  385. package/dist/team/state.js.map +1 -1
  386. package/dist/team/team-ops.d.ts +1 -1
  387. package/dist/team/team-ops.d.ts.map +1 -1
  388. package/dist/team/team-ops.js.map +1 -1
  389. package/dist/team/tmux-session.d.ts +2 -0
  390. package/dist/team/tmux-session.d.ts.map +1 -1
  391. package/dist/team/tmux-session.js +161 -13
  392. package/dist/team/tmux-session.js.map +1 -1
  393. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  394. package/dist/team/worker-bootstrap.js +63 -0
  395. package/dist/team/worker-bootstrap.js.map +1 -1
  396. package/dist/utils/__tests__/agents-model-table.test.js +4 -2
  397. package/dist/utils/__tests__/agents-model-table.test.js.map +1 -1
  398. package/dist/utils/agents-model-table.d.ts.map +1 -1
  399. package/dist/utils/agents-model-table.js +3 -0
  400. package/dist/utils/agents-model-table.js.map +1 -1
  401. package/dist/verification/__tests__/ci-rust-gates.test.js +81 -1
  402. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  403. package/package.json +8 -8
  404. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  405. package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +334 -21
  406. package/plugins/oh-my-codex/hooks/hooks.json +1 -2
  407. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +13 -6
  408. package/plugins/oh-my-codex/skills/code-review/SKILL.md +7 -7
  409. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +9 -4
  410. package/plugins/oh-my-codex/skills/ralph/SKILL.md +22 -22
  411. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +12 -0
  412. package/plugins/oh-my-codex/skills/team/SKILL.md +16 -0
  413. package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +9 -0
  414. package/plugins/oh-my-codex/skills/worker/SKILL.md +14 -0
  415. package/skills/autopilot/SKILL.md +13 -6
  416. package/skills/code-review/SKILL.md +7 -7
  417. package/skills/deep-interview/SKILL.md +9 -4
  418. package/skills/ralph/SKILL.md +22 -22
  419. package/skills/ralplan/SKILL.md +12 -0
  420. package/skills/team/SKILL.md +16 -0
  421. package/skills/ultraqa/SKILL.md +9 -0
  422. package/skills/worker/SKILL.md +14 -0
  423. package/src/scripts/__tests__/codex-native-hook.test.ts +4435 -2083
  424. package/src/scripts/__tests__/notify-state-io.test.ts +95 -0
  425. package/src/scripts/__tests__/notify-tmux-injection.test.ts +82 -0
  426. package/src/scripts/__tests__/run-test-files.test.ts +102 -0
  427. package/src/scripts/__tests__/verify-native-agents.test.ts +75 -0
  428. package/src/scripts/codex-native-hook.ts +536 -51
  429. package/src/scripts/codex-native-pre-post.ts +80 -0
  430. package/src/scripts/demo-team-e2e.sh +10 -7
  431. package/src/scripts/eval/eval-parity-smoke.ts +1 -1
  432. package/src/scripts/hook-payload-guard.ts +113 -0
  433. package/src/scripts/notify-fallback-watcher.ts +8 -1
  434. package/src/scripts/notify-hook/__tests__/payload-guard.test.ts +41 -0
  435. package/src/scripts/notify-hook/auto-nudge.ts +3 -1
  436. package/src/scripts/notify-hook/ralph-session-resume.ts +2 -8
  437. package/src/scripts/notify-hook/state-io.ts +75 -37
  438. package/src/scripts/notify-hook/team-leader-nudge.ts +7 -0
  439. package/src/scripts/notify-hook/team-worker-stop.ts +193 -52
  440. package/src/scripts/notify-hook/tmux-injection.ts +35 -19
  441. package/src/scripts/notify-hook.ts +105 -6
  442. package/src/scripts/run-test-files.ts +192 -22
  443. package/src/scripts/sync-plugin-mirror.ts +98 -9
  444. package/src/scripts/verify-native-agents.ts +65 -1
@@ -4,8 +4,9 @@ import { appendFile, mkdir, readFile, readdir, stat, writeFile } from "fs/promis
4
4
  import { extname, join, relative, resolve } from "path";
5
5
  import { pathToFileURL } from "url";
6
6
  import { readModeStateForActiveDecision, readModeStateForSession, updateModeState } from "../modes/base.js";
7
+ import { redactAuthSecrets } from "../auth/redact.js";
7
8
  import { SKILL_ACTIVE_STATE_FILE, extractSessionIdFromInitializedStatePath, getSkillActiveStatePathsForStateDir, listActiveSkills, readSkillActiveState, readVisibleSkillActiveStateForStateDir, } from "../state/skill-active.js";
8
- import { readSubagentSessionSummary, readSubagentTrackingState, recordSubagentTurnForSession, } from "../subagents/tracker.js";
9
+ import { isTrustedSubagentThread, readSubagentSessionSummary, readSubagentTrackingState, recordSubagentTurnForSession, } from "../subagents/tracker.js";
9
10
  import { resolveCanonicalTeamStateRoot, resolveWorkerNotifyTeamStateRootPath } from "../team/state-root.js";
10
11
  import { appendToLog, isSessionStateUsable, readSessionState, readUsableSessionState, reconcileNativeSessionStart, } from "../hooks/session.js";
11
12
  import { appendTeamEvent, readTeamLeaderAttention, readTeamConfig, readTeamManifestV2, readTeamPhase, writeTeamLeaderAttention, writeTeamPhase, } from "../team/state.js";
@@ -14,6 +15,7 @@ import { findGitLayout } from "../utils/git-layout.js";
14
15
  import { getBaseStateDir, getStateFilePath, getStatePath } from "../mcp/state-paths.js";
15
16
  import { detectKeywords, detectPrimaryKeyword, recordSkillActivation, } from "../hooks/keyword-detector.js";
16
17
  import { buildDeepInterviewConfigInstruction } from "../hooks/deep-interview-config-instruction.js";
18
+ import { readTeamModeConfig } from "../config/team-mode.js";
17
19
  import { detectNativeStopStallPattern, loadAutoNudgeConfig, normalizeAutoNudgeSignatureText, resolveEffectiveAutoNudgeResponse, } from "./notify-hook/auto-nudge.js";
18
20
  import { SLOPPY_FALLBACK_GROUNDING_PATTERNS, SLOPPY_FALLBACK_IMPLEMENTATION_CONTEXT_PATTERNS, SLOPPY_FALLBACK_PHRASE_PATTERNS, buildNativePostToolUseOutput, buildNativePreToolUseOutput, detectMcpTransportFailure, hasAnyPattern, } from "./codex-native-pre-post.js";
19
21
  import { handleTeamWorkerPostToolUseSuccess } from "./notify-hook/team-worker-posttooluse.js";
@@ -36,6 +38,7 @@ import { isPendingDeepInterviewQuestionEnforcement, reconcileDeepInterviewQuesti
36
38
  import { readAutopilotDeepInterviewQuestionWaitState } from "../question/autopilot-wait.js";
37
39
  import { buildDocumentRefreshAdvisoryOutput, evaluateFinalHandoffDocumentRefresh, isFinalHandoffDocumentRefreshCandidate, } from "../document-refresh/enforcer.js";
38
40
  import { buildExecFollowupStopOutput } from "../exec/followup.js";
41
+ import { MAX_NATIVE_STDIN_JSON_BYTES, extractRawCodexHookEventName, } from "./hook-payload-guard.js";
39
42
  const TERMINAL_MODE_PHASES = new Set(["complete", "completed", "failed", "cancelled"]);
40
43
  const SKILL_STOP_BLOCKERS = new Set(["ralplan"]);
41
44
  const TEAM_STOP_BLOCKING_TASK_STATUSES = new Set(["pending", "in_progress", "blocked"]);
@@ -45,6 +48,7 @@ const ORDINARY_STOP_NO_PROGRESS_DEFAULT_MAX_REPEATS = 8;
45
48
  const RALPH_ORPHANED_STARTING_STALE_MS = 15 * 60_000;
46
49
  const ORDINARY_STOP_NO_PROGRESS_DEFAULT_IDLE_MS = 10 * 60_000;
47
50
  const ORDINARY_STOP_NO_PROGRESS_MAX_MESSAGE_LENGTH = 240;
51
+ const OMX_OWNER_SESSION_ID_PATTERN = /^omx-[A-Za-z0-9_-]{1,60}$/;
48
52
  const STABLE_FINAL_RECOMMENDATION_PATTERNS = [
49
53
  /^\s*(?:launch|release|ship)-?ready\s*:\s*(?:yes|no)\b[^\n\r]*/im,
50
54
  /^\s*ready to release\s*:\s*(?:yes|no)\b[^\n\r]*/im,
@@ -71,6 +75,25 @@ function safeString(value) {
71
75
  function safeObject(value) {
72
76
  return value && typeof value === "object" ? value : {};
73
77
  }
78
+ function resolveHudReconcileSessionId(currentSessionState, canonicalSessionId, sessionIdForState) {
79
+ const ownerOmxSessionId = safeString(currentSessionState?.owner_omx_session_id).trim();
80
+ if (OMX_OWNER_SESSION_ID_PATTERN.test(ownerOmxSessionId))
81
+ return ownerOmxSessionId;
82
+ return canonicalSessionId || sessionIdForState || undefined;
83
+ }
84
+ function resolveHudReconcileSessionIds(currentSessionState, canonicalSessionId, sessionIdForState, nativeSessionId) {
85
+ const ownerOmxSessionId = safeString(currentSessionState?.owner_omx_session_id).trim();
86
+ return uniqueNonEmpty([
87
+ resolveHudReconcileSessionId(currentSessionState, canonicalSessionId, sessionIdForState),
88
+ canonicalSessionId ?? undefined,
89
+ sessionIdForState ?? undefined,
90
+ nativeSessionId ?? undefined,
91
+ safeString(currentSessionState?.session_id),
92
+ safeString(currentSessionState?.native_session_id),
93
+ OMX_OWNER_SESSION_ID_PATTERN.test(ownerOmxSessionId) ? ownerOmxSessionId : undefined,
94
+ safeString(currentSessionState?.owner_codex_session_id),
95
+ ]);
96
+ }
74
97
  function safeContextSnippet(value, maxLength = 300) {
75
98
  const text = safeString(value).replace(/\s+/g, " ").trim();
76
99
  if (text.length <= maxLength)
@@ -134,18 +157,25 @@ function readNativeSubagentSessionStartMetadata(transcriptPath) {
134
157
  }
135
158
  }
136
159
  async function recordNativeSubagentSessionStart(cwd, canonicalSessionId, childSessionId, metadata, transcriptPath) {
160
+ const parentThreadId = metadata.parentThreadId.trim();
161
+ const childThreadId = childSessionId.trim();
137
162
  const trackingSessionIds = [...new Set([
138
163
  canonicalSessionId.trim(),
139
- metadata.parentThreadId.trim(),
164
+ parentThreadId,
140
165
  ].filter(Boolean))];
141
166
  for (const sessionId of trackingSessionIds) {
167
+ if (parentThreadId && parentThreadId !== childThreadId) {
168
+ await recordSubagentTurnForSession(cwd, {
169
+ sessionId,
170
+ threadId: parentThreadId,
171
+ kind: 'leader',
172
+ }).catch(() => { });
173
+ }
142
174
  await recordSubagentTurnForSession(cwd, {
143
175
  sessionId,
144
- threadId: metadata.parentThreadId,
145
- }).catch(() => { });
146
- await recordSubagentTurnForSession(cwd, {
147
- sessionId,
148
- threadId: childSessionId,
176
+ threadId: childThreadId,
177
+ kind: 'subagent',
178
+ ...(parentThreadId && parentThreadId !== childThreadId ? { leaderThreadId: parentThreadId } : {}),
149
179
  mode: metadata.agentRole,
150
180
  }).catch(() => { });
151
181
  }
@@ -176,17 +206,42 @@ async function nativeSubagentSessionStartBelongsToCanonicalSession(cwd, canonica
176
206
  return true;
177
207
  return summary.allThreadIds.includes(parentThreadId);
178
208
  }
179
- async function isNativeSubagentHook(cwd, canonicalSessionId, nativeSessionId, threadId) {
180
- const candidateIds = [nativeSessionId, threadId]
209
+ async function isNativeSubagentHook(cwd, canonicalSessionId, nativeSessionId, threadId, canonicalLeaderNativeSessionId = "") {
210
+ const nativeId = nativeSessionId.trim();
211
+ const promptThreadId = threadId.trim();
212
+ const candidateIds = [nativeId, promptThreadId]
181
213
  .map((value) => value.trim())
182
214
  .filter(Boolean);
183
215
  if (candidateIds.length === 0)
184
216
  return false;
185
217
  const sessionId = canonicalSessionId.trim();
186
- if (sessionId) {
187
- const summary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
188
- if (summary && candidateIds.some((id) => summary.allSubagentThreadIds.includes(id))) {
189
- return true;
218
+ const currentLeaderNativeSessionId = canonicalLeaderNativeSessionId.trim();
219
+ const summary = sessionId
220
+ ? await readSubagentSessionSummary(cwd, sessionId).catch(() => null)
221
+ : null;
222
+ const currentLeaderIds = new Set([
223
+ currentLeaderNativeSessionId,
224
+ summary?.leaderThreadId?.trim(),
225
+ ].filter(Boolean));
226
+ if (summary
227
+ && candidateIds.some((id) => !currentLeaderIds.has(id) && summary.allSubagentThreadIds.includes(id))) {
228
+ return true;
229
+ }
230
+ // Native UserPromptSubmit can carry a per-turn thread_id that differs from
231
+ // the long-lived native session id. Treat the current canonical native
232
+ // session as the leader before consulting stale/global tracker state.
233
+ if (sessionId
234
+ && currentLeaderNativeSessionId
235
+ && (nativeId === currentLeaderNativeSessionId
236
+ || (!nativeId && promptThreadId === currentLeaderNativeSessionId))) {
237
+ return false;
238
+ }
239
+ if (summary) {
240
+ const leaderThreadId = summary.leaderThreadId?.trim();
241
+ if (leaderThreadId
242
+ && (nativeId === leaderThreadId
243
+ || (!nativeId && promptThreadId === leaderThreadId))) {
244
+ return false;
190
245
  }
191
246
  }
192
247
  // Native Codex resume can report the child native session as the canonical
@@ -199,7 +254,7 @@ async function isNativeSubagentHook(cwd, canonicalSessionId, nativeSessionId, th
199
254
  const trackingState = await readSubagentTrackingState(cwd).catch(() => null);
200
255
  if (!trackingState)
201
256
  return false;
202
- return Object.values(trackingState.sessions).some((session) => (candidateIds.some((id) => session.threads[id]?.kind === "subagent")));
257
+ return Object.values(trackingState.sessions).some((session) => (candidateIds.some((id) => isTrustedSubagentThread(session, id))));
203
258
  }
204
259
  function shouldSuppressSubagentLifecycleHookDispatch() {
205
260
  const config = getNotificationConfig();
@@ -1343,6 +1398,8 @@ function buildTeamHelpInstruction(cwd, payload) {
1343
1398
  }).teamHelpInstruction;
1344
1399
  }
1345
1400
  function buildNativeOutsideTmuxTeamPromptBlockState(prompt, cwd, payload, sessionId, threadId, turnId) {
1401
+ if (!readTeamModeConfig(cwd).enabled)
1402
+ return null;
1346
1403
  const match = detectPrimaryKeyword(prompt);
1347
1404
  if (match?.skill !== "team")
1348
1405
  return null;
@@ -1374,23 +1431,37 @@ function buildNativeOutsideTmuxTeamPromptBlockState(prompt, cwd, payload, sessio
1374
1431
  function buildSkillStateCliInstruction(mode, statePath) {
1375
1432
  return `skill: ${mode} activated and initial state initialized at ${statePath}; use CLI-first state updates via \`omx state write/read/clear --input '<json>' --json\`; use omx_state MCP only when explicit MCP compatibility is enabled.`;
1376
1433
  }
1377
- function buildAutopilotPromptActivationNote(skillState) {
1434
+ function buildAutopilotPromptActivationNote(skillState, options = {}) {
1378
1435
  if (skillState?.initialized_mode !== "autopilot")
1379
1436
  return null;
1437
+ const teamHandoff = readTeamModeConfig(options.cwd).enabled
1438
+ ? " (+ $team if needed)"
1439
+ : "";
1380
1440
  return [
1381
- "Autopilot protocol: the durable default chain is $deep-interview -> $ralplan -> $ultragoal (+ $team if needed) -> $code-review -> $ultraqa (deep-interview -> ralplan -> ultragoal -> code-review -> ultraqa).",
1441
+ `Autopilot protocol: the durable default chain is $deep-interview -> $ralplan -> $ultragoal${teamHandoff} -> $code-review -> $ultraqa (deep-interview -> ralplan -> ultragoal -> code-review -> ultraqa).`,
1382
1442
  "Start/resume at current_phase=deep-interview unless the task is clear and bounded; if deep-interview is intentionally skipped, persist and state an explicit deep_interview_gate.skip_reason before moving to ralplan.",
1443
+ "Deep-interview is a structured question chain, not a one-question gate: after an omx question answer, re-score ambiguity against the active threshold, treat max_rounds as a cap, and crystallize once ambiguity is at or below threshold and readiness gates pass.",
1444
+ options.markedQuestionAnswer
1445
+ ? "This turn is a marked omx question answer. Treat ordinary selected option/freeform answer text as interview input, then re-score. Do not close merely because the first question was answered; if ambiguity is at or below threshold and readiness gates pass, write interview_complete evidence and hand off. Ask another deep-interview follow-up only when a readiness gate remains unresolved and the answer would materially change execution."
1446
+ : null,
1447
+ "Do not advance from deep-interview to ralplan merely because the first question was answered; persist explicit interview_complete evidence before setting current_phase=ralplan, and do advance when threshold plus readiness gates are satisfied.",
1383
1448
  "The ralplan phase is not complete until Planner output has been reviewed sequentially by Architect and then Critic; do not hand off to Ultragoal or implementation until the ralplan state/artifact records both ralplan_architect_review and ralplan_critic_review with approval or an explicit blocker.",
1384
1449
  "Do not silently fall back to ordinary $plan/ralplan-only handling; keep autopilot-state.json, skill-active-state.json, HUD/statusline, and Codex goal-mode handoff guidance visible while the workflow is active.",
1385
1450
  "When Codex goal tools are available, call get_goal/create_goal only from the active thread handoff and treat the active goal as the completion contract until code-review and ultraqa are clean.",
1386
- ].join(" ");
1451
+ ].filter(Boolean).join(" ");
1452
+ }
1453
+ function formatExecutionHandoffList(cwd) {
1454
+ return readTeamModeConfig(cwd).enabled
1455
+ ? "`$ultragoal`, `$team`, or `$ralph`"
1456
+ : "`$ultragoal` or `$ralph`";
1387
1457
  }
1388
1458
  function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(), payload) {
1389
1459
  if (!prompt)
1390
1460
  return null;
1391
1461
  const promptPriorityMessage = buildPromptPriorityMessage(prompt);
1392
- const matches = detectKeywords(prompt);
1393
- const match = detectPrimaryKeyword(prompt);
1462
+ const teamMode = readTeamModeConfig(cwd);
1463
+ const matches = detectKeywords(prompt).filter((entry) => teamMode.enabled || entry.skill !== "team");
1464
+ const match = matches[0] ?? null;
1394
1465
  if (!match) {
1395
1466
  const continuedSkill = safeString(skillState?.skill).trim();
1396
1467
  if (!continuedSkill)
@@ -1399,6 +1470,8 @@ function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(),
1399
1470
  ? buildDeepInterviewQuestionBridgeInstruction(cwd, payload)
1400
1471
  : null;
1401
1472
  const deepInterviewConfigPromptActivationNote = buildDeepInterviewConfigInstruction(cwd, skillState);
1473
+ const markedQuestionAnswer = /^\s*\[omx question answered\]/i.test(prompt);
1474
+ const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState, { markedQuestionAnswer, cwd });
1402
1475
  return [
1403
1476
  `OMX native UserPromptSubmit continued active workflow skill "${continuedSkill}".`,
1404
1477
  promptPriorityMessage,
@@ -1407,12 +1480,34 @@ function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(),
1407
1480
  : null,
1408
1481
  deepInterviewPromptActivationNote,
1409
1482
  deepInterviewConfigPromptActivationNote,
1483
+ autopilotPromptActivationNote,
1410
1484
  "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
1411
1485
  ].filter(Boolean).join(" ");
1412
1486
  }
1413
1487
  const detectedKeywordMessage = matches.length > 1
1414
1488
  ? `OMX native UserPromptSubmit detected workflow keywords ${matches.map((entry) => `"${entry.keyword}" -> ${entry.skill}`).join(", ")}.`
1415
1489
  : `OMX native UserPromptSubmit detected workflow keyword "${match.keyword}" -> ${match.skill}.`;
1490
+ const continuedSkill = safeString(skillState?.skill).trim();
1491
+ if (continuedSkill
1492
+ && continuedSkill !== match.skill
1493
+ && /^\s*\[omx question answered\]/i.test(prompt)) {
1494
+ const deepInterviewPromptActivationNote = skillState?.initialized_mode === "deep-interview"
1495
+ ? buildDeepInterviewQuestionBridgeInstruction(cwd, payload)
1496
+ : null;
1497
+ const deepInterviewConfigPromptActivationNote = buildDeepInterviewConfigInstruction(cwd, skillState);
1498
+ const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState, { markedQuestionAnswer: true, cwd });
1499
+ return [
1500
+ `OMX native UserPromptSubmit continued active workflow skill "${continuedSkill}"; workflow-like tokens inside the marked omx question answer are treated as answer text, not a new workflow activation.`,
1501
+ promptPriorityMessage,
1502
+ skillState?.initialized_mode && skillState.initialized_state_path
1503
+ ? buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path)
1504
+ : null,
1505
+ deepInterviewPromptActivationNote,
1506
+ deepInterviewConfigPromptActivationNote,
1507
+ autopilotPromptActivationNote,
1508
+ "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
1509
+ ].filter(Boolean).join(" ");
1510
+ }
1416
1511
  const activeSkills = Array.isArray(skillState?.active_skills)
1417
1512
  ? skillState.active_skills.map((entry) => entry.skill)
1418
1513
  : [];
@@ -1433,7 +1528,7 @@ function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(),
1433
1528
  const ultragoalPromptActivationNote = match.skill === "ultragoal"
1434
1529
  ? "Ultragoal protocol: use `omx ultragoal create-goals` / `complete-goals` / `checkpoint` for `.omx/ultragoal` artifacts, then use Codex goal model tools only from the active agent handoff (`get_goal`, `create_goal`, `update_goal`) and never overwrite a different active Codex goal. Ultragoal does not call `/goal clear`; for multiple sequential ultragoal runs in one Codex session/thread, manually clear the completed Codex goal in the UI before creating the next aggregate goal."
1435
1530
  : null;
1436
- const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState);
1531
+ const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState, { cwd });
1437
1532
  const combinedTransitionMessage = (() => {
1438
1533
  if (!skillState?.transition_message)
1439
1534
  return null;
@@ -1942,8 +2037,10 @@ function readPayloadThreadId(payload) {
1942
2037
  function readPayloadTurnId(payload) {
1943
2038
  return safeString(payload.turn_id ?? payload.turnId).trim();
1944
2039
  }
1945
- async function resolveInternalSessionIdForPayload(cwd, payloadSessionId) {
1946
- const currentSession = await readUsableSessionState(cwd);
2040
+ async function resolveInternalSessionIdForPayload(cwd, payloadSessionId, stateDir) {
2041
+ const currentSession = stateDir
2042
+ ? await readUsableSessionStateFromStateDir(cwd, stateDir)
2043
+ : await readUsableSessionState(cwd);
1947
2044
  const canonicalSessionId = safeString(currentSession?.session_id).trim();
1948
2045
  if (!canonicalSessionId)
1949
2046
  return payloadSessionId;
@@ -1956,6 +2053,19 @@ async function resolveInternalSessionIdForPayload(cwd, payloadSessionId) {
1956
2053
  return canonicalSessionId;
1957
2054
  return payloadSessionId;
1958
2055
  }
2056
+ async function readUsableSessionStateFromStateDir(cwd, stateDir) {
2057
+ const sessionPath = join(stateDir, "session.json");
2058
+ if (!existsSync(sessionPath))
2059
+ return null;
2060
+ try {
2061
+ const content = await readFile(sessionPath, "utf-8");
2062
+ const state = JSON.parse(content);
2063
+ return isSessionStateUsable(state, cwd) ? state : null;
2064
+ }
2065
+ catch {
2066
+ return null;
2067
+ }
2068
+ }
1959
2069
  async function readStopSessionPinnedState(fileName, cwd, sessionId, stateDir) {
1960
2070
  const statePath = stateDir && sessionId
1961
2071
  ? join(stateDir, "sessions", sessionId, fileName)
@@ -1968,13 +2078,31 @@ const DEEP_INTERVIEW_ALLOWED_WRITE_PREFIXES = [
1968
2078
  ".omx/specs",
1969
2079
  ".omx/state",
1970
2080
  ];
1971
- const DEEP_INTERVIEW_IMPLEMENTATION_TOOL_NAMES = new Set([
2081
+ const RALPLAN_ALLOWED_WRITE_PREFIXES = [
2082
+ ".omx/context",
2083
+ ".omx/plans",
2084
+ ".omx/specs",
2085
+ ".omx/state",
2086
+ ];
2087
+ const PLANNING_MODE_IMPLEMENTATION_TOOL_NAMES = new Set([
1972
2088
  "Write",
1973
2089
  "Edit",
1974
2090
  "MultiEdit",
2091
+ "NotebookEdit",
1975
2092
  "apply_patch",
1976
2093
  "ApplyPatch",
1977
2094
  ]);
2095
+ const DEEP_INTERVIEW_IMPLEMENTATION_TOOL_NAMES = PLANNING_MODE_IMPLEMENTATION_TOOL_NAMES;
2096
+ const RALPLAN_EXECUTION_HANDOFF_SKILLS = new Set([
2097
+ // Autopilot is intentionally excluded: it supervises planning phases such as
2098
+ // ralplan/replan and is not by itself an execution authorization.
2099
+ "autoresearch",
2100
+ "ralph",
2101
+ "team",
2102
+ "ultragoal",
2103
+ "ultrawork",
2104
+ "ultraqa",
2105
+ ]);
1978
2106
  function isActiveDeepInterviewPhase(state) {
1979
2107
  if (!state || state.active !== true)
1980
2108
  return false;
@@ -1986,7 +2114,31 @@ function isActiveDeepInterviewPhase(state) {
1986
2114
  return false;
1987
2115
  return true;
1988
2116
  }
1989
- function isAllowedDeepInterviewArtifactPath(cwd, rawPath) {
2117
+ function isActiveRalplanPhase(state) {
2118
+ if (!state || state.active !== true)
2119
+ return false;
2120
+ const mode = safeString(state.mode).trim();
2121
+ if (mode && mode !== "ralplan")
2122
+ return false;
2123
+ const phase = safeString(state.current_phase ?? state.currentPhase).trim().toLowerCase();
2124
+ if (phase && (TERMINAL_MODE_PHASES.has(phase) || phase === "completing"))
2125
+ return false;
2126
+ return true;
2127
+ }
2128
+ function isActiveAutopilotRalplanPhase(state) {
2129
+ if (!state || state.active !== true)
2130
+ return false;
2131
+ const mode = safeString(state.mode).trim();
2132
+ if (mode && mode !== "autopilot")
2133
+ return false;
2134
+ const phase = safeString(state.current_phase ?? state.currentPhase).trim().toLowerCase();
2135
+ return phase === "ralplan" || phase === "replan" || phase === "autopilot:replan";
2136
+ }
2137
+ function hasExplicitExecutionHandoffSkill(state, sessionId, threadId) {
2138
+ return listActiveSkills(state ?? {}).some((entry) => (RALPLAN_EXECUTION_HANDOFF_SKILLS.has(entry.skill)
2139
+ && matchesSkillStopContext(entry, state ?? {}, sessionId, threadId)));
2140
+ }
2141
+ function isAllowedPlanningArtifactPath(cwd, rawPath, allowedPrefixes) {
1990
2142
  const trimmed = rawPath.trim().replace(/^['"]|['"]$/g, "");
1991
2143
  if (!trimmed || trimmed.includes("\0"))
1992
2144
  return false;
@@ -2000,7 +2152,13 @@ function isAllowedDeepInterviewArtifactPath(cwd, rawPath) {
2000
2152
  }
2001
2153
  if (!relativePath || relativePath.startsWith("..") || relativePath.startsWith("/"))
2002
2154
  return false;
2003
- return DEEP_INTERVIEW_ALLOWED_WRITE_PREFIXES.some((prefix) => (relativePath === prefix || relativePath.startsWith(`${prefix}/`)));
2155
+ return allowedPrefixes.some((prefix) => (relativePath === prefix || relativePath.startsWith(`${prefix}/`)));
2156
+ }
2157
+ function isAllowedDeepInterviewArtifactPath(cwd, rawPath) {
2158
+ return isAllowedPlanningArtifactPath(cwd, rawPath, DEEP_INTERVIEW_ALLOWED_WRITE_PREFIXES);
2159
+ }
2160
+ function isAllowedRalplanArtifactPath(cwd, rawPath) {
2161
+ return isAllowedPlanningArtifactPath(cwd, rawPath, RALPLAN_ALLOWED_WRITE_PREFIXES);
2004
2162
  }
2005
2163
  function readPreToolUseCommand(payload) {
2006
2164
  const toolInput = safeObject(payload.tool_input);
@@ -2059,13 +2217,91 @@ async function readActiveDeepInterviewStateForPreToolUse(cwd, stateDir, sessionI
2059
2217
  ? await readVisibleSkillActiveStateForStateDir(stateDir, sessionId)
2060
2218
  : await readSkillActiveState(join(stateDir, SKILL_ACTIVE_STATE_FILE));
2061
2219
  if (!canonicalState)
2062
- return modeState;
2220
+ return null;
2063
2221
  const hasActiveDeepInterviewSkill = listActiveSkills(canonicalState).some((entry) => (entry.skill === "deep-interview"
2064
2222
  && matchesSkillStopContext(entry, canonicalState, sessionId, threadId)));
2065
2223
  return hasActiveDeepInterviewSkill ? modeState : null;
2066
2224
  }
2067
- async function buildDeepInterviewPreToolUseBoundaryOutput(payload, cwd, stateDir) {
2068
- const sessionId = readPayloadSessionId(payload);
2225
+ async function readActiveRalplanStateForPreToolUse(cwd, stateDir, sessionId, threadId) {
2226
+ const modeState = sessionId
2227
+ ? await readStopSessionPinnedState("ralplan-state.json", cwd, sessionId, stateDir)
2228
+ : await readJsonIfExists(join(stateDir, "ralplan-state.json"));
2229
+ const canonicalState = sessionId
2230
+ ? await readVisibleSkillActiveStateForStateDir(stateDir, sessionId)
2231
+ : await readSkillActiveState(join(stateDir, SKILL_ACTIVE_STATE_FILE));
2232
+ if (isActiveRalplanPhase(modeState) && modeState && modeStateMatchesSkillStopContext(modeState, cwd, sessionId)) {
2233
+ if (hasExplicitExecutionHandoffSkill(canonicalState, sessionId, threadId))
2234
+ return null;
2235
+ if (!canonicalState)
2236
+ return null;
2237
+ const hasActiveRalplanSkill = listActiveSkills(canonicalState).some((entry) => (entry.skill === "ralplan"
2238
+ && matchesSkillStopContext(entry, canonicalState, sessionId, threadId)));
2239
+ if (hasActiveRalplanSkill)
2240
+ return modeState;
2241
+ }
2242
+ const autopilotState = sessionId
2243
+ ? await readStopSessionPinnedState("autopilot-state.json", cwd, sessionId, stateDir)
2244
+ : await readJsonIfExists(join(stateDir, "autopilot-state.json"));
2245
+ if (!isActiveAutopilotRalplanPhase(autopilotState) || !autopilotState)
2246
+ return null;
2247
+ if (!modeStateMatchesSkillStopContext(autopilotState, cwd, sessionId))
2248
+ return null;
2249
+ const terminalAutopilotRunState = await readCanonicalTerminalRunStateForStop(cwd, sessionId, "autopilot");
2250
+ if (terminalAutopilotRunState)
2251
+ return null;
2252
+ if (!canonicalState)
2253
+ return null;
2254
+ const hasActiveAutopilotSkill = listActiveSkills(canonicalState).some((entry) => (entry.skill === "autopilot"
2255
+ && matchesSkillStopContext(entry, canonicalState, sessionId, threadId)));
2256
+ return hasActiveAutopilotSkill ? autopilotState : null;
2257
+ }
2258
+ function isAllowedRalplanBashWrite(cwd, command) {
2259
+ if (!commandHasDeepInterviewWriteIntent(command))
2260
+ return true;
2261
+ if (/\bomx\s+(?:state\s+(?:write|read|clear)|question)\b/.test(command))
2262
+ return true;
2263
+ const targets = extractDeepInterviewCommandWriteTargets(command);
2264
+ return targets.length > 0 && targets.every((target) => isAllowedRalplanArtifactPath(cwd, target));
2265
+ }
2266
+ async function buildRalplanPreToolUseBoundaryOutput(payload, cwd, stateDir, resolvedSessionId) {
2267
+ const sessionId = safeString(resolvedSessionId ?? readPayloadSessionId(payload)).trim();
2268
+ const threadId = readPayloadThreadId(payload);
2269
+ const activeState = await readActiveRalplanStateForPreToolUse(cwd, stateDir, sessionId, threadId);
2270
+ if (!activeState)
2271
+ return null;
2272
+ const toolName = safeString(payload.tool_name).trim();
2273
+ const command = readPreToolUseCommand(payload);
2274
+ const pathCandidates = readPreToolUsePathCandidates(payload);
2275
+ let blocked = false;
2276
+ if (toolName === "Bash") {
2277
+ blocked = !isAllowedRalplanBashWrite(cwd, command);
2278
+ }
2279
+ else if (PLANNING_MODE_IMPLEMENTATION_TOOL_NAMES.has(toolName)) {
2280
+ blocked = pathCandidates.length === 0
2281
+ || !pathCandidates.every((candidate) => isAllowedRalplanArtifactPath(cwd, candidate));
2282
+ }
2283
+ if (!blocked)
2284
+ return null;
2285
+ const phase = formatPhase(activeState.current_phase ?? activeState.currentPhase, "planning");
2286
+ const activeMode = safeString(activeState.mode).trim().toLowerCase();
2287
+ const planningModeLabel = activeMode === "autopilot" ? "Autopilot planning" : "Ralplan";
2288
+ const planningModeDescription = activeMode === "autopilot"
2289
+ ? "Autopilot is supervising a planning phase"
2290
+ : "Ralplan is consensus-planning mode";
2291
+ return {
2292
+ decision: "block",
2293
+ reason: `${planningModeLabel} is active (phase: ${phase}); implementation/write tools are blocked until an explicit execution handoff workflow is activated.`,
2294
+ hookSpecificOutput: {
2295
+ hookEventName: "PreToolUse",
2296
+ additionalContext: `${planningModeDescription}. `
2297
+ + "Write only planning artifacts under `.omx/context/`, `.omx/plans/`, `.omx/specs/`, or required `.omx/state/` files. "
2298
+ + "Do not edit implementation files or run implementation-focused writes from planning phases. "
2299
+ + `To execute, first process an explicit handoff such as ${formatExecutionHandoffList(cwd)}, which must emit terminal planning state before implementation begins.`,
2300
+ },
2301
+ };
2302
+ }
2303
+ async function buildDeepInterviewPreToolUseBoundaryOutput(payload, cwd, stateDir, resolvedSessionId) {
2304
+ const sessionId = safeString(resolvedSessionId ?? readPayloadSessionId(payload)).trim();
2069
2305
  const threadId = readPayloadThreadId(payload);
2070
2306
  const activeState = await readActiveDeepInterviewStateForPreToolUse(cwd, stateDir, sessionId, threadId);
2071
2307
  if (!activeState)
@@ -2089,7 +2325,7 @@ async function buildDeepInterviewPreToolUseBoundaryOutput(payload, cwd, stateDir
2089
2325
  reason: `Deep-interview is active (phase: ${phase}); implementation/write tools are blocked until an explicit handoff workflow is activated.`,
2090
2326
  hookSpecificOutput: {
2091
2327
  hookEventName: "PreToolUse",
2092
- additionalContext: "Deep-interview is requirements/spec mode. Treat detailed user answers as interview/spec material, not implicit implementation authorization. You may write only deep-interview artifacts under `.omx/context/`, `.omx/interviews/`, `.omx/specs/`, or required `.omx/state/` files. To implement, first ask for or process an explicit transition such as `$ralplan`, `$autopilot`, `$ralph`, `$team`, or `$ultragoal`.",
2328
+ additionalContext: `Deep-interview is requirements/spec mode. Treat detailed user answers as interview/spec material, not implicit implementation authorization. You may write only deep-interview artifacts under \`.omx/context/\`, \`.omx/interviews/\`, \`.omx/specs/\`, or required \`.omx/state/\` files. To implement, first ask for or process an explicit transition such as \`$ralplan\`, \`$autopilot\`, ${formatExecutionHandoffList(cwd)}.`,
2093
2329
  },
2094
2330
  };
2095
2331
  }
@@ -2278,7 +2514,7 @@ async function reconcileStaleRootSkillActiveStateForStop(cwd, stateDir, sessionI
2278
2514
  }
2279
2515
  await writeFile(rootPath, JSON.stringify(nextRoot, null, 2));
2280
2516
  }
2281
- function buildRalplanContinuationStatus(blocker, activeSubagentCount) {
2517
+ function buildRalplanContinuationStatus(blocker, activeSubagentCount, cwd) {
2282
2518
  const phase = blocker.phase || "planning";
2283
2519
  const artifact = blocker.latestPlanPath
2284
2520
  ? ` Artifact: ${blocker.latestPlanPath}.`
@@ -2304,12 +2540,12 @@ function buildRalplanContinuationStatus(blocker, activeSubagentCount) {
2304
2540
  };
2305
2541
  }
2306
2542
  const completeHint = blocker.planningComplete
2307
- ? " The planning artifacts are present; if consensus is approved, emit the final complete/approved handoff instead of stopping here."
2543
+ ? ` The planning artifacts are present; if consensus is approved, emit terminal ralplan complete/approved handoff state and stop planning. Implementation must wait for an explicit ${formatExecutionHandoffList(cwd).replaceAll("`", "")} handoff.`
2308
2544
  : "";
2309
2545
  return {
2310
- reason: `Status: continue_from_artifact — ralplan is still active (phase: ${phase}) and has not emitted a terminal complete/paused/waiting status. Continue from the current ralplan artifact, resolve any review ambiguity conservatively or ask the user if needed, and proceed to the next planning/review step before stopping.${artifact}${completeHint}`,
2546
+ reason: `Status: continue_from_artifact — ralplan is still active (phase: ${phase}) and has not emitted a terminal complete/paused/waiting status. Continue from the current ralplan artifact, resolve any review ambiguity conservatively or ask the user if needed, and proceed to the next planning/review step before stopping; do not begin implementation from ralplan.${artifact}${completeHint}`,
2311
2547
  stopReasonSuffix: "continue_artifact",
2312
- systemMessage: `OMX ralplan status: continue_from_artifact at phase ${phase}; continue from the current ralplan artifact and finish by stating whether ralplan is complete, paused for review, waiting for input, or still continuing.`,
2548
+ systemMessage: `OMX ralplan status: continue_from_artifact at phase ${phase}; continue from the current ralplan artifact and finish by stating whether ralplan is complete, paused for review, waiting for input, or still continuing; do not begin implementation from ralplan.`,
2313
2549
  };
2314
2550
  }
2315
2551
  async function readStopAutoNudgePhase(cwd, stateDir, sessionId, threadId) {
@@ -2463,6 +2699,9 @@ function readIsoTimeMs(value) {
2463
2699
  return Number.isFinite(parsed) ? parsed : null;
2464
2700
  }
2465
2701
  async function maybeBuildOrdinaryStopNoProgressOutput(payload, stateDir, canonicalSessionId) {
2702
+ const lastAssistantMessage = safeString(payload.last_assistant_message ?? payload.lastAssistantMessage).trim();
2703
+ if (!lastAssistantMessage)
2704
+ return null;
2466
2705
  const statePath = join(stateDir, NATIVE_STOP_STATE_FILE);
2467
2706
  const state = await readJsonIfExists(statePath) ?? {};
2468
2707
  const sessions = safeObject(state.sessions);
@@ -2492,9 +2731,6 @@ async function maybeBuildOrdinaryStopNoProgressOutput(payload, stateDir, canonic
2492
2731
  };
2493
2732
  await mkdir(stateDir, { recursive: true });
2494
2733
  await writeFile(statePath, JSON.stringify({ ...state, sessions }, null, 2));
2495
- const stopHookActive = payload.stop_hook_active === true || payload.stopHookActive === true;
2496
- if (!stopHookActive)
2497
- return null;
2498
2734
  const maxRepeats = parseBoundedPositiveInteger(process.env.OMX_NATIVE_STOP_NO_PROGRESS_MAX_REPEATS, ORDINARY_STOP_NO_PROGRESS_DEFAULT_MAX_REPEATS);
2499
2735
  const idleMs = parseBoundedNonNegativeInteger(process.env.OMX_NATIVE_STOP_NO_PROGRESS_IDLE_MS, ORDINARY_STOP_NO_PROGRESS_DEFAULT_IDLE_MS);
2500
2736
  const firstSeenMs = readIsoTimeMs(firstSeenAt) ?? Date.now();
@@ -2624,7 +2860,7 @@ async function buildSkillStopOutput(cwd, stateDir, sessionId, threadId) {
2624
2860
  const subagentSummary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
2625
2861
  const activeSubagentCount = subagentSummary?.activeSubagentThreadIds.length ?? 0;
2626
2862
  if (blocker.skill === "ralplan") {
2627
- const status = buildRalplanContinuationStatus(blocker, activeSubagentCount);
2863
+ const status = buildRalplanContinuationStatus(blocker, activeSubagentCount, cwd);
2628
2864
  return {
2629
2865
  decision: "block",
2630
2866
  reason: status.reason,
@@ -2878,10 +3114,20 @@ async function buildStopHookOutput(payload, cwd, stateDir, options = {}) {
2878
3114
  export async function dispatchCodexNativeHook(payload, options = {}) {
2879
3115
  const hookEventName = readHookEventName(payload);
2880
3116
  const cwd = options.cwd ?? (safeString(payload.cwd).trim() || process.cwd());
3117
+ if (hookEventName === "Stop" && !hasNativeStopRuntimeSurface(cwd)) {
3118
+ return {
3119
+ hookEventName,
3120
+ omxEventName: mapCodexHookEventToOmxEvent(hookEventName),
3121
+ skillState: null,
3122
+ outputJson: null,
3123
+ };
3124
+ }
2881
3125
  // Native hooks must use the same authoritative runtime state root as HUD/MCP
2882
3126
  // when boxed/team roots are active; do not bypass it with cwd/.omx/state.
2883
3127
  const stateDir = getBaseStateDir(cwd);
2884
- await mkdir(stateDir, { recursive: true });
3128
+ if (hookEventName !== "Stop") {
3129
+ await mkdir(stateDir, { recursive: true });
3130
+ }
2885
3131
  const omxEventName = mapCodexHookEventToOmxEvent(hookEventName);
2886
3132
  let skillState = null;
2887
3133
  let triageAdditionalContext = null;
@@ -2938,14 +3184,16 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
2938
3184
  const sessionIdForState = canonicalSessionId || nativeSessionId;
2939
3185
  let outputJson = null;
2940
3186
  const isSubagentPromptSubmit = hookEventName === "UserPromptSubmit"
2941
- ? await isNativeSubagentHook(cwd, canonicalSessionId, nativeSessionId, threadId)
3187
+ ? await isNativeSubagentHook(cwd, canonicalSessionId, nativeSessionId, threadId, safeString(currentSessionState?.native_session_id).trim())
2942
3188
  : false;
2943
3189
  const isSubagentStop = hookEventName === "Stop"
2944
3190
  ? (await Promise.all([...new Set([
2945
3191
  canonicalSessionId,
2946
3192
  safeString(currentSessionState?.session_id).trim(),
2947
3193
  ].filter(Boolean))]
2948
- .map((candidateSessionId) => isNativeSubagentHook(cwd, candidateSessionId, nativeSessionId, threadId)))).some(Boolean)
3194
+ .map((candidateSessionId) => isNativeSubagentHook(cwd, candidateSessionId, nativeSessionId, threadId, candidateSessionId === safeString(currentSessionState?.session_id).trim()
3195
+ ? safeString(currentSessionState?.native_session_id).trim()
3196
+ : "")))).some(Boolean)
2949
3197
  : false;
2950
3198
  const suppressNoisySubagentLifecycleDispatch = (isSubagentSessionStart || isSubagentStop)
2951
3199
  && shouldSuppressSubagentLifecycleHookDispatch();
@@ -3040,11 +3288,14 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
3040
3288
  triageAdditionalContext = null;
3041
3289
  }
3042
3290
  }
3291
+ const skipHudReconcileForDoctorSmoke = process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE === "1";
3043
3292
  const skipHudReconcileForTeamWorkerPane = !isSubagentPromptSubmit
3044
3293
  && await isConfirmedTeamWorkerPromptSubmitPane(cwd).catch(() => false);
3045
- if (!skipHudReconcileForTeamWorkerPane) {
3294
+ if (!skipHudReconcileForDoctorSmoke && !skipHudReconcileForTeamWorkerPane) {
3046
3295
  const reconcileHudForPromptSubmitFn = options.reconcileHudForPromptSubmitFn ?? reconcileHudForPromptSubmit;
3047
- await reconcileHudForPromptSubmitFn(cwd, { sessionId: canonicalSessionId || sessionIdForState || undefined }).catch(() => { });
3296
+ const hudSessionId = resolveHudReconcileSessionId(currentSessionState, canonicalSessionId, sessionIdForState);
3297
+ const hudSessionIds = resolveHudReconcileSessionIds(currentSessionState, canonicalSessionId, sessionIdForState, nativeSessionId);
3298
+ await reconcileHudForPromptSubmitFn(cwd, { sessionId: hudSessionId, sessionIds: hudSessionIds }).catch(() => { });
3048
3299
  }
3049
3300
  }
3050
3301
  if (omxEventName && !skipCanonicalSessionStartContext && !suppressNoisySubagentLifecycleDispatch) {
@@ -3100,7 +3351,12 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
3100
3351
  }
3101
3352
  }
3102
3353
  else if (hookEventName === "PreToolUse") {
3103
- outputJson = await buildDeepInterviewPreToolUseBoundaryOutput(payload, cwd, stateDir)
3354
+ const payloadSessionId = readPayloadSessionId(payload);
3355
+ const preToolUseSessionId = payloadSessionId
3356
+ ? await resolveInternalSessionIdForPayload(cwd, payloadSessionId, stateDir)
3357
+ : "";
3358
+ outputJson = await buildDeepInterviewPreToolUseBoundaryOutput(payload, cwd, stateDir, preToolUseSessionId)
3359
+ ?? await buildRalplanPreToolUseBoundaryOutput(payload, cwd, stateDir, preToolUseSessionId)
3104
3360
  ?? buildNativePreToolUseOutput(payload);
3105
3361
  }
3106
3362
  else if (hookEventName === "PostToolUse") {
@@ -3122,6 +3378,28 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
3122
3378
  outputJson,
3123
3379
  };
3124
3380
  }
3381
+ function hasNativeStopRuntimeSurface(cwd) {
3382
+ if (existsSync(join(cwd, ".omx")))
3383
+ return true;
3384
+ if (findGitLayout(cwd))
3385
+ return true;
3386
+ const omxRoot = safeString(process.env.OMX_ROOT).trim();
3387
+ if (omxRoot && existsSync(join(omxRoot, ".omx")))
3388
+ return true;
3389
+ const stateRoot = safeString(process.env.OMX_STATE_ROOT).trim();
3390
+ if (stateRoot && existsSync(stateRoot))
3391
+ return true;
3392
+ return [
3393
+ process.env.OMX_SESSION_ID,
3394
+ process.env.OMX_TEAM_INTERNAL_WORKER,
3395
+ process.env.OMX_TEAM_WORKER,
3396
+ process.env.OMX_TEAM_STATE_ROOT,
3397
+ process.env.OMX_TEAM_LEADER_CWD,
3398
+ process.env.OMX_NOTIFY_HOOK_TRUSTED_MANAGED_CWD,
3399
+ process.env.OMX_TMUX_HUD_OWNER,
3400
+ process.env.OMX_TMUX_HUD_LEADER_PANE,
3401
+ ].some((value) => safeString(value).trim() !== "");
3402
+ }
3125
3403
  export function isCodexNativeHookMainModule(moduleUrl, argv1) {
3126
3404
  if (!argv1)
3127
3405
  return false;
@@ -3129,30 +3407,129 @@ export function isCodexNativeHookMainModule(moduleUrl, argv1) {
3129
3407
  }
3130
3408
  async function readStdinJson() {
3131
3409
  const chunks = [];
3410
+ let totalBytes = 0;
3411
+ let oversized = false;
3132
3412
  for await (const chunk of process.stdin) {
3133
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
3413
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
3414
+ totalBytes += buffer.byteLength;
3415
+ if (totalBytes > MAX_NATIVE_STDIN_JSON_BYTES) {
3416
+ const remaining = Math.max(0, MAX_NATIVE_STDIN_JSON_BYTES - (totalBytes - buffer.byteLength));
3417
+ if (remaining > 0)
3418
+ chunks.push(Buffer.from(buffer.subarray(0, remaining)));
3419
+ oversized = true;
3420
+ process.stdin.destroy();
3421
+ break;
3422
+ }
3423
+ chunks.push(buffer);
3134
3424
  }
3135
3425
  const raw = Buffer.concat(chunks).toString("utf-8").trim();
3426
+ const rawHookEventName = extractRawCodexHookEventName(raw);
3427
+ if (oversized) {
3428
+ return {
3429
+ payload: {},
3430
+ parseError: null,
3431
+ rawInput: raw,
3432
+ oversized: true,
3433
+ rawHookEventName,
3434
+ };
3435
+ }
3136
3436
  if (!raw) {
3137
- return { payload: {}, parseError: null };
3437
+ return { payload: {}, parseError: null, rawInput: raw, oversized: false, rawHookEventName };
3138
3438
  }
3139
3439
  try {
3140
3440
  return {
3141
3441
  payload: safeObject(JSON.parse(raw)),
3142
3442
  parseError: null,
3443
+ rawInput: raw,
3444
+ oversized: false,
3445
+ rawHookEventName,
3143
3446
  };
3144
3447
  }
3145
3448
  catch (error) {
3146
3449
  return {
3147
3450
  payload: {},
3148
3451
  parseError: error instanceof Error ? error : new Error(String(error)),
3452
+ rawInput: raw,
3453
+ oversized: false,
3454
+ rawHookEventName,
3149
3455
  };
3150
3456
  }
3151
3457
  }
3458
+ function inferHookEventNameFromMalformedInput(raw) {
3459
+ const match = raw.match(/(?:\"|['"])?hook[_-]?event[_-]?name(?:\"|['"])?\s*:\s*(?:\"|['"])?(SessionStart|PreToolUse|PostToolUse|UserPromptSubmit|PreCompact|PostCompact|Stop)\b/i);
3460
+ const value = match?.[1];
3461
+ if (!value)
3462
+ return null;
3463
+ return readHookEventName({ hook_event_name: value });
3464
+ }
3465
+ function buildMalformedStdinHookOutput(parseError, rawInput) {
3466
+ const reason = "OMX native hook received malformed JSON input. Preserve runtime state, inspect the emitting hook payload yourself, and retry with valid JSON.";
3467
+ const systemMessage = `${reason} stdin JSON parsing failed inside codex-native-hook: ${parseError.message}.`;
3468
+ if (inferHookEventNameFromMalformedInput(rawInput) === "Stop") {
3469
+ return {
3470
+ decision: "block",
3471
+ reason,
3472
+ stopReason: "native_hook_stdin_parse_error",
3473
+ systemMessage,
3474
+ };
3475
+ }
3476
+ return {
3477
+ continue: false,
3478
+ stopReason: "native_hook_stdin_parse_error",
3479
+ systemMessage,
3480
+ };
3481
+ }
3482
+ async function buildOversizedStopActiveWorkflowOutput(cwd) {
3483
+ const currentSession = await readUsableSessionState(cwd);
3484
+ const currentSessionId = safeString(currentSession?.session_id).trim()
3485
+ || safeString(process.env.OMX_SESSION_ID || process.env.CODEX_SESSION_ID).trim();
3486
+ if (!currentSessionId)
3487
+ return null;
3488
+ if (await readCanonicalTerminalRunStateForStop(cwd, currentSessionId, "autopilot"))
3489
+ return null;
3490
+ const autopilotState = await readModeStateForActiveDecision("autopilot", currentSessionId, cwd);
3491
+ if (!autopilotState || !shouldContinueRun(autopilotState))
3492
+ return null;
3493
+ const phase = formatPhase(autopilotState.current_phase);
3494
+ const reason = `OMX native Stop received oversized stdin before parsing while the current session has active OMX autopilot state (phase: ${phase}); continue once with a compact response or reduce hook payload size so normal Stop gates can run.`;
3495
+ return {
3496
+ decision: "block",
3497
+ reason,
3498
+ stopReason: "native_stop_stdin_oversized_active_workflow",
3499
+ systemMessage: "OMX native Stop rejected oversized stdin before parsing; active current-session workflow state is present, so Stop is blocked instead of silently allowing termination.",
3500
+ };
3501
+ }
3502
+ async function buildOversizedStdinHookOutput(rawHookEventName, cwd) {
3503
+ if (rawHookEventName === "Stop") {
3504
+ return await buildOversizedStopActiveWorkflowOutput(cwd) ?? {};
3505
+ }
3506
+ const systemMessage = `OMX native hook rejected oversized stdin JSON before parsing; maxBytes=${MAX_NATIVE_STDIN_JSON_BYTES}.`;
3507
+ return {
3508
+ continue: false,
3509
+ stopReason: "native_hook_stdin_oversized",
3510
+ systemMessage,
3511
+ };
3512
+ }
3152
3513
  function writeNativeHookJsonStdout(output) {
3153
3514
  process.stdout.write(`${JSON.stringify(output)}\n`);
3154
3515
  }
3155
- async function logNativeHookCliError(cwd, type, error, payload = {}) {
3516
+ function redactMalformedHookPreview(rawInput) {
3517
+ const withoutControls = rawInput.replace(/[\u0000-\u001f\u007f-\u009f]/g, "");
3518
+ const withoutAuthSecrets = redactAuthSecrets(withoutControls);
3519
+ return withoutAuthSecrets
3520
+ .replace(/(["']?(?:prompt|user_prompt|input|text)["']?\s*:\s*)(["'])(?:\\.|(?!\2)[^\\])*\2/gi, "$1$2[REDACTED]$2")
3521
+ .replace(/(["']?(?:prompt|user_prompt|input|text)["']?\s*:\s*)(["'])(?:\\.|[^\\])*$/gi, "$1$2[REDACTED]$2")
3522
+ .replace(/(["']?(?:prompt|user_prompt|input|text)["']?\s*:\s*)(?!["'])[^,}]*/gi, "$1[REDACTED]");
3523
+ }
3524
+ function buildRawInputLogFields(rawInput) {
3525
+ if (!rawInput)
3526
+ return {};
3527
+ return {
3528
+ raw_input_length: Buffer.byteLength(rawInput, "utf-8"),
3529
+ raw_input_prefix: redactMalformedHookPreview(rawInput).slice(0, 240),
3530
+ };
3531
+ }
3532
+ async function logNativeHookCliError(cwd, type, error, payload = {}, details = {}) {
3156
3533
  const logsDir = join(cwd || process.cwd(), ".omx", "logs");
3157
3534
  await mkdir(logsDir, { recursive: true }).catch(() => { });
3158
3535
  const logPath = join(logsDir, `native-hook-${new Date().toISOString().split("T")[0]}.jsonl`);
@@ -3164,6 +3541,7 @@ async function logNativeHookCliError(cwd, type, error, payload = {}) {
3164
3541
  thread_id: readPayloadThreadId(payload) || undefined,
3165
3542
  turn_id: readPayloadTurnId(payload) || undefined,
3166
3543
  error: error instanceof Error ? error.message : String(error),
3544
+ ...details,
3167
3545
  }) + "\n").catch(() => { });
3168
3546
  }
3169
3547
  function isStopDispatchFailureTestTrigger(payload) {
@@ -3186,17 +3564,14 @@ function buildStopDispatchFailureOutput(error) {
3186
3564
  };
3187
3565
  }
3188
3566
  export async function runCodexNativeHookCli() {
3189
- const { payload, parseError } = await readStdinJson();
3567
+ const { payload, parseError, rawInput, oversized, rawHookEventName } = await readStdinJson();
3568
+ if (oversized) {
3569
+ writeNativeHookJsonStdout(await buildOversizedStdinHookOutput(rawHookEventName, process.cwd()));
3570
+ return;
3571
+ }
3190
3572
  if (parseError) {
3191
- await logNativeHookCliError(process.cwd(), "native_hook_stdin_parse_error", parseError);
3192
- writeNativeHookJsonStdout({
3193
- decision: "block",
3194
- reason: "OMX native hook received malformed JSON input. Preserve runtime state, inspect the emitting hook payload yourself, and retry with valid JSON.",
3195
- hookSpecificOutput: {
3196
- hookEventName: "Unknown",
3197
- additionalContext: `stdin JSON parsing failed inside codex-native-hook: ${parseError.message}. Emit valid JSON from the native hook caller before retrying.`,
3198
- },
3199
- });
3573
+ await logNativeHookCliError(process.cwd(), "native_hook_stdin_parse_error", parseError, {}, buildRawInputLogFields(rawInput));
3574
+ writeNativeHookJsonStdout(buildMalformedStdinHookOutput(parseError, rawInput));
3200
3575
  return;
3201
3576
  }
3202
3577
  try {