oh-my-codex 0.17.3 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (381) hide show
  1. package/Cargo.lock +13 -5
  2. package/Cargo.toml +2 -1
  3. package/README.md +44 -19
  4. package/crates/omx-api/Cargo.toml +19 -0
  5. package/crates/omx-api/src/lib.rs +2997 -0
  6. package/crates/omx-api/src/main.rs +10 -0
  7. package/crates/omx-api/tests/cli.rs +558 -0
  8. package/crates/omx-explore/src/main.rs +4 -0
  9. package/crates/omx-sparkshell/src/codex_bridge.rs +437 -123
  10. package/crates/omx-sparkshell/src/exec.rs +127 -1
  11. package/crates/omx-sparkshell/src/main.rs +829 -30
  12. package/crates/omx-sparkshell/src/prompt.rs +25 -3
  13. package/crates/omx-sparkshell/src/redaction.rs +241 -0
  14. package/crates/omx-sparkshell/tests/execution.rs +702 -237
  15. package/dist/cli/__tests__/api.test.d.ts +2 -0
  16. package/dist/cli/__tests__/api.test.d.ts.map +1 -0
  17. package/dist/cli/__tests__/api.test.js +175 -0
  18. package/dist/cli/__tests__/api.test.js.map +1 -0
  19. package/dist/cli/__tests__/ask.test.js +72 -5
  20. package/dist/cli/__tests__/ask.test.js.map +1 -1
  21. package/dist/cli/__tests__/autoresearch-goal.test.js +14 -1
  22. package/dist/cli/__tests__/autoresearch-goal.test.js.map +1 -1
  23. package/dist/cli/__tests__/codex-plugin-layout.test.js +15 -7
  24. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  25. package/dist/cli/__tests__/doctor-warning-copy.test.js +76 -3
  26. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  27. package/dist/cli/__tests__/explore.test.js +23 -0
  28. package/dist/cli/__tests__/explore.test.js.map +1 -1
  29. package/dist/cli/__tests__/index.test.js +171 -5
  30. package/dist/cli/__tests__/index.test.js.map +1 -1
  31. package/dist/cli/__tests__/install-docs-contract.test.d.ts +2 -0
  32. package/dist/cli/__tests__/install-docs-contract.test.d.ts.map +1 -0
  33. package/dist/cli/__tests__/install-docs-contract.test.js +55 -0
  34. package/dist/cli/__tests__/install-docs-contract.test.js.map +1 -0
  35. package/dist/cli/__tests__/launch-fallback.test.js +191 -0
  36. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  37. package/dist/cli/__tests__/package-bin-contract.test.js +4 -3
  38. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  39. package/dist/cli/__tests__/question.test.js +27 -41
  40. package/dist/cli/__tests__/question.test.js.map +1 -1
  41. package/dist/cli/__tests__/setup-install-mode.test.js +232 -35
  42. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  43. package/dist/cli/__tests__/sparkshell-cli.test.js +25 -1
  44. package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
  45. package/dist/cli/__tests__/sparkshell-packaging.test.js +1 -0
  46. package/dist/cli/__tests__/sparkshell-packaging.test.js.map +1 -1
  47. package/dist/cli/__tests__/ultragoal.test.js +227 -4
  48. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  49. package/dist/cli/__tests__/update.test.js +72 -1
  50. package/dist/cli/__tests__/update.test.js.map +1 -1
  51. package/dist/cli/__tests__/version-sync-contract.test.js +4 -0
  52. package/dist/cli/__tests__/version-sync-contract.test.js.map +1 -1
  53. package/dist/cli/__tests__/windows-popup-loop-contract.test.js +1 -1
  54. package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
  55. package/dist/cli/api.d.ts +26 -0
  56. package/dist/cli/api.d.ts.map +1 -0
  57. package/dist/cli/api.js +153 -0
  58. package/dist/cli/api.js.map +1 -0
  59. package/dist/cli/codex-feature-probe.d.ts +5 -0
  60. package/dist/cli/codex-feature-probe.d.ts.map +1 -1
  61. package/dist/cli/codex-feature-probe.js +13 -7
  62. package/dist/cli/codex-feature-probe.js.map +1 -1
  63. package/dist/cli/doctor.d.ts +7 -0
  64. package/dist/cli/doctor.d.ts.map +1 -1
  65. package/dist/cli/doctor.js +119 -10
  66. package/dist/cli/doctor.js.map +1 -1
  67. package/dist/cli/explore.d.ts +2 -0
  68. package/dist/cli/explore.d.ts.map +1 -1
  69. package/dist/cli/explore.js +43 -1
  70. package/dist/cli/explore.js.map +1 -1
  71. package/dist/cli/index.d.ts +12 -4
  72. package/dist/cli/index.d.ts.map +1 -1
  73. package/dist/cli/index.js +460 -87
  74. package/dist/cli/index.js.map +1 -1
  75. package/dist/cli/native-assets.d.ts +2 -1
  76. package/dist/cli/native-assets.d.ts.map +1 -1
  77. package/dist/cli/native-assets.js +1 -0
  78. package/dist/cli/native-assets.js.map +1 -1
  79. package/dist/cli/plugin-marketplace.d.ts +2 -0
  80. package/dist/cli/plugin-marketplace.d.ts.map +1 -1
  81. package/dist/cli/plugin-marketplace.js +15 -1
  82. package/dist/cli/plugin-marketplace.js.map +1 -1
  83. package/dist/cli/setup.d.ts.map +1 -1
  84. package/dist/cli/setup.js +71 -11
  85. package/dist/cli/setup.js.map +1 -1
  86. package/dist/cli/sparkshell.d.ts +7 -1
  87. package/dist/cli/sparkshell.d.ts.map +1 -1
  88. package/dist/cli/sparkshell.js +31 -4
  89. package/dist/cli/sparkshell.js.map +1 -1
  90. package/dist/cli/ultragoal.d.ts +1 -1
  91. package/dist/cli/ultragoal.d.ts.map +1 -1
  92. package/dist/cli/ultragoal.js +184 -10
  93. package/dist/cli/ultragoal.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 +14 -3
  97. package/dist/cli/update.js.map +1 -1
  98. package/dist/compat/__tests__/doctor-contract.test.js +3 -0
  99. package/dist/compat/__tests__/doctor-contract.test.js.map +1 -1
  100. package/dist/config/__tests__/codex-feature-flags.test.js +11 -1
  101. package/dist/config/__tests__/codex-feature-flags.test.js.map +1 -1
  102. package/dist/config/__tests__/codex-hooks.test.js +19 -8
  103. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  104. package/dist/config/__tests__/commit-lore-guard.test.d.ts +2 -0
  105. package/dist/config/__tests__/commit-lore-guard.test.d.ts.map +1 -0
  106. package/dist/config/__tests__/commit-lore-guard.test.js +20 -0
  107. package/dist/config/__tests__/commit-lore-guard.test.js.map +1 -0
  108. package/dist/config/codex-feature-flags.d.ts +4 -0
  109. package/dist/config/codex-feature-flags.d.ts.map +1 -1
  110. package/dist/config/codex-feature-flags.js +4 -0
  111. package/dist/config/codex-feature-flags.js.map +1 -1
  112. package/dist/config/codex-hooks.js +6 -6
  113. package/dist/config/codex-hooks.js.map +1 -1
  114. package/dist/config/commit-lore-guard.d.ts +1 -0
  115. package/dist/config/commit-lore-guard.d.ts.map +1 -1
  116. package/dist/config/commit-lore-guard.js +29 -3
  117. package/dist/config/commit-lore-guard.js.map +1 -1
  118. package/dist/config/generator.d.ts +3 -1
  119. package/dist/config/generator.d.ts.map +1 -1
  120. package/dist/config/generator.js +114 -10
  121. package/dist/config/generator.js.map +1 -1
  122. package/dist/goal-workflows/codex-goal-snapshot.d.ts +1 -0
  123. package/dist/goal-workflows/codex-goal-snapshot.d.ts.map +1 -1
  124. package/dist/goal-workflows/codex-goal-snapshot.js +5 -1
  125. package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -1
  126. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +10 -6
  127. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  128. package/dist/hooks/__tests__/best-practice-research-skill.test.d.ts +2 -0
  129. package/dist/hooks/__tests__/best-practice-research-skill.test.d.ts.map +1 -0
  130. package/dist/hooks/__tests__/best-practice-research-skill.test.js +27 -0
  131. package/dist/hooks/__tests__/best-practice-research-skill.test.js.map +1 -0
  132. package/dist/hooks/__tests__/consensus-execution-handoff.test.d.ts +1 -1
  133. package/dist/hooks/__tests__/consensus-execution-handoff.test.js +13 -11
  134. package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
  135. package/dist/hooks/__tests__/deep-interview-contract.test.js +4 -3
  136. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  137. package/dist/hooks/__tests__/keyword-detector.test.js +15 -3
  138. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  139. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +6 -0
  140. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  141. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +33 -0
  142. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  143. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js +4 -0
  144. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js.map +1 -1
  145. package/dist/hooks/extensibility/__tests__/dispatcher.test.js +26 -3
  146. package/dist/hooks/extensibility/__tests__/dispatcher.test.js.map +1 -1
  147. package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
  148. package/dist/hooks/extensibility/dispatcher.js +29 -14
  149. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  150. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  151. package/dist/hooks/keyword-detector.js +8 -3
  152. package/dist/hooks/keyword-detector.js.map +1 -1
  153. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  154. package/dist/hooks/keyword-registry.js +1 -0
  155. package/dist/hooks/keyword-registry.js.map +1 -1
  156. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  157. package/dist/hooks/prompt-guidance-contract.js +3 -2
  158. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  159. package/dist/hud/__tests__/hud-tmux-injection.test.js +14 -8
  160. package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
  161. package/dist/hud/__tests__/reconcile.test.js +4 -4
  162. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  163. package/dist/hud/__tests__/resource-leak-watch.test.d.ts +2 -0
  164. package/dist/hud/__tests__/resource-leak-watch.test.d.ts.map +1 -0
  165. package/dist/hud/__tests__/resource-leak-watch.test.js +28 -0
  166. package/dist/hud/__tests__/resource-leak-watch.test.js.map +1 -0
  167. package/dist/hud/__tests__/tmux.test.js +23 -18
  168. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  169. package/dist/hud/index.d.ts +1 -1
  170. package/dist/hud/index.d.ts.map +1 -1
  171. package/dist/hud/index.js +10 -4
  172. package/dist/hud/index.js.map +1 -1
  173. package/dist/hud/tmux.d.ts.map +1 -1
  174. package/dist/hud/tmux.js +9 -8
  175. package/dist/hud/tmux.js.map +1 -1
  176. package/dist/mcp/__tests__/bootstrap.test.js +75 -1
  177. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  178. package/dist/mcp/bootstrap.d.ts +3 -1
  179. package/dist/mcp/bootstrap.d.ts.map +1 -1
  180. package/dist/mcp/bootstrap.js +71 -2
  181. package/dist/mcp/bootstrap.js.map +1 -1
  182. package/dist/notifications/__tests__/http-client-resource.test.d.ts +2 -0
  183. package/dist/notifications/__tests__/http-client-resource.test.d.ts.map +1 -0
  184. package/dist/notifications/__tests__/http-client-resource.test.js +41 -0
  185. package/dist/notifications/__tests__/http-client-resource.test.js.map +1 -0
  186. package/dist/notifications/__tests__/verbosity.test.js +20 -0
  187. package/dist/notifications/__tests__/verbosity.test.js.map +1 -1
  188. package/dist/notifications/config.d.ts.map +1 -1
  189. package/dist/notifications/config.js +6 -3
  190. package/dist/notifications/config.js.map +1 -1
  191. package/dist/notifications/http-client.d.ts.map +1 -1
  192. package/dist/notifications/http-client.js +78 -27
  193. package/dist/notifications/http-client.js.map +1 -1
  194. package/dist/notifications/types.d.ts +2 -0
  195. package/dist/notifications/types.d.ts.map +1 -1
  196. package/dist/openclaw/__tests__/dispatcher.test.js +49 -1
  197. package/dist/openclaw/__tests__/dispatcher.test.js.map +1 -1
  198. package/dist/openclaw/dispatcher.d.ts +7 -4
  199. package/dist/openclaw/dispatcher.d.ts.map +1 -1
  200. package/dist/openclaw/dispatcher.js +32 -69
  201. package/dist/openclaw/dispatcher.js.map +1 -1
  202. package/dist/pipeline/__tests__/orchestrator.test.js +65 -3
  203. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  204. package/dist/pipeline/__tests__/stages.test.js +50 -5
  205. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  206. package/dist/pipeline/index.d.ts +8 -2
  207. package/dist/pipeline/index.d.ts.map +1 -1
  208. package/dist/pipeline/index.js +5 -2
  209. package/dist/pipeline/index.js.map +1 -1
  210. package/dist/pipeline/orchestrator.d.ts +5 -4
  211. package/dist/pipeline/orchestrator.d.ts.map +1 -1
  212. package/dist/pipeline/orchestrator.js +56 -15
  213. package/dist/pipeline/orchestrator.js.map +1 -1
  214. package/dist/pipeline/stages/code-review.d.ts +2 -2
  215. package/dist/pipeline/stages/code-review.d.ts.map +1 -1
  216. package/dist/pipeline/stages/code-review.js +5 -3
  217. package/dist/pipeline/stages/code-review.js.map +1 -1
  218. package/dist/pipeline/stages/deep-interview.d.ts +15 -0
  219. package/dist/pipeline/stages/deep-interview.d.ts.map +1 -0
  220. package/dist/pipeline/stages/deep-interview.js +32 -0
  221. package/dist/pipeline/stages/deep-interview.js.map +1 -0
  222. package/dist/pipeline/stages/ralph-verify.d.ts +5 -5
  223. package/dist/pipeline/stages/ralph-verify.d.ts.map +1 -1
  224. package/dist/pipeline/stages/ralph-verify.js +2 -2
  225. package/dist/pipeline/stages/ralph-verify.js.map +1 -1
  226. package/dist/pipeline/stages/ultragoal.d.ts +19 -0
  227. package/dist/pipeline/stages/ultragoal.d.ts.map +1 -0
  228. package/dist/pipeline/stages/ultragoal.js +38 -0
  229. package/dist/pipeline/stages/ultragoal.js.map +1 -0
  230. package/dist/pipeline/stages/ultraqa.d.ts +30 -0
  231. package/dist/pipeline/stages/ultraqa.d.ts.map +1 -0
  232. package/dist/pipeline/stages/ultraqa.js +46 -0
  233. package/dist/pipeline/stages/ultraqa.js.map +1 -0
  234. package/dist/pipeline/types.d.ts +8 -6
  235. package/dist/pipeline/types.d.ts.map +1 -1
  236. package/dist/pipeline/types.js +2 -2
  237. package/dist/scripts/__tests__/codex-native-hook.test.js +1488 -117
  238. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  239. package/dist/scripts/__tests__/notify-dispatcher.test.js +183 -1
  240. package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -1
  241. package/dist/scripts/__tests__/smoke-packed-install.test.js +27 -2
  242. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  243. package/dist/scripts/__tests__/verify-native-agents.test.js +16 -1
  244. package/dist/scripts/__tests__/verify-native-agents.test.js.map +1 -1
  245. package/dist/scripts/build-api.d.ts +2 -0
  246. package/dist/scripts/build-api.d.ts.map +1 -0
  247. package/dist/scripts/build-api.js +44 -0
  248. package/dist/scripts/build-api.js.map +1 -0
  249. package/dist/scripts/cleanup-explore-harness.js +1 -0
  250. package/dist/scripts/cleanup-explore-harness.js.map +1 -1
  251. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  252. package/dist/scripts/codex-native-hook.js +364 -16
  253. package/dist/scripts/codex-native-hook.js.map +1 -1
  254. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  255. package/dist/scripts/codex-native-pre-post.js +98 -25
  256. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  257. package/dist/scripts/notify-dispatcher.js +88 -0
  258. package/dist/scripts/notify-dispatcher.js.map +1 -1
  259. package/dist/scripts/notify-hook/process-runner.d.ts.map +1 -1
  260. package/dist/scripts/notify-hook/process-runner.js +39 -17
  261. package/dist/scripts/notify-hook/process-runner.js.map +1 -1
  262. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  263. package/dist/scripts/notify-hook/team-dispatch.js +36 -14
  264. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  265. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  266. package/dist/scripts/notify-hook/team-leader-nudge.js +26 -11
  267. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  268. package/dist/scripts/notify-hook/team-tmux-guard.d.ts +2 -1
  269. package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
  270. package/dist/scripts/notify-hook/team-tmux-guard.js +45 -1
  271. package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
  272. package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
  273. package/dist/scripts/notify-hook/team-worker-stop.js +27 -14
  274. package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
  275. package/dist/scripts/run-provider-advisor.js +9 -3
  276. package/dist/scripts/run-provider-advisor.js.map +1 -1
  277. package/dist/scripts/smoke-packed-install.d.ts +4 -1
  278. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  279. package/dist/scripts/smoke-packed-install.js +101 -1
  280. package/dist/scripts/smoke-packed-install.js.map +1 -1
  281. package/dist/scripts/sync-plugin-mirror.js +2 -2
  282. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  283. package/dist/scripts/verify-native-agents.js +2 -2
  284. package/dist/scripts/verify-native-agents.js.map +1 -1
  285. package/dist/sidecar/__tests__/resource-leak-watch.test.d.ts +2 -0
  286. package/dist/sidecar/__tests__/resource-leak-watch.test.d.ts.map +1 -0
  287. package/dist/sidecar/__tests__/resource-leak-watch.test.js +38 -0
  288. package/dist/sidecar/__tests__/resource-leak-watch.test.js.map +1 -0
  289. package/dist/sidecar/index.d.ts +1 -1
  290. package/dist/sidecar/index.d.ts.map +1 -1
  291. package/dist/sidecar/index.js +29 -12
  292. package/dist/sidecar/index.js.map +1 -1
  293. package/dist/state/__tests__/operations-ralph-phase.test.js +88 -1
  294. package/dist/state/__tests__/operations-ralph-phase.test.js.map +1 -1
  295. package/dist/state/operations.d.ts.map +1 -1
  296. package/dist/state/operations.js +11 -0
  297. package/dist/state/operations.js.map +1 -1
  298. package/dist/team/__tests__/runtime.test.js +2 -2
  299. package/dist/team/__tests__/runtime.test.js.map +1 -1
  300. package/dist/team/__tests__/tmux-session.test.js +207 -22
  301. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  302. package/dist/team/tmux-session.d.ts +1 -0
  303. package/dist/team/tmux-session.d.ts.map +1 -1
  304. package/dist/team/tmux-session.js +73 -28
  305. package/dist/team/tmux-session.js.map +1 -1
  306. package/dist/ultragoal/__tests__/artifacts.test.js +714 -10
  307. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  308. package/dist/ultragoal/__tests__/docs-contract.test.js +57 -1
  309. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
  310. package/dist/ultragoal/__tests__/steering-fixtures.d.ts +68 -0
  311. package/dist/ultragoal/__tests__/steering-fixtures.d.ts.map +1 -0
  312. package/dist/ultragoal/__tests__/steering-fixtures.js +259 -0
  313. package/dist/ultragoal/__tests__/steering-fixtures.js.map +1 -0
  314. package/dist/ultragoal/__tests__/steering-fixtures.test.d.ts +2 -0
  315. package/dist/ultragoal/__tests__/steering-fixtures.test.d.ts.map +1 -0
  316. package/dist/ultragoal/__tests__/steering-fixtures.test.js +65 -0
  317. package/dist/ultragoal/__tests__/steering-fixtures.test.js.map +1 -0
  318. package/dist/ultragoal/artifacts.d.ts +97 -2
  319. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  320. package/dist/ultragoal/artifacts.js +811 -256
  321. package/dist/ultragoal/artifacts.js.map +1 -1
  322. package/dist/utils/__tests__/sleep-resource.test.d.ts +2 -0
  323. package/dist/utils/__tests__/sleep-resource.test.d.ts.map +1 -0
  324. package/dist/utils/__tests__/sleep-resource.test.js +39 -0
  325. package/dist/utils/__tests__/sleep-resource.test.js.map +1 -0
  326. package/dist/utils/sleep.d.ts.map +1 -1
  327. package/dist/utils/sleep.js +17 -6
  328. package/dist/utils/sleep.js.map +1 -1
  329. package/dist/verification/__tests__/ci-rust-gates.test.js +85 -10
  330. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  331. package/dist/verification/__tests__/explore-harness-release-workflow.test.js +1 -0
  332. package/dist/verification/__tests__/explore-harness-release-workflow.test.js.map +1 -1
  333. package/package.json +5 -3
  334. package/plugins/oh-my-codex/.codex-plugin/plugin.json +4 -3
  335. package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +56 -0
  336. package/plugins/oh-my-codex/hooks/hooks.json +77 -0
  337. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +77 -47
  338. package/plugins/oh-my-codex/skills/best-practice-research/SKILL.md +83 -0
  339. package/plugins/oh-my-codex/skills/cancel/SKILL.md +2 -2
  340. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +9 -8
  341. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +1 -1
  342. package/plugins/oh-my-codex/skills/pipeline/SKILL.md +22 -11
  343. package/plugins/oh-my-codex/skills/plan/SKILL.md +8 -8
  344. package/plugins/oh-my-codex/skills/ralph/SKILL.md +7 -0
  345. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +5 -5
  346. package/plugins/oh-my-codex/skills/team/SKILL.md +1 -1
  347. package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +38 -4
  348. package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +1 -1
  349. package/prompts/planner.md +1 -1
  350. package/prompts/researcher.md +15 -10
  351. package/skills/autopilot/SKILL.md +77 -47
  352. package/skills/best-practice-research/SKILL.md +83 -0
  353. package/skills/cancel/SKILL.md +2 -2
  354. package/skills/deep-interview/SKILL.md +9 -8
  355. package/skills/omx-setup/SKILL.md +1 -1
  356. package/skills/pipeline/SKILL.md +22 -11
  357. package/skills/plan/SKILL.md +8 -8
  358. package/skills/ralph/SKILL.md +7 -0
  359. package/skills/ralplan/SKILL.md +5 -5
  360. package/skills/team/SKILL.md +1 -1
  361. package/skills/ultragoal/SKILL.md +38 -4
  362. package/skills/ultrawork/SKILL.md +1 -1
  363. package/src/scripts/__tests__/codex-native-hook.test.ts +1758 -166
  364. package/src/scripts/__tests__/notify-dispatcher.test.ts +223 -1
  365. package/src/scripts/__tests__/smoke-packed-install.test.ts +39 -2
  366. package/src/scripts/__tests__/verify-native-agents.test.ts +21 -1
  367. package/src/scripts/build-api.ts +48 -0
  368. package/src/scripts/cleanup-explore-harness.ts +1 -0
  369. package/src/scripts/codex-native-hook.ts +416 -18
  370. package/src/scripts/codex-native-pre-post.ts +119 -25
  371. package/src/scripts/notify-dispatcher.ts +97 -0
  372. package/src/scripts/notify-hook/process-runner.ts +40 -16
  373. package/src/scripts/notify-hook/team-dispatch.ts +36 -13
  374. package/src/scripts/notify-hook/team-leader-nudge.ts +25 -11
  375. package/src/scripts/notify-hook/team-tmux-guard.ts +49 -0
  376. package/src/scripts/notify-hook/team-worker-stop.ts +24 -13
  377. package/src/scripts/run-provider-advisor.ts +11 -3
  378. package/src/scripts/smoke-packed-install.ts +107 -0
  379. package/src/scripts/sync-plugin-mirror.ts +3 -3
  380. package/src/scripts/verify-native-agents.ts +2 -2
  381. package/templates/catalog-manifest.json +7 -0
@@ -1,6 +1,6 @@
1
1
  import { execFileSync } from "child_process";
2
2
  import { closeSync, existsSync, openSync, readFileSync, readSync } from "fs";
3
- import { appendFile, mkdir, readFile, readdir, writeFile } from "fs/promises";
3
+ import { appendFile, mkdir, readFile, readdir, stat, writeFile } from "fs/promises";
4
4
  import { extname, join, relative, resolve } from "path";
5
5
  import { pathToFileURL } from "url";
6
6
  import { readModeState, readModeStateForActiveDecision, readModeStateForSession, updateModeState } from "../modes/base.js";
@@ -69,6 +69,7 @@ import {
69
69
  } from "../hooks/extensibility/events.js";
70
70
  import type { HookEventEnvelope } from "../hooks/extensibility/types.js";
71
71
  import { dispatchHookEventRuntime } from "../hooks/extensibility/runtime.js";
72
+ import { getNotificationConfig, getVerbosity } from "../notifications/config.js";
72
73
  import { reconcileHudForPromptSubmit } from "../hud/reconcile.js";
73
74
  import {
74
75
  onPreCompact as buildWikiPreCompactContext,
@@ -78,6 +79,11 @@ import { readAutoresearchCompletionStatus, readAutoresearchModeStateForActiveDec
78
79
  import { readRunState } from "../runtime/run-state.js";
79
80
  import { evaluateRalphCompletionAuditEvidence, isRalphCompletePhase } from "../ralph/completion-audit.js";
80
81
  import { getRunContinuationSnapshot, shouldContinueRun } from "../runtime/run-loop.js";
82
+ import {
83
+ parseUltragoalSteeringDirective,
84
+ steerUltragoal,
85
+ type UltragoalSteeringProposal,
86
+ } from "../ultragoal/artifacts.js";
81
87
  import { triagePrompt } from "../hooks/triage-heuristic.js";
82
88
  import { readTriageConfig } from "../hooks/triage-config.js";
83
89
  import {
@@ -128,6 +134,7 @@ const TEAM_STOP_BLOCKING_TASK_STATUSES = new Set(["pending", "in_progress", "blo
128
134
  const TEAM_WORKER_TERMINAL_RUN_STATES = new Set(["done", "complete", "completed", "failed", "stopped", "cancelled"]);
129
135
  const NATIVE_STOP_STATE_FILE = "native-stop-state.json";
130
136
  const ORDINARY_STOP_NO_PROGRESS_DEFAULT_MAX_REPEATS = 8;
137
+ const RALPH_ORPHANED_STARTING_STALE_MS = 15 * 60_000;
131
138
  const ORDINARY_STOP_NO_PROGRESS_DEFAULT_IDLE_MS = 10 * 60_000;
132
139
  const ORDINARY_STOP_NO_PROGRESS_MAX_MESSAGE_LENGTH = 240;
133
140
  const STABLE_FINAL_RECOMMENDATION_PATTERNS = [
@@ -304,6 +311,13 @@ async function isNativeSubagentHook(
304
311
  return candidateIds.some((id) => summary.allSubagentThreadIds.includes(id));
305
312
  }
306
313
 
314
+ function shouldSuppressSubagentLifecycleHookDispatch(): boolean {
315
+ const config = getNotificationConfig();
316
+ if (config?.includeChildAgents === true) return false;
317
+ const verbosity = getVerbosity(config);
318
+ return verbosity !== "agent" && verbosity !== "verbose";
319
+ }
320
+
307
321
  async function recordIgnoredNativeSubagentSessionStart(
308
322
  cwd: string,
309
323
  canonicalSessionId: string,
@@ -418,6 +432,104 @@ function readPromptText(payload: CodexHookPayload): string {
418
432
  return "";
419
433
  }
420
434
 
435
+
436
+ function extractBalancedJsonObject(text: string, startIndex: number): string | null {
437
+ let depth = 0;
438
+ let inString = false;
439
+ let escaped = false;
440
+ for (let index = startIndex; index < text.length; index++) {
441
+ const char = text[index];
442
+ if (inString) {
443
+ if (escaped) escaped = false;
444
+ else if (char === "\\") escaped = true;
445
+ else if (char === '"') inString = false;
446
+ continue;
447
+ }
448
+ if (char === '"') {
449
+ inString = true;
450
+ continue;
451
+ }
452
+ if (char === "{") depth += 1;
453
+ else if (char === "}") {
454
+ depth -= 1;
455
+ if (depth === 0) return text.slice(startIndex, index + 1);
456
+ }
457
+ }
458
+ return null;
459
+ }
460
+
461
+ function normalizePromptSteeringProposal(raw: unknown, prompt: string): UltragoalSteeringProposal | null {
462
+ const candidate = safeObject(raw);
463
+ const nested = candidate.omx_ultragoal_steer ?? candidate.ultragoal_steer ?? candidate.steering ?? candidate;
464
+ const proposal = parseUltragoalSteeringDirective(JSON.stringify(nested));
465
+ if (!proposal) return null;
466
+ if (proposal.source !== "user_prompt_submit") return null;
467
+ const normalized = prompt.trim().toLowerCase();
468
+ return {
469
+ ...proposal,
470
+ directiveText: proposal.directiveText ?? safeContextSnippet(prompt, 600),
471
+ promptSignature: proposal.promptSignature ?? promptSignature(normalized),
472
+ idempotencyKey: proposal.idempotencyKey ?? `user_prompt_submit:${promptSignature(normalized)}`,
473
+ };
474
+ }
475
+
476
+ function parseUserPromptUltragoalSteeringDirective(prompt: string): UltragoalSteeringProposal | null {
477
+ const trimmed = prompt.trim();
478
+ if (!trimmed) return null;
479
+ const fenced = trimmed.match(/```(?:omx-ultragoal-steer|ultragoal-steer)\s*([\s\S]*?)```/i);
480
+ if (fenced?.[1]) {
481
+ try {
482
+ return normalizePromptSteeringProposal(JSON.parse(fenced[1]), prompt);
483
+ } catch {
484
+ return null;
485
+ }
486
+ }
487
+
488
+ const label = trimmed.match(/(?:^|\n)\s*(?:OMX_ULTRAGOAL_STEER|omx\.ultragoal\.steer|omx ultragoal steer)\s*:\s*{/i);
489
+ if (label?.index !== undefined) {
490
+ const brace = trimmed.indexOf("{", label.index);
491
+ const json = brace >= 0 ? extractBalancedJsonObject(trimmed, brace) : null;
492
+ if (json) {
493
+ try {
494
+ return normalizePromptSteeringProposal(JSON.parse(json), prompt);
495
+ } catch {
496
+ return null;
497
+ }
498
+ }
499
+ }
500
+
501
+ if (trimmed.startsWith("{")) {
502
+ try {
503
+ const parsed = JSON.parse(trimmed);
504
+ const object = safeObject(parsed);
505
+ if ("omx_ultragoal_steer" in object || "ultragoal_steer" in object) {
506
+ return normalizePromptSteeringProposal(parsed, prompt);
507
+ }
508
+ } catch {
509
+ return null;
510
+ }
511
+ }
512
+ return null;
513
+ }
514
+
515
+ async function applyUserPromptUltragoalSteering(cwd: string, prompt: string): Promise<string | null> {
516
+ const proposal = parseUserPromptUltragoalSteeringDirective(prompt);
517
+ if (!proposal) return null;
518
+ try {
519
+ const result = await steerUltragoal(cwd, proposal);
520
+ const status = result.deduped ? "deduped" : result.accepted ? "accepted" : "rejected";
521
+ const reasons = result.rejectedReasons.length > 0 ? ` rejectedReasons=${result.rejectedReasons.join("; ")}` : "";
522
+ return [
523
+ `OMX native UserPromptSubmit applied bounded .omx/ultragoal steering for G002-cli-and-prompt-submit-bridge: ${status}.`,
524
+ `mutation=${result.audit.kind}; source=${result.audit.source}; targets=${result.audit.targetGoalIds.join(",") || "none"}; idempotencyKey=${result.audit.idempotencyKey ?? "none"}.${reasons}`,
525
+ "Only explicit structured steering directives are parsed; normal prose is ignored and cannot mutate .omx/ultragoal.",
526
+ ].join(" ");
527
+ } catch (error) {
528
+ const message = error instanceof Error ? error.message : String(error);
529
+ return `OMX native UserPromptSubmit rejected bounded .omx/ultragoal steering for G002-cli-and-prompt-submit-bridge: ${message}`;
530
+ }
531
+ }
532
+
421
533
  function sanitizePayloadForHookContext(
422
534
  payload: CodexHookPayload,
423
535
  hookEventName: CodexHookEventName,
@@ -513,6 +625,52 @@ function isRalphStartingPhase(state: Record<string, unknown>): boolean {
513
625
  return safeString(state.current_phase ?? state.currentPhase).trim().toLowerCase() === "starting";
514
626
  }
515
627
 
628
+
629
+ function parseTimestampMs(value: unknown): number | null {
630
+ const text = safeString(value).trim();
631
+ if (!text) return null;
632
+ const ms = Date.parse(text);
633
+ return Number.isFinite(ms) ? ms : null;
634
+ }
635
+
636
+ function numericValue(value: unknown): number | null {
637
+ if (typeof value === "number" && Number.isFinite(value)) return value;
638
+ if (typeof value === "string" && value.trim()) {
639
+ const parsed = Number(value);
640
+ return Number.isFinite(parsed) ? parsed : null;
641
+ }
642
+ return null;
643
+ }
644
+
645
+ function hasRalphOwnerHint(state: Record<string, unknown>): boolean {
646
+ return [
647
+ state.owner_omx_session_id,
648
+ state.owner_codex_session_id,
649
+ state.owner_codex_thread_id,
650
+ state.thread_id,
651
+ state.tmux_pane_id,
652
+ state.task_slug,
653
+ ].some((value) => safeString(value).trim() !== "");
654
+ }
655
+
656
+ async function isStaleOrphanedRalphStartingState(
657
+ state: Record<string, unknown>,
658
+ path: string,
659
+ nowMs = Date.now(),
660
+ ): Promise<boolean> {
661
+ if (!isRalphStartingPhase(state)) return false;
662
+ if (numericValue(state.iteration) !== 0) return false;
663
+ if (hasRalphOwnerHint(state)) return false;
664
+
665
+ const timestampMs = parseTimestampMs(state.updated_at)
666
+ ?? parseTimestampMs(state.started_at)
667
+ ?? parseTimestampMs(state.created_at)
668
+ ?? await stat(path).then((info) => info.mtimeMs, () => null);
669
+ if (timestampMs === null) return false;
670
+
671
+ return nowMs - timestampMs > RALPH_ORPHANED_STARTING_STALE_MS;
672
+ }
673
+
516
674
  function hasValue(values: string[], value: string): boolean {
517
675
  return value !== "" && values.some((candidate) => candidate === value);
518
676
  }
@@ -597,6 +755,99 @@ async function hasConsistentRalphSkillActivation(stateDir: string, sessionId: st
597
755
  return true;
598
756
  }
599
757
 
758
+ function isShadowableRalphStartingSeed(state: Record<string, unknown>): boolean {
759
+ if (state.active !== true) return false;
760
+ if (!isRalphStartingPhase(state)) return false;
761
+ if (state.completion_audit || state.completionAudit) return false;
762
+ const iteration = numericValue(state.iteration);
763
+ return iteration === null || iteration <= 0;
764
+ }
765
+
766
+ function hasPassingCompletedRalphAudit(state: Record<string, unknown> | null, cwd: string): boolean {
767
+ if (!state) return false;
768
+ if (state.mode && safeString(state.mode) !== "ralph") return false;
769
+ if (!isRalphCompletePhase(state.current_phase ?? state.currentPhase)) return false;
770
+ if (state.active === true) return false;
771
+ return evaluateRalphCompletionAuditEvidence(state, cwd).complete === true;
772
+ }
773
+
774
+ function shouldRetireShadowedRalphStartingSeed(
775
+ seedState: Record<string, unknown>,
776
+ completedState: Record<string, unknown> | null,
777
+ cwd: string,
778
+ ownerContext?: {
779
+ completedSessionId?: string;
780
+ payloadSessionId?: string;
781
+ threadId?: string;
782
+ currentNativeSessionId?: string;
783
+ tmuxPaneId?: string;
784
+ },
785
+ ): boolean {
786
+ if (!isShadowableRalphStartingSeed(seedState)) return false;
787
+ if (!hasPassingCompletedRalphAudit(completedState, cwd)) return false;
788
+ if (!completedState) return false;
789
+
790
+ const completedSessionId = safeString(ownerContext?.completedSessionId ?? completedState.session_id).trim();
791
+ if (
792
+ completedSessionId
793
+ && !activeRalphStateMatchesStopOwner(completedState, {
794
+ sessionId: completedSessionId,
795
+ payloadSessionId: safeString(ownerContext?.payloadSessionId).trim(),
796
+ threadId: safeString(ownerContext?.threadId).trim(),
797
+ currentNativeSessionId: safeString(ownerContext?.currentNativeSessionId).trim(),
798
+ tmuxPaneId: safeString(ownerContext?.tmuxPaneId).trim(),
799
+ })
800
+ ) {
801
+ return false;
802
+ }
803
+
804
+ const seedThreadId = safeString(seedState.owner_codex_thread_id ?? seedState.thread_id).trim();
805
+ const completedThreadId = safeString(completedState?.owner_codex_thread_id ?? completedState?.thread_id).trim();
806
+ const stopThreadId = safeString(ownerContext?.threadId).trim();
807
+ if (seedThreadId && completedThreadId && seedThreadId !== completedThreadId) return false;
808
+ if (seedThreadId && stopThreadId && seedThreadId !== stopThreadId) return false;
809
+ if (completedThreadId && stopThreadId && completedThreadId !== stopThreadId) return false;
810
+
811
+ const seedPaneId = safeString(seedState.tmux_pane_id).trim();
812
+ const completedPaneId = safeString(completedState?.tmux_pane_id).trim();
813
+ const stopPaneId = safeString(ownerContext?.tmuxPaneId).trim();
814
+ if (seedPaneId && completedPaneId && seedPaneId !== completedPaneId) return false;
815
+ if (seedPaneId && stopPaneId && seedPaneId !== stopPaneId) return false;
816
+ if (completedPaneId && stopPaneId && completedPaneId !== stopPaneId) return false;
817
+
818
+ const seedStartedAt = parseTimestampMs(seedState.started_at ?? seedState.startedAt);
819
+ const completedAt = parseTimestampMs(completedState?.completed_at ?? completedState?.completedAt);
820
+ if (completedAt === null) return false;
821
+ if (seedStartedAt !== null && seedStartedAt > completedAt) return false;
822
+
823
+ return true;
824
+ }
825
+
826
+ async function retireShadowedRalphStartingSeed(
827
+ path: string,
828
+ seedState: Record<string, unknown>,
829
+ completedSessionId: string,
830
+ completedPath: string,
831
+ completedState: Record<string, unknown>,
832
+ ): Promise<void> {
833
+ const nowIso = new Date().toISOString();
834
+ const completedAt = safeString(completedState.completed_at ?? completedState.completedAt).trim() || nowIso;
835
+ const next: Record<string, unknown> = {
836
+ ...seedState,
837
+ active: false,
838
+ current_phase: "complete",
839
+ completed_at: completedAt,
840
+ stop_reason: "shadowed_by_completed_canonical_ralph",
841
+ shadowed_by_completed_canonical_ralph: {
842
+ session_id: completedSessionId,
843
+ state_path: completedPath,
844
+ completed_at: completedAt,
845
+ reconciled_at: nowIso,
846
+ },
847
+ };
848
+ await writeFile(path, JSON.stringify(next, null, 2));
849
+ }
850
+
600
851
 
601
852
  async function readRalphCompletionAuditBlockState(
602
853
  cwd: string,
@@ -653,13 +904,12 @@ async function reopenRalphCompletionAuditBlock(block: RalphCompletionAuditBlockS
653
904
  const nowIso = new Date().toISOString();
654
905
  const next: Record<string, unknown> = {
655
906
  ...block.state,
656
- active: true,
657
- current_phase: "verifying",
907
+ active: false,
908
+ current_phase: "complete",
658
909
  completion_audit_gate: "blocked",
659
910
  completion_audit_missing_reason: block.reason,
660
911
  completion_audit_blocked_at: nowIso,
661
912
  };
662
- delete next.completed_at;
663
913
  await writeFile(block.path, JSON.stringify(next, null, 2));
664
914
  }
665
915
 
@@ -686,6 +936,12 @@ async function readActiveRalphState(
686
936
  safeString(preferredSessionId).trim(),
687
937
  currentOmxSessionId,
688
938
  ].filter(Boolean))];
939
+ const completedCanonicalPath = currentOmxSessionId
940
+ ? getStateFilePath("ralph-state.json", cwd, currentOmxSessionId)
941
+ : "";
942
+ const completedCanonicalState = completedCanonicalPath
943
+ ? await readJsonIfExists(completedCanonicalPath)
944
+ : null;
689
945
 
690
946
  // Ralph Stop stays authoritative-scope-only once the Stop payload is session-bound.
691
947
  // That is intentionally stricter than generic state MCP reads: do not scan sibling
@@ -699,12 +955,37 @@ async function readActiveRalphState(
699
955
  }
700
956
  const sessionScopedPath = getStateFilePath("ralph-state.json", cwd, sessionId);
701
957
  const sessionScoped = await readJsonIfExists(sessionScopedPath);
702
- if (
703
- sessionScoped?.active === true
704
- && isRalphStartingPhase(sessionScoped)
705
- && !(await isVisibleRalphActiveForSession(stateDir, sessionId))
706
- ) {
707
- continue;
958
+ if (sessionScoped?.active === true) {
959
+ if (
960
+ currentOmxSessionId
961
+ && sessionId !== currentOmxSessionId
962
+ && completedCanonicalState
963
+ && shouldRetireShadowedRalphStartingSeed(sessionScoped, completedCanonicalState, cwd, {
964
+ completedSessionId: currentOmxSessionId,
965
+ payloadSessionId: safeString(ownerContext?.payloadSessionId).trim(),
966
+ threadId: safeString(ownerContext?.threadId).trim(),
967
+ currentNativeSessionId,
968
+ tmuxPaneId: safeString(ownerContext?.tmuxPaneId).trim(),
969
+ })
970
+ ) {
971
+ await retireShadowedRalphStartingSeed(
972
+ sessionScopedPath,
973
+ sessionScoped,
974
+ currentOmxSessionId,
975
+ completedCanonicalPath,
976
+ completedCanonicalState,
977
+ );
978
+ continue;
979
+ }
980
+ if (await isStaleOrphanedRalphStartingState(sessionScoped, sessionScopedPath)) {
981
+ continue;
982
+ }
983
+ if (
984
+ isRalphStartingPhase(sessionScoped)
985
+ && !(await isVisibleRalphActiveForSession(stateDir, sessionId))
986
+ ) {
987
+ continue;
988
+ }
708
989
  }
709
990
  if (
710
991
  sessionScoped?.active === true
@@ -1433,7 +1714,7 @@ function buildAdditionalContextMessage(
1433
1714
  ? "Ultrawork protocol: ground the task before editing, define pass/fail acceptance criteria, keep shared-file work local, and use direct-tool plus background evidence lanes only for truly independent work. Direct ultrawork provides lightweight verification only; Ralph owns persistence and the full verified-completion promise."
1434
1715
  : null;
1435
1716
  const ultragoalPromptActivationNote = match.skill === "ultragoal"
1436
- ? "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."
1717
+ ? "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."
1437
1718
  : null;
1438
1719
  const combinedTransitionMessage = (() => {
1439
1720
  if (!skillState?.transition_message) return null;
@@ -1696,14 +1977,20 @@ async function buildModeBasedStopOutput(
1696
1977
  cwd: string,
1697
1978
  sessionId?: string,
1698
1979
  ): Promise<Record<string, unknown> | null> {
1980
+ if (await readCanonicalTerminalRunStateForStop(cwd, sessionId, mode)) {
1981
+ return null;
1982
+ }
1699
1983
  const state = await readModeStateForActiveDecision(mode, sessionId?.trim() || undefined, cwd);
1700
1984
  if (!state || !shouldContinueRun(state)) return null;
1701
1985
  const phase = formatPhase(state.current_phase);
1986
+ const systemMessage = mode === "autopilot" && phase.toLowerCase().replace(/_/g, "-") === "code-review"
1987
+ ? "OMX autopilot is still active (phase: code-review). Run the required $code-review step before completing or clearing Autopilot state."
1988
+ : `OMX ${mode} is still active (phase: ${phase}).`;
1702
1989
  return {
1703
1990
  decision: "block",
1704
1991
  reason: `OMX ${mode} is still active (phase: ${phase}); continue the task and gather fresh verification evidence before stopping.`,
1705
1992
  stopReason: `${mode}_${phase}`,
1706
- systemMessage: `OMX ${mode} is still active (phase: ${phase}).`,
1993
+ systemMessage,
1707
1994
  };
1708
1995
  }
1709
1996
 
@@ -1715,6 +2002,28 @@ export function looksLikeGoalCompletionPrompt(text: string): boolean {
1715
2002
  || /(?:^|[.!?]\s+)(?:the\s+)?goal\s+(?:is\s+|now\s+|has\s+been\s+)?(?:complete|completed|finished|closed)(?:\s*(?:[.!?]|$)|\s*[:;]\s*\S|\s*[—–-]\s*\S)/i.test(text);
1716
2003
  }
1717
2004
 
2005
+ function reportsAutoresearchGoalObjectiveMismatch(text: string): boolean {
2006
+ return /\bautoresearch[-\s]goal\b/i.test(text)
2007
+ && /\b(?:complete|completion|reconciliation)\b/i.test(text)
2008
+ && /objective mismatch/i.test(text);
2009
+ }
2010
+
2011
+ function reportsBlockedPerformanceGoalObjectiveMismatch(state: unknown): boolean {
2012
+ const performanceState = safeObject(state);
2013
+ const lastValidation = safeObject(performanceState.lastValidation);
2014
+ if (safeString(performanceState.workflow) !== "performance-goal") return false;
2015
+ if (safeString(performanceState.status) !== "blocked") return false;
2016
+ if (safeString(lastValidation.status) !== "blocked") return false;
2017
+
2018
+ const evidence = [
2019
+ safeString(lastValidation.evidence),
2020
+ safeString(lastValidation.message),
2021
+ safeString(performanceState.evidence),
2022
+ safeString(performanceState.message),
2023
+ ].join(" ");
2024
+ return /objective mismatch/i.test(evidence);
2025
+ }
2026
+
1718
2027
  async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Promise<{ workflow: string; command: string; remediation?: string } | null> {
1719
2028
  const ultragoal = await readJsonIfExists(join(cwd, ".omx", "ultragoal", "goals.json"));
1720
2029
  const aggregateCompletion = safeObject(ultragoal?.aggregateCompletion);
@@ -1732,7 +2041,7 @@ async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Pro
1732
2041
  `If get_goal returns a completed task-scoped objective for the same aggregate ultragoal plan, checkpoint ${goalId} with evidence naming ${goalId} plus .omx/ultragoal/goals.json or ledger.jsonl and pass final quality-gate JSON; OMX will reconcile the completed planned scope without mutating Codex goal state.`,
1733
2042
  `If get_goal instead returns a different completed legacy objective and complete checkpointing fails, do not repeat --status complete in this thread.`,
1734
2043
  `Record the non-terminal blocker with: omx ultragoal checkpoint --goal-id ${goalId} --status blocked --codex-goal-json '<different completed get_goal JSON or path>' --evidence '<completed legacy Codex goal blocks create_goal in this thread>'.`,
1735
- "Then continue this ultragoal from a fresh Codex thread in the same repo/worktree and create the intended goal there.",
2044
+ "Then continue only from a Codex goal context with no active/completed conflicting goal in the same repo/worktree and create the intended goal there.",
1736
2045
  ].join(" "),
1737
2046
  };
1738
2047
  }
@@ -1742,6 +2051,9 @@ async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Pro
1742
2051
  if (!entry.isDirectory()) continue;
1743
2052
  const state = await readJsonIfExists(join(performanceRoot, entry.name, "state.json"));
1744
2053
  const status = safeString(state?.status);
2054
+ if (reportsBlockedPerformanceGoalObjectiveMismatch(state)) {
2055
+ continue;
2056
+ }
1745
2057
  if (state?.workflow === "performance-goal" && status && status !== "complete") {
1746
2058
  return {
1747
2059
  workflow: "performance-goal",
@@ -1758,10 +2070,19 @@ async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Pro
1758
2070
  const completion = await readJsonIfExists(join(autoresearchRoot, entry.name, "completion.json"));
1759
2071
  const completionVerdict = safeString(completion?.verdict);
1760
2072
  const completionPassed = completion?.passed === true || completionVerdict === "pass";
1761
- if (mission?.workflow === "autoresearch-goal" && status && status !== "complete" && completionPassed) {
2073
+ if (
2074
+ mission?.workflow === "autoresearch-goal"
2075
+ && status
2076
+ && status !== "complete"
2077
+ && completionPassed
2078
+ ) {
1762
2079
  return {
1763
2080
  workflow: "autoresearch-goal",
1764
2081
  command: `omx autoresearch-goal complete --slug ${safeString(mission.slug) || entry.name} --codex-goal-json '<get_goal JSON or path>'`,
2082
+ remediation: [
2083
+ "If that command fails with a Codex goal objective mismatch after a refreshed get_goal snapshot, do not repeat the same complete command blindly in this thread.",
2084
+ "Either retry with a correct refreshed snapshot or record an explicit blocked verdict for this autoresearch-goal and continue from the explicit blocker path.",
2085
+ ].join(" "),
1765
2086
  };
1766
2087
  }
1767
2088
  }
@@ -1789,6 +2110,9 @@ async function buildGoalWorkflowReconciliationStopOutput(
1789
2110
  if (!looksLikeGoalCompletionPrompt(lastAssistantMessage)) return null;
1790
2111
  const requirement = await findActiveGoalWorkflowReconciliationRequirement(cwd);
1791
2112
  if (!requirement) return null;
2113
+ if (requirement.workflow === "autoresearch-goal" && reportsAutoresearchGoalObjectiveMismatch(lastAssistantMessage)) {
2114
+ return null;
2115
+ }
1792
2116
  const systemMessage =
1793
2117
  [
1794
2118
  `OMX ${requirement.workflow} requires get_goal snapshot reconciliation before completion; call get_goal and pass --codex-goal-json to ${requirement.command}.`,
@@ -1914,7 +2238,7 @@ function readPayloadSessionId(payload: CodexHookPayload): string {
1914
2238
  }
1915
2239
 
1916
2240
  function readPayloadThreadId(payload: CodexHookPayload): string {
1917
- return safeString(payload.thread_id ?? payload.threadId).trim();
2241
+ return safeString(payload.owner_codex_thread_id ?? payload.thread_id ?? payload.threadId).trim();
1918
2242
  }
1919
2243
 
1920
2244
  function readPayloadTurnId(payload: CodexHookPayload): string {
@@ -2017,6 +2341,14 @@ async function readBlockingSkillForStop(
2017
2341
  const modeSnapshot = getRunContinuationSnapshot(modeState);
2018
2342
  if (modeSnapshot?.terminal === true) continue;
2019
2343
 
2344
+ if (await shouldIgnoreSessionSkillBlockerForCanonicalInactiveRoot(
2345
+ cwd,
2346
+ stateDir,
2347
+ skill,
2348
+ sessionId,
2349
+ threadId,
2350
+ )) continue;
2351
+
2020
2352
  const phase = formatPhase(
2021
2353
  modeState.current_phase,
2022
2354
  formatPhase(
@@ -2068,6 +2400,58 @@ function isTerminalOrInactiveModeState(state: Record<string, unknown> | null): b
2068
2400
  return phase !== "" && TERMINAL_MODE_PHASES.has(phase);
2069
2401
  }
2070
2402
 
2403
+ function rootSkillStateHasNoActiveSkillForStopContext(
2404
+ rootState: SkillActiveStateLike | null,
2405
+ skill: string,
2406
+ sessionId: string,
2407
+ threadId: string,
2408
+ ): boolean {
2409
+ if (!rootState) return false;
2410
+ return !listActiveSkills(rootState).some((entry) => (
2411
+ entry.skill === skill
2412
+ && matchesSkillStopContext(entry, rootState, sessionId, threadId)
2413
+ ));
2414
+ }
2415
+
2416
+ function rootModeStateIsCanonicalForStopContext(
2417
+ state: Record<string, unknown>,
2418
+ cwd: string,
2419
+ sessionId: string,
2420
+ threadId: string,
2421
+ ): boolean {
2422
+ if (!modeStateMatchesSkillStopContext(state, cwd, sessionId)) return false;
2423
+
2424
+ const stateSessionId = safeString(
2425
+ state.owner_omx_session_id
2426
+ ?? state.session_id
2427
+ ?? state.codex_session_id
2428
+ ?? state.owner_codex_session_id,
2429
+ ).trim();
2430
+ if (sessionId && stateSessionId !== sessionId) return false;
2431
+
2432
+ const stateThreadId = safeString(state.owner_codex_thread_id ?? state.thread_id).trim();
2433
+ if (threadId && stateThreadId && stateThreadId !== threadId) return false;
2434
+
2435
+ return true;
2436
+ }
2437
+
2438
+ async function shouldIgnoreSessionSkillBlockerForCanonicalInactiveRoot(
2439
+ cwd: string,
2440
+ stateDir: string,
2441
+ skill: string,
2442
+ sessionId: string,
2443
+ threadId: string,
2444
+ ): Promise<boolean> {
2445
+ const rootModeState = await readJsonIfExists(join(stateDir, `${skill}-state.json`));
2446
+ if (!rootModeState) return false;
2447
+ if (!rootModeStateIsCanonicalForStopContext(rootModeState, cwd, sessionId, threadId)) return false;
2448
+ if (!isTerminalOrInactiveModeState(rootModeState)) return false;
2449
+
2450
+ const { rootPath } = getSkillActiveStatePathsForStateDir(stateDir);
2451
+ const rootSkillState = await readSkillActiveState(rootPath);
2452
+ return rootSkillStateHasNoActiveSkillForStopContext(rootSkillState, skill, sessionId, threadId);
2453
+ }
2454
+
2071
2455
  async function readSessionScopedModeStateForRootSkill(
2072
2456
  cwd: string,
2073
2457
  stateDir: string,
@@ -2788,7 +3172,7 @@ async function buildStopHookOutput(
2788
3172
  `OMX Ralph completion audit is missing required evidence (${ralphCompletionAuditBlock.reason}; state: ${blockingPath}).`,
2789
3173
  "Continue verification and do not report complete yet.",
2790
3174
  "Record machine-readable completion evidence before stopping:",
2791
- "- either set state.completion_audit = { passed: true, prompt_to_artifact_checklist: [...], verification_evidence: [...] }",
3175
+ '- either set "completion_audit" on the Ralph state object, for example: omx state write --input \'{"mode":"ralph","active":false,"current_phase":"complete","completion_audit":{"passed":true,"prompt_to_artifact_checklist":["..."],"verification_evidence":["..."]}}\' --json',
2792
3176
  "- or set completion_audit_path / completion_audit_evidence_path to a repo-relative JSON file with those same fields.",
2793
3177
  "Markdown artifacts and flat top-level checklist/evidence fields are not accepted by the Ralph Stop gate.",
2794
3178
  ].join(" ");
@@ -3085,6 +3469,7 @@ export async function dispatchCodexNativeHook(
3085
3469
  let skillState: SkillActiveState | null = null;
3086
3470
  let triageAdditionalContext: string | null = null;
3087
3471
  let goalWorkflowAdditionalContext: string | null = null;
3472
+ let ultragoalSteeringAdditionalContext: string | null = null;
3088
3473
 
3089
3474
  const nativeSessionId = safeString(payload.session_id ?? payload.sessionId).trim();
3090
3475
  const threadId = safeString(payload.thread_id ?? payload.threadId).trim();
@@ -3093,11 +3478,13 @@ export async function dispatchCodexNativeHook(
3093
3478
  let canonicalSessionId = safeString(currentSessionState?.session_id).trim();
3094
3479
  let resolvedNativeSessionId = nativeSessionId;
3095
3480
  let skipCanonicalSessionStartContext = false;
3481
+ let isSubagentSessionStart = false;
3096
3482
 
3097
3483
  if (hookEventName === "SessionStart" && nativeSessionId) {
3098
3484
  const transcriptPath = safeString(payload.transcript_path ?? payload.transcriptPath).trim();
3099
3485
  const subagentSessionStart = readNativeSubagentSessionStartMetadata(transcriptPath);
3100
3486
  if (subagentSessionStart && canonicalSessionId) {
3487
+ isSubagentSessionStart = true;
3101
3488
  const belongsToCanonicalSession = await nativeSubagentSessionStartBelongsToCanonicalSession(
3102
3489
  cwd,
3103
3490
  canonicalSessionId,
@@ -3166,10 +3553,16 @@ export async function dispatchCodexNativeHook(
3166
3553
  .map((candidateSessionId) => isNativeSubagentHook(cwd, candidateSessionId, nativeSessionId, threadId)),
3167
3554
  )).some(Boolean)
3168
3555
  : false;
3556
+ const suppressNoisySubagentLifecycleDispatch =
3557
+ (isSubagentSessionStart || isSubagentStop)
3558
+ && shouldSuppressSubagentLifecycleHookDispatch();
3169
3559
 
3170
3560
  if (hookEventName === "UserPromptSubmit") {
3171
3561
  const prompt = readPromptText(payload);
3172
3562
  goalWorkflowAdditionalContext = await buildGoalWorkflowReconciliationPromptWarning(cwd, prompt).catch(() => null);
3563
+ ultragoalSteeringAdditionalContext = prompt && !isSubagentPromptSubmit
3564
+ ? await applyUserPromptUltragoalSteering(cwd, prompt).catch((error) => `OMX native UserPromptSubmit rejected bounded .omx/ultragoal steering for G002-cli-and-prompt-submit-bridge: ${error instanceof Error ? error.message : String(error)}`)
3565
+ : null;
3173
3566
  if (prompt && !isSubagentPromptSubmit) {
3174
3567
  skillState = buildNativeOutsideTmuxTeamPromptBlockState(
3175
3568
  prompt,
@@ -3261,7 +3654,7 @@ export async function dispatchCodexNativeHook(
3261
3654
  await reconcileHudForPromptSubmitFn(cwd, { sessionId: canonicalSessionId || sessionIdForState || undefined }).catch(() => {});
3262
3655
  }
3263
3656
 
3264
- if (omxEventName && !skipCanonicalSessionStartContext) {
3657
+ if (omxEventName && !skipCanonicalSessionStartContext && !suppressNoisySubagentLifecycleDispatch) {
3265
3658
  const baseContext = buildBaseContext(cwd, payload, hookEventName!, canonicalSessionId);
3266
3659
  if (resolvedNativeSessionId) {
3267
3660
  baseContext.native_session_id = resolvedNativeSessionId;
@@ -3302,7 +3695,12 @@ export async function dispatchCodexNativeHook(
3302
3695
  })
3303
3696
  : isSubagentPromptSubmit
3304
3697
  ? null
3305
- : (buildAdditionalContextMessage(readPromptText(payload), skillState, cwd, payload) ?? goalWorkflowAdditionalContext ?? triageAdditionalContext);
3698
+ : [
3699
+ buildAdditionalContextMessage(readPromptText(payload), skillState, cwd, payload),
3700
+ ultragoalSteeringAdditionalContext,
3701
+ goalWorkflowAdditionalContext,
3702
+ triageAdditionalContext,
3703
+ ].filter((entry): entry is string => Boolean(entry)).join("\n\n") || null;
3306
3704
  if (additionalContext) {
3307
3705
  outputJson = {
3308
3706
  hookSpecificOutput: {