oh-my-codex 0.15.3 → 0.16.0

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 (452) hide show
  1. package/Cargo.lock +10 -7
  2. package/Cargo.toml +1 -1
  3. package/README.md +3 -0
  4. package/crates/omx-explore/Cargo.toml +3 -0
  5. package/crates/omx-explore/src/main.rs +517 -16
  6. package/dist/autoresearch/goal.d.ts +90 -0
  7. package/dist/autoresearch/goal.d.ts.map +1 -0
  8. package/dist/autoresearch/goal.js +237 -0
  9. package/dist/autoresearch/goal.js.map +1 -0
  10. package/dist/autoresearch/skill-validation.d.ts +1 -0
  11. package/dist/autoresearch/skill-validation.d.ts.map +1 -1
  12. package/dist/autoresearch/skill-validation.js +10 -3
  13. package/dist/autoresearch/skill-validation.js.map +1 -1
  14. package/dist/catalog/__tests__/generator.test.js +9 -4
  15. package/dist/catalog/__tests__/generator.test.js.map +1 -1
  16. package/dist/catalog/__tests__/plugin-bundle-ssot.test.js +20 -1
  17. package/dist/catalog/__tests__/plugin-bundle-ssot.test.js.map +1 -1
  18. package/dist/catalog/__tests__/schema.test.js +14 -3
  19. package/dist/catalog/__tests__/schema.test.js.map +1 -1
  20. package/dist/catalog/schema.js +1 -1
  21. package/dist/catalog/schema.js.map +1 -1
  22. package/dist/cli/__tests__/autoresearch-goal.test.d.ts +2 -0
  23. package/dist/cli/__tests__/autoresearch-goal.test.d.ts.map +1 -0
  24. package/dist/cli/__tests__/autoresearch-goal.test.js +194 -0
  25. package/dist/cli/__tests__/autoresearch-goal.test.js.map +1 -0
  26. package/dist/cli/__tests__/cleanup.test.js +82 -1
  27. package/dist/cli/__tests__/cleanup.test.js.map +1 -1
  28. package/dist/cli/__tests__/codex-plugin-layout.test.js +7 -4
  29. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  30. package/dist/cli/__tests__/doctor-warning-copy.test.js +23 -0
  31. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  32. package/dist/cli/__tests__/explore.test.js +8 -1
  33. package/dist/cli/__tests__/explore.test.js.map +1 -1
  34. package/dist/cli/__tests__/index.test.js +82 -3
  35. package/dist/cli/__tests__/index.test.js.map +1 -1
  36. package/dist/cli/__tests__/launch-fallback.test.js +58 -0
  37. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  38. package/dist/cli/__tests__/native-assets.test.js +26 -1
  39. package/dist/cli/__tests__/native-assets.test.js.map +1 -1
  40. package/dist/cli/__tests__/package-bin-contract.test.js +2 -2
  41. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  42. package/dist/cli/__tests__/performance-goal.test.d.ts +2 -0
  43. package/dist/cli/__tests__/performance-goal.test.d.ts.map +1 -0
  44. package/dist/cli/__tests__/performance-goal.test.js +144 -0
  45. package/dist/cli/__tests__/performance-goal.test.js.map +1 -0
  46. package/dist/cli/__tests__/question.test.js +8 -0
  47. package/dist/cli/__tests__/question.test.js.map +1 -1
  48. package/dist/cli/__tests__/ralph-goal-mode-contract.test.d.ts +2 -0
  49. package/dist/cli/__tests__/ralph-goal-mode-contract.test.d.ts.map +1 -0
  50. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +31 -0
  51. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -0
  52. package/dist/cli/__tests__/ralph-prd-deep-interview.test.js +5 -4
  53. package/dist/cli/__tests__/ralph-prd-deep-interview.test.js.map +1 -1
  54. package/dist/cli/__tests__/ralph-prd-smoke.test.js +7 -0
  55. package/dist/cli/__tests__/ralph-prd-smoke.test.js.map +1 -1
  56. package/dist/cli/__tests__/setup-install-mode.test.js +57 -21
  57. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  58. package/dist/cli/__tests__/setup-refresh.test.js +27 -8
  59. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  60. package/dist/cli/__tests__/setup-scope.test.js +18 -9
  61. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  62. package/dist/cli/__tests__/setup-skill-validation.test.js +11 -11
  63. package/dist/cli/__tests__/setup-skill-validation.test.js.map +1 -1
  64. package/dist/cli/__tests__/setup-skills-overwrite.test.js +12 -12
  65. package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
  66. package/dist/cli/__tests__/team.test.js +187 -0
  67. package/dist/cli/__tests__/team.test.js.map +1 -1
  68. package/dist/cli/__tests__/ultragoal.test.d.ts +2 -0
  69. package/dist/cli/__tests__/ultragoal.test.d.ts.map +1 -0
  70. package/dist/cli/__tests__/ultragoal.test.js +106 -0
  71. package/dist/cli/__tests__/ultragoal.test.js.map +1 -0
  72. package/dist/cli/__tests__/uninstall.test.js +11 -0
  73. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  74. package/dist/cli/autoresearch-goal.d.ts +3 -0
  75. package/dist/cli/autoresearch-goal.d.ts.map +1 -0
  76. package/dist/cli/autoresearch-goal.js +175 -0
  77. package/dist/cli/autoresearch-goal.js.map +1 -0
  78. package/dist/cli/cleanup.d.ts +3 -1
  79. package/dist/cli/cleanup.d.ts.map +1 -1
  80. package/dist/cli/cleanup.js +42 -2
  81. package/dist/cli/cleanup.js.map +1 -1
  82. package/dist/cli/doctor.d.ts.map +1 -1
  83. package/dist/cli/doctor.js +49 -0
  84. package/dist/cli/doctor.js.map +1 -1
  85. package/dist/cli/explore.d.ts.map +1 -1
  86. package/dist/cli/explore.js +10 -2
  87. package/dist/cli/explore.js.map +1 -1
  88. package/dist/cli/index.d.ts +6 -2
  89. package/dist/cli/index.d.ts.map +1 -1
  90. package/dist/cli/index.js +145 -18
  91. package/dist/cli/index.js.map +1 -1
  92. package/dist/cli/native-assets.js +1 -1
  93. package/dist/cli/native-assets.js.map +1 -1
  94. package/dist/cli/performance-goal.d.ts +3 -0
  95. package/dist/cli/performance-goal.d.ts.map +1 -0
  96. package/dist/cli/performance-goal.js +186 -0
  97. package/dist/cli/performance-goal.js.map +1 -0
  98. package/dist/cli/ralph.d.ts.map +1 -1
  99. package/dist/cli/ralph.js +8 -0
  100. package/dist/cli/ralph.js.map +1 -1
  101. package/dist/cli/setup.d.ts.map +1 -1
  102. package/dist/cli/setup.js +13 -6
  103. package/dist/cli/setup.js.map +1 -1
  104. package/dist/cli/team.d.ts +2 -0
  105. package/dist/cli/team.d.ts.map +1 -1
  106. package/dist/cli/team.js +72 -17
  107. package/dist/cli/team.js.map +1 -1
  108. package/dist/cli/tmux-hook.d.ts.map +1 -1
  109. package/dist/cli/tmux-hook.js +2 -1
  110. package/dist/cli/tmux-hook.js.map +1 -1
  111. package/dist/cli/ultragoal.d.ts +3 -0
  112. package/dist/cli/ultragoal.d.ts.map +1 -0
  113. package/dist/cli/ultragoal.js +191 -0
  114. package/dist/cli/ultragoal.js.map +1 -0
  115. package/dist/cli/uninstall.d.ts.map +1 -1
  116. package/dist/cli/uninstall.js +4 -2
  117. package/dist/cli/uninstall.js.map +1 -1
  118. package/dist/config/__tests__/generator-idempotent.test.js +12 -1
  119. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  120. package/dist/config/__tests__/generator-notify.test.js +5 -0
  121. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  122. package/dist/config/commit-lore-guard.d.ts +3 -0
  123. package/dist/config/commit-lore-guard.d.ts.map +1 -0
  124. package/dist/config/commit-lore-guard.js +9 -0
  125. package/dist/config/commit-lore-guard.js.map +1 -0
  126. package/dist/config/generator.d.ts +3 -2
  127. package/dist/config/generator.d.ts.map +1 -1
  128. package/dist/config/generator.js +52 -8
  129. package/dist/config/generator.js.map +1 -1
  130. package/dist/config/omx-first-party-mcp.d.ts +1 -0
  131. package/dist/config/omx-first-party-mcp.d.ts.map +1 -1
  132. package/dist/config/omx-first-party-mcp.js +4 -1
  133. package/dist/config/omx-first-party-mcp.js.map +1 -1
  134. package/dist/goal-workflows/__tests__/artifacts.test.d.ts +2 -0
  135. package/dist/goal-workflows/__tests__/artifacts.test.d.ts.map +1 -0
  136. package/dist/goal-workflows/__tests__/artifacts.test.js +96 -0
  137. package/dist/goal-workflows/__tests__/artifacts.test.js.map +1 -0
  138. package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.d.ts +2 -0
  139. package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.d.ts.map +1 -0
  140. package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js +54 -0
  141. package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js.map +1 -0
  142. package/dist/goal-workflows/artifacts.d.ts +62 -0
  143. package/dist/goal-workflows/artifacts.d.ts.map +1 -0
  144. package/dist/goal-workflows/artifacts.js +132 -0
  145. package/dist/goal-workflows/artifacts.js.map +1 -0
  146. package/dist/goal-workflows/codex-goal-snapshot.d.ts +28 -0
  147. package/dist/goal-workflows/codex-goal-snapshot.d.ts.map +1 -0
  148. package/dist/goal-workflows/codex-goal-snapshot.js +110 -0
  149. package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -0
  150. package/dist/goal-workflows/handoff.d.ts +10 -0
  151. package/dist/goal-workflows/handoff.d.ts.map +1 -0
  152. package/dist/goal-workflows/handoff.js +31 -0
  153. package/dist/goal-workflows/handoff.js.map +1 -0
  154. package/dist/goal-workflows/validation.d.ts +13 -0
  155. package/dist/goal-workflows/validation.d.ts.map +1 -0
  156. package/dist/goal-workflows/validation.js +36 -0
  157. package/dist/goal-workflows/validation.js.map +1 -0
  158. package/dist/hooks/__tests__/anti-slop-workflow.test.js +3 -3
  159. package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
  160. package/dist/hooks/__tests__/keyword-detector.test.js +45 -32
  161. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  162. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +3 -3
  163. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  164. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +2 -1
  165. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  166. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +17 -24
  167. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  168. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js +3 -3
  169. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js.map +1 -1
  170. package/dist/hooks/__tests__/task-size-detector.test.js +1 -1
  171. package/dist/hooks/__tests__/task-size-detector.test.js.map +1 -1
  172. package/dist/hooks/__tests__/visual-ralph-skill.test.js +3 -3
  173. package/dist/hooks/__tests__/visual-ralph-skill.test.js.map +1 -1
  174. package/dist/hooks/__tests__/visual-verdict-loop.test.js +7 -11
  175. package/dist/hooks/__tests__/visual-verdict-loop.test.js.map +1 -1
  176. package/dist/hooks/agents-overlay.d.ts.map +1 -1
  177. package/dist/hooks/agents-overlay.js +2 -2
  178. package/dist/hooks/agents-overlay.js.map +1 -1
  179. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  180. package/dist/hooks/keyword-detector.js +12 -13
  181. package/dist/hooks/keyword-detector.js.map +1 -1
  182. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  183. package/dist/hooks/keyword-registry.js +2 -10
  184. package/dist/hooks/keyword-registry.js.map +1 -1
  185. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  186. package/dist/hooks/prompt-guidance-contract.js +0 -4
  187. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  188. package/dist/hooks/session.js +2 -2
  189. package/dist/hooks/session.js.map +1 -1
  190. package/dist/hooks/task-size-detector.d.ts.map +1 -1
  191. package/dist/hooks/task-size-detector.js +1 -0
  192. package/dist/hooks/task-size-detector.js.map +1 -1
  193. package/dist/hud/__tests__/reconcile.test.js +29 -7
  194. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  195. package/dist/hud/reconcile.d.ts +2 -1
  196. package/dist/hud/reconcile.d.ts.map +1 -1
  197. package/dist/hud/reconcile.js +12 -0
  198. package/dist/hud/reconcile.js.map +1 -1
  199. package/dist/mcp/__tests__/bootstrap.test.js +15 -2
  200. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  201. package/dist/mcp/__tests__/state-paths.test.js +54 -0
  202. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  203. package/dist/mcp/__tests__/state-server.test.js +36 -0
  204. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  205. package/dist/mcp/bootstrap.d.ts +1 -1
  206. package/dist/mcp/bootstrap.d.ts.map +1 -1
  207. package/dist/mcp/bootstrap.js +9 -7
  208. package/dist/mcp/bootstrap.js.map +1 -1
  209. package/dist/mcp/state-paths.d.ts +17 -0
  210. package/dist/mcp/state-paths.d.ts.map +1 -1
  211. package/dist/mcp/state-paths.js +36 -2
  212. package/dist/mcp/state-paths.js.map +1 -1
  213. package/dist/modes/__tests__/base-session-scope.test.js +26 -0
  214. package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
  215. package/dist/modes/base.d.ts +1 -0
  216. package/dist/modes/base.d.ts.map +1 -1
  217. package/dist/modes/base.js +35 -5
  218. package/dist/modes/base.js.map +1 -1
  219. package/dist/notifications/__tests__/http-client.test.d.ts +2 -0
  220. package/dist/notifications/__tests__/http-client.test.d.ts.map +1 -0
  221. package/dist/notifications/__tests__/http-client.test.js +90 -0
  222. package/dist/notifications/__tests__/http-client.test.js.map +1 -0
  223. package/dist/notifications/__tests__/notifier.test.js +22 -60
  224. package/dist/notifications/__tests__/notifier.test.js.map +1 -1
  225. package/dist/notifications/dispatcher.d.ts.map +1 -1
  226. package/dist/notifications/dispatcher.js +35 -60
  227. package/dist/notifications/dispatcher.js.map +1 -1
  228. package/dist/notifications/http-client.d.ts +22 -0
  229. package/dist/notifications/http-client.d.ts.map +1 -0
  230. package/dist/notifications/http-client.js +298 -0
  231. package/dist/notifications/http-client.js.map +1 -0
  232. package/dist/notifications/notifier.d.ts +3 -2
  233. package/dist/notifications/notifier.d.ts.map +1 -1
  234. package/dist/notifications/notifier.js +17 -22
  235. package/dist/notifications/notifier.js.map +1 -1
  236. package/dist/openclaw/__tests__/dispatcher.test.js +62 -1
  237. package/dist/openclaw/__tests__/dispatcher.test.js.map +1 -1
  238. package/dist/openclaw/dispatcher.d.ts.map +1 -1
  239. package/dist/openclaw/dispatcher.js +3 -2
  240. package/dist/openclaw/dispatcher.js.map +1 -1
  241. package/dist/performance-goal/artifacts.d.ts +76 -0
  242. package/dist/performance-goal/artifacts.d.ts.map +1 -0
  243. package/dist/performance-goal/artifacts.js +221 -0
  244. package/dist/performance-goal/artifacts.js.map +1 -0
  245. package/dist/pipeline/__tests__/stages.test.js +30 -5
  246. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  247. package/dist/pipeline/stages/team-exec.d.ts.map +1 -1
  248. package/dist/pipeline/stages/team-exec.js +2 -19
  249. package/dist/pipeline/stages/team-exec.js.map +1 -1
  250. package/dist/planning/__tests__/artifacts.test.js +16 -1
  251. package/dist/planning/__tests__/artifacts.test.js.map +1 -1
  252. package/dist/planning/artifacts.d.ts +1 -0
  253. package/dist/planning/artifacts.d.ts.map +1 -1
  254. package/dist/planning/artifacts.js +9 -12
  255. package/dist/planning/artifacts.js.map +1 -1
  256. package/dist/ralplan/__tests__/runtime.test.js +2 -0
  257. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  258. package/dist/ralplan/runtime.d.ts.map +1 -1
  259. package/dist/ralplan/runtime.js +6 -0
  260. package/dist/ralplan/runtime.js.map +1 -1
  261. package/dist/scripts/__tests__/codex-native-hook.test.js +1516 -205
  262. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  263. package/dist/scripts/__tests__/hook-derived-watcher.test.js +33 -1
  264. package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -1
  265. package/dist/scripts/__tests__/run-test-files.test.js +36 -0
  266. package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
  267. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  268. package/dist/scripts/codex-native-hook.js +497 -51
  269. package/dist/scripts/codex-native-hook.js.map +1 -1
  270. package/dist/scripts/codex-native-pre-post.d.ts +7 -0
  271. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  272. package/dist/scripts/codex-native-pre-post.js +222 -19
  273. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  274. package/dist/scripts/hook-derived-watcher.js +2 -1
  275. package/dist/scripts/hook-derived-watcher.js.map +1 -1
  276. package/dist/scripts/notify-fallback-watcher.js +2 -1
  277. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  278. package/dist/scripts/notify-hook/orchestration-intent.d.ts +1 -2
  279. package/dist/scripts/notify-hook/orchestration-intent.d.ts.map +1 -1
  280. package/dist/scripts/notify-hook/orchestration-intent.js +2 -3
  281. package/dist/scripts/notify-hook/orchestration-intent.js.map +1 -1
  282. package/dist/scripts/notify-hook/team-leader-nudge.d.ts +0 -2
  283. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  284. package/dist/scripts/notify-hook/team-leader-nudge.js +8 -60
  285. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  286. package/dist/scripts/notify-hook/team-worker-stop.d.ts +15 -0
  287. package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -0
  288. package/dist/scripts/notify-hook/team-worker-stop.js +224 -0
  289. package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -0
  290. package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
  291. package/dist/scripts/notify-hook/team-worker.js +26 -18
  292. package/dist/scripts/notify-hook/team-worker.js.map +1 -1
  293. package/dist/scripts/run-test-files.js +17 -1
  294. package/dist/scripts/run-test-files.js.map +1 -1
  295. package/dist/scripts/sync-plugin-mirror.js +2 -2
  296. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  297. package/dist/state/__tests__/operations.test.js +26 -0
  298. package/dist/state/__tests__/operations.test.js.map +1 -1
  299. package/dist/state/__tests__/skill-active.test.js +35 -0
  300. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  301. package/dist/state/operations.d.ts +3 -1
  302. package/dist/state/operations.d.ts.map +1 -1
  303. package/dist/state/operations.js +8 -4
  304. package/dist/state/operations.js.map +1 -1
  305. package/dist/state/skill-active.d.ts +1 -0
  306. package/dist/state/skill-active.d.ts.map +1 -1
  307. package/dist/state/skill-active.js +54 -13
  308. package/dist/state/skill-active.js.map +1 -1
  309. package/dist/team/__tests__/api-interop.test.js +59 -0
  310. package/dist/team/__tests__/api-interop.test.js.map +1 -1
  311. package/dist/team/__tests__/approved-execution.test.d.ts +2 -0
  312. package/dist/team/__tests__/approved-execution.test.d.ts.map +1 -0
  313. package/dist/team/__tests__/approved-execution.test.js +124 -0
  314. package/dist/team/__tests__/approved-execution.test.js.map +1 -0
  315. package/dist/team/__tests__/delivery-e2e-smoke.test.js +2 -4
  316. package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
  317. package/dist/team/__tests__/delivery-log.test.d.ts +2 -0
  318. package/dist/team/__tests__/delivery-log.test.d.ts.map +1 -0
  319. package/dist/team/__tests__/delivery-log.test.js +44 -0
  320. package/dist/team/__tests__/delivery-log.test.js.map +1 -0
  321. package/dist/team/__tests__/role-router.test.js +4 -4
  322. package/dist/team/__tests__/role-router.test.js.map +1 -1
  323. package/dist/team/__tests__/runtime-boxed-state.test.d.ts +2 -0
  324. package/dist/team/__tests__/runtime-boxed-state.test.d.ts.map +1 -0
  325. package/dist/team/__tests__/runtime-boxed-state.test.js +39 -0
  326. package/dist/team/__tests__/runtime-boxed-state.test.js.map +1 -0
  327. package/dist/team/__tests__/runtime.test.js +118 -6
  328. package/dist/team/__tests__/runtime.test.js.map +1 -1
  329. package/dist/team/__tests__/state-root.test.js +13 -0
  330. package/dist/team/__tests__/state-root.test.js.map +1 -1
  331. package/dist/team/__tests__/tmux-session.test.js +3 -0
  332. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  333. package/dist/team/__tests__/worker-bootstrap.test.js +50 -0
  334. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  335. package/dist/team/api-interop.d.ts.map +1 -1
  336. package/dist/team/api-interop.js +4 -3
  337. package/dist/team/api-interop.js.map +1 -1
  338. package/dist/team/approved-execution.d.ts +37 -0
  339. package/dist/team/approved-execution.d.ts.map +1 -0
  340. package/dist/team/approved-execution.js +136 -0
  341. package/dist/team/approved-execution.js.map +1 -0
  342. package/dist/team/delivery-log.d.ts.map +1 -1
  343. package/dist/team/delivery-log.js +2 -1
  344. package/dist/team/delivery-log.js.map +1 -1
  345. package/dist/team/followup-planner.js +2 -2
  346. package/dist/team/followup-planner.js.map +1 -1
  347. package/dist/team/goal-workflow.d.ts +20 -0
  348. package/dist/team/goal-workflow.d.ts.map +1 -0
  349. package/dist/team/goal-workflow.js +57 -0
  350. package/dist/team/goal-workflow.js.map +1 -0
  351. package/dist/team/orchestrator.js +2 -2
  352. package/dist/team/orchestrator.js.map +1 -1
  353. package/dist/team/role-router.js +5 -5
  354. package/dist/team/role-router.js.map +1 -1
  355. package/dist/team/runtime.d.ts +6 -0
  356. package/dist/team/runtime.d.ts.map +1 -1
  357. package/dist/team/runtime.js +46 -6
  358. package/dist/team/runtime.js.map +1 -1
  359. package/dist/team/scaling.d.ts.map +1 -1
  360. package/dist/team/scaling.js +2 -0
  361. package/dist/team/scaling.js.map +1 -1
  362. package/dist/team/tmux-session.d.ts.map +1 -1
  363. package/dist/team/tmux-session.js +4 -2
  364. package/dist/team/tmux-session.js.map +1 -1
  365. package/dist/team/worker-bootstrap.d.ts +2 -0
  366. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  367. package/dist/team/worker-bootstrap.js +19 -2
  368. package/dist/team/worker-bootstrap.js.map +1 -1
  369. package/dist/ultragoal/__tests__/artifacts.test.d.ts +2 -0
  370. package/dist/ultragoal/__tests__/artifacts.test.d.ts.map +1 -0
  371. package/dist/ultragoal/__tests__/artifacts.test.js +93 -0
  372. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -0
  373. package/dist/ultragoal/artifacts.d.ts +89 -0
  374. package/dist/ultragoal/artifacts.d.ts.map +1 -0
  375. package/dist/ultragoal/artifacts.js +233 -0
  376. package/dist/ultragoal/artifacts.js.map +1 -0
  377. package/dist/utils/__tests__/agents-model-table.test.js +3 -1
  378. package/dist/utils/__tests__/agents-model-table.test.js.map +1 -1
  379. package/dist/utils/__tests__/paths.test.js +31 -1
  380. package/dist/utils/__tests__/paths.test.js.map +1 -1
  381. package/dist/utils/agents-model-table.d.ts.map +1 -1
  382. package/dist/utils/agents-model-table.js +12 -1
  383. package/dist/utils/agents-model-table.js.map +1 -1
  384. package/dist/utils/paths.d.ts +2 -0
  385. package/dist/utils/paths.d.ts.map +1 -1
  386. package/dist/utils/paths.js +23 -7
  387. package/dist/utils/paths.js.map +1 -1
  388. package/dist/verification/__tests__/ci-rust-gates.test.js +30 -19
  389. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  390. package/package.json +5 -5
  391. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  392. package/plugins/oh-my-codex/skills/ask/SKILL.md +58 -0
  393. package/plugins/oh-my-codex/skills/autoresearch-goal/SKILL.md +36 -0
  394. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +2 -2
  395. package/plugins/oh-my-codex/skills/performance-goal/SKILL.md +65 -0
  396. package/plugins/oh-my-codex/skills/plan/SKILL.md +1 -1
  397. package/plugins/oh-my-codex/skills/ralph/SKILL.md +22 -3
  398. package/plugins/oh-my-codex/skills/team/SKILL.md +6 -2
  399. package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +49 -0
  400. package/plugins/oh-my-codex/skills/visual-ralph/SKILL.md +9 -9
  401. package/prompts/api-reviewer.md +1 -1
  402. package/prompts/code-reviewer.md +2 -0
  403. package/prompts/performance-reviewer.md +1 -1
  404. package/prompts/quality-reviewer.md +1 -1
  405. package/prompts/quality-strategist.md +2 -2
  406. package/prompts/style-reviewer.md +1 -1
  407. package/prompts/test-engineer.md +1 -1
  408. package/skills/ask/SKILL.md +58 -0
  409. package/skills/ask-claude/SKILL.md +3 -54
  410. package/skills/ask-gemini/SKILL.md +3 -54
  411. package/skills/autoresearch-goal/SKILL.md +36 -0
  412. package/skills/build-fix/SKILL.md +4 -139
  413. package/skills/deepsearch/SKILL.md +4 -32
  414. package/skills/ecomode/SKILL.md +4 -108
  415. package/skills/help/SKILL.md +4 -196
  416. package/skills/note/SKILL.md +4 -56
  417. package/skills/omx-setup/SKILL.md +2 -2
  418. package/skills/performance-goal/SKILL.md +65 -0
  419. package/skills/plan/SKILL.md +1 -1
  420. package/skills/ralph/SKILL.md +22 -3
  421. package/skills/ralph-init/SKILL.md +4 -40
  422. package/skills/review/SKILL.md +4 -32
  423. package/skills/security-review/SKILL.md +4 -294
  424. package/skills/swarm/SKILL.md +4 -19
  425. package/skills/tdd/SKILL.md +4 -100
  426. package/skills/team/SKILL.md +6 -2
  427. package/skills/trace/SKILL.md +4 -27
  428. package/skills/ultragoal/SKILL.md +49 -0
  429. package/skills/visual-ralph/SKILL.md +9 -9
  430. package/skills/visual-verdict/SKILL.md +4 -70
  431. package/skills/web-clone/SKILL.md +4 -18
  432. package/src/scripts/__tests__/codex-native-hook.test.ts +1654 -157
  433. package/src/scripts/__tests__/hook-derived-watcher.test.ts +45 -1
  434. package/src/scripts/__tests__/run-test-files.test.ts +46 -0
  435. package/src/scripts/codex-native-hook.ts +592 -52
  436. package/src/scripts/codex-native-pre-post.ts +252 -20
  437. package/src/scripts/hook-derived-watcher.ts +2 -1
  438. package/src/scripts/notify-fallback-watcher.ts +2 -1
  439. package/src/scripts/notify-hook/orchestration-intent.ts +1 -3
  440. package/src/scripts/notify-hook/team-leader-nudge.ts +7 -63
  441. package/src/scripts/notify-hook/team-worker-stop.ts +246 -0
  442. package/src/scripts/notify-hook/team-worker.ts +23 -14
  443. package/src/scripts/run-test-files.ts +20 -1
  444. package/src/scripts/sync-plugin-mirror.ts +2 -2
  445. package/templates/catalog-manifest.json +45 -27
  446. package/plugins/oh-my-codex/skills/ask-claude/SKILL.md +0 -61
  447. package/plugins/oh-my-codex/skills/ask-gemini/SKILL.md +0 -61
  448. package/plugins/oh-my-codex/skills/help/SKILL.md +0 -202
  449. package/plugins/oh-my-codex/skills/note/SKILL.md +0 -62
  450. package/plugins/oh-my-codex/skills/security-review/SKILL.md +0 -300
  451. package/plugins/oh-my-codex/skills/trace/SKILL.md +0 -33
  452. package/plugins/oh-my-codex/skills/visual-verdict/SKILL.md +0 -76
@@ -1,10 +1,10 @@
1
1
  import { execFileSync } from "child_process";
2
2
  import { closeSync, existsSync, openSync, readFileSync, readSync } from "fs";
3
3
  import { mkdir, readFile, readdir, writeFile } from "fs/promises";
4
- import { join, relative, resolve } from "path";
4
+ import { extname, join, relative, resolve } from "path";
5
5
  import { pathToFileURL } from "url";
6
- import { readModeState, readModeStateForSession, updateModeState } from "../modes/base.js";
7
- import { listActiveSkills, readVisibleSkillActiveState, } from "../state/skill-active.js";
6
+ import { readModeState, readModeStateForActiveDecision, readModeStateForSession, updateModeState } from "../modes/base.js";
7
+ import { extractSessionIdFromInitializedStatePath, listActiveSkills, readVisibleSkillActiveState, } from "../state/skill-active.js";
8
8
  import { readSubagentSessionSummary, recordSubagentTurnForSession, } from "../subagents/tracker.js";
9
9
  import { resolveCanonicalTeamStateRoot, resolveWorkerNotifyTeamStateRootPath } from "../team/state-root.js";
10
10
  import { appendToLog, isSessionStateUsable, readSessionState, readUsableSessionState, reconcileNativeSessionStart, } from "../hooks/session.js";
@@ -14,14 +14,15 @@ import { findGitLayout } from "../utils/git-layout.js";
14
14
  import { getStateFilePath, getStatePath } from "../mcp/state-paths.js";
15
15
  import { detectKeywords, detectPrimaryKeyword, recordSkillActivation, } from "../hooks/keyword-detector.js";
16
16
  import { detectNativeStopStallPattern, loadAutoNudgeConfig, normalizeAutoNudgeSignatureText, resolveEffectiveAutoNudgeResponse, } from "./notify-hook/auto-nudge.js";
17
- import { buildNativePostToolUseOutput, buildNativePreToolUseOutput, detectMcpTransportFailure, } from "./codex-native-pre-post.js";
17
+ import { SLOPPY_FALLBACK_GROUNDING_PATTERNS, SLOPPY_FALLBACK_IMPLEMENTATION_CONTEXT_PATTERNS, SLOPPY_FALLBACK_PHRASE_PATTERNS, buildNativePostToolUseOutput, buildNativePreToolUseOutput, detectMcpTransportFailure, hasAnyPattern, } from "./codex-native-pre-post.js";
18
18
  import { handleTeamWorkerPostToolUseSuccess } from "./notify-hook/team-worker-posttooluse.js";
19
+ import { maybeNudgeLeaderForAllowedWorkerStop } from "./notify-hook/team-worker-stop.js";
19
20
  import { resolveCodexExecutionSurface, } from "./codex-execution-surface.js";
20
21
  import { buildNativeHookEvent, } from "../hooks/extensibility/events.js";
21
- import { dispatchHookEvent } from "../hooks/extensibility/dispatcher.js";
22
+ import { dispatchHookEventRuntime } from "../hooks/extensibility/runtime.js";
22
23
  import { reconcileHudForPromptSubmit } from "../hud/reconcile.js";
23
24
  import { onSessionStart as buildWikiSessionStartContext } from "../wiki/lifecycle.js";
24
- import { readAutoresearchCompletionStatus, readAutoresearchModeState } from "../autoresearch/skill-validation.js";
25
+ import { readAutoresearchCompletionStatus, readAutoresearchModeStateForActiveDecision } from "../autoresearch/skill-validation.js";
25
26
  import { readRunState } from "../runtime/run-state.js";
26
27
  import { getRunContinuationSnapshot, shouldContinueRun } from "../runtime/run-loop.js";
27
28
  import { triagePrompt } from "../hooks/triage-heuristic.js";
@@ -32,8 +33,8 @@ import { buildDocumentRefreshAdvisoryOutput, evaluateFinalHandoffDocumentRefresh
32
33
  import { buildExecFollowupStopOutput } from "../exec/followup.js";
33
34
  const TERMINAL_MODE_PHASES = new Set(["complete", "completed", "failed", "cancelled"]);
34
35
  const SKILL_STOP_BLOCKERS = new Set(["ralplan"]);
35
- const TEAM_TERMINAL_TASK_STATUSES = new Set(["completed", "failed"]);
36
- const TEAM_WORKER_STOP_ACTIVE_STATES = new Set(["working", "blocked"]);
36
+ const TEAM_STOP_BLOCKING_TASK_STATUSES = new Set(["pending", "in_progress", "blocked"]);
37
+ const TEAM_WORKER_TERMINAL_RUN_STATES = new Set(["done", "complete", "completed", "failed", "stopped", "cancelled"]);
37
38
  const NATIVE_STOP_STATE_FILE = "native-stop-state.json";
38
39
  const STABLE_FINAL_RECOMMENDATION_PATTERNS = [
39
40
  /^\s*(?:launch|release|ship)-?ready\s*:\s*(?:yes|no)\b[^\n\r]*/im,
@@ -160,6 +161,20 @@ async function nativeSubagentSessionStartBelongsToCanonicalSession(cwd, canonica
160
161
  return true;
161
162
  return summary.allThreadIds.includes(parentThreadId);
162
163
  }
164
+ async function isNativeSubagentHook(cwd, canonicalSessionId, nativeSessionId, threadId) {
165
+ const sessionId = canonicalSessionId.trim();
166
+ if (!sessionId)
167
+ return false;
168
+ const summary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
169
+ if (!summary)
170
+ return false;
171
+ const candidateIds = [nativeSessionId, threadId]
172
+ .map((value) => value.trim())
173
+ .filter(Boolean);
174
+ if (candidateIds.length === 0)
175
+ return false;
176
+ return candidateIds.some((id) => summary.allSubagentThreadIds.includes(id));
177
+ }
163
178
  async function recordIgnoredNativeSubagentSessionStart(cwd, canonicalSessionId, childSessionId, metadata, transcriptPath) {
164
179
  await appendToLog(cwd, {
165
180
  event: "subagent_session_start_ignored",
@@ -303,7 +318,7 @@ async function readActiveAutoresearchState(cwd, sessionId) {
303
318
  const normalizedSessionId = sessionId?.trim() || undefined;
304
319
  if (!normalizedSessionId)
305
320
  return null;
306
- const state = await readAutoresearchModeState(cwd, normalizedSessionId);
321
+ const state = await readAutoresearchModeStateForActiveDecision(cwd, normalizedSessionId);
307
322
  if (state?.active !== true)
308
323
  return null;
309
324
  if (!isNonTerminalPhase(state.current_phase ?? state.currentPhase ?? 'executing'))
@@ -367,6 +382,18 @@ async function isVisibleRalphActiveForSession(cwd, sessionId) {
367
382
  return listActiveSkills(canonicalState).some((entry) => (entry.skill === "ralph"
368
383
  && matchesSkillStopContext(entry, canonicalState, sessionId, "")));
369
384
  }
385
+ async function hasConsistentRalphSkillActivation(cwd, sessionId) {
386
+ const canonicalState = await readVisibleSkillActiveState(cwd, sessionId);
387
+ if (!canonicalState)
388
+ return true;
389
+ const initializedMode = safeString(canonicalState.initialized_mode).trim();
390
+ if (initializedMode && initializedMode !== "ralph")
391
+ return true;
392
+ const initializedPathSessionId = extractSessionIdFromInitializedStatePath(canonicalState.initialized_state_path);
393
+ if (initializedPathSessionId && initializedPathSessionId !== sessionId)
394
+ return false;
395
+ return true;
396
+ }
370
397
  async function readActiveRalphState(stateDir, preferredSessionId, ownerContext) {
371
398
  const cwd = resolve(stateDir, "..", "..");
372
399
  const [rawSessionInfo, usableSessionInfo] = await Promise.all([
@@ -407,7 +434,8 @@ async function readActiveRalphState(stateDir, preferredSessionId, ownerContext)
407
434
  threadId: safeString(ownerContext?.threadId).trim(),
408
435
  currentNativeSessionId,
409
436
  tmuxPaneId: safeString(ownerContext?.tmuxPaneId).trim(),
410
- })) {
437
+ })
438
+ && await hasConsistentRalphSkillActivation(cwd, sessionId)) {
411
439
  return { state: sessionScoped, path: sessionScopedPath };
412
440
  }
413
441
  }
@@ -522,6 +550,182 @@ function tryReadGitValue(cwd, args) {
522
550
  return null;
523
551
  }
524
552
  }
553
+ const SOURCE_DIFF_EXTENSIONS = new Set([
554
+ ".c",
555
+ ".cc",
556
+ ".cjs",
557
+ ".cpp",
558
+ ".cs",
559
+ ".cts",
560
+ ".go",
561
+ ".h",
562
+ ".hpp",
563
+ ".java",
564
+ ".js",
565
+ ".jsx",
566
+ ".kt",
567
+ ".mjs",
568
+ ".mts",
569
+ ".php",
570
+ ".py",
571
+ ".rb",
572
+ ".rs",
573
+ ".sh",
574
+ ".swift",
575
+ ".ts",
576
+ ".tsx",
577
+ ]);
578
+ function gitOutput(cwd, args) {
579
+ try {
580
+ return execFileSync("git", args, {
581
+ cwd,
582
+ encoding: "utf-8",
583
+ stdio: ["ignore", "pipe", "ignore"],
584
+ windowsHide: true,
585
+ maxBuffer: 10 * 1024 * 1024,
586
+ });
587
+ }
588
+ catch {
589
+ return "";
590
+ }
591
+ }
592
+ function normalizeGitPath(path) {
593
+ return path.replace(/\\/g, "/").replace(/^\.\//, "");
594
+ }
595
+ function isDiffAuditableSourcePath(path) {
596
+ const normalized = normalizeGitPath(path).toLowerCase();
597
+ if (!normalized || normalized.startsWith(".git/") || normalized.startsWith(".omx/"))
598
+ return false;
599
+ if (/(^|\/)(?:docs?|documentation|changelog|changeset|\.github)(?:\/|$)/i.test(normalized))
600
+ return false;
601
+ if (/(^|\/)(?:__tests__|__test__|test|tests|spec|specs|fixtures?|mocks?)(?:\/|$)/i.test(normalized))
602
+ return false;
603
+ if (/(?:^|\/)[^\/]+\.(?:test|spec)\.[^.\/]+$/i.test(normalized))
604
+ return false;
605
+ if (/(?:^|\/)(?:readme|changelog|changes|license|notice)(?:\.[^\/]*)?$/i.test(normalized))
606
+ return false;
607
+ if (/\.(?:md|mdx|markdown|txt|rst|adoc|ya?ml|json|lock)$/i.test(normalized))
608
+ return false;
609
+ return SOURCE_DIFF_EXTENSIONS.has(extname(normalized));
610
+ }
611
+ function isDiffHeaderLine(line) {
612
+ return line.startsWith("+++") || line.startsWith("---") || line.startsWith("@@") || line.startsWith("diff --git ");
613
+ }
614
+ function isSuspiciousSloppyFallbackAddedLine(line, nearbyContext) {
615
+ const trimmed = line.trim();
616
+ if (!trimmed)
617
+ return false;
618
+ if (!hasAnyPattern(trimmed, SLOPPY_FALLBACK_PHRASE_PATTERNS))
619
+ return false;
620
+ if (!hasAnyPattern(trimmed, SLOPPY_FALLBACK_IMPLEMENTATION_CONTEXT_PATTERNS))
621
+ return false;
622
+ if (hasAnyPattern(nearbyContext, SLOPPY_FALLBACK_GROUNDING_PATTERNS))
623
+ return false;
624
+ if (/compatib(?:le|ility)|fail-?safe|tested|regression|coverage|because|issue|PR\s*#?\d|#\d/i.test(nearbyContext))
625
+ return false;
626
+ return true;
627
+ }
628
+ function collectFindingsFromCandidateLines(path, lines, source) {
629
+ if (!path || !isDiffAuditableSourcePath(path))
630
+ return [];
631
+ const findings = [];
632
+ for (let index = 0; index < lines.length; index += 1) {
633
+ const candidate = lines[index];
634
+ if (!candidate?.added)
635
+ continue;
636
+ const nearbyContext = lines
637
+ .slice(Math.max(0, index - 2), Math.min(lines.length, index + 3))
638
+ .map((line) => line.text)
639
+ .join("\n");
640
+ if (isSuspiciousSloppyFallbackAddedLine(candidate.text, nearbyContext)) {
641
+ findings.push({ path, line: candidate.text.trim(), source });
642
+ }
643
+ }
644
+ return findings;
645
+ }
646
+ function collectSloppyFallbackFindingsFromPatch(patch, source) {
647
+ const findings = [];
648
+ let currentPath = "";
649
+ let hunkLines = [];
650
+ const flushHunk = () => {
651
+ findings.push(...collectFindingsFromCandidateLines(currentPath, hunkLines, source));
652
+ hunkLines = [];
653
+ };
654
+ for (const rawLine of patch.split(/\r?\n/)) {
655
+ const fileMatch = rawLine.match(/^diff --git a\/(.*?) b\/(.*)$/);
656
+ if (fileMatch) {
657
+ flushHunk();
658
+ currentPath = normalizeGitPath(fileMatch[2] || fileMatch[1] || "");
659
+ continue;
660
+ }
661
+ const renameMatch = rawLine.match(/^\+\+\+ b\/(.*)$/);
662
+ if (renameMatch) {
663
+ currentPath = normalizeGitPath(renameMatch[1] || currentPath);
664
+ continue;
665
+ }
666
+ if (rawLine.startsWith("@@")) {
667
+ flushHunk();
668
+ continue;
669
+ }
670
+ if (!currentPath || !isDiffAuditableSourcePath(currentPath) || isDiffHeaderLine(rawLine))
671
+ continue;
672
+ if (rawLine.startsWith("+")) {
673
+ hunkLines.push({ text: rawLine.slice(1), added: true });
674
+ }
675
+ else if (rawLine.startsWith(" ")) {
676
+ hunkLines.push({ text: rawLine.slice(1), added: false });
677
+ }
678
+ }
679
+ flushHunk();
680
+ return findings;
681
+ }
682
+ function collectSloppyFallbackFindingsFromUntracked(cwd) {
683
+ const output = gitOutput(cwd, ["ls-files", "--others", "--exclude-standard", "-z"]);
684
+ if (!output)
685
+ return [];
686
+ const findings = [];
687
+ for (const rawPath of output.split("\0")) {
688
+ const path = normalizeGitPath(rawPath.trim());
689
+ if (!path || !isDiffAuditableSourcePath(path))
690
+ continue;
691
+ let content = "";
692
+ try {
693
+ content = readFileSync(join(cwd, path), "utf-8");
694
+ }
695
+ catch {
696
+ continue;
697
+ }
698
+ findings.push(...collectFindingsFromCandidateLines(path, content.split(/\r?\n/).map((text) => ({ text, added: true })), "untracked"));
699
+ }
700
+ return findings;
701
+ }
702
+ function findSloppyFallbackDiffFindings(cwd) {
703
+ const layout = findGitLayout(cwd);
704
+ if (!layout)
705
+ return [];
706
+ const auditRoot = layout.worktreeRoot;
707
+ return [
708
+ ...collectSloppyFallbackFindingsFromPatch(gitOutput(auditRoot, ["diff", "--cached", "--no-ext-diff", "--unified=3"]), "staged"),
709
+ ...collectSloppyFallbackFindingsFromPatch(gitOutput(auditRoot, ["diff", "--no-ext-diff", "--unified=3"]), "unstaged"),
710
+ ...collectSloppyFallbackFindingsFromUntracked(auditRoot),
711
+ ];
712
+ }
713
+ function buildSloppyFallbackDiffStopOutput(findings) {
714
+ if (findings.length === 0)
715
+ return null;
716
+ const preview = findings
717
+ .slice(0, 3)
718
+ .map((finding) => `${finding.path} (${finding.source}): ${finding.line}`)
719
+ .join("; ");
720
+ const systemMessage = `Sloppy fallback/workaround diff audit detected ungrounded fallback code in added source lines: ${preview}. `
721
+ + "Continue by replacing the bypass/workaround with a grounded design, or add explicit compatibility/fail-safe/tested/issue rationale near the code if the fallback is intentional.";
722
+ return {
723
+ decision: "block",
724
+ reason: systemMessage,
725
+ stopReason: "sloppy_fallback_diff_audit",
726
+ systemMessage,
727
+ };
728
+ }
525
729
  function localExcludeAlreadyIgnoresOmx(cwd) {
526
730
  const layout = findGitLayout(cwd);
527
731
  if (!layout)
@@ -845,6 +1049,9 @@ function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(),
845
1049
  const ultraworkPromptActivationNote = skillState?.initialized_mode === "ultrawork"
846
1050
  ? "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."
847
1051
  : null;
1052
+ const ultragoalPromptActivationNote = match.skill === "ultragoal"
1053
+ ? "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."
1054
+ : null;
848
1055
  const combinedTransitionMessage = (() => {
849
1056
  if (!skillState?.transition_message)
850
1057
  return null;
@@ -872,6 +1079,7 @@ function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(),
872
1079
  ? `planning preserved over simultaneous execution follow-up; deferred skills: ${deferredSkills.join(", ")}.`
873
1080
  : null,
874
1081
  promptPriorityMessage,
1082
+ ultragoalPromptActivationNote,
875
1083
  skillState.initialized_mode && skillState.initialized_state_path
876
1084
  ? `skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`
877
1085
  : null,
@@ -896,6 +1104,7 @@ function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(),
896
1104
  initializedStateMessage,
897
1105
  deepInterviewPromptActivationNote,
898
1106
  ultraworkPromptActivationNote,
1107
+ ultragoalPromptActivationNote,
899
1108
  buildTeamRuntimeInstruction(cwd, payload),
900
1109
  buildTeamHelpInstruction(cwd, payload),
901
1110
  "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
@@ -912,11 +1121,12 @@ function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(),
912
1121
  `skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`,
913
1122
  deepInterviewPromptActivationNote,
914
1123
  ultraworkPromptActivationNote,
1124
+ ultragoalPromptActivationNote,
915
1125
  ralphPromptActivationNote,
916
1126
  "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
917
1127
  ].join(" ");
918
1128
  }
919
- return [detectedKeywordMessage, promptPriorityMessage, "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules."].filter(Boolean).join(" ");
1129
+ return [detectedKeywordMessage, promptPriorityMessage, ultragoalPromptActivationNote, "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules."].filter(Boolean).join(" ");
920
1130
  }
921
1131
  function parseTeamWorkerEnv(rawValue) {
922
1132
  const match = /^([a-z0-9][a-z0-9-]{0,29})\/(worker-\d+)$/.exec(rawValue.trim());
@@ -928,23 +1138,51 @@ function parseTeamWorkerEnv(rawValue) {
928
1138
  };
929
1139
  }
930
1140
  async function resolveTeamStateDirForWorkerContext(cwd, workerContext) {
931
- return resolveWorkerNotifyTeamStateRootPath(cwd, workerContext, process.env);
1141
+ const resolved = await resolveWorkerNotifyTeamStateRootPath(cwd, workerContext, process.env).catch(() => null);
1142
+ if (resolved)
1143
+ return resolved;
1144
+ const explicit = safeString(process.env.OMX_TEAM_STATE_ROOT).trim();
1145
+ if (explicit) {
1146
+ const candidate = resolve(cwd, explicit);
1147
+ const workerRoot = join(candidate, "team", workerContext.teamName, "workers", workerContext.workerName);
1148
+ if (existsSync(workerRoot))
1149
+ return candidate;
1150
+ return candidate;
1151
+ }
1152
+ return null;
932
1153
  }
933
- async function buildTeamWorkerStopOutput(cwd) {
934
- const workerContext = parseTeamWorkerEnv(safeString(process.env.OMX_TEAM_INTERNAL_WORKER || process.env.OMX_TEAM_WORKER));
1154
+ async function resolveTeamWorkerStopDecision(cwd) {
1155
+ const workerContext = parseTeamWorkerEnv(safeString(process.env.OMX_TEAM_INTERNAL_WORKER))
1156
+ || parseTeamWorkerEnv(safeString(process.env.OMX_TEAM_WORKER));
935
1157
  if (!workerContext)
936
- return null;
1158
+ return { kind: "unresolved", reason: "missing_worker_context" };
1159
+ const blockWorkerStop = (reasonCode, detail, stateDirForDecision = join(cwd, ".omx", "state")) => ({
1160
+ kind: "blocked",
1161
+ stateDir: stateDirForDecision,
1162
+ workerContext,
1163
+ allowRepeatDuringStopHook: false,
1164
+ output: {
1165
+ decision: "block",
1166
+ reason: `OMX team worker ${workerContext.workerName} Stop cannot be allowed for ${reasonCode}: ${detail}. ` +
1167
+ "Continue the assigned task, repair worker state, or report a concrete blocker before stopping.",
1168
+ stopReason: `team_worker_${workerContext.workerName}_${reasonCode}`,
1169
+ systemMessage: `OMX team worker ${workerContext.workerName} Stop lacks completed task evidence (${reasonCode}).`,
1170
+ },
1171
+ });
937
1172
  const stateDir = await resolveTeamStateDirForWorkerContext(cwd, workerContext);
938
- if (!stateDir)
939
- return null;
1173
+ if (!stateDir) {
1174
+ return blockWorkerStop("missing_state_dir", "team state root could not be resolved");
1175
+ }
940
1176
  const workerRoot = join(stateDir, "team", workerContext.teamName, "workers", workerContext.workerName);
941
1177
  const [identity, status] = await Promise.all([
942
1178
  readJsonIfExists(join(workerRoot, "identity.json")),
943
1179
  readJsonIfExists(join(workerRoot, "status.json")),
944
1180
  ]);
945
- const workerState = safeString(status?.state).trim().toLowerCase();
946
- if (!TEAM_WORKER_STOP_ACTIVE_STATES.has(workerState))
947
- return null;
1181
+ const workerRunState = safeString(status?.state).trim().toLowerCase();
1182
+ const workerRunStateIsTerminal = TEAM_WORKER_TERMINAL_RUN_STATES.has(workerRunState);
1183
+ if (!identity && !status && !existsSync(workerRoot)) {
1184
+ return blockWorkerStop("missing_worker_state", "worker identity/status state is missing", stateDir);
1185
+ }
948
1186
  const candidateTaskIds = new Set();
949
1187
  const currentTaskId = safeString(status?.current_task_id).trim();
950
1188
  if (currentTaskId)
@@ -955,22 +1193,57 @@ async function buildTeamWorkerStopOutput(cwd) {
955
1193
  if (normalized)
956
1194
  candidateTaskIds.add(normalized);
957
1195
  }
1196
+ const tasksDir = join(stateDir, "team", workerContext.teamName, "tasks");
1197
+ if (existsSync(tasksDir)) {
1198
+ const taskFiles = await readdir(tasksDir).catch(() => []);
1199
+ for (const entry of taskFiles) {
1200
+ if (!/^task-\d+\.json$/.test(entry))
1201
+ continue;
1202
+ const task = await readJsonIfExists(join(tasksDir, entry));
1203
+ const taskOwner = safeString(task?.owner).trim();
1204
+ const taskClaimOwner = safeString(safeObject(task?.claim).owner).trim();
1205
+ if (taskOwner !== workerContext.workerName && taskClaimOwner !== workerContext.workerName)
1206
+ continue;
1207
+ const idFromFile = /^task-(\d+)\.json$/.exec(entry)?.[1] ?? "";
1208
+ const taskId = safeString(task?.id).trim() || idFromFile;
1209
+ if (taskId)
1210
+ candidateTaskIds.add(taskId);
1211
+ }
1212
+ }
1213
+ if (candidateTaskIds.size === 0) {
1214
+ return blockWorkerStop("missing_task_assignment", "no current_task_id or assigned_tasks are recorded", stateDir);
1215
+ }
1216
+ let completedTaskCount = 0;
958
1217
  for (const taskId of candidateTaskIds) {
959
1218
  const task = await readJsonIfExists(join(stateDir, "team", workerContext.teamName, "tasks", `task-${taskId}.json`));
960
1219
  const statusValue = safeString(task?.status).trim().toLowerCase();
961
- if (!statusValue || TEAM_TERMINAL_TASK_STATUSES.has(statusValue))
1220
+ if (!statusValue) {
1221
+ return blockWorkerStop(`missing_task_state_${taskId}`, `task ${taskId} has no readable status`, stateDir);
1222
+ }
1223
+ if (statusValue === "completed") {
1224
+ completedTaskCount += 1;
962
1225
  continue;
1226
+ }
1227
+ if (!TEAM_STOP_BLOCKING_TASK_STATUSES.has(statusValue)) {
1228
+ return blockWorkerStop(`non_completed_task_${taskId}_${statusValue}`, `task ${taskId} is ${statusValue}, not completed`, stateDir);
1229
+ }
963
1230
  return {
964
- decision: "block",
965
- reason: `OMX team worker ${workerContext.workerName} is still assigned non-terminal task ${taskId} (${statusValue}); continue the current assigned task or report a concrete blocker before stopping.`,
966
- stopReason: `team_worker_${workerContext.workerName}_${taskId}_${statusValue}`,
967
- systemMessage: `OMX team worker ${workerContext.workerName} is still assigned task ${taskId} (${statusValue}).`,
1231
+ kind: "blocked",
1232
+ stateDir,
1233
+ workerContext,
1234
+ allowRepeatDuringStopHook: !workerRunStateIsTerminal,
1235
+ output: {
1236
+ decision: "block",
1237
+ reason: `OMX team worker ${workerContext.workerName} is still assigned non-terminal task ${taskId} (${statusValue}); continue the current assigned task or report a concrete blocker before stopping.`,
1238
+ stopReason: `team_worker_${workerContext.workerName}_${taskId}_${statusValue}`,
1239
+ systemMessage: `OMX team worker ${workerContext.workerName} is still assigned task ${taskId} (${statusValue}).`,
1240
+ },
968
1241
  };
969
1242
  }
970
- return null;
971
- }
972
- function hasTeamWorkerContext() {
973
- return parseTeamWorkerEnv(safeString(process.env.OMX_TEAM_INTERNAL_WORKER || process.env.OMX_TEAM_WORKER)) !== null;
1243
+ if (completedTaskCount === candidateTaskIds.size) {
1244
+ return { kind: "allowed", stateDir, workerContext };
1245
+ }
1246
+ return blockWorkerStop("missing_completed_task_evidence", "no referenced worker task is completed", stateDir);
974
1247
  }
975
1248
  function isStopExempt(payload) {
976
1249
  const candidates = [
@@ -989,9 +1262,7 @@ function isStopExempt(payload) {
989
1262
  || value.includes("limit"));
990
1263
  }
991
1264
  async function buildModeBasedStopOutput(mode, cwd, sessionId) {
992
- const state = sessionId
993
- ? await readModeStateForSession(mode, sessionId, cwd)
994
- : await readModeState(mode, cwd);
1265
+ const state = await readModeStateForActiveDecision(mode, sessionId?.trim() || undefined, cwd);
995
1266
  if (!state || !shouldContinueRun(state))
996
1267
  return null;
997
1268
  const phase = formatPhase(state.current_phase);
@@ -1002,6 +1273,76 @@ async function buildModeBasedStopOutput(mode, cwd, sessionId) {
1002
1273
  systemMessage: `OMX ${mode} is still active (phase: ${phase}).`,
1003
1274
  };
1004
1275
  }
1276
+ function looksLikeGoalCompletionPrompt(text) {
1277
+ return /\b(?:complete|checkpoint|finish|close|mark)\b.{0,80}\b(?:goal|ultragoal|performance-goal|autoresearch-goal)\b/i.test(text)
1278
+ || /\bupdate_goal\s*\(/i.test(text)
1279
+ || /\bomx\s+(?:ultragoal|performance-goal|autoresearch-goal)\s+(?:checkpoint|complete)\b/i.test(text);
1280
+ }
1281
+ async function findActiveGoalWorkflowReconciliationRequirement(cwd) {
1282
+ const ultragoal = await readJsonIfExists(join(cwd, ".omx", "ultragoal", "goals.json"));
1283
+ const ultragoals = Array.isArray(ultragoal?.goals) ? ultragoal.goals.map(safeObject) : [];
1284
+ const activeUltragoal = ultragoals.find((goal) => safeString(goal.status) === "in_progress" || safeString(goal.id) === safeString(ultragoal?.activeGoalId));
1285
+ if (activeUltragoal) {
1286
+ return {
1287
+ workflow: "ultragoal",
1288
+ command: `omx ultragoal checkpoint --goal-id ${safeString(activeUltragoal.id) || "<goal-id>"} --status complete --codex-goal-json '<get_goal JSON or path>' --evidence '<evidence>'`,
1289
+ };
1290
+ }
1291
+ const performanceRoot = join(cwd, ".omx", "goals", "performance");
1292
+ for (const entry of await readdir(performanceRoot, { withFileTypes: true }).catch(() => [])) {
1293
+ if (!entry.isDirectory())
1294
+ continue;
1295
+ const state = await readJsonIfExists(join(performanceRoot, entry.name, "state.json"));
1296
+ const status = safeString(state?.status);
1297
+ if (state?.workflow === "performance-goal" && status && status !== "complete") {
1298
+ return {
1299
+ workflow: "performance-goal",
1300
+ command: `omx performance-goal complete --slug ${safeString(state.slug) || entry.name} --codex-goal-json '<get_goal JSON or path>' --evidence '<evidence>'`,
1301
+ };
1302
+ }
1303
+ }
1304
+ const autoresearchRoot = join(cwd, ".omx", "goals", "autoresearch");
1305
+ for (const entry of await readdir(autoresearchRoot, { withFileTypes: true }).catch(() => [])) {
1306
+ if (!entry.isDirectory())
1307
+ continue;
1308
+ const mission = await readJsonIfExists(join(autoresearchRoot, entry.name, "mission.json"));
1309
+ const status = safeString(mission?.status);
1310
+ if (mission?.workflow === "autoresearch-goal" && status && status !== "complete") {
1311
+ return {
1312
+ workflow: "autoresearch-goal",
1313
+ command: `omx autoresearch-goal complete --slug ${safeString(mission.slug) || entry.name} --codex-goal-json '<get_goal JSON or path>'`,
1314
+ };
1315
+ }
1316
+ }
1317
+ return null;
1318
+ }
1319
+ async function buildGoalWorkflowReconciliationPromptWarning(cwd, prompt) {
1320
+ if (!looksLikeGoalCompletionPrompt(prompt))
1321
+ return null;
1322
+ const requirement = await findActiveGoalWorkflowReconciliationRequirement(cwd);
1323
+ if (!requirement)
1324
+ return null;
1325
+ return [
1326
+ `OMX ${requirement.workflow} goal workflow requires Codex goal snapshot reconciliation before completion.`,
1327
+ "Call get_goal, pass the resulting JSON or a path with --codex-goal-json, and do not rely on hooks or shell commands to mutate Codex-owned goal state.",
1328
+ `Required command shape: ${requirement.command}.`,
1329
+ ].join(" ");
1330
+ }
1331
+ async function buildGoalWorkflowReconciliationStopOutput(payload, cwd) {
1332
+ const lastAssistantMessage = safeString(payload.last_assistant_message ?? payload.lastAssistantMessage);
1333
+ if (!looksLikeGoalCompletionPrompt(lastAssistantMessage))
1334
+ return null;
1335
+ const requirement = await findActiveGoalWorkflowReconciliationRequirement(cwd);
1336
+ if (!requirement)
1337
+ return null;
1338
+ const systemMessage = `OMX ${requirement.workflow} requires get_goal snapshot reconciliation before completion; call get_goal and pass --codex-goal-json to ${requirement.command}. Hooks must not mutate Codex goal state.`;
1339
+ return {
1340
+ decision: "block",
1341
+ reason: systemMessage,
1342
+ stopReason: `${requirement.workflow}_codex_goal_snapshot_required`,
1343
+ systemMessage,
1344
+ };
1345
+ }
1005
1346
  async function readTeamModeStateForStop(cwd, sessionId) {
1006
1347
  const normalizedSessionId = safeString(sessionId).trim();
1007
1348
  if (!normalizedSessionId) {
@@ -1170,7 +1511,13 @@ async function readBlockingSkillForStop(cwd, sessionId, threadId, requiredSkill)
1170
1511
  continue;
1171
1512
  }
1172
1513
  if (!canonicalState) {
1173
- return { skill, phase };
1514
+ return {
1515
+ skill,
1516
+ phase,
1517
+ latestPlanPath: safeString(modeState.latest_plan_path ?? modeState.latestPlanPath).trim() || undefined,
1518
+ planningComplete: modeState.planning_complete === true || modeState.planningComplete === true,
1519
+ runOutcome: safeString(modeState.run_outcome ?? modeState.outcome).trim() || undefined,
1520
+ };
1174
1521
  }
1175
1522
  const blocker = visibleEntries.find((entry) => (entry.skill === skill
1176
1523
  && matchesSkillStopContext(entry, canonicalState, sessionId, threadId)));
@@ -1179,10 +1526,47 @@ async function readBlockingSkillForStop(cwd, sessionId, threadId, requiredSkill)
1179
1526
  return {
1180
1527
  skill,
1181
1528
  phase: formatPhase(modeState.current_phase ?? blocker.phase ?? canonicalState.phase, "planning"),
1529
+ latestPlanPath: safeString(modeState.latest_plan_path ?? modeState.latestPlanPath).trim() || undefined,
1530
+ planningComplete: modeState.planning_complete === true || modeState.planningComplete === true,
1531
+ runOutcome: safeString(modeState.run_outcome ?? modeState.outcome).trim() || undefined,
1182
1532
  };
1183
1533
  }
1184
1534
  return null;
1185
1535
  }
1536
+ function buildRalplanContinuationStatus(blocker, activeSubagentCount) {
1537
+ const phase = blocker.phase || "planning";
1538
+ const artifact = blocker.latestPlanPath
1539
+ ? ` Artifact: ${blocker.latestPlanPath}.`
1540
+ : " Artifact: use the latest `.omx/plans/` ralplan artifact if present.";
1541
+ if (activeSubagentCount > 0) {
1542
+ return {
1543
+ reason: `Status: waiting — ralplan is waiting for ${activeSubagentCount} active native subagent thread(s) to finish (phase: ${phase}). Do not stop silently; wait for the subagent result, then continue from the current ralplan artifact and proceed to the next planning/review step.${artifact}`,
1544
+ stopReasonSuffix: "waiting_subagent",
1545
+ systemMessage: `OMX ralplan status: waiting for ${activeSubagentCount} active native subagent thread(s) at phase ${phase}; after they finish, continue from the current ralplan artifact and state the next status explicitly.`,
1546
+ };
1547
+ }
1548
+ const normalizedPhase = phase.toLowerCase();
1549
+ const normalizedOutcome = (blocker.runOutcome ?? "").toLowerCase();
1550
+ const waitingForInput = normalizedOutcome === "blocked_on_user"
1551
+ || normalizedPhase.includes("blocked")
1552
+ || normalizedPhase.includes("input")
1553
+ || normalizedPhase.includes("question");
1554
+ if (waitingForInput) {
1555
+ return {
1556
+ reason: `Status: waiting_for_input — ralplan is paused for required user/operator input (phase: ${phase}). Ask the missing question or present the review choice explicitly before stopping.${artifact}`,
1557
+ stopReasonSuffix: "waiting_input",
1558
+ systemMessage: `OMX ralplan status: waiting for input at phase ${phase}; ask the required question or present the explicit review choice before stopping.`,
1559
+ };
1560
+ }
1561
+ const completeHint = blocker.planningComplete
1562
+ ? " The planning artifacts are present; if consensus is approved, emit the final complete/approved handoff instead of stopping here."
1563
+ : "";
1564
+ return {
1565
+ 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}`,
1566
+ stopReasonSuffix: "continue_artifact",
1567
+ 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.`,
1568
+ };
1569
+ }
1186
1570
  async function readStopAutoNudgePhase(cwd, sessionId, threadId) {
1187
1571
  const normalizedSessionId = sessionId.trim();
1188
1572
  if (normalizedSessionId) {
@@ -1251,13 +1635,20 @@ async function buildDeepInterviewQuestionStopOutput(cwd, sessionId, threadId) {
1251
1635
  };
1252
1636
  }
1253
1637
  function resolveRepeatableStopSessionId(payload, canonicalSessionId) {
1254
- return canonicalSessionId?.trim() || readPayloadSessionId(payload) || "";
1638
+ const inheritedSessionId = safeString(process.env.OMX_SESSION_ID || process.env.CODEX_SESSION_ID).trim();
1639
+ return canonicalSessionId?.trim() || readPayloadSessionId(payload) || inheritedSessionId || "";
1640
+ }
1641
+ function isStateLevelStopSignatureKind(kind) {
1642
+ return kind === "team-worker-stop" || kind === "team-stop";
1255
1643
  }
1256
1644
  function buildRepeatableStopSignature(payload, kind, detail = "", canonicalSessionId) {
1257
1645
  const sessionId = resolveRepeatableStopSessionId(payload, canonicalSessionId) || "no-session";
1258
1646
  const threadId = readPayloadThreadId(payload) || "no-thread";
1259
- const turnId = readPayloadTurnId(payload);
1260
1647
  const normalizedDetail = normalizeAutoNudgeSignatureText(detail) || safeString(detail).trim().toLowerCase();
1648
+ if (isStateLevelStopSignatureKind(kind)) {
1649
+ return [kind, sessionId, threadId, normalizedDetail || "no-detail"].join("|");
1650
+ }
1651
+ const turnId = readPayloadTurnId(payload);
1261
1652
  const transcriptPath = safeString(payload.transcript_path ?? payload.transcriptPath).trim() || "no-transcript";
1262
1653
  const lastAssistantMessage = normalizeAutoNudgeSignatureText(payload.last_assistant_message ?? payload.lastAssistantMessage) || "no-message";
1263
1654
  if (turnId) {
@@ -1402,7 +1793,17 @@ async function buildSkillStopOutput(cwd, sessionId, threadId) {
1402
1793
  if (!blocker)
1403
1794
  return null;
1404
1795
  const subagentSummary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
1405
- if (subagentSummary && subagentSummary.activeSubagentThreadIds.length > 0) {
1796
+ const activeSubagentCount = subagentSummary?.activeSubagentThreadIds.length ?? 0;
1797
+ if (blocker.skill === "ralplan") {
1798
+ const status = buildRalplanContinuationStatus(blocker, activeSubagentCount);
1799
+ return {
1800
+ decision: "block",
1801
+ reason: status.reason,
1802
+ stopReason: `skill_${blocker.skill}_${blocker.phase}_${status.stopReasonSuffix}`,
1803
+ systemMessage: status.systemMessage,
1804
+ };
1805
+ }
1806
+ if (activeSubagentCount > 0) {
1406
1807
  return null;
1407
1808
  }
1408
1809
  return {
@@ -1489,7 +1890,7 @@ async function markTeamTransportFailure(cwd, payload) {
1489
1890
  // Canonical team state already carries the preserved failure for coarse-state-missing sessions.
1490
1891
  }
1491
1892
  }
1492
- async function buildStopHookOutput(payload, cwd, stateDir) {
1893
+ async function buildStopHookOutput(payload, cwd, stateDir, options = {}) {
1493
1894
  if (isStopExempt(payload)) {
1494
1895
  return null;
1495
1896
  }
@@ -1499,11 +1900,13 @@ async function buildStopHookOutput(payload, cwd, stateDir) {
1499
1900
  const execFollowupOutput = await buildExecFollowupStopOutput(cwd, canonicalSessionId);
1500
1901
  if (execFollowupOutput)
1501
1902
  return execFollowupOutput;
1502
- const ralphState = await readActiveRalphState(stateDir, canonicalSessionId, {
1503
- payloadSessionId: sessionId,
1504
- threadId,
1505
- tmuxPaneId: safeString(process.env.TMUX_PANE).trim(),
1506
- });
1903
+ const ralphState = options.skipRalphStopBlock === true
1904
+ ? null
1905
+ : await readActiveRalphState(stateDir, canonicalSessionId, {
1906
+ payloadSessionId: sessionId,
1907
+ threadId,
1908
+ tmuxPaneId: safeString(process.env.TMUX_PANE).trim(),
1909
+ });
1507
1910
  if (!ralphState) {
1508
1911
  const autoresearchState = await readActiveAutoresearchState(cwd, canonicalSessionId);
1509
1912
  if (autoresearchState) {
@@ -1519,9 +1922,22 @@ async function buildStopHookOutput(payload, cwd, stateDir) {
1519
1922
  }, canonicalSessionId, { allowRepeatDuringStopHook: true });
1520
1923
  }
1521
1924
  }
1522
- const teamWorkerOutput = await buildTeamWorkerStopOutput(cwd);
1523
- if (hasTeamWorkerContext() && teamWorkerOutput) {
1524
- return await returnPersistentStopBlock(payload, stateDir, "team-worker-stop", safeString(teamWorkerOutput.stopReason), teamWorkerOutput, canonicalSessionId, { allowRepeatDuringStopHook: false });
1925
+ const teamWorkerDecision = await resolveTeamWorkerStopDecision(cwd);
1926
+ if (teamWorkerDecision.kind === "blocked") {
1927
+ return await returnPersistentStopBlock(payload, stateDir, "team-worker-stop", safeString(teamWorkerDecision.output.stopReason), teamWorkerDecision.output, canonicalSessionId, { allowRepeatDuringStopHook: teamWorkerDecision.allowRepeatDuringStopHook });
1928
+ }
1929
+ if (teamWorkerDecision.kind === "allowed") {
1930
+ try {
1931
+ await maybeNudgeLeaderForAllowedWorkerStop({
1932
+ stateDir: teamWorkerDecision.stateDir,
1933
+ logsDir: join(cwd, ".omx", "logs"),
1934
+ workerContext: teamWorkerDecision.workerContext,
1935
+ });
1936
+ }
1937
+ catch (err) {
1938
+ void err;
1939
+ }
1940
+ return null;
1525
1941
  }
1526
1942
  const autopilotOutput = await buildModeBasedStopOutput("autopilot", cwd, canonicalSessionId);
1527
1943
  if (autopilotOutput) {
@@ -1562,6 +1978,10 @@ async function buildStopHookOutput(payload, cwd, stateDir) {
1562
1978
  }
1563
1979
  }
1564
1980
  const lastAssistantMessage = safeString(payload.last_assistant_message ?? payload.lastAssistantMessage);
1981
+ const goalWorkflowStopOutput = await buildGoalWorkflowReconciliationStopOutput(payload, cwd);
1982
+ if (goalWorkflowStopOutput) {
1983
+ return await returnPersistentStopBlock(payload, stateDir, "goal-workflow-reconciliation-stop", safeString(goalWorkflowStopOutput.stopReason), goalWorkflowStopOutput, canonicalSessionId, { allowRepeatDuringStopHook: true });
1984
+ }
1565
1985
  const autoNudgeConfig = await loadAutoNudgeConfig();
1566
1986
  const autoNudgePhase = await readStopAutoNudgePhase(cwd, canonicalSessionId, threadId);
1567
1987
  if (autoNudgeConfig.enabled
@@ -1574,6 +1994,11 @@ async function buildStopHookOutput(payload, cwd, stateDir) {
1574
1994
  systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.",
1575
1995
  }, canonicalSessionId);
1576
1996
  }
1997
+ const sloppyFallbackDiffFindings = findSloppyFallbackDiffFindings(cwd);
1998
+ const sloppyFallbackDiffOutput = buildSloppyFallbackDiffStopOutput(sloppyFallbackDiffFindings);
1999
+ if (sloppyFallbackDiffOutput) {
2000
+ return await returnPersistentStopBlock(payload, stateDir, "sloppy-fallback-diff-stop", JSON.stringify(sloppyFallbackDiffFindings), sloppyFallbackDiffOutput, canonicalSessionId, { allowRepeatDuringStopHook: true });
2001
+ }
1577
2002
  if (isFinalHandoffDocumentRefreshCandidate(lastAssistantMessage)) {
1578
2003
  const documentRefreshWarning = evaluateFinalHandoffDocumentRefresh(cwd, lastAssistantMessage);
1579
2004
  if (documentRefreshWarning) {
@@ -1601,6 +2026,7 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
1601
2026
  const omxEventName = mapCodexHookEventToOmxEvent(hookEventName);
1602
2027
  let skillState = null;
1603
2028
  let triageAdditionalContext = null;
2029
+ let goalWorkflowAdditionalContext = null;
1604
2030
  const nativeSessionId = safeString(payload.session_id ?? payload.sessionId).trim();
1605
2031
  const threadId = safeString(payload.thread_id ?? payload.threadId).trim();
1606
2032
  const turnId = safeString(payload.turn_id ?? payload.turnId).trim();
@@ -1636,7 +2062,8 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
1636
2062
  canonicalSessionId = safeString(currentSessionState?.session_id).trim();
1637
2063
  }
1638
2064
  if (hookEventName === "Stop") {
1639
- const stopCanonicalSessionId = await resolveInternalSessionIdForPayload(cwd, readPayloadSessionId(payload));
2065
+ const inheritedSessionId = safeString(process.env.OMX_SESSION_ID || process.env.CODEX_SESSION_ID).trim();
2066
+ const stopCanonicalSessionId = await resolveInternalSessionIdForPayload(cwd, readPayloadSessionId(payload) || inheritedSessionId);
1640
2067
  if (stopCanonicalSessionId) {
1641
2068
  canonicalSessionId = stopCanonicalSessionId;
1642
2069
  }
@@ -1648,9 +2075,20 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
1648
2075
  const eventSessionId = canonicalSessionId || nativeSessionId || undefined;
1649
2076
  const sessionIdForState = canonicalSessionId || nativeSessionId;
1650
2077
  let outputJson = null;
2078
+ const isSubagentPromptSubmit = hookEventName === "UserPromptSubmit"
2079
+ ? await isNativeSubagentHook(cwd, canonicalSessionId, nativeSessionId, threadId)
2080
+ : false;
2081
+ const isSubagentStop = hookEventName === "Stop"
2082
+ ? (await Promise.all([...new Set([
2083
+ canonicalSessionId,
2084
+ safeString(currentSessionState?.session_id).trim(),
2085
+ ].filter(Boolean))]
2086
+ .map((candidateSessionId) => isNativeSubagentHook(cwd, candidateSessionId, nativeSessionId, threadId)))).some(Boolean)
2087
+ : false;
1651
2088
  if (hookEventName === "UserPromptSubmit") {
1652
2089
  const prompt = readPromptText(payload);
1653
- if (prompt) {
2090
+ goalWorkflowAdditionalContext = await buildGoalWorkflowReconciliationPromptWarning(cwd, prompt).catch(() => null);
2091
+ if (prompt && !isSubagentPromptSubmit) {
1654
2092
  skillState = buildNativeOutsideTmuxTeamPromptBlockState(prompt, cwd, payload, sessionIdForState, threadId || undefined, turnId || undefined) ?? await recordSkillActivation({
1655
2093
  stateDir,
1656
2094
  text: prompt,
@@ -1660,7 +2098,7 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
1660
2098
  });
1661
2099
  }
1662
2100
  // --- Triage classifier (advisory-only, non-keyword prompts) ---
1663
- if (prompt && skillState === null) {
2101
+ if (prompt && skillState === null && !isSubagentPromptSubmit) {
1664
2102
  try {
1665
2103
  if (readTriageConfig().enabled) {
1666
2104
  const normalized = prompt.trim().toLowerCase();
@@ -1752,7 +2190,11 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
1752
2190
  turn_id: turnId || undefined,
1753
2191
  mode: safeString(payload.mode).trim() || undefined,
1754
2192
  });
1755
- await dispatchHookEvent(event, { cwd });
2193
+ await dispatchHookEventRuntime({
2194
+ event,
2195
+ cwd,
2196
+ allowTeamWorkerSideEffects: false,
2197
+ });
1756
2198
  }
1757
2199
  if ((hookEventName === "SessionStart" && !skipCanonicalSessionStartContext) || hookEventName === "UserPromptSubmit") {
1758
2200
  const additionalContext = hookEventName === "SessionStart"
@@ -1762,7 +2204,9 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
1762
2204
  canonicalSessionId,
1763
2205
  nativeSessionId: resolvedNativeSessionId || nativeSessionId,
1764
2206
  })
1765
- : (buildAdditionalContextMessage(readPromptText(payload), skillState, cwd, payload) ?? triageAdditionalContext);
2207
+ : isSubagentPromptSubmit
2208
+ ? null
2209
+ : (buildAdditionalContextMessage(readPromptText(payload), skillState, cwd, payload) ?? goalWorkflowAdditionalContext ?? triageAdditionalContext);
1766
2210
  if (additionalContext) {
1767
2211
  outputJson = {
1768
2212
  hookSpecificOutput: {
@@ -1783,7 +2227,9 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
1783
2227
  await handleTeamWorkerPostToolUseSuccess(payload, cwd);
1784
2228
  }
1785
2229
  else if (hookEventName === "Stop") {
1786
- outputJson = await buildStopHookOutput(payload, cwd, stateDir);
2230
+ outputJson = await buildStopHookOutput(payload, cwd, stateDir, {
2231
+ skipRalphStopBlock: isSubagentStop,
2232
+ });
1787
2233
  }
1788
2234
  return {
1789
2235
  hookEventName,