oh-my-codex 0.15.2 → 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 (524) 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/agents/__tests__/native-config.test.js +33 -0
  7. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  8. package/dist/autoresearch/goal.d.ts +90 -0
  9. package/dist/autoresearch/goal.d.ts.map +1 -0
  10. package/dist/autoresearch/goal.js +237 -0
  11. package/dist/autoresearch/goal.js.map +1 -0
  12. package/dist/autoresearch/skill-validation.d.ts +1 -0
  13. package/dist/autoresearch/skill-validation.d.ts.map +1 -1
  14. package/dist/autoresearch/skill-validation.js +10 -3
  15. package/dist/autoresearch/skill-validation.js.map +1 -1
  16. package/dist/catalog/__tests__/generator.test.js +9 -4
  17. package/dist/catalog/__tests__/generator.test.js.map +1 -1
  18. package/dist/catalog/__tests__/plugin-bundle-ssot.test.js +29 -2
  19. package/dist/catalog/__tests__/plugin-bundle-ssot.test.js.map +1 -1
  20. package/dist/catalog/__tests__/schema.test.js +14 -3
  21. package/dist/catalog/__tests__/schema.test.js.map +1 -1
  22. package/dist/catalog/schema.js +1 -1
  23. package/dist/catalog/schema.js.map +1 -1
  24. package/dist/cli/__tests__/autoresearch-goal.test.d.ts +2 -0
  25. package/dist/cli/__tests__/autoresearch-goal.test.d.ts.map +1 -0
  26. package/dist/cli/__tests__/autoresearch-goal.test.js +194 -0
  27. package/dist/cli/__tests__/autoresearch-goal.test.js.map +1 -0
  28. package/dist/cli/__tests__/cleanup.test.js +82 -1
  29. package/dist/cli/__tests__/cleanup.test.js.map +1 -1
  30. package/dist/cli/__tests__/codex-plugin-layout.test.js +7 -4
  31. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  32. package/dist/cli/__tests__/doctor-context-window-warning.test.d.ts +2 -0
  33. package/dist/cli/__tests__/doctor-context-window-warning.test.d.ts.map +1 -0
  34. package/dist/cli/__tests__/doctor-context-window-warning.test.js +122 -0
  35. package/dist/cli/__tests__/doctor-context-window-warning.test.js.map +1 -0
  36. package/dist/cli/__tests__/doctor-warning-copy.test.js +25 -2
  37. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  38. package/dist/cli/__tests__/exec.test.js +1 -0
  39. package/dist/cli/__tests__/exec.test.js.map +1 -1
  40. package/dist/cli/__tests__/explore.test.js +48 -18
  41. package/dist/cli/__tests__/explore.test.js.map +1 -1
  42. package/dist/cli/__tests__/index.test.js +222 -10
  43. package/dist/cli/__tests__/index.test.js.map +1 -1
  44. package/dist/cli/__tests__/launch-fallback.test.js +58 -0
  45. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  46. package/dist/cli/__tests__/mcp-serve.test.js +27 -1
  47. package/dist/cli/__tests__/mcp-serve.test.js.map +1 -1
  48. package/dist/cli/__tests__/native-assets.test.js +26 -1
  49. package/dist/cli/__tests__/native-assets.test.js.map +1 -1
  50. package/dist/cli/__tests__/package-bin-contract.test.js +2 -2
  51. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  52. package/dist/cli/__tests__/performance-goal.test.d.ts +2 -0
  53. package/dist/cli/__tests__/performance-goal.test.d.ts.map +1 -0
  54. package/dist/cli/__tests__/performance-goal.test.js +144 -0
  55. package/dist/cli/__tests__/performance-goal.test.js.map +1 -0
  56. package/dist/cli/__tests__/question.test.js +8 -0
  57. package/dist/cli/__tests__/question.test.js.map +1 -1
  58. package/dist/cli/__tests__/ralph-goal-mode-contract.test.d.ts +2 -0
  59. package/dist/cli/__tests__/ralph-goal-mode-contract.test.d.ts.map +1 -0
  60. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +31 -0
  61. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -0
  62. package/dist/cli/__tests__/ralph-prd-deep-interview.test.js +5 -4
  63. package/dist/cli/__tests__/ralph-prd-deep-interview.test.js.map +1 -1
  64. package/dist/cli/__tests__/ralph-prd-smoke.test.js +7 -0
  65. package/dist/cli/__tests__/ralph-prd-smoke.test.js.map +1 -1
  66. package/dist/cli/__tests__/ralph.test.js +59 -1
  67. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  68. package/dist/cli/__tests__/setup-install-mode.test.js +57 -21
  69. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  70. package/dist/cli/__tests__/setup-refresh.test.js +27 -8
  71. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  72. package/dist/cli/__tests__/setup-scope.test.js +20 -10
  73. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  74. package/dist/cli/__tests__/setup-skill-validation.test.js +11 -11
  75. package/dist/cli/__tests__/setup-skill-validation.test.js.map +1 -1
  76. package/dist/cli/__tests__/setup-skills-overwrite.test.js +12 -12
  77. package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
  78. package/dist/cli/__tests__/team.test.js +242 -10
  79. package/dist/cli/__tests__/team.test.js.map +1 -1
  80. package/dist/cli/__tests__/ultragoal.test.d.ts +2 -0
  81. package/dist/cli/__tests__/ultragoal.test.d.ts.map +1 -0
  82. package/dist/cli/__tests__/ultragoal.test.js +106 -0
  83. package/dist/cli/__tests__/ultragoal.test.js.map +1 -0
  84. package/dist/cli/__tests__/uninstall.test.js +11 -0
  85. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  86. package/dist/cli/autoresearch-goal.d.ts +3 -0
  87. package/dist/cli/autoresearch-goal.d.ts.map +1 -0
  88. package/dist/cli/autoresearch-goal.js +175 -0
  89. package/dist/cli/autoresearch-goal.js.map +1 -0
  90. package/dist/cli/cleanup.d.ts +3 -1
  91. package/dist/cli/cleanup.d.ts.map +1 -1
  92. package/dist/cli/cleanup.js +42 -2
  93. package/dist/cli/cleanup.js.map +1 -1
  94. package/dist/cli/doctor.d.ts.map +1 -1
  95. package/dist/cli/doctor.js +95 -3
  96. package/dist/cli/doctor.js.map +1 -1
  97. package/dist/cli/explore.d.ts.map +1 -1
  98. package/dist/cli/explore.js +10 -2
  99. package/dist/cli/explore.js.map +1 -1
  100. package/dist/cli/index.d.ts +21 -2
  101. package/dist/cli/index.d.ts.map +1 -1
  102. package/dist/cli/index.js +268 -30
  103. package/dist/cli/index.js.map +1 -1
  104. package/dist/cli/mcp-serve.d.ts +1 -0
  105. package/dist/cli/mcp-serve.d.ts.map +1 -1
  106. package/dist/cli/mcp-serve.js +8 -0
  107. package/dist/cli/mcp-serve.js.map +1 -1
  108. package/dist/cli/native-assets.js +1 -1
  109. package/dist/cli/native-assets.js.map +1 -1
  110. package/dist/cli/performance-goal.d.ts +3 -0
  111. package/dist/cli/performance-goal.d.ts.map +1 -0
  112. package/dist/cli/performance-goal.js +186 -0
  113. package/dist/cli/performance-goal.js.map +1 -0
  114. package/dist/cli/ralph.d.ts +2 -0
  115. package/dist/cli/ralph.d.ts.map +1 -1
  116. package/dist/cli/ralph.js +25 -1
  117. package/dist/cli/ralph.js.map +1 -1
  118. package/dist/cli/setup.d.ts.map +1 -1
  119. package/dist/cli/setup.js +13 -6
  120. package/dist/cli/setup.js.map +1 -1
  121. package/dist/cli/team.d.ts +6 -0
  122. package/dist/cli/team.d.ts.map +1 -1
  123. package/dist/cli/team.js +113 -33
  124. package/dist/cli/team.js.map +1 -1
  125. package/dist/cli/tmux-hook.d.ts.map +1 -1
  126. package/dist/cli/tmux-hook.js +2 -1
  127. package/dist/cli/tmux-hook.js.map +1 -1
  128. package/dist/cli/ultragoal.d.ts +3 -0
  129. package/dist/cli/ultragoal.d.ts.map +1 -0
  130. package/dist/cli/ultragoal.js +191 -0
  131. package/dist/cli/ultragoal.js.map +1 -0
  132. package/dist/cli/uninstall.d.ts.map +1 -1
  133. package/dist/cli/uninstall.js +4 -2
  134. package/dist/cli/uninstall.js.map +1 -1
  135. package/dist/config/__tests__/generator-idempotent.test.js +39 -6
  136. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  137. package/dist/config/__tests__/generator-notify.test.js +5 -0
  138. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  139. package/dist/config/commit-lore-guard.d.ts +3 -0
  140. package/dist/config/commit-lore-guard.d.ts.map +1 -0
  141. package/dist/config/commit-lore-guard.js +9 -0
  142. package/dist/config/commit-lore-guard.js.map +1 -0
  143. package/dist/config/generator.d.ts +14 -4
  144. package/dist/config/generator.d.ts.map +1 -1
  145. package/dist/config/generator.js +166 -66
  146. package/dist/config/generator.js.map +1 -1
  147. package/dist/config/omx-first-party-mcp.d.ts +1 -0
  148. package/dist/config/omx-first-party-mcp.d.ts.map +1 -1
  149. package/dist/config/omx-first-party-mcp.js +4 -1
  150. package/dist/config/omx-first-party-mcp.js.map +1 -1
  151. package/dist/goal-workflows/__tests__/artifacts.test.d.ts +2 -0
  152. package/dist/goal-workflows/__tests__/artifacts.test.d.ts.map +1 -0
  153. package/dist/goal-workflows/__tests__/artifacts.test.js +96 -0
  154. package/dist/goal-workflows/__tests__/artifacts.test.js.map +1 -0
  155. package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.d.ts +2 -0
  156. package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.d.ts.map +1 -0
  157. package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js +54 -0
  158. package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js.map +1 -0
  159. package/dist/goal-workflows/artifacts.d.ts +62 -0
  160. package/dist/goal-workflows/artifacts.d.ts.map +1 -0
  161. package/dist/goal-workflows/artifacts.js +132 -0
  162. package/dist/goal-workflows/artifacts.js.map +1 -0
  163. package/dist/goal-workflows/codex-goal-snapshot.d.ts +28 -0
  164. package/dist/goal-workflows/codex-goal-snapshot.d.ts.map +1 -0
  165. package/dist/goal-workflows/codex-goal-snapshot.js +110 -0
  166. package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -0
  167. package/dist/goal-workflows/handoff.d.ts +10 -0
  168. package/dist/goal-workflows/handoff.d.ts.map +1 -0
  169. package/dist/goal-workflows/handoff.js +31 -0
  170. package/dist/goal-workflows/handoff.js.map +1 -0
  171. package/dist/goal-workflows/validation.d.ts +13 -0
  172. package/dist/goal-workflows/validation.d.ts.map +1 -0
  173. package/dist/goal-workflows/validation.js +36 -0
  174. package/dist/goal-workflows/validation.js.map +1 -0
  175. package/dist/hooks/__tests__/agents-overlay.test.js +59 -0
  176. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  177. package/dist/hooks/__tests__/anti-slop-workflow.test.js +109 -18
  178. package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
  179. package/dist/hooks/__tests__/keyword-detector.test.js +45 -32
  180. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  181. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +3 -3
  182. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  183. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +2 -1
  184. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  185. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +17 -24
  186. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  187. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js +3 -3
  188. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js.map +1 -1
  189. package/dist/hooks/__tests__/task-size-detector.test.js +1 -1
  190. package/dist/hooks/__tests__/task-size-detector.test.js.map +1 -1
  191. package/dist/hooks/__tests__/visual-ralph-skill.test.js +3 -3
  192. package/dist/hooks/__tests__/visual-ralph-skill.test.js.map +1 -1
  193. package/dist/hooks/__tests__/visual-verdict-loop.test.js +7 -11
  194. package/dist/hooks/__tests__/visual-verdict-loop.test.js.map +1 -1
  195. package/dist/hooks/agents-overlay.d.ts.map +1 -1
  196. package/dist/hooks/agents-overlay.js +23 -2
  197. package/dist/hooks/agents-overlay.js.map +1 -1
  198. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  199. package/dist/hooks/keyword-detector.js +12 -13
  200. package/dist/hooks/keyword-detector.js.map +1 -1
  201. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  202. package/dist/hooks/keyword-registry.js +2 -10
  203. package/dist/hooks/keyword-registry.js.map +1 -1
  204. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  205. package/dist/hooks/prompt-guidance-contract.js +0 -4
  206. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  207. package/dist/hooks/session.js +2 -2
  208. package/dist/hooks/session.js.map +1 -1
  209. package/dist/hooks/task-size-detector.d.ts.map +1 -1
  210. package/dist/hooks/task-size-detector.js +1 -0
  211. package/dist/hooks/task-size-detector.js.map +1 -1
  212. package/dist/hud/__tests__/index.test.js +30 -14
  213. package/dist/hud/__tests__/index.test.js.map +1 -1
  214. package/dist/hud/__tests__/reconcile.test.js +29 -7
  215. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  216. package/dist/hud/reconcile.d.ts +2 -1
  217. package/dist/hud/reconcile.d.ts.map +1 -1
  218. package/dist/hud/reconcile.js +12 -0
  219. package/dist/hud/reconcile.js.map +1 -1
  220. package/dist/mcp/__tests__/bootstrap.test.js +15 -2
  221. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  222. package/dist/mcp/__tests__/state-paths.test.js +54 -0
  223. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  224. package/dist/mcp/__tests__/state-server.test.js +36 -0
  225. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  226. package/dist/mcp/bootstrap.d.ts +1 -1
  227. package/dist/mcp/bootstrap.d.ts.map +1 -1
  228. package/dist/mcp/bootstrap.js +9 -7
  229. package/dist/mcp/bootstrap.js.map +1 -1
  230. package/dist/mcp/state-paths.d.ts +17 -0
  231. package/dist/mcp/state-paths.d.ts.map +1 -1
  232. package/dist/mcp/state-paths.js +36 -2
  233. package/dist/mcp/state-paths.js.map +1 -1
  234. package/dist/modes/__tests__/base-session-scope.test.js +26 -0
  235. package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
  236. package/dist/modes/base.d.ts +1 -0
  237. package/dist/modes/base.d.ts.map +1 -1
  238. package/dist/modes/base.js +35 -5
  239. package/dist/modes/base.js.map +1 -1
  240. package/dist/notifications/__tests__/http-client.test.d.ts +2 -0
  241. package/dist/notifications/__tests__/http-client.test.d.ts.map +1 -0
  242. package/dist/notifications/__tests__/http-client.test.js +90 -0
  243. package/dist/notifications/__tests__/http-client.test.js.map +1 -0
  244. package/dist/notifications/__tests__/notifier.test.js +22 -60
  245. package/dist/notifications/__tests__/notifier.test.js.map +1 -1
  246. package/dist/notifications/dispatcher.d.ts.map +1 -1
  247. package/dist/notifications/dispatcher.js +35 -60
  248. package/dist/notifications/dispatcher.js.map +1 -1
  249. package/dist/notifications/http-client.d.ts +22 -0
  250. package/dist/notifications/http-client.d.ts.map +1 -0
  251. package/dist/notifications/http-client.js +298 -0
  252. package/dist/notifications/http-client.js.map +1 -0
  253. package/dist/notifications/notifier.d.ts +3 -2
  254. package/dist/notifications/notifier.d.ts.map +1 -1
  255. package/dist/notifications/notifier.js +17 -22
  256. package/dist/notifications/notifier.js.map +1 -1
  257. package/dist/openclaw/__tests__/dispatcher.test.js +63 -2
  258. package/dist/openclaw/__tests__/dispatcher.test.js.map +1 -1
  259. package/dist/openclaw/dispatcher.d.ts.map +1 -1
  260. package/dist/openclaw/dispatcher.js +3 -2
  261. package/dist/openclaw/dispatcher.js.map +1 -1
  262. package/dist/performance-goal/artifacts.d.ts +76 -0
  263. package/dist/performance-goal/artifacts.d.ts.map +1 -0
  264. package/dist/performance-goal/artifacts.js +221 -0
  265. package/dist/performance-goal/artifacts.js.map +1 -0
  266. package/dist/pipeline/__tests__/stages.test.js +423 -14
  267. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  268. package/dist/pipeline/stages/team-exec.d.ts +8 -4
  269. package/dist/pipeline/stages/team-exec.d.ts.map +1 -1
  270. package/dist/pipeline/stages/team-exec.js +181 -13
  271. package/dist/pipeline/stages/team-exec.js.map +1 -1
  272. package/dist/planning/__tests__/artifacts.test.js +261 -1
  273. package/dist/planning/__tests__/artifacts.test.js.map +1 -1
  274. package/dist/planning/artifact-names.d.ts +13 -0
  275. package/dist/planning/artifact-names.d.ts.map +1 -0
  276. package/dist/planning/artifact-names.js +108 -0
  277. package/dist/planning/artifact-names.js.map +1 -0
  278. package/dist/planning/artifacts.d.ts +23 -1
  279. package/dist/planning/artifacts.d.ts.map +1 -1
  280. package/dist/planning/artifacts.js +171 -59
  281. package/dist/planning/artifacts.js.map +1 -1
  282. package/dist/ralph/__tests__/persistence.test.js +21 -1
  283. package/dist/ralph/__tests__/persistence.test.js.map +1 -1
  284. package/dist/ralph/persistence.d.ts.map +1 -1
  285. package/dist/ralph/persistence.js +6 -4
  286. package/dist/ralph/persistence.js.map +1 -1
  287. package/dist/ralplan/__tests__/runtime.test.js +2 -0
  288. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  289. package/dist/ralplan/runtime.d.ts.map +1 -1
  290. package/dist/ralplan/runtime.js +6 -0
  291. package/dist/ralplan/runtime.js.map +1 -1
  292. package/dist/scripts/__tests__/codex-native-hook.test.js +1749 -88
  293. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  294. package/dist/scripts/__tests__/hook-derived-watcher.test.js +33 -1
  295. package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -1
  296. package/dist/scripts/__tests__/run-test-files.test.js +36 -0
  297. package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
  298. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  299. package/dist/scripts/codex-native-hook.js +570 -45
  300. package/dist/scripts/codex-native-hook.js.map +1 -1
  301. package/dist/scripts/codex-native-pre-post.d.ts +7 -0
  302. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  303. package/dist/scripts/codex-native-pre-post.js +341 -15
  304. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  305. package/dist/scripts/hook-derived-watcher.js +2 -1
  306. package/dist/scripts/hook-derived-watcher.js.map +1 -1
  307. package/dist/scripts/notify-fallback-watcher.js +2 -1
  308. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  309. package/dist/scripts/notify-hook/orchestration-intent.d.ts +1 -2
  310. package/dist/scripts/notify-hook/orchestration-intent.d.ts.map +1 -1
  311. package/dist/scripts/notify-hook/orchestration-intent.js +2 -3
  312. package/dist/scripts/notify-hook/orchestration-intent.js.map +1 -1
  313. package/dist/scripts/notify-hook/team-leader-nudge.d.ts +0 -2
  314. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  315. package/dist/scripts/notify-hook/team-leader-nudge.js +8 -60
  316. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  317. package/dist/scripts/notify-hook/team-worker-posttooluse.js +1 -1
  318. package/dist/scripts/notify-hook/team-worker-posttooluse.js.map +1 -1
  319. package/dist/scripts/notify-hook/team-worker-stop.d.ts +15 -0
  320. package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -0
  321. package/dist/scripts/notify-hook/team-worker-stop.js +224 -0
  322. package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -0
  323. package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
  324. package/dist/scripts/notify-hook/team-worker.js +26 -18
  325. package/dist/scripts/notify-hook/team-worker.js.map +1 -1
  326. package/dist/scripts/notify-hook.js +1 -1
  327. package/dist/scripts/notify-hook.js.map +1 -1
  328. package/dist/scripts/run-test-files.js +17 -1
  329. package/dist/scripts/run-test-files.js.map +1 -1
  330. package/dist/scripts/sync-plugin-mirror.d.ts +1 -0
  331. package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
  332. package/dist/scripts/sync-plugin-mirror.js +10 -4
  333. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  334. package/dist/state/__tests__/operations.test.js +26 -0
  335. package/dist/state/__tests__/operations.test.js.map +1 -1
  336. package/dist/state/__tests__/skill-active.test.js +76 -0
  337. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  338. package/dist/state/operations.d.ts +3 -1
  339. package/dist/state/operations.d.ts.map +1 -1
  340. package/dist/state/operations.js +8 -4
  341. package/dist/state/operations.js.map +1 -1
  342. package/dist/state/skill-active.d.ts +1 -0
  343. package/dist/state/skill-active.d.ts.map +1 -1
  344. package/dist/state/skill-active.js +54 -13
  345. package/dist/state/skill-active.js.map +1 -1
  346. package/dist/team/__tests__/api-interop.test.js +279 -0
  347. package/dist/team/__tests__/api-interop.test.js.map +1 -1
  348. package/dist/team/__tests__/approved-execution.test.d.ts +2 -0
  349. package/dist/team/__tests__/approved-execution.test.d.ts.map +1 -0
  350. package/dist/team/__tests__/approved-execution.test.js +124 -0
  351. package/dist/team/__tests__/approved-execution.test.js.map +1 -0
  352. package/dist/team/__tests__/delivery-e2e-smoke.test.js +2 -4
  353. package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
  354. package/dist/team/__tests__/delivery-log.test.d.ts +2 -0
  355. package/dist/team/__tests__/delivery-log.test.d.ts.map +1 -0
  356. package/dist/team/__tests__/delivery-log.test.js +44 -0
  357. package/dist/team/__tests__/delivery-log.test.js.map +1 -0
  358. package/dist/team/__tests__/model-contract.test.js +40 -9
  359. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  360. package/dist/team/__tests__/repo-aware-decomposition.test.js +41 -0
  361. package/dist/team/__tests__/repo-aware-decomposition.test.js.map +1 -1
  362. package/dist/team/__tests__/role-router.test.js +4 -4
  363. package/dist/team/__tests__/role-router.test.js.map +1 -1
  364. package/dist/team/__tests__/runtime-boxed-state.test.d.ts +2 -0
  365. package/dist/team/__tests__/runtime-boxed-state.test.d.ts.map +1 -0
  366. package/dist/team/__tests__/runtime-boxed-state.test.js +39 -0
  367. package/dist/team/__tests__/runtime-boxed-state.test.js.map +1 -0
  368. package/dist/team/__tests__/runtime-cli.test.js +24 -0
  369. package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
  370. package/dist/team/__tests__/runtime.test.js +563 -72
  371. package/dist/team/__tests__/runtime.test.js.map +1 -1
  372. package/dist/team/__tests__/state-root.test.js +13 -0
  373. package/dist/team/__tests__/state-root.test.js.map +1 -1
  374. package/dist/team/__tests__/state.test.js +13 -0
  375. package/dist/team/__tests__/state.test.js.map +1 -1
  376. package/dist/team/__tests__/team-identity.test.d.ts +2 -0
  377. package/dist/team/__tests__/team-identity.test.d.ts.map +1 -0
  378. package/dist/team/__tests__/team-identity.test.js +166 -0
  379. package/dist/team/__tests__/team-identity.test.js.map +1 -0
  380. package/dist/team/__tests__/tmux-session.test.js +58 -1
  381. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  382. package/dist/team/__tests__/worker-bootstrap.test.js +62 -0
  383. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  384. package/dist/team/api-interop.d.ts +1 -0
  385. package/dist/team/api-interop.d.ts.map +1 -1
  386. package/dist/team/api-interop.js +163 -132
  387. package/dist/team/api-interop.js.map +1 -1
  388. package/dist/team/approved-execution.d.ts +37 -0
  389. package/dist/team/approved-execution.d.ts.map +1 -0
  390. package/dist/team/approved-execution.js +136 -0
  391. package/dist/team/approved-execution.js.map +1 -0
  392. package/dist/team/delivery-log.d.ts +1 -1
  393. package/dist/team/delivery-log.d.ts.map +1 -1
  394. package/dist/team/delivery-log.js +2 -1
  395. package/dist/team/delivery-log.js.map +1 -1
  396. package/dist/team/followup-planner.js +2 -2
  397. package/dist/team/followup-planner.js.map +1 -1
  398. package/dist/team/goal-workflow.d.ts +20 -0
  399. package/dist/team/goal-workflow.d.ts.map +1 -0
  400. package/dist/team/goal-workflow.js +57 -0
  401. package/dist/team/goal-workflow.js.map +1 -0
  402. package/dist/team/orchestrator.js +2 -2
  403. package/dist/team/orchestrator.js.map +1 -1
  404. package/dist/team/repo-aware-decomposition.d.ts +3 -0
  405. package/dist/team/repo-aware-decomposition.d.ts.map +1 -1
  406. package/dist/team/repo-aware-decomposition.js +2 -0
  407. package/dist/team/repo-aware-decomposition.js.map +1 -1
  408. package/dist/team/role-router.js +5 -5
  409. package/dist/team/role-router.js.map +1 -1
  410. package/dist/team/runtime-cli.d.ts +32 -2
  411. package/dist/team/runtime-cli.d.ts.map +1 -1
  412. package/dist/team/runtime-cli.js +78 -26
  413. package/dist/team/runtime-cli.js.map +1 -1
  414. package/dist/team/runtime.d.ts +7 -1
  415. package/dist/team/runtime.d.ts.map +1 -1
  416. package/dist/team/runtime.js +383 -40
  417. package/dist/team/runtime.js.map +1 -1
  418. package/dist/team/scaling.d.ts.map +1 -1
  419. package/dist/team/scaling.js +2 -0
  420. package/dist/team/scaling.js.map +1 -1
  421. package/dist/team/state.d.ts +9 -0
  422. package/dist/team/state.d.ts.map +1 -1
  423. package/dist/team/state.js +21 -0
  424. package/dist/team/state.js.map +1 -1
  425. package/dist/team/team-identity.d.ts +26 -0
  426. package/dist/team/team-identity.d.ts.map +1 -0
  427. package/dist/team/team-identity.js +169 -0
  428. package/dist/team/team-identity.js.map +1 -0
  429. package/dist/team/tmux-session.d.ts +18 -0
  430. package/dist/team/tmux-session.d.ts.map +1 -1
  431. package/dist/team/tmux-session.js +65 -3
  432. package/dist/team/tmux-session.js.map +1 -1
  433. package/dist/team/worker-bootstrap.d.ts +4 -0
  434. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  435. package/dist/team/worker-bootstrap.js +28 -2
  436. package/dist/team/worker-bootstrap.js.map +1 -1
  437. package/dist/ultragoal/__tests__/artifacts.test.d.ts +2 -0
  438. package/dist/ultragoal/__tests__/artifacts.test.d.ts.map +1 -0
  439. package/dist/ultragoal/__tests__/artifacts.test.js +93 -0
  440. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -0
  441. package/dist/ultragoal/artifacts.d.ts +89 -0
  442. package/dist/ultragoal/artifacts.d.ts.map +1 -0
  443. package/dist/ultragoal/artifacts.js +233 -0
  444. package/dist/ultragoal/artifacts.js.map +1 -0
  445. package/dist/utils/__tests__/agents-model-table.test.js +3 -1
  446. package/dist/utils/__tests__/agents-model-table.test.js.map +1 -1
  447. package/dist/utils/__tests__/paths.test.js +31 -1
  448. package/dist/utils/__tests__/paths.test.js.map +1 -1
  449. package/dist/utils/agents-model-table.d.ts.map +1 -1
  450. package/dist/utils/agents-model-table.js +12 -1
  451. package/dist/utils/agents-model-table.js.map +1 -1
  452. package/dist/utils/paths.d.ts +2 -0
  453. package/dist/utils/paths.d.ts.map +1 -1
  454. package/dist/utils/paths.js +23 -7
  455. package/dist/utils/paths.js.map +1 -1
  456. package/dist/verification/__tests__/ci-rust-gates.test.js +30 -19
  457. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  458. package/package.json +5 -5
  459. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  460. package/plugins/oh-my-codex/skills/ai-slop-cleaner/SKILL.md +30 -5
  461. package/plugins/oh-my-codex/skills/ask/SKILL.md +58 -0
  462. package/plugins/oh-my-codex/skills/autoresearch-goal/SKILL.md +36 -0
  463. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +2 -2
  464. package/plugins/oh-my-codex/skills/performance-goal/SKILL.md +65 -0
  465. package/plugins/oh-my-codex/skills/plan/SKILL.md +1 -1
  466. package/plugins/oh-my-codex/skills/ralph/SKILL.md +22 -3
  467. package/plugins/oh-my-codex/skills/team/SKILL.md +6 -2
  468. package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +49 -0
  469. package/plugins/oh-my-codex/skills/visual-ralph/SKILL.md +9 -9
  470. package/prompts/api-reviewer.md +1 -1
  471. package/prompts/code-reviewer.md +2 -0
  472. package/prompts/performance-reviewer.md +1 -1
  473. package/prompts/quality-reviewer.md +1 -1
  474. package/prompts/quality-strategist.md +2 -2
  475. package/prompts/style-reviewer.md +1 -1
  476. package/prompts/test-engineer.md +1 -1
  477. package/skills/ai-slop-cleaner/SKILL.md +30 -5
  478. package/skills/ask/SKILL.md +58 -0
  479. package/skills/ask-claude/SKILL.md +3 -54
  480. package/skills/ask-gemini/SKILL.md +3 -54
  481. package/skills/autoresearch-goal/SKILL.md +36 -0
  482. package/skills/build-fix/SKILL.md +4 -139
  483. package/skills/deepsearch/SKILL.md +4 -32
  484. package/skills/ecomode/SKILL.md +4 -108
  485. package/skills/help/SKILL.md +4 -196
  486. package/skills/note/SKILL.md +4 -56
  487. package/skills/omx-setup/SKILL.md +2 -2
  488. package/skills/performance-goal/SKILL.md +65 -0
  489. package/skills/plan/SKILL.md +1 -1
  490. package/skills/ralph/SKILL.md +22 -3
  491. package/skills/ralph-init/SKILL.md +4 -40
  492. package/skills/review/SKILL.md +4 -32
  493. package/skills/security-review/SKILL.md +4 -294
  494. package/skills/swarm/SKILL.md +4 -19
  495. package/skills/tdd/SKILL.md +4 -100
  496. package/skills/team/SKILL.md +6 -2
  497. package/skills/trace/SKILL.md +4 -27
  498. package/skills/ultragoal/SKILL.md +49 -0
  499. package/skills/visual-ralph/SKILL.md +9 -9
  500. package/skills/visual-verdict/SKILL.md +4 -70
  501. package/skills/web-clone/SKILL.md +4 -18
  502. package/src/scripts/__tests__/codex-native-hook.test.ts +2923 -1030
  503. package/src/scripts/__tests__/hook-derived-watcher.test.ts +45 -1
  504. package/src/scripts/__tests__/run-test-files.test.ts +46 -0
  505. package/src/scripts/codex-native-hook.ts +696 -46
  506. package/src/scripts/codex-native-pre-post.ts +369 -16
  507. package/src/scripts/hook-derived-watcher.ts +2 -1
  508. package/src/scripts/notify-fallback-watcher.ts +2 -1
  509. package/src/scripts/notify-hook/orchestration-intent.ts +1 -3
  510. package/src/scripts/notify-hook/team-leader-nudge.ts +7 -63
  511. package/src/scripts/notify-hook/team-worker-posttooluse.ts +1 -1
  512. package/src/scripts/notify-hook/team-worker-stop.ts +246 -0
  513. package/src/scripts/notify-hook/team-worker.ts +23 -14
  514. package/src/scripts/notify-hook.ts +1 -1
  515. package/src/scripts/run-test-files.ts +20 -1
  516. package/src/scripts/sync-plugin-mirror.ts +13 -4
  517. package/templates/catalog-manifest.json +45 -27
  518. package/plugins/oh-my-codex/skills/ask-claude/SKILL.md +0 -61
  519. package/plugins/oh-my-codex/skills/ask-gemini/SKILL.md +0 -61
  520. package/plugins/oh-my-codex/skills/help/SKILL.md +0 -202
  521. package/plugins/oh-my-codex/skills/note/SKILL.md +0 -62
  522. package/plugins/oh-my-codex/skills/security-review/SKILL.md +0 -300
  523. package/plugins/oh-my-codex/skills/trace/SKILL.md +0 -33
  524. package/plugins/oh-my-codex/skills/visual-verdict/SKILL.md +0 -76
@@ -1,10 +1,11 @@
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";
6
+ import { readModeState, readModeStateForActiveDecision, readModeStateForSession, updateModeState } from "../modes/base.js";
7
7
  import {
8
+ extractSessionIdFromInitializedStatePath,
8
9
  listActiveSkills,
9
10
  readVisibleSkillActiveState,
10
11
  } from "../state/skill-active.js";
@@ -45,11 +46,16 @@ import {
45
46
  resolveEffectiveAutoNudgeResponse,
46
47
  } from "./notify-hook/auto-nudge.js";
47
48
  import {
49
+ SLOPPY_FALLBACK_GROUNDING_PATTERNS,
50
+ SLOPPY_FALLBACK_IMPLEMENTATION_CONTEXT_PATTERNS,
51
+ SLOPPY_FALLBACK_PHRASE_PATTERNS,
48
52
  buildNativePostToolUseOutput,
49
53
  buildNativePreToolUseOutput,
50
54
  detectMcpTransportFailure,
55
+ hasAnyPattern,
51
56
  } from "./codex-native-pre-post.js";
52
57
  import { handleTeamWorkerPostToolUseSuccess } from "./notify-hook/team-worker-posttooluse.js";
58
+ import { maybeNudgeLeaderForAllowedWorkerStop } from "./notify-hook/team-worker-stop.js";
53
59
  import {
54
60
  resolveCodexExecutionSurface,
55
61
  type CodexLauncherKind,
@@ -59,10 +65,10 @@ import {
59
65
  buildNativeHookEvent,
60
66
  } from "../hooks/extensibility/events.js";
61
67
  import type { HookEventEnvelope } from "../hooks/extensibility/types.js";
62
- import { dispatchHookEvent } from "../hooks/extensibility/dispatcher.js";
68
+ import { dispatchHookEventRuntime } from "../hooks/extensibility/runtime.js";
63
69
  import { reconcileHudForPromptSubmit } from "../hud/reconcile.js";
64
70
  import { onSessionStart as buildWikiSessionStartContext } from "../wiki/lifecycle.js";
65
- import { readAutoresearchCompletionStatus, readAutoresearchModeState } from "../autoresearch/skill-validation.js";
71
+ import { readAutoresearchCompletionStatus, readAutoresearchModeStateForActiveDecision } from "../autoresearch/skill-validation.js";
66
72
  import { readRunState } from "../runtime/run-state.js";
67
73
  import { getRunContinuationSnapshot, shouldContinueRun } from "../runtime/run-loop.js";
68
74
  import { triagePrompt } from "../hooks/triage-heuristic.js";
@@ -107,9 +113,10 @@ export interface NativeHookDispatchResult {
107
113
  outputJson: Record<string, unknown> | null;
108
114
  }
109
115
 
110
- const TERMINAL_MODE_PHASES = new Set(["complete", "failed", "cancelled"]);
116
+ const TERMINAL_MODE_PHASES = new Set(["complete", "completed", "failed", "cancelled"]);
111
117
  const SKILL_STOP_BLOCKERS = new Set(["ralplan"]);
112
- const TEAM_TERMINAL_TASK_STATUSES = new Set(["completed", "failed"]);
118
+ const TEAM_STOP_BLOCKING_TASK_STATUSES = new Set(["pending", "in_progress", "blocked"]);
119
+ const TEAM_WORKER_TERMINAL_RUN_STATES = new Set(["done", "complete", "completed", "failed", "stopped", "cancelled"]);
113
120
  const NATIVE_STOP_STATE_FILE = "native-stop-state.json";
114
121
  const STABLE_FINAL_RECOMMENDATION_PATTERNS = [
115
122
  /^\s*(?:launch|release|ship)-?ready\s*:\s*(?:yes|no)\b[^\n\r]*/im,
@@ -259,6 +266,26 @@ async function nativeSubagentSessionStartBelongsToCanonicalSession(
259
266
  return summary.allThreadIds.includes(parentThreadId);
260
267
  }
261
268
 
269
+ async function isNativeSubagentHook(
270
+ cwd: string,
271
+ canonicalSessionId: string,
272
+ nativeSessionId: string,
273
+ threadId: string,
274
+ ): Promise<boolean> {
275
+ const sessionId = canonicalSessionId.trim();
276
+ if (!sessionId) return false;
277
+
278
+ const summary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
279
+ if (!summary) return false;
280
+
281
+ const candidateIds = [nativeSessionId, threadId]
282
+ .map((value) => value.trim())
283
+ .filter(Boolean);
284
+ if (candidateIds.length === 0) return false;
285
+
286
+ return candidateIds.some((id) => summary.allSubagentThreadIds.includes(id));
287
+ }
288
+
262
289
  async function recordIgnoredNativeSubagentSessionStart(
263
290
  cwd: string,
264
291
  canonicalSessionId: string,
@@ -433,7 +460,7 @@ async function readActiveAutoresearchState(
433
460
  ): Promise<Record<string, unknown> | null> {
434
461
  const normalizedSessionId = sessionId?.trim() || undefined;
435
462
  if (!normalizedSessionId) return null;
436
- const state = await readAutoresearchModeState(cwd, normalizedSessionId);
463
+ const state = await readAutoresearchModeStateForActiveDecision(cwd, normalizedSessionId);
437
464
  if (state?.active !== true) return null;
438
465
  if (!isNonTerminalPhase(state.current_phase ?? state.currentPhase ?? 'executing')) return null;
439
466
  return state;
@@ -444,10 +471,59 @@ interface ActiveRalphStopState {
444
471
  path: string;
445
472
  }
446
473
 
474
+ interface RalphStopOwnershipContext {
475
+ sessionId: string;
476
+ payloadSessionId: string;
477
+ threadId: string;
478
+ currentNativeSessionId: string;
479
+ tmuxPaneId: string;
480
+ }
481
+
447
482
  function isRalphStartingPhase(state: Record<string, unknown>): boolean {
448
483
  return safeString(state.current_phase ?? state.currentPhase).trim().toLowerCase() === "starting";
449
484
  }
450
485
 
486
+ function hasValue(values: string[], value: string): boolean {
487
+ return value !== "" && values.some((candidate) => candidate === value);
488
+ }
489
+
490
+ function activeRalphStateMatchesStopOwner(
491
+ state: Record<string, unknown>,
492
+ context: RalphStopOwnershipContext,
493
+ ): boolean {
494
+ const ownerOmxSessionId = safeString(state.owner_omx_session_id).trim();
495
+ if (ownerOmxSessionId && ownerOmxSessionId !== context.sessionId) {
496
+ return false;
497
+ }
498
+
499
+ const stateSessionId = safeString(state.session_id).trim();
500
+ if (!ownerOmxSessionId && stateSessionId && stateSessionId !== context.sessionId) {
501
+ return false;
502
+ }
503
+
504
+ const codexOwnerSessionId = safeString(state.owner_codex_session_id).trim();
505
+ if (codexOwnerSessionId) {
506
+ const stopCodexSessionIds = [
507
+ context.payloadSessionId,
508
+ context.currentNativeSessionId,
509
+ context.sessionId,
510
+ ].filter(Boolean);
511
+ if (!hasValue(stopCodexSessionIds, codexOwnerSessionId)) return false;
512
+ }
513
+
514
+ const stateThreadId = safeString(state.owner_codex_thread_id ?? state.thread_id).trim();
515
+ if (stateThreadId && context.threadId && stateThreadId !== context.threadId) {
516
+ return false;
517
+ }
518
+
519
+ const statePaneId = safeString(state.tmux_pane_id).trim();
520
+ if (statePaneId && context.tmuxPaneId && statePaneId !== context.tmuxPaneId) {
521
+ return false;
522
+ }
523
+
524
+ return true;
525
+ }
526
+
451
527
  function shouldHonorCanonicalTerminalRunState(
452
528
  runState: Record<string, unknown> | null,
453
529
  mode: string,
@@ -478,9 +554,27 @@ async function isVisibleRalphActiveForSession(cwd: string, sessionId: string): P
478
554
  ));
479
555
  }
480
556
 
557
+ async function hasConsistentRalphSkillActivation(cwd: string, sessionId: string): Promise<boolean> {
558
+ const canonicalState = await readVisibleSkillActiveState(cwd, sessionId);
559
+ if (!canonicalState) return true;
560
+
561
+ const initializedMode = safeString(canonicalState.initialized_mode).trim();
562
+ if (initializedMode && initializedMode !== "ralph") return true;
563
+
564
+ const initializedPathSessionId = extractSessionIdFromInitializedStatePath(canonicalState.initialized_state_path);
565
+ if (initializedPathSessionId && initializedPathSessionId !== sessionId) return false;
566
+
567
+ return true;
568
+ }
569
+
481
570
  async function readActiveRalphState(
482
571
  stateDir: string,
483
572
  preferredSessionId?: string,
573
+ ownerContext?: {
574
+ payloadSessionId?: string;
575
+ threadId?: string;
576
+ tmuxPaneId?: string;
577
+ },
484
578
  ): Promise<ActiveRalphStopState | null> {
485
579
  const cwd = resolve(stateDir, "..", "..");
486
580
  const [rawSessionInfo, usableSessionInfo] = await Promise.all([
@@ -488,6 +582,7 @@ async function readActiveRalphState(
488
582
  readUsableSessionState(cwd),
489
583
  ]);
490
584
  const currentOmxSessionId = safeString(usableSessionInfo?.session_id).trim();
585
+ const currentNativeSessionId = safeString(usableSessionInfo?.native_session_id).trim();
491
586
  const staleCurrentSessionId = rawSessionInfo && !isSessionStateUsable(rawSessionInfo, cwd)
492
587
  ? safeString(rawSessionInfo.session_id).trim()
493
588
  : "";
@@ -515,7 +610,18 @@ async function readActiveRalphState(
515
610
  ) {
516
611
  continue;
517
612
  }
518
- if (sessionScoped?.active === true && shouldContinueRun(sessionScoped)) {
613
+ if (
614
+ sessionScoped?.active === true
615
+ && shouldContinueRun(sessionScoped)
616
+ && activeRalphStateMatchesStopOwner(sessionScoped, {
617
+ sessionId,
618
+ payloadSessionId: safeString(ownerContext?.payloadSessionId).trim(),
619
+ threadId: safeString(ownerContext?.threadId).trim(),
620
+ currentNativeSessionId,
621
+ tmuxPaneId: safeString(ownerContext?.tmuxPaneId).trim(),
622
+ })
623
+ && await hasConsistentRalphSkillActivation(cwd, sessionId)
624
+ ) {
519
625
  return { state: sessionScoped, path: sessionScopedPath };
520
626
  }
521
627
  }
@@ -643,6 +749,192 @@ function tryReadGitValue(cwd: string, args: string[]): string | null {
643
749
  }
644
750
  }
645
751
 
752
+ interface SloppyFallbackDiffFinding {
753
+ path: string;
754
+ line: string;
755
+ source: "staged" | "unstaged" | "untracked";
756
+ }
757
+
758
+ const SOURCE_DIFF_EXTENSIONS = new Set([
759
+ ".c",
760
+ ".cc",
761
+ ".cjs",
762
+ ".cpp",
763
+ ".cs",
764
+ ".cts",
765
+ ".go",
766
+ ".h",
767
+ ".hpp",
768
+ ".java",
769
+ ".js",
770
+ ".jsx",
771
+ ".kt",
772
+ ".mjs",
773
+ ".mts",
774
+ ".php",
775
+ ".py",
776
+ ".rb",
777
+ ".rs",
778
+ ".sh",
779
+ ".swift",
780
+ ".ts",
781
+ ".tsx",
782
+ ]);
783
+
784
+ function gitOutput(cwd: string, args: string[]): string {
785
+ try {
786
+ return execFileSync("git", args, {
787
+ cwd,
788
+ encoding: "utf-8",
789
+ stdio: ["ignore", "pipe", "ignore"],
790
+ windowsHide: true,
791
+ maxBuffer: 10 * 1024 * 1024,
792
+ });
793
+ } catch {
794
+ return "";
795
+ }
796
+ }
797
+
798
+ function normalizeGitPath(path: string): string {
799
+ return path.replace(/\\/g, "/").replace(/^\.\//, "");
800
+ }
801
+
802
+ function isDiffAuditableSourcePath(path: string): boolean {
803
+ const normalized = normalizeGitPath(path).toLowerCase();
804
+ if (!normalized || normalized.startsWith(".git/") || normalized.startsWith(".omx/")) return false;
805
+ if (/(^|\/)(?:docs?|documentation|changelog|changeset|\.github)(?:\/|$)/i.test(normalized)) return false;
806
+ if (/(^|\/)(?:__tests__|__test__|test|tests|spec|specs|fixtures?|mocks?)(?:\/|$)/i.test(normalized)) return false;
807
+ if (/(?:^|\/)[^\/]+\.(?:test|spec)\.[^.\/]+$/i.test(normalized)) return false;
808
+ if (/(?:^|\/)(?:readme|changelog|changes|license|notice)(?:\.[^\/]*)?$/i.test(normalized)) return false;
809
+ if (/\.(?:md|mdx|markdown|txt|rst|adoc|ya?ml|json|lock)$/i.test(normalized)) return false;
810
+ return SOURCE_DIFF_EXTENSIONS.has(extname(normalized));
811
+ }
812
+
813
+ function isDiffHeaderLine(line: string): boolean {
814
+ return line.startsWith("+++") || line.startsWith("---") || line.startsWith("@@") || line.startsWith("diff --git ");
815
+ }
816
+
817
+ function isSuspiciousSloppyFallbackAddedLine(line: string, nearbyContext: string): boolean {
818
+ const trimmed = line.trim();
819
+ if (!trimmed) return false;
820
+ if (!hasAnyPattern(trimmed, SLOPPY_FALLBACK_PHRASE_PATTERNS)) return false;
821
+ if (!hasAnyPattern(trimmed, SLOPPY_FALLBACK_IMPLEMENTATION_CONTEXT_PATTERNS)) return false;
822
+ if (hasAnyPattern(nearbyContext, SLOPPY_FALLBACK_GROUNDING_PATTERNS)) return false;
823
+ if (/compatib(?:le|ility)|fail-?safe|tested|regression|coverage|because|issue|PR\s*#?\d|#\d/i.test(nearbyContext)) return false;
824
+ return true;
825
+ }
826
+
827
+ interface SloppyFallbackCandidateLine {
828
+ text: string;
829
+ added: boolean;
830
+ }
831
+
832
+ function collectFindingsFromCandidateLines(
833
+ path: string,
834
+ lines: SloppyFallbackCandidateLine[],
835
+ source: SloppyFallbackDiffFinding["source"],
836
+ ): SloppyFallbackDiffFinding[] {
837
+ if (!path || !isDiffAuditableSourcePath(path)) return [];
838
+ const findings: SloppyFallbackDiffFinding[] = [];
839
+ for (let index = 0; index < lines.length; index += 1) {
840
+ const candidate = lines[index];
841
+ if (!candidate?.added) continue;
842
+ const nearbyContext = lines
843
+ .slice(Math.max(0, index - 2), Math.min(lines.length, index + 3))
844
+ .map((line) => line.text)
845
+ .join("\n");
846
+ if (isSuspiciousSloppyFallbackAddedLine(candidate.text, nearbyContext)) {
847
+ findings.push({ path, line: candidate.text.trim(), source });
848
+ }
849
+ }
850
+ return findings;
851
+ }
852
+
853
+ function collectSloppyFallbackFindingsFromPatch(
854
+ patch: string,
855
+ source: SloppyFallbackDiffFinding["source"],
856
+ ): SloppyFallbackDiffFinding[] {
857
+ const findings: SloppyFallbackDiffFinding[] = [];
858
+ let currentPath = "";
859
+ let hunkLines: SloppyFallbackCandidateLine[] = [];
860
+
861
+ const flushHunk = () => {
862
+ findings.push(...collectFindingsFromCandidateLines(currentPath, hunkLines, source));
863
+ hunkLines = [];
864
+ };
865
+
866
+ for (const rawLine of patch.split(/\r?\n/)) {
867
+ const fileMatch = rawLine.match(/^diff --git a\/(.*?) b\/(.*)$/);
868
+ if (fileMatch) {
869
+ flushHunk();
870
+ currentPath = normalizeGitPath(fileMatch[2] || fileMatch[1] || "");
871
+ continue;
872
+ }
873
+ const renameMatch = rawLine.match(/^\+\+\+ b\/(.*)$/);
874
+ if (renameMatch) {
875
+ currentPath = normalizeGitPath(renameMatch[1] || currentPath);
876
+ continue;
877
+ }
878
+ if (rawLine.startsWith("@@")) {
879
+ flushHunk();
880
+ continue;
881
+ }
882
+ if (!currentPath || !isDiffAuditableSourcePath(currentPath) || isDiffHeaderLine(rawLine)) continue;
883
+ if (rawLine.startsWith("+")) {
884
+ hunkLines.push({ text: rawLine.slice(1), added: true });
885
+ } else if (rawLine.startsWith(" ")) {
886
+ hunkLines.push({ text: rawLine.slice(1), added: false });
887
+ }
888
+ }
889
+ flushHunk();
890
+ return findings;
891
+ }
892
+
893
+ function collectSloppyFallbackFindingsFromUntracked(cwd: string): SloppyFallbackDiffFinding[] {
894
+ const output = gitOutput(cwd, ["ls-files", "--others", "--exclude-standard", "-z"]);
895
+ if (!output) return [];
896
+ const findings: SloppyFallbackDiffFinding[] = [];
897
+ for (const rawPath of output.split("\0")) {
898
+ const path = normalizeGitPath(rawPath.trim());
899
+ if (!path || !isDiffAuditableSourcePath(path)) continue;
900
+ let content = "";
901
+ try {
902
+ content = readFileSync(join(cwd, path), "utf-8");
903
+ } catch {
904
+ continue;
905
+ }
906
+ findings.push(...collectFindingsFromCandidateLines(path, content.split(/\r?\n/).map((text) => ({ text, added: true })), "untracked"));
907
+ }
908
+ return findings;
909
+ }
910
+
911
+ function findSloppyFallbackDiffFindings(cwd: string): SloppyFallbackDiffFinding[] {
912
+ const layout = findGitLayout(cwd);
913
+ if (!layout) return [];
914
+ const auditRoot = layout.worktreeRoot;
915
+ return [
916
+ ...collectSloppyFallbackFindingsFromPatch(gitOutput(auditRoot, ["diff", "--cached", "--no-ext-diff", "--unified=3"]), "staged"),
917
+ ...collectSloppyFallbackFindingsFromPatch(gitOutput(auditRoot, ["diff", "--no-ext-diff", "--unified=3"]), "unstaged"),
918
+ ...collectSloppyFallbackFindingsFromUntracked(auditRoot),
919
+ ];
920
+ }
921
+
922
+ function buildSloppyFallbackDiffStopOutput(findings: SloppyFallbackDiffFinding[]): Record<string, unknown> | null {
923
+ if (findings.length === 0) return null;
924
+ const preview = findings
925
+ .slice(0, 3)
926
+ .map((finding) => `${finding.path} (${finding.source}): ${finding.line}`)
927
+ .join("; ");
928
+ const systemMessage =
929
+ `Sloppy fallback/workaround diff audit detected ungrounded fallback code in added source lines: ${preview}. `
930
+ + "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.";
931
+ return {
932
+ decision: "block",
933
+ reason: systemMessage,
934
+ stopReason: "sloppy_fallback_diff_audit",
935
+ systemMessage,
936
+ };
937
+ }
646
938
 
647
939
  function localExcludeAlreadyIgnoresOmx(cwd: string): boolean {
648
940
  const layout = findGitLayout(cwd);
@@ -1035,6 +1327,9 @@ function buildAdditionalContextMessage(
1035
1327
  const ultraworkPromptActivationNote = skillState?.initialized_mode === "ultrawork"
1036
1328
  ? "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."
1037
1329
  : null;
1330
+ const ultragoalPromptActivationNote = match.skill === "ultragoal"
1331
+ ? "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."
1332
+ : null;
1038
1333
  const combinedTransitionMessage = (() => {
1039
1334
  if (!skillState?.transition_message) return null;
1040
1335
  if (matches.length <= 1 || activeSkills.length <= 1) return skillState.transition_message;
@@ -1061,6 +1356,7 @@ function buildAdditionalContextMessage(
1061
1356
  ? `planning preserved over simultaneous execution follow-up; deferred skills: ${deferredSkills.join(", ")}.`
1062
1357
  : null,
1063
1358
  promptPriorityMessage,
1359
+ ultragoalPromptActivationNote,
1064
1360
  skillState.initialized_mode && skillState.initialized_state_path
1065
1361
  ? `skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`
1066
1362
  : null,
@@ -1086,6 +1382,7 @@ function buildAdditionalContextMessage(
1086
1382
  initializedStateMessage,
1087
1383
  deepInterviewPromptActivationNote,
1088
1384
  ultraworkPromptActivationNote,
1385
+ ultragoalPromptActivationNote,
1089
1386
  buildTeamRuntimeInstruction(cwd, payload),
1090
1387
  buildTeamHelpInstruction(cwd, payload),
1091
1388
  "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
@@ -1103,12 +1400,13 @@ function buildAdditionalContextMessage(
1103
1400
  `skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`,
1104
1401
  deepInterviewPromptActivationNote,
1105
1402
  ultraworkPromptActivationNote,
1403
+ ultragoalPromptActivationNote,
1106
1404
  ralphPromptActivationNote,
1107
1405
  "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
1108
1406
  ].join(" ");
1109
1407
  }
1110
1408
 
1111
- return [detectedKeywordMessage, promptPriorityMessage, "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules."].filter(Boolean).join(" ");
1409
+ return [detectedKeywordMessage, promptPriorityMessage, ultragoalPromptActivationNote, "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules."].filter(Boolean).join(" ");
1112
1410
  }
1113
1411
 
1114
1412
  function parseTeamWorkerEnv(rawValue: string): { teamName: string; workerName: string } | null {
@@ -1124,23 +1422,79 @@ async function resolveTeamStateDirForWorkerContext(
1124
1422
  cwd: string,
1125
1423
  workerContext: { teamName: string; workerName: string },
1126
1424
  ): Promise<string | null> {
1127
- return resolveWorkerNotifyTeamStateRootPath(cwd, workerContext, process.env);
1425
+ const resolved = await resolveWorkerNotifyTeamStateRootPath(cwd, workerContext, process.env).catch(() => null);
1426
+ if (resolved) return resolved;
1427
+ const explicit = safeString(process.env.OMX_TEAM_STATE_ROOT).trim();
1428
+ if (explicit) {
1429
+ const candidate = resolve(cwd, explicit);
1430
+ const workerRoot = join(candidate, "team", workerContext.teamName, "workers", workerContext.workerName);
1431
+ if (existsSync(workerRoot)) return candidate;
1432
+ return candidate;
1433
+ }
1434
+ return null;
1128
1435
  }
1129
1436
 
1130
1437
 
1131
- async function buildTeamWorkerStopOutput(
1438
+ type TeamWorkerStopDecision =
1439
+ | {
1440
+ kind: "blocked";
1441
+ stateDir: string;
1442
+ workerContext: { teamName: string; workerName: string };
1443
+ output: Record<string, unknown>;
1444
+ allowRepeatDuringStopHook: boolean;
1445
+ }
1446
+ | {
1447
+ kind: "allowed";
1448
+ stateDir: string;
1449
+ workerContext: { teamName: string; workerName: string };
1450
+ }
1451
+ | {
1452
+ kind: "unresolved";
1453
+ reason: string;
1454
+ };
1455
+
1456
+ async function resolveTeamWorkerStopDecision(
1132
1457
  cwd: string,
1133
- ): Promise<Record<string, unknown> | null> {
1134
- const workerContext = parseTeamWorkerEnv(safeString(process.env.OMX_TEAM_WORKER));
1135
- if (!workerContext) return null;
1458
+ ): Promise<TeamWorkerStopDecision> {
1459
+ const workerContext =
1460
+ parseTeamWorkerEnv(safeString(process.env.OMX_TEAM_INTERNAL_WORKER))
1461
+ || parseTeamWorkerEnv(safeString(process.env.OMX_TEAM_WORKER));
1462
+ if (!workerContext) return { kind: "unresolved", reason: "missing_worker_context" };
1463
+
1464
+ const blockWorkerStop = (
1465
+ reasonCode: string,
1466
+ detail: string,
1467
+ stateDirForDecision = join(cwd, ".omx", "state"),
1468
+ ): TeamWorkerStopDecision => ({
1469
+ kind: "blocked",
1470
+ stateDir: stateDirForDecision,
1471
+ workerContext,
1472
+ allowRepeatDuringStopHook: false,
1473
+ output: {
1474
+ decision: "block",
1475
+ reason:
1476
+ `OMX team worker ${workerContext.workerName} Stop cannot be allowed for ${reasonCode}: ${detail}. ` +
1477
+ "Continue the assigned task, repair worker state, or report a concrete blocker before stopping.",
1478
+ stopReason: `team_worker_${workerContext.workerName}_${reasonCode}`,
1479
+ systemMessage:
1480
+ `OMX team worker ${workerContext.workerName} Stop lacks completed task evidence (${reasonCode}).`,
1481
+ },
1482
+ });
1136
1483
 
1137
1484
  const stateDir = await resolveTeamStateDirForWorkerContext(cwd, workerContext);
1138
- if (!stateDir) return null;
1485
+ if (!stateDir) {
1486
+ return blockWorkerStop("missing_state_dir", "team state root could not be resolved");
1487
+ }
1139
1488
  const workerRoot = join(stateDir, "team", workerContext.teamName, "workers", workerContext.workerName);
1140
1489
  const [identity, status] = await Promise.all([
1141
1490
  readJsonIfExists(join(workerRoot, "identity.json")),
1142
1491
  readJsonIfExists(join(workerRoot, "status.json")),
1143
1492
  ]);
1493
+ const workerRunState = safeString(status?.state).trim().toLowerCase();
1494
+ const workerRunStateIsTerminal = TEAM_WORKER_TERMINAL_RUN_STATES.has(workerRunState);
1495
+ if (!identity && !status && !existsSync(workerRoot)) {
1496
+ return blockWorkerStop("missing_worker_state", "worker identity/status state is missing", stateDir);
1497
+ }
1144
1498
 
1145
1499
  const candidateTaskIds = new Set<string>();
1146
1500
  const currentTaskId = safeString(status?.current_task_id).trim();
@@ -1151,27 +1505,66 @@ async function buildTeamWorkerStopOutput(
1151
1505
  if (normalized) candidateTaskIds.add(normalized);
1152
1506
  }
1153
1507
 
1508
+ const tasksDir = join(stateDir, "team", workerContext.teamName, "tasks");
1509
+ if (existsSync(tasksDir)) {
1510
+ const taskFiles = await readdir(tasksDir).catch(() => []);
1511
+ for (const entry of taskFiles) {
1512
+ if (!/^task-\d+\.json$/.test(entry)) continue;
1513
+ const task = await readJsonIfExists(join(tasksDir, entry));
1514
+ const taskOwner = safeString(task?.owner).trim();
1515
+ const taskClaimOwner = safeString(safeObject(task?.claim).owner).trim();
1516
+ if (taskOwner !== workerContext.workerName && taskClaimOwner !== workerContext.workerName) continue;
1517
+ const idFromFile = /^task-(\d+)\.json$/.exec(entry)?.[1] ?? "";
1518
+ const taskId = safeString(task?.id).trim() || idFromFile;
1519
+ if (taskId) candidateTaskIds.add(taskId);
1520
+ }
1521
+ }
1522
+
1523
+ if (candidateTaskIds.size === 0) {
1524
+ return blockWorkerStop("missing_task_assignment", "no current_task_id or assigned_tasks are recorded", stateDir);
1525
+ }
1526
+
1527
+ let completedTaskCount = 0;
1154
1528
  for (const taskId of candidateTaskIds) {
1155
1529
  const task = await readJsonIfExists(
1156
1530
  join(stateDir, "team", workerContext.teamName, "tasks", `task-${taskId}.json`),
1157
1531
  );
1158
1532
  const statusValue = safeString(task?.status).trim().toLowerCase();
1159
- if (!statusValue || TEAM_TERMINAL_TASK_STATUSES.has(statusValue)) continue;
1533
+ if (!statusValue) {
1534
+ return blockWorkerStop(`missing_task_state_${taskId}`, `task ${taskId} has no readable status`, stateDir);
1535
+ }
1536
+ if (statusValue === "completed") {
1537
+ completedTaskCount += 1;
1538
+ continue;
1539
+ }
1540
+ if (!TEAM_STOP_BLOCKING_TASK_STATUSES.has(statusValue)) {
1541
+ return blockWorkerStop(
1542
+ `non_completed_task_${taskId}_${statusValue}`,
1543
+ `task ${taskId} is ${statusValue}, not completed`,
1544
+ stateDir,
1545
+ );
1546
+ }
1160
1547
  return {
1161
- decision: "block",
1162
- reason:
1163
- `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.`,
1164
- stopReason: `team_worker_${workerContext.workerName}_${taskId}_${statusValue}`,
1165
- systemMessage:
1166
- `OMX team worker ${workerContext.workerName} is still assigned task ${taskId} (${statusValue}).`,
1548
+ kind: "blocked",
1549
+ stateDir,
1550
+ workerContext,
1551
+ allowRepeatDuringStopHook: !workerRunStateIsTerminal,
1552
+ output: {
1553
+ decision: "block",
1554
+ reason:
1555
+ `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.`,
1556
+ stopReason: `team_worker_${workerContext.workerName}_${taskId}_${statusValue}`,
1557
+ systemMessage:
1558
+ `OMX team worker ${workerContext.workerName} is still assigned task ${taskId} (${statusValue}).`,
1559
+ },
1167
1560
  };
1168
1561
  }
1169
1562
 
1170
- return null;
1171
- }
1563
+ if (completedTaskCount === candidateTaskIds.size) {
1564
+ return { kind: "allowed", stateDir, workerContext };
1565
+ }
1172
1566
 
1173
- function hasTeamWorkerContext(): boolean {
1174
- return parseTeamWorkerEnv(safeString(process.env.OMX_TEAM_WORKER)) !== null;
1567
+ return blockWorkerStop("missing_completed_task_evidence", "no referenced worker task is completed", stateDir);
1175
1568
  }
1176
1569
 
1177
1570
  function isStopExempt(payload: CodexHookPayload): boolean {
@@ -1198,9 +1591,7 @@ async function buildModeBasedStopOutput(
1198
1591
  cwd: string,
1199
1592
  sessionId?: string,
1200
1593
  ): Promise<Record<string, unknown> | null> {
1201
- const state = sessionId
1202
- ? await readModeStateForSession(mode, sessionId, cwd)
1203
- : await readModeState(mode, cwd);
1594
+ const state = await readModeStateForActiveDecision(mode, sessionId?.trim() || undefined, cwd);
1204
1595
  if (!state || !shouldContinueRun(state)) return null;
1205
1596
  const phase = formatPhase(state.current_phase);
1206
1597
  return {
@@ -1211,6 +1602,81 @@ async function buildModeBasedStopOutput(
1211
1602
  };
1212
1603
  }
1213
1604
 
1605
+ function looksLikeGoalCompletionPrompt(text: string): boolean {
1606
+ return /\b(?:complete|checkpoint|finish|close|mark)\b.{0,80}\b(?:goal|ultragoal|performance-goal|autoresearch-goal)\b/i.test(text)
1607
+ || /\bupdate_goal\s*\(/i.test(text)
1608
+ || /\bomx\s+(?:ultragoal|performance-goal|autoresearch-goal)\s+(?:checkpoint|complete)\b/i.test(text);
1609
+ }
1610
+
1611
+ async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Promise<{ workflow: string; command: string } | null> {
1612
+ const ultragoal = await readJsonIfExists(join(cwd, ".omx", "ultragoal", "goals.json"));
1613
+ const ultragoals = Array.isArray(ultragoal?.goals) ? ultragoal.goals.map(safeObject) : [];
1614
+ const activeUltragoal = ultragoals.find((goal) => safeString(goal.status) === "in_progress" || safeString(goal.id) === safeString(ultragoal?.activeGoalId));
1615
+ if (activeUltragoal) {
1616
+ return {
1617
+ workflow: "ultragoal",
1618
+ command: `omx ultragoal checkpoint --goal-id ${safeString(activeUltragoal.id) || "<goal-id>"} --status complete --codex-goal-json '<get_goal JSON or path>' --evidence '<evidence>'`,
1619
+ };
1620
+ }
1621
+
1622
+ const performanceRoot = join(cwd, ".omx", "goals", "performance");
1623
+ for (const entry of await readdir(performanceRoot, { withFileTypes: true }).catch(() => [])) {
1624
+ if (!entry.isDirectory()) continue;
1625
+ const state = await readJsonIfExists(join(performanceRoot, entry.name, "state.json"));
1626
+ const status = safeString(state?.status);
1627
+ if (state?.workflow === "performance-goal" && status && status !== "complete") {
1628
+ return {
1629
+ workflow: "performance-goal",
1630
+ command: `omx performance-goal complete --slug ${safeString(state.slug) || entry.name} --codex-goal-json '<get_goal JSON or path>' --evidence '<evidence>'`,
1631
+ };
1632
+ }
1633
+ }
1634
+
1635
+ const autoresearchRoot = join(cwd, ".omx", "goals", "autoresearch");
1636
+ for (const entry of await readdir(autoresearchRoot, { withFileTypes: true }).catch(() => [])) {
1637
+ if (!entry.isDirectory()) continue;
1638
+ const mission = await readJsonIfExists(join(autoresearchRoot, entry.name, "mission.json"));
1639
+ const status = safeString(mission?.status);
1640
+ if (mission?.workflow === "autoresearch-goal" && status && status !== "complete") {
1641
+ return {
1642
+ workflow: "autoresearch-goal",
1643
+ command: `omx autoresearch-goal complete --slug ${safeString(mission.slug) || entry.name} --codex-goal-json '<get_goal JSON or path>'`,
1644
+ };
1645
+ }
1646
+ }
1647
+
1648
+ return null;
1649
+ }
1650
+
1651
+ async function buildGoalWorkflowReconciliationPromptWarning(cwd: string, prompt: string): Promise<string | null> {
1652
+ if (!looksLikeGoalCompletionPrompt(prompt)) return null;
1653
+ const requirement = await findActiveGoalWorkflowReconciliationRequirement(cwd);
1654
+ if (!requirement) return null;
1655
+ return [
1656
+ `OMX ${requirement.workflow} goal workflow requires Codex goal snapshot reconciliation before completion.`,
1657
+ "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.",
1658
+ `Required command shape: ${requirement.command}.`,
1659
+ ].join(" ");
1660
+ }
1661
+
1662
+ async function buildGoalWorkflowReconciliationStopOutput(
1663
+ payload: CodexHookPayload,
1664
+ cwd: string,
1665
+ ): Promise<Record<string, unknown> | null> {
1666
+ const lastAssistantMessage = safeString(payload.last_assistant_message ?? payload.lastAssistantMessage);
1667
+ if (!looksLikeGoalCompletionPrompt(lastAssistantMessage)) return null;
1668
+ const requirement = await findActiveGoalWorkflowReconciliationRequirement(cwd);
1669
+ if (!requirement) return null;
1670
+ const systemMessage =
1671
+ `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.`;
1672
+ return {
1673
+ decision: "block",
1674
+ reason: systemMessage,
1675
+ stopReason: `${requirement.workflow}_codex_goal_snapshot_required`,
1676
+ systemMessage,
1677
+ };
1678
+ }
1679
+
1214
1680
  async function readTeamModeStateForStop(
1215
1681
  cwd: string,
1216
1682
  sessionId?: string,
@@ -1366,12 +1832,42 @@ function matchesSkillStopContext(
1366
1832
  return true;
1367
1833
  }
1368
1834
 
1835
+ function modeStateMatchesSkillStopContext(
1836
+ state: Record<string, unknown>,
1837
+ cwd: string,
1838
+ sessionId: string,
1839
+ ): boolean {
1840
+ const stateSessionId = safeString(
1841
+ state.owner_omx_session_id
1842
+ ?? state.session_id
1843
+ ?? state.codex_session_id
1844
+ ?? state.owner_codex_session_id,
1845
+ ).trim();
1846
+ if (sessionId && stateSessionId && stateSessionId !== sessionId) return false;
1847
+
1848
+ const stateCwd = safeString(
1849
+ state.cwd
1850
+ ?? state.workingDirectory
1851
+ ?? state.working_directory
1852
+ ?? state.project_path,
1853
+ ).trim();
1854
+ if (stateCwd) {
1855
+ try {
1856
+ if (resolve(stateCwd) !== resolve(cwd)) return false;
1857
+ } catch {
1858
+ return false;
1859
+ }
1860
+ }
1861
+
1862
+ return true;
1863
+ }
1864
+
1369
1865
  async function readBlockingSkillForStop(
1370
1866
  cwd: string,
1371
1867
  sessionId: string,
1372
1868
  threadId: string,
1373
1869
  requiredSkill?: string,
1374
- ): Promise<{ skill: string; phase: string } | null> {
1870
+ ): Promise<{ skill: string; phase: string; latestPlanPath?: string; planningComplete?: boolean; runOutcome?: string } | null> {
1375
1871
  const canonicalState = await readVisibleSkillActiveState(cwd, sessionId);
1376
1872
  const visibleEntries = canonicalState ? listActiveSkills(canonicalState) : [];
1377
1873
  const candidateSkills = requiredSkill
@@ -1379,8 +1875,15 @@ async function readBlockingSkillForStop(
1379
1875
  : [...SKILL_STOP_BLOCKERS];
1380
1876
 
1381
1877
  for (const skill of candidateSkills) {
1878
+ const terminalRunState = await readCanonicalTerminalRunStateForStop(cwd, sessionId, skill);
1879
+ if (terminalRunState) continue;
1880
+
1382
1881
  const modeState = await readStopSessionPinnedState(`${skill}-state.json`, cwd, sessionId);
1383
1882
  if (!modeState || modeState.active !== true) continue;
1883
+ if (!modeStateMatchesSkillStopContext(modeState, cwd, sessionId)) continue;
1884
+
1885
+ const modeSnapshot = getRunContinuationSnapshot(modeState);
1886
+ if (modeSnapshot?.terminal === true) continue;
1384
1887
 
1385
1888
  const phase = formatPhase(
1386
1889
  modeState.current_phase,
@@ -1394,7 +1897,13 @@ async function readBlockingSkillForStop(
1394
1897
  }
1395
1898
 
1396
1899
  if (!canonicalState) {
1397
- return { skill, phase };
1900
+ return {
1901
+ skill,
1902
+ phase,
1903
+ latestPlanPath: safeString(modeState.latest_plan_path ?? modeState.latestPlanPath).trim() || undefined,
1904
+ planningComplete: modeState.planning_complete === true || modeState.planningComplete === true,
1905
+ runOutcome: safeString(modeState.run_outcome ?? modeState.outcome).trim() || undefined,
1906
+ };
1398
1907
  }
1399
1908
 
1400
1909
  const blocker = visibleEntries.find((entry) => (
@@ -1406,12 +1915,65 @@ async function readBlockingSkillForStop(
1406
1915
  return {
1407
1916
  skill,
1408
1917
  phase: formatPhase(modeState.current_phase ?? blocker.phase ?? canonicalState.phase, "planning"),
1918
+ latestPlanPath: safeString(modeState.latest_plan_path ?? modeState.latestPlanPath).trim() || undefined,
1919
+ planningComplete: modeState.planning_complete === true || modeState.planningComplete === true,
1920
+ runOutcome: safeString(modeState.run_outcome ?? modeState.outcome).trim() || undefined,
1409
1921
  };
1410
1922
  }
1411
1923
 
1412
1924
  return null;
1413
1925
  }
1414
1926
 
1927
+ function buildRalplanContinuationStatus(
1928
+ blocker: { phase: string; latestPlanPath?: string; planningComplete?: boolean; runOutcome?: string },
1929
+ activeSubagentCount: number,
1930
+ ): { reason: string; systemMessage: string; stopReasonSuffix: string } {
1931
+ const phase = blocker.phase || "planning";
1932
+ const artifact = blocker.latestPlanPath
1933
+ ? ` Artifact: ${blocker.latestPlanPath}.`
1934
+ : " Artifact: use the latest `.omx/plans/` ralplan artifact if present.";
1935
+
1936
+ if (activeSubagentCount > 0) {
1937
+ return {
1938
+ reason:
1939
+ `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}`,
1940
+ stopReasonSuffix: "waiting_subagent",
1941
+ systemMessage:
1942
+ `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.`,
1943
+ };
1944
+ }
1945
+
1946
+ const normalizedPhase = phase.toLowerCase();
1947
+ const normalizedOutcome = (blocker.runOutcome ?? "").toLowerCase();
1948
+ const waitingForInput =
1949
+ normalizedOutcome === "blocked_on_user"
1950
+ || normalizedPhase.includes("blocked")
1951
+ || normalizedPhase.includes("input")
1952
+ || normalizedPhase.includes("question");
1953
+
1954
+ if (waitingForInput) {
1955
+ return {
1956
+ reason:
1957
+ `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}`,
1958
+ stopReasonSuffix: "waiting_input",
1959
+ systemMessage:
1960
+ `OMX ralplan status: waiting for input at phase ${phase}; ask the required question or present the explicit review choice before stopping.`,
1961
+ };
1962
+ }
1963
+
1964
+ const completeHint = blocker.planningComplete
1965
+ ? " The planning artifacts are present; if consensus is approved, emit the final complete/approved handoff instead of stopping here."
1966
+ : "";
1967
+
1968
+ return {
1969
+ reason:
1970
+ `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}`,
1971
+ stopReasonSuffix: "continue_artifact",
1972
+ systemMessage:
1973
+ `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.`,
1974
+ };
1975
+ }
1976
+
1415
1977
  async function readStopAutoNudgePhase(
1416
1978
  cwd: string,
1417
1979
  sessionId: string,
@@ -1506,7 +2068,12 @@ function resolveRepeatableStopSessionId(
1506
2068
  payload: CodexHookPayload,
1507
2069
  canonicalSessionId?: string,
1508
2070
  ): string {
1509
- return canonicalSessionId?.trim() || readPayloadSessionId(payload) || "";
2071
+ const inheritedSessionId = safeString(process.env.OMX_SESSION_ID || process.env.CODEX_SESSION_ID).trim();
2072
+ return canonicalSessionId?.trim() || readPayloadSessionId(payload) || inheritedSessionId || "";
2073
+ }
2074
+
2075
+ function isStateLevelStopSignatureKind(kind: string): boolean {
2076
+ return kind === "team-worker-stop" || kind === "team-stop";
1510
2077
  }
1511
2078
 
1512
2079
  function buildRepeatableStopSignature(
@@ -1517,8 +2084,11 @@ function buildRepeatableStopSignature(
1517
2084
  ): string {
1518
2085
  const sessionId = resolveRepeatableStopSessionId(payload, canonicalSessionId) || "no-session";
1519
2086
  const threadId = readPayloadThreadId(payload) || "no-thread";
1520
- const turnId = readPayloadTurnId(payload);
1521
2087
  const normalizedDetail = normalizeAutoNudgeSignatureText(detail) || safeString(detail).trim().toLowerCase();
2088
+ if (isStateLevelStopSignatureKind(kind)) {
2089
+ return [kind, sessionId, threadId, normalizedDetail || "no-detail"].join("|");
2090
+ }
2091
+ const turnId = readPayloadTurnId(payload);
1522
2092
  const transcriptPath = safeString(payload.transcript_path ?? payload.transcriptPath).trim() || "no-transcript";
1523
2093
  const lastAssistantMessage = normalizeAutoNudgeSignatureText(
1524
2094
  payload.last_assistant_message ?? payload.lastAssistantMessage,
@@ -1734,7 +2304,19 @@ async function buildSkillStopOutput(
1734
2304
  if (!blocker) return null;
1735
2305
 
1736
2306
  const subagentSummary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
1737
- if (subagentSummary && subagentSummary.activeSubagentThreadIds.length > 0) {
2307
+ const activeSubagentCount = subagentSummary?.activeSubagentThreadIds.length ?? 0;
2308
+
2309
+ if (blocker.skill === "ralplan") {
2310
+ const status = buildRalplanContinuationStatus(blocker, activeSubagentCount);
2311
+ return {
2312
+ decision: "block",
2313
+ reason: status.reason,
2314
+ stopReason: `skill_${blocker.skill}_${blocker.phase}_${status.stopReasonSuffix}`,
2315
+ systemMessage: status.systemMessage,
2316
+ };
2317
+ }
2318
+
2319
+ if (activeSubagentCount > 0) {
1738
2320
  return null;
1739
2321
  }
1740
2322
 
@@ -1855,6 +2437,7 @@ async function buildStopHookOutput(
1855
2437
  payload: CodexHookPayload,
1856
2438
  cwd: string,
1857
2439
  stateDir: string,
2440
+ options: { skipRalphStopBlock?: boolean } = {},
1858
2441
  ): Promise<Record<string, unknown> | null> {
1859
2442
  if (isStopExempt(payload)) {
1860
2443
  return null;
@@ -1865,7 +2448,13 @@ async function buildStopHookOutput(
1865
2448
  const threadId = readPayloadThreadId(payload);
1866
2449
  const execFollowupOutput = await buildExecFollowupStopOutput(cwd, canonicalSessionId);
1867
2450
  if (execFollowupOutput) return execFollowupOutput;
1868
- const ralphState = await readActiveRalphState(stateDir, canonicalSessionId);
2451
+ const ralphState = options.skipRalphStopBlock === true
2452
+ ? null
2453
+ : await readActiveRalphState(stateDir, canonicalSessionId, {
2454
+ payloadSessionId: sessionId,
2455
+ threadId,
2456
+ tmuxPaneId: safeString(process.env.TMUX_PANE).trim(),
2457
+ });
1869
2458
  if (!ralphState) {
1870
2459
  const autoresearchState = await readActiveAutoresearchState(cwd, canonicalSessionId);
1871
2460
  if (autoresearchState) {
@@ -1889,18 +2478,30 @@ async function buildStopHookOutput(
1889
2478
  }
1890
2479
  }
1891
2480
 
1892
- const teamWorkerOutput = await buildTeamWorkerStopOutput(cwd);
1893
- if (hasTeamWorkerContext() && teamWorkerOutput) {
2481
+ const teamWorkerDecision = await resolveTeamWorkerStopDecision(cwd);
2482
+ if (teamWorkerDecision.kind === "blocked") {
1894
2483
  return await returnPersistentStopBlock(
1895
2484
  payload,
1896
2485
  stateDir,
1897
2486
  "team-worker-stop",
1898
- safeString(teamWorkerOutput.stopReason),
1899
- teamWorkerOutput,
2487
+ safeString(teamWorkerDecision.output.stopReason),
2488
+ teamWorkerDecision.output,
1900
2489
  canonicalSessionId,
1901
- { allowRepeatDuringStopHook: false },
2490
+ { allowRepeatDuringStopHook: teamWorkerDecision.allowRepeatDuringStopHook },
1902
2491
  );
1903
2492
  }
2493
+ if (teamWorkerDecision.kind === "allowed") {
2494
+ try {
2495
+ await maybeNudgeLeaderForAllowedWorkerStop({
2496
+ stateDir: teamWorkerDecision.stateDir,
2497
+ logsDir: join(cwd, ".omx", "logs"),
2498
+ workerContext: teamWorkerDecision.workerContext,
2499
+ });
2500
+ } catch (err) {
2501
+ void err;
2502
+ }
2503
+ return null;
2504
+ }
1904
2505
 
1905
2506
  const autopilotOutput = await buildModeBasedStopOutput("autopilot", cwd, canonicalSessionId);
1906
2507
  if (autopilotOutput) {
@@ -2013,6 +2614,18 @@ async function buildStopHookOutput(
2013
2614
  const lastAssistantMessage = safeString(
2014
2615
  payload.last_assistant_message ?? payload.lastAssistantMessage,
2015
2616
  );
2617
+ const goalWorkflowStopOutput = await buildGoalWorkflowReconciliationStopOutput(payload, cwd);
2618
+ if (goalWorkflowStopOutput) {
2619
+ return await returnPersistentStopBlock(
2620
+ payload,
2621
+ stateDir,
2622
+ "goal-workflow-reconciliation-stop",
2623
+ safeString(goalWorkflowStopOutput.stopReason),
2624
+ goalWorkflowStopOutput,
2625
+ canonicalSessionId,
2626
+ { allowRepeatDuringStopHook: true },
2627
+ );
2628
+ }
2016
2629
  const autoNudgeConfig = await loadAutoNudgeConfig();
2017
2630
  const autoNudgePhase = await readStopAutoNudgePhase(cwd, canonicalSessionId, threadId);
2018
2631
 
@@ -2037,6 +2650,20 @@ async function buildStopHookOutput(
2037
2650
  );
2038
2651
  }
2039
2652
 
2653
+ const sloppyFallbackDiffFindings = findSloppyFallbackDiffFindings(cwd);
2654
+ const sloppyFallbackDiffOutput = buildSloppyFallbackDiffStopOutput(sloppyFallbackDiffFindings);
2655
+ if (sloppyFallbackDiffOutput) {
2656
+ return await returnPersistentStopBlock(
2657
+ payload,
2658
+ stateDir,
2659
+ "sloppy-fallback-diff-stop",
2660
+ JSON.stringify(sloppyFallbackDiffFindings),
2661
+ sloppyFallbackDiffOutput,
2662
+ canonicalSessionId,
2663
+ { allowRepeatDuringStopHook: true },
2664
+ );
2665
+ }
2666
+
2040
2667
  if (isFinalHandoffDocumentRefreshCandidate(lastAssistantMessage)) {
2041
2668
  const documentRefreshWarning = evaluateFinalHandoffDocumentRefresh(cwd, lastAssistantMessage);
2042
2669
  if (documentRefreshWarning) {
@@ -2092,6 +2719,7 @@ export async function dispatchCodexNativeHook(
2092
2719
  const omxEventName = mapCodexHookEventToOmxEvent(hookEventName);
2093
2720
  let skillState: SkillActiveState | null = null;
2094
2721
  let triageAdditionalContext: string | null = null;
2722
+ let goalWorkflowAdditionalContext: string | null = null;
2095
2723
 
2096
2724
  const nativeSessionId = safeString(payload.session_id ?? payload.sessionId).trim();
2097
2725
  const threadId = safeString(payload.thread_id ?? payload.threadId).trim();
@@ -2144,9 +2772,10 @@ export async function dispatchCodexNativeHook(
2144
2772
  }
2145
2773
 
2146
2774
  if (hookEventName === "Stop") {
2775
+ const inheritedSessionId = safeString(process.env.OMX_SESSION_ID || process.env.CODEX_SESSION_ID).trim();
2147
2776
  const stopCanonicalSessionId = await resolveInternalSessionIdForPayload(
2148
2777
  cwd,
2149
- readPayloadSessionId(payload),
2778
+ readPayloadSessionId(payload) || inheritedSessionId,
2150
2779
  );
2151
2780
  if (stopCanonicalSessionId) {
2152
2781
  canonicalSessionId = stopCanonicalSessionId;
@@ -2160,10 +2789,23 @@ export async function dispatchCodexNativeHook(
2160
2789
  const eventSessionId = canonicalSessionId || nativeSessionId || undefined;
2161
2790
  const sessionIdForState = canonicalSessionId || nativeSessionId;
2162
2791
  let outputJson: Record<string, unknown> | null = null;
2792
+ const isSubagentPromptSubmit = hookEventName === "UserPromptSubmit"
2793
+ ? await isNativeSubagentHook(cwd, canonicalSessionId, nativeSessionId, threadId)
2794
+ : false;
2795
+ const isSubagentStop = hookEventName === "Stop"
2796
+ ? (await Promise.all(
2797
+ [...new Set([
2798
+ canonicalSessionId,
2799
+ safeString(currentSessionState?.session_id).trim(),
2800
+ ].filter(Boolean))]
2801
+ .map((candidateSessionId) => isNativeSubagentHook(cwd, candidateSessionId, nativeSessionId, threadId)),
2802
+ )).some(Boolean)
2803
+ : false;
2163
2804
 
2164
2805
  if (hookEventName === "UserPromptSubmit") {
2165
2806
  const prompt = readPromptText(payload);
2166
- if (prompt) {
2807
+ goalWorkflowAdditionalContext = await buildGoalWorkflowReconciliationPromptWarning(cwd, prompt).catch(() => null);
2808
+ if (prompt && !isSubagentPromptSubmit) {
2167
2809
  skillState = buildNativeOutsideTmuxTeamPromptBlockState(
2168
2810
  prompt,
2169
2811
  cwd,
@@ -2180,7 +2822,7 @@ export async function dispatchCodexNativeHook(
2180
2822
  });
2181
2823
  }
2182
2824
  // --- Triage classifier (advisory-only, non-keyword prompts) ---
2183
- if (prompt && skillState === null) {
2825
+ if (prompt && skillState === null && !isSubagentPromptSubmit) {
2184
2826
  try {
2185
2827
  if (readTriageConfig().enabled) {
2186
2828
  const normalized = prompt.trim().toLowerCase();
@@ -2272,7 +2914,11 @@ export async function dispatchCodexNativeHook(
2272
2914
  mode: safeString(payload.mode).trim() || undefined,
2273
2915
  },
2274
2916
  );
2275
- await dispatchHookEvent(event, { cwd });
2917
+ await dispatchHookEventRuntime({
2918
+ event,
2919
+ cwd,
2920
+ allowTeamWorkerSideEffects: false,
2921
+ });
2276
2922
  }
2277
2923
 
2278
2924
  if ((hookEventName === "SessionStart" && !skipCanonicalSessionStartContext) || hookEventName === "UserPromptSubmit") {
@@ -2283,7 +2929,9 @@ export async function dispatchCodexNativeHook(
2283
2929
  canonicalSessionId,
2284
2930
  nativeSessionId: resolvedNativeSessionId || nativeSessionId,
2285
2931
  })
2286
- : (buildAdditionalContextMessage(readPromptText(payload), skillState, cwd, payload) ?? triageAdditionalContext);
2932
+ : isSubagentPromptSubmit
2933
+ ? null
2934
+ : (buildAdditionalContextMessage(readPromptText(payload), skillState, cwd, payload) ?? goalWorkflowAdditionalContext ?? triageAdditionalContext);
2287
2935
  if (additionalContext) {
2288
2936
  outputJson = {
2289
2937
  hookSpecificOutput: {
@@ -2301,7 +2949,9 @@ export async function dispatchCodexNativeHook(
2301
2949
  outputJson = buildNativePostToolUseOutput(payload);
2302
2950
  await handleTeamWorkerPostToolUseSuccess(payload, cwd);
2303
2951
  } else if (hookEventName === "Stop") {
2304
- outputJson = await buildStopHookOutput(payload, cwd, stateDir);
2952
+ outputJson = await buildStopHookOutput(payload, cwd, stateDir, {
2953
+ skipRalphStopBlock: isSubagentStop,
2954
+ });
2305
2955
  }
2306
2956
 
2307
2957
  return {