oh-my-codex 0.16.0 → 0.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (357) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/README.md +2 -2
  4. package/crates/omx-explore/src/main.rs +434 -28
  5. package/dist/agents/__tests__/native-config.test.js +50 -0
  6. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  7. package/dist/agents/native-config.d.ts.map +1 -1
  8. package/dist/agents/native-config.js +3 -2
  9. package/dist/agents/native-config.js.map +1 -1
  10. package/dist/cli/__tests__/codex-plugin-layout.test.js +1 -0
  11. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  12. package/dist/cli/__tests__/doctor-warning-copy.test.js +1 -1
  13. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  14. package/dist/cli/__tests__/explore.test.js +120 -3
  15. package/dist/cli/__tests__/explore.test.js.map +1 -1
  16. package/dist/cli/__tests__/imagegen-continuation.test.d.ts +2 -0
  17. package/dist/cli/__tests__/imagegen-continuation.test.d.ts.map +1 -0
  18. package/dist/cli/__tests__/imagegen-continuation.test.js +135 -0
  19. package/dist/cli/__tests__/imagegen-continuation.test.js.map +1 -0
  20. package/dist/cli/__tests__/index.test.js +182 -18
  21. package/dist/cli/__tests__/index.test.js.map +1 -1
  22. package/dist/cli/__tests__/launch-fallback.test.js +88 -2
  23. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  24. package/dist/cli/__tests__/ralph.test.js +62 -0
  25. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  26. package/dist/cli/__tests__/setup-install-mode.test.js +48 -0
  27. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  28. package/dist/cli/__tests__/setup-scope.test.js +12 -0
  29. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  30. package/dist/cli/__tests__/team.test.js +465 -12
  31. package/dist/cli/__tests__/team.test.js.map +1 -1
  32. package/dist/cli/__tests__/ultragoal.test.js +50 -5
  33. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  34. package/dist/cli/__tests__/uninstall.test.js +6 -2
  35. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  36. package/dist/cli/explore.d.ts.map +1 -1
  37. package/dist/cli/explore.js +211 -12
  38. package/dist/cli/explore.js.map +1 -1
  39. package/dist/cli/index.d.ts +11 -3
  40. package/dist/cli/index.d.ts.map +1 -1
  41. package/dist/cli/index.js +124 -18
  42. package/dist/cli/index.js.map +1 -1
  43. package/dist/cli/ralph.d.ts.map +1 -1
  44. package/dist/cli/ralph.js +37 -3
  45. package/dist/cli/ralph.js.map +1 -1
  46. package/dist/cli/setup.d.ts.map +1 -1
  47. package/dist/cli/setup.js +100 -9
  48. package/dist/cli/setup.js.map +1 -1
  49. package/dist/cli/team.d.ts +1 -0
  50. package/dist/cli/team.d.ts.map +1 -1
  51. package/dist/cli/team.js +42 -7
  52. package/dist/cli/team.js.map +1 -1
  53. package/dist/cli/ultragoal.d.ts +1 -1
  54. package/dist/cli/ultragoal.d.ts.map +1 -1
  55. package/dist/cli/ultragoal.js +29 -8
  56. package/dist/cli/ultragoal.js.map +1 -1
  57. package/dist/cli/uninstall.d.ts.map +1 -1
  58. package/dist/cli/uninstall.js +2 -1
  59. package/dist/cli/uninstall.js.map +1 -1
  60. package/dist/config/__tests__/codex-hooks.test.js +97 -2
  61. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  62. package/dist/config/__tests__/generator-idempotent.test.js +1 -1
  63. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  64. package/dist/config/__tests__/generator-notify.test.js +22 -0
  65. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  66. package/dist/config/__tests__/models.test.js +18 -1
  67. package/dist/config/__tests__/models.test.js.map +1 -1
  68. package/dist/config/__tests__/wiki-config-contract.test.js +2 -1
  69. package/dist/config/__tests__/wiki-config-contract.test.js.map +1 -1
  70. package/dist/config/codex-hooks.d.ts +17 -3
  71. package/dist/config/codex-hooks.d.ts.map +1 -1
  72. package/dist/config/codex-hooks.js +102 -2
  73. package/dist/config/codex-hooks.js.map +1 -1
  74. package/dist/config/generator.d.ts +4 -1
  75. package/dist/config/generator.d.ts.map +1 -1
  76. package/dist/config/generator.js +69 -12
  77. package/dist/config/generator.js.map +1 -1
  78. package/dist/config/models.d.ts +6 -0
  79. package/dist/config/models.d.ts.map +1 -1
  80. package/dist/config/models.js +37 -0
  81. package/dist/config/models.js.map +1 -1
  82. package/dist/exec/followup.d.ts +1 -0
  83. package/dist/exec/followup.d.ts.map +1 -1
  84. package/dist/exec/followup.js +9 -3
  85. package/dist/exec/followup.js.map +1 -1
  86. package/dist/hooks/__tests__/anti-slop-workflow.test.js +19 -0
  87. package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
  88. package/dist/hooks/__tests__/consensus-execution-handoff.test.js +19 -2
  89. package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
  90. package/dist/hooks/__tests__/deep-interview-contract.test.js +40 -0
  91. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  92. package/dist/hooks/__tests__/foreground-isolation-contract.test.d.ts +2 -0
  93. package/dist/hooks/__tests__/foreground-isolation-contract.test.d.ts.map +1 -0
  94. package/dist/hooks/__tests__/foreground-isolation-contract.test.js +28 -0
  95. package/dist/hooks/__tests__/foreground-isolation-contract.test.js.map +1 -0
  96. package/dist/hooks/__tests__/keyword-detector.test.js +37 -25
  97. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  98. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +10 -4
  99. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  100. package/dist/hooks/__tests__/session.test.js +32 -0
  101. package/dist/hooks/__tests__/session.test.js.map +1 -1
  102. package/dist/hooks/__tests__/wiki-docs-contract.test.js +6 -4
  103. package/dist/hooks/__tests__/wiki-docs-contract.test.js.map +1 -1
  104. package/dist/hooks/codebase-map.d.ts.map +1 -1
  105. package/dist/hooks/codebase-map.js +3 -2
  106. package/dist/hooks/codebase-map.js.map +1 -1
  107. package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
  108. package/dist/hooks/extensibility/dispatcher.js +6 -4
  109. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  110. package/dist/hooks/extensibility/logging.d.ts.map +1 -1
  111. package/dist/hooks/extensibility/logging.js +3 -2
  112. package/dist/hooks/extensibility/logging.js.map +1 -1
  113. package/dist/hooks/extensibility/sdk/paths.d.ts.map +1 -1
  114. package/dist/hooks/extensibility/sdk/paths.js +4 -3
  115. package/dist/hooks/extensibility/sdk/paths.js.map +1 -1
  116. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  117. package/dist/hooks/keyword-detector.js +2 -4
  118. package/dist/hooks/keyword-detector.js.map +1 -1
  119. package/dist/hooks/session.d.ts.map +1 -1
  120. package/dist/hooks/session.js +22 -12
  121. package/dist/hooks/session.js.map +1 -1
  122. package/dist/hud/__tests__/hud-tmux-injection.test.js +8 -7
  123. package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
  124. package/dist/hud/__tests__/reconcile.test.js +1 -1
  125. package/dist/hud/__tests__/state.test.js +24 -0
  126. package/dist/hud/__tests__/state.test.js.map +1 -1
  127. package/dist/hud/index.js +1 -1
  128. package/dist/hud/index.js.map +1 -1
  129. package/dist/hud/state.d.ts.map +1 -1
  130. package/dist/hud/state.js +22 -8
  131. package/dist/hud/state.js.map +1 -1
  132. package/dist/hud/tmux.js +1 -1
  133. package/dist/hud/tmux.js.map +1 -1
  134. package/dist/imagegen/continuation.d.ts +44 -0
  135. package/dist/imagegen/continuation.d.ts.map +1 -0
  136. package/dist/imagegen/continuation.js +220 -0
  137. package/dist/imagegen/continuation.js.map +1 -0
  138. package/dist/mcp/__tests__/bootstrap.test.js +47 -2
  139. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  140. package/dist/mcp/__tests__/server-lifecycle.test.js +49 -1
  141. package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
  142. package/dist/mcp/__tests__/state-server.test.js +145 -6
  143. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  144. package/dist/mcp/__tests__/wiki-server.test.js +97 -1
  145. package/dist/mcp/__tests__/wiki-server.test.js.map +1 -1
  146. package/dist/mcp/bootstrap.d.ts +2 -0
  147. package/dist/mcp/bootstrap.d.ts.map +1 -1
  148. package/dist/mcp/bootstrap.js +95 -15
  149. package/dist/mcp/bootstrap.js.map +1 -1
  150. package/dist/mcp/lifecycle-telemetry.d.ts +16 -0
  151. package/dist/mcp/lifecycle-telemetry.d.ts.map +1 -0
  152. package/dist/mcp/lifecycle-telemetry.js +95 -0
  153. package/dist/mcp/lifecycle-telemetry.js.map +1 -0
  154. package/dist/mcp/wiki-server.d.ts.map +1 -1
  155. package/dist/mcp/wiki-server.js +11 -2
  156. package/dist/mcp/wiki-server.js.map +1 -1
  157. package/dist/pipeline/__tests__/stages.test.js +274 -5
  158. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  159. package/dist/pipeline/stages/team-exec.d.ts +2 -0
  160. package/dist/pipeline/stages/team-exec.d.ts.map +1 -1
  161. package/dist/pipeline/stages/team-exec.js +51 -26
  162. package/dist/pipeline/stages/team-exec.js.map +1 -1
  163. package/dist/planning/__tests__/artifacts.test.js +138 -3
  164. package/dist/planning/__tests__/artifacts.test.js.map +1 -1
  165. package/dist/planning/__tests__/context-pack-status.test.d.ts +2 -0
  166. package/dist/planning/__tests__/context-pack-status.test.d.ts.map +1 -0
  167. package/dist/planning/__tests__/context-pack-status.test.js +271 -0
  168. package/dist/planning/__tests__/context-pack-status.test.js.map +1 -0
  169. package/dist/planning/artifacts.d.ts +12 -1
  170. package/dist/planning/artifacts.d.ts.map +1 -1
  171. package/dist/planning/artifacts.js +32 -9
  172. package/dist/planning/artifacts.js.map +1 -1
  173. package/dist/planning/context-pack-status.d.ts +42 -0
  174. package/dist/planning/context-pack-status.d.ts.map +1 -0
  175. package/dist/planning/context-pack-status.js +479 -0
  176. package/dist/planning/context-pack-status.js.map +1 -0
  177. package/dist/runtime/__tests__/process-tree.test.d.ts +2 -0
  178. package/dist/runtime/__tests__/process-tree.test.d.ts.map +1 -0
  179. package/dist/runtime/__tests__/process-tree.test.js +107 -0
  180. package/dist/runtime/__tests__/process-tree.test.js.map +1 -0
  181. package/dist/runtime/process-tree.d.ts +28 -0
  182. package/dist/runtime/process-tree.d.ts.map +1 -0
  183. package/dist/runtime/process-tree.js +230 -0
  184. package/dist/runtime/process-tree.js.map +1 -0
  185. package/dist/scripts/__tests__/codex-native-hook.test.js +267 -2
  186. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  187. package/dist/scripts/__tests__/notify-state-io.test.d.ts +2 -0
  188. package/dist/scripts/__tests__/notify-state-io.test.d.ts.map +1 -0
  189. package/dist/scripts/__tests__/notify-state-io.test.js +40 -0
  190. package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -0
  191. package/dist/scripts/codex-execution-surface.d.ts +1 -1
  192. package/dist/scripts/codex-execution-surface.d.ts.map +1 -1
  193. package/dist/scripts/codex-execution-surface.js.map +1 -1
  194. package/dist/scripts/codex-native-hook.d.ts +1 -1
  195. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  196. package/dist/scripts/codex-native-hook.js +141 -9
  197. package/dist/scripts/codex-native-hook.js.map +1 -1
  198. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  199. package/dist/scripts/notify-hook/managed-tmux.js +6 -9
  200. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  201. package/dist/scripts/notify-hook/process-runner.d.ts.map +1 -1
  202. package/dist/scripts/notify-hook/process-runner.js +4 -1
  203. package/dist/scripts/notify-hook/process-runner.js.map +1 -1
  204. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  205. package/dist/scripts/notify-hook/state-io.js +4 -7
  206. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  207. package/dist/scripts/notify-hook.js +25 -3
  208. package/dist/scripts/notify-hook.js.map +1 -1
  209. package/dist/scripts/verify-native-agents.d.ts.map +1 -1
  210. package/dist/scripts/verify-native-agents.js +3 -1
  211. package/dist/scripts/verify-native-agents.js.map +1 -1
  212. package/dist/sidecar/__tests__/tmux.test.js +1 -1
  213. package/dist/sidecar/__tests__/tmux.test.js.map +1 -1
  214. package/dist/sidecar/tmux.js +1 -1
  215. package/dist/sidecar/tmux.js.map +1 -1
  216. package/dist/state/__tests__/operations.test.js +79 -0
  217. package/dist/state/__tests__/operations.test.js.map +1 -1
  218. package/dist/state/__tests__/skill-active.test.js +10 -18
  219. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  220. package/dist/state/__tests__/workflow-transition.test.js +45 -1
  221. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  222. package/dist/state/operations.d.ts.map +1 -1
  223. package/dist/state/operations.js +1 -20
  224. package/dist/state/operations.js.map +1 -1
  225. package/dist/state/skill-active.d.ts +1 -0
  226. package/dist/state/skill-active.d.ts.map +1 -1
  227. package/dist/state/skill-active.js +28 -18
  228. package/dist/state/skill-active.js.map +1 -1
  229. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  230. package/dist/state/workflow-transition-reconcile.js +3 -2
  231. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  232. package/dist/state/workflow-transition.js +2 -2
  233. package/dist/state/workflow-transition.js.map +1 -1
  234. package/dist/team/__tests__/approved-execution.test.js +96 -0
  235. package/dist/team/__tests__/approved-execution.test.js.map +1 -1
  236. package/dist/team/__tests__/followup-planner.test.js +16 -0
  237. package/dist/team/__tests__/followup-planner.test.js.map +1 -1
  238. package/dist/team/__tests__/model-contract.test.js +16 -0
  239. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  240. package/dist/team/__tests__/repo-aware-decomposition.test.js +20 -0
  241. package/dist/team/__tests__/repo-aware-decomposition.test.js.map +1 -1
  242. package/dist/team/__tests__/runtime-cli.test.js +16 -0
  243. package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
  244. package/dist/team/__tests__/runtime.test.js +209 -11
  245. package/dist/team/__tests__/runtime.test.js.map +1 -1
  246. package/dist/team/__tests__/scaling.test.js +110 -0
  247. package/dist/team/__tests__/scaling.test.js.map +1 -1
  248. package/dist/team/__tests__/tmux-session.test.js +9 -0
  249. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  250. package/dist/team/__tests__/worker-runtime-identity.test.js +6 -0
  251. package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
  252. package/dist/team/approved-execution.d.ts +13 -0
  253. package/dist/team/approved-execution.d.ts.map +1 -1
  254. package/dist/team/approved-execution.js +40 -22
  255. package/dist/team/approved-execution.js.map +1 -1
  256. package/dist/team/followup-planner.d.ts +1 -0
  257. package/dist/team/followup-planner.d.ts.map +1 -1
  258. package/dist/team/followup-planner.js +9 -9
  259. package/dist/team/followup-planner.js.map +1 -1
  260. package/dist/team/model-contract.d.ts +1 -1
  261. package/dist/team/model-contract.d.ts.map +1 -1
  262. package/dist/team/model-contract.js +4 -3
  263. package/dist/team/model-contract.js.map +1 -1
  264. package/dist/team/repo-aware-decomposition.d.ts +1 -0
  265. package/dist/team/repo-aware-decomposition.d.ts.map +1 -1
  266. package/dist/team/repo-aware-decomposition.js +5 -1
  267. package/dist/team/repo-aware-decomposition.js.map +1 -1
  268. package/dist/team/runtime-cli.d.ts +4 -0
  269. package/dist/team/runtime-cli.d.ts.map +1 -1
  270. package/dist/team/runtime-cli.js +14 -1
  271. package/dist/team/runtime-cli.js.map +1 -1
  272. package/dist/team/runtime.d.ts +1 -0
  273. package/dist/team/runtime.d.ts.map +1 -1
  274. package/dist/team/runtime.js +46 -16
  275. package/dist/team/runtime.js.map +1 -1
  276. package/dist/team/scaling.d.ts.map +1 -1
  277. package/dist/team/scaling.js +13 -6
  278. package/dist/team/scaling.js.map +1 -1
  279. package/dist/team/tmux-session.d.ts.map +1 -1
  280. package/dist/team/tmux-session.js +7 -0
  281. package/dist/team/tmux-session.js.map +1 -1
  282. package/dist/ultragoal/__tests__/artifacts.test.js +129 -4
  283. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  284. package/dist/ultragoal/__tests__/docs-contract.test.d.ts +2 -0
  285. package/dist/ultragoal/__tests__/docs-contract.test.d.ts.map +1 -0
  286. package/dist/ultragoal/__tests__/docs-contract.test.js +31 -0
  287. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -0
  288. package/dist/ultragoal/artifacts.d.ts +6 -2
  289. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  290. package/dist/ultragoal/artifacts.js +108 -4
  291. package/dist/ultragoal/artifacts.js.map +1 -1
  292. package/dist/utils/paths.d.ts +3 -1
  293. package/dist/utils/paths.d.ts.map +1 -1
  294. package/dist/utils/paths.js +6 -2
  295. package/dist/utils/paths.js.map +1 -1
  296. package/dist/verification/__tests__/ci-rust-gates.test.js +44 -14
  297. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  298. package/dist/wiki/__tests__/ingest.test.js +35 -1
  299. package/dist/wiki/__tests__/ingest.test.js.map +1 -1
  300. package/dist/wiki/__tests__/lint.test.js +14 -1
  301. package/dist/wiki/__tests__/lint.test.js.map +1 -1
  302. package/dist/wiki/__tests__/query.test.js +28 -3
  303. package/dist/wiki/__tests__/query.test.js.map +1 -1
  304. package/dist/wiki/__tests__/session-hooks.test.js +30 -2
  305. package/dist/wiki/__tests__/session-hooks.test.js.map +1 -1
  306. package/dist/wiki/__tests__/storage.test.js +62 -22
  307. package/dist/wiki/__tests__/storage.test.js.map +1 -1
  308. package/dist/wiki/index.d.ts +2 -2
  309. package/dist/wiki/index.d.ts.map +1 -1
  310. package/dist/wiki/index.js +2 -2
  311. package/dist/wiki/index.js.map +1 -1
  312. package/dist/wiki/ingest.js +2 -2
  313. package/dist/wiki/ingest.js.map +1 -1
  314. package/dist/wiki/lifecycle.d.ts +5 -0
  315. package/dist/wiki/lifecycle.d.ts.map +1 -1
  316. package/dist/wiki/lifecycle.js +31 -4
  317. package/dist/wiki/lifecycle.js.map +1 -1
  318. package/dist/wiki/lint.d.ts.map +1 -1
  319. package/dist/wiki/lint.js +12 -8
  320. package/dist/wiki/lint.js.map +1 -1
  321. package/dist/wiki/query.d.ts.map +1 -1
  322. package/dist/wiki/query.js +3 -2
  323. package/dist/wiki/query.js.map +1 -1
  324. package/dist/wiki/storage.d.ts +4 -0
  325. package/dist/wiki/storage.d.ts.map +1 -1
  326. package/dist/wiki/storage.js +54 -18
  327. package/dist/wiki/storage.js.map +1 -1
  328. package/package.json +1 -1
  329. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  330. package/plugins/oh-my-codex/skills/ai-slop-cleaner/SKILL.md +9 -0
  331. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +25 -2
  332. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +1 -1
  333. package/plugins/oh-my-codex/skills/plan/SKILL.md +7 -4
  334. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +13 -3
  335. package/plugins/oh-my-codex/skills/team/SKILL.md +2 -2
  336. package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +11 -7
  337. package/plugins/oh-my-codex/skills/visual-ralph/SKILL.md +8 -0
  338. package/plugins/oh-my-codex/skills/wiki/SKILL.md +5 -5
  339. package/prompts/planner.md +1 -1
  340. package/skills/ai-slop-cleaner/SKILL.md +9 -0
  341. package/skills/deep-interview/SKILL.md +25 -2
  342. package/skills/omx-setup/SKILL.md +1 -1
  343. package/skills/plan/SKILL.md +7 -4
  344. package/skills/ralplan/SKILL.md +13 -3
  345. package/skills/team/SKILL.md +2 -2
  346. package/skills/ultragoal/SKILL.md +11 -7
  347. package/skills/visual-ralph/SKILL.md +8 -0
  348. package/skills/wiki/SKILL.md +5 -5
  349. package/src/scripts/__tests__/codex-native-hook.test.ts +302 -2
  350. package/src/scripts/__tests__/notify-state-io.test.ts +73 -0
  351. package/src/scripts/codex-execution-surface.ts +2 -0
  352. package/src/scripts/codex-native-hook.ts +163 -16
  353. package/src/scripts/notify-hook/managed-tmux.ts +6 -7
  354. package/src/scripts/notify-hook/process-runner.ts +4 -1
  355. package/src/scripts/notify-hook/state-io.ts +5 -7
  356. package/src/scripts/notify-hook.ts +26 -3
  357. package/src/scripts/verify-native-agents.ts +3 -1
@@ -54,14 +54,24 @@ The consensus workflow:
54
54
  d. Return to Critic evaluation
55
55
  e. Repeat this loop until Critic returns `APPROVE` or 5 iterations are reached
56
56
  f. If 5 iterations are reached without `APPROVE`, present the best version to the user
57
- 6. On Critic approval *(--interactive only)*: If `--interactive` is set, use the structured question UI to present the plan with approval options (Approve and execute via ralph / Approve and implement via team / Request changes / Reject). Final plan must include ADR (Decision, Drivers, Alternatives considered, Why chosen, Consequences, Follow-ups), an explicit available-agent-types roster, concrete follow-up staffing guidance for both `ralph` and `team`, suggested reasoning levels by lane, explicit `omx team` / `$team` launch hints, and a concrete **team verification** path. Otherwise, output the final plan and stop.
58
- 7. *(--interactive only)* User chooses: Approve (ralph or team), Request changes, or Reject
59
- 8. *(--interactive only)* On approval: invoke `$ralph` for sequential execution or `$team` for parallel team execution with the explicit available-agent-types roster, reasoning-by-lane guidance, role/staffing allocation guidance, launch hints, and verification-path guidance from the approved plan -- never implement directly
57
+ 6. On Critic approval *(--interactive only)*: If `--interactive` is set, use the structured question UI to present the plan with approval options (Approve and execute via ralph / Approve and implement via team / Start a goal-mode follow-up / Request changes / Reject). Final plan must include ADR (Decision, Drivers, Alternatives considered, Why chosen, Consequences, Follow-ups), an explicit available-agent-types roster, concrete follow-up staffing guidance for both `ralph` and `team`, suggested reasoning levels by lane, explicit `omx team` / `$team` launch hints, a concrete **team verification** path, and a product-facing **Goal-Mode Follow-up Suggestions** section. Recommend `$ultragoal` by default for goal-mode follow-up, use `$autoresearch-goal` instead when the context is a research project, and use `$performance-goal` instead when the context is an optimization or performance project. Otherwise, output the final plan and stop.
58
+ 7. *(--interactive only)* User chooses: Approve (ralph, team, or a goal-mode follow-up), Request changes, or Reject
59
+ 8. *(--interactive only)* On approval: invoke `$ralph` for sequential execution, `$team` for parallel team execution, or the selected goal-mode follow-up (`$ultragoal`, `$autoresearch-goal`, or `$performance-goal`) with the approved plan and matching success/evaluator context -- never implement directly. Preserve the explicit available-agent-types roster, reasoning-by-lane guidance, role/staffing allocation guidance, launch hints, and verification-path guidance from the approved plan for Ralph/team paths.
60
60
 
61
61
  > **Important:** Steps 3 and 4 MUST run sequentially. Do NOT issue both agent calls in the same parallel batch. Always await the Architect result before invoking Critic.
62
62
 
63
63
  Follow the Plan skill's full documentation for consensus mode details.
64
64
 
65
+ ## Goal-Mode Follow-up Suggestions
66
+
67
+ When ralplan outputs a final handoff or asks the user to choose a next lane, include product-facing goal-mode suggestions alongside the existing Ralph and team options:
68
+
69
+ - `$ultragoal` — **default goal-mode follow-up** for implementation or general goal-oriented follow-up plans that should become durable Codex/OMX goals with sequential completion tracking.
70
+ - `$autoresearch-goal` — research-project follow-up when the plan centers on a question, literature/reference gathering, evaluator-backed research, or a professor/critic-style research deliverable.
71
+ - `$performance-goal` — optimization/performance follow-up when the plan centers on speed, latency, throughput, memory, benchmark, or other measurable performance work.
72
+
73
+ Keep `$ralph` and `$team` as first-class execution options where appropriate: use Ralph for persistent single-owner completion/verification pressure and team for coordinated parallel implementation. Do not present the goal-mode options as replacements for Ralph/team when the task is mainly implementation delivery; present them as better fits when durable goal tracking, research validation, or performance evaluators are the primary need.
74
+
65
75
  ## Pre-context Intake
66
76
 
67
77
  Before consensus planning or execution handoff, ensure a grounded context snapshot exists:
@@ -183,7 +183,7 @@ Default-model rule:
183
183
  Thinking-level rule (critical):
184
184
  - **No model-name heuristic mapping.**
185
185
  - Team runtime must **not** infer `model_reasoning_effort` from model-name substrings (e.g., `spark`, `high-capability`, `mini`).
186
- - When the leader assigns teammate roles/tasks, OMX allocates **per-worker reasoning effort dynamically** from the resolved worker role (`low`, `medium`, `high`).
186
+ - When the leader assigns teammate roles/tasks, OMX allocates **per-worker reasoning effort dynamically** from the resolved worker role and `agentReasoning` overrides (`low`, `medium`, `high`, `xhigh`).
187
187
  - Explicit launch args still win: if `OMX_TEAM_WORKER_LAUNCH_ARGS` already includes `-c model_reasoning_effort=...`, that explicit value overrides dynamic allocation for every worker.
188
188
 
189
189
  Normalization requirements:
@@ -191,7 +191,7 @@ Normalization requirements:
191
191
  - Remove duplicate/conflicting model flags
192
192
  - Emit exactly one final canonical flag: `--model <value>`
193
193
  - Preserve unrelated args in worker launch config
194
- - If explicit reasoning exists, preserve canonical `-c model_reasoning_effort="<level>"`; otherwise inject the worker role's default reasoning level
194
+ - If explicit reasoning exists, preserve canonical `-c model_reasoning_effort="<level>"`; otherwise inject the worker role's default or `agentReasoning`-overridden reasoning level
195
195
 
196
196
  ## Required Lifecycle (Operator Contract)
197
197
 
@@ -9,7 +9,7 @@ Use when the user asks for `ultragoal`, `create-goals`, `complete-goals`, durabl
9
9
 
10
10
  ## Purpose
11
11
 
12
- `ultragoal` turns a brief into repo-native artifacts and then drives one goal at a time through Codex goal tools:
12
+ `ultragoal` turns a brief into repo-native artifacts and then drives a Codex goal safely through goal tools. New plans default to an aggregate Codex goal for the whole ultragoal run while OMX tracks G001/G002 story progress in the ledger.
13
13
 
14
14
  - `.omx/ultragoal/brief.md`
15
15
  - `.omx/ultragoal/goals.json`
@@ -21,6 +21,7 @@ Use when the user asks for `ultragoal`, `create-goals`, `complete-goals`, durabl
21
21
  - `omx ultragoal create-goals --brief "<brief>"`
22
22
  - `omx ultragoal create-goals --brief-file <path>`
23
23
  - `cat <brief> | omx ultragoal create-goals --from-stdin`
24
+ - `omx ultragoal create-goals --codex-goal-mode per-story --brief "<brief>"` only when one fresh Codex thread per story is explicitly preferred
24
25
  2. Inspect `.omx/ultragoal/goals.json` and refine if needed.
25
26
 
26
27
  ## Complete goals
@@ -30,20 +31,23 @@ Loop until `omx ultragoal status` reports all goals complete:
30
31
  1. Run `omx ultragoal complete-goals`.
31
32
  2. Read the printed handoff.
32
33
  3. Call `get_goal`.
33
- 4. If no active Codex goal exists, call `create_goal` with the printed payload.
34
- 5. Complete that single goal only.
35
- 6. Run a completion audit against the objective and real artifacts/tests.
36
- 7. When complete, call `update_goal({status: "complete"})`, then call `get_goal` again for a fresh completion snapshot.
34
+ 4. If no active Codex goal exists, call `create_goal` with the printed payload. In aggregate mode, if the same aggregate Codex objective is already active, continue the current OMX story without creating a new Codex goal.
35
+ 5. Complete the current OMX story only.
36
+ 6. Run a completion audit against the story objective and real artifacts/tests.
37
+ 7. In aggregate mode, do **not** call `update_goal` for intermediate stories; checkpoint with a fresh `get_goal` snapshot whose aggregate objective is still `active`. On the final story only, call `update_goal({status: "complete"})`, then call `get_goal` again for a fresh `complete` snapshot.
37
38
  8. Checkpoint the durable ledger with that snapshot:
38
39
  `omx ultragoal checkpoint --goal-id <id> --status complete --evidence "<evidence>" --codex-goal-json <get_goal-json-or-path>`
39
40
  9. If blocked or failed, checkpoint failure:
40
41
  `omx ultragoal checkpoint --goal-id <id> --status failed --evidence "<blocker/evidence>"`
41
- 10. Resume failed goals with `omx ultragoal complete-goals --retry-failed`.
42
+ 10. For legacy per-story completed-goal blockers, preserve the non-terminal blocker with:
43
+ `omx ultragoal checkpoint --goal-id <id> --status blocked --evidence "<completed legacy Codex goal blocks create_goal in this thread>" --codex-goal-json <get_goal-json-or-path>`
44
+ 11. Resume failed goals with `omx ultragoal complete-goals --retry-failed`.
42
45
 
43
46
  ## Constraints
44
47
 
45
48
  - The shell command cannot directly invoke Codex interactive `/goal`; it emits a model-facing handoff for the active Codex agent.
46
49
  - Never call `create_goal` when `get_goal` reports a different active goal.
47
- - Never call `update_goal` unless the current goal is actually complete.
50
+ - Never call `update_goal` unless the aggregate run or legacy per-story goal is actually complete.
51
+ - In aggregate mode, intermediate story checkpoints require a matching `active` Codex snapshot; final story completion requires a matching `complete` snapshot after `update_goal`.
48
52
  - Completion checkpoints require read-only Codex snapshot reconciliation: pass fresh `get_goal` JSON/path with `--codex-goal-json`; shell commands and hooks must not mutate Codex goal state.
49
53
  - Treat `ledger.jsonl` as the durable audit trail; checkpoint after every success or failure.
@@ -65,6 +65,14 @@ Prompt requirements:
65
65
  - forbid logos/watermarks/unrequested brand marks,
66
66
  - ask imagegen to avoid impossible UI details or unreadable text.
67
67
 
68
+ When running under OMX CLI/runtime and a generated reference is part of an active Ralph-style loop, queue a continuation checkpoint before invoking the built-in image tool:
69
+
70
+ ```bash
71
+ omx imagegen continuation <session-id> --artifact <slug-or-filename> --generated-dir "$CODEX_HOME/generated_images/<session>" --work-dir ".omx/artifacts/visual-ralph/<slug>"
72
+ ```
73
+
74
+ This helper records `.omx/state/sessions/<session>/imagegen-pending.json` and uses the existing Stop-hook follow-up queue. It exists because built-in image generation may have to end the assistant turn immediately; the next Stop checkpoint should resume artifact recovery, copy the generated image into the workspace, and run the required visual QA/verdict gate instead of relying on a manual `$ralph` re-prompt.
75
+
68
76
  For project-bound implementation, copy the approved reference into the workspace, for example under `.omx/artifacts/visual-ralph/<slug>/reference.png`. Never leave the implementation reference only in `$CODEX_HOME/generated_images/...`.
69
77
 
70
78
  ### 3. Require explicit user approval
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: wiki
3
- description: Persistent markdown project wiki stored under .omx/wiki with keyword search and lifecycle capture
3
+ description: Persistent markdown project wiki stored under repository omx_wiki with keyword search and lifecycle capture
4
4
  triggers: ["wiki add", "wiki lint", "wiki query", "wiki read", "wiki delete"]
5
5
  ---
6
6
 
@@ -42,9 +42,9 @@ wiki_refresh()
42
42
  `architecture`, `decision`, `pattern`, `debugging`, `environment`, `session-log`, `reference`, `convention`
43
43
 
44
44
  ## Storage
45
- - Pages: `.omx/wiki/*.md`
46
- - Index: `.omx/wiki/index.md`
47
- - Log: `.omx/wiki/log.md`
45
+ - Pages: `omx_wiki/*.md`
46
+ - Index: `omx_wiki/index.md`
47
+ - Log: `omx_wiki/log.md`
48
48
 
49
49
  ## Cross-References
50
50
  Use `[[page-name]]` wiki-link syntax to create cross-references between pages.
@@ -54,4 +54,4 @@ At session end, discoveries can be captured as `session-log-*` pages. Configure
54
54
 
55
55
  ## Hard Constraints
56
56
  - No vector embeddings — query uses keyword + tag matching only
57
- - Wiki files remain local project state under `.omx/wiki/`
57
+ - Wiki files are repository project knowledge under `omx_wiki/`; legacy `.omx/wiki/` is read-only compatibility input when no canonical wiki exists
@@ -1,5 +1,5 @@
1
1
  import assert from "node:assert/strict";
2
- import { execFileSync } from "node:child_process";
2
+ import { execFileSync, spawnSync } from "node:child_process";
3
3
  import { existsSync } from "node:fs";
4
4
  import { chmod, mkdir, mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises";
5
5
  import { tmpdir } from "node:os";
@@ -24,6 +24,8 @@ import { writeSessionStart } from "../../hooks/session.js";
24
24
  import { resetTriageConfigCache } from "../../hooks/triage-config.js";
25
25
  import { executeStateOperation } from "../../state/operations.js";
26
26
  import { OMX_TMUX_HUD_OWNER_ENV } from "../../hud/reconcile.js";
27
+ import { writePage } from "../../wiki/storage.js";
28
+ import { WIKI_SCHEMA_VERSION } from "../../wiki/types.js";
27
29
 
28
30
  function nativeHookScriptPath(): string {
29
31
  return join(process.cwd(), "dist", "scripts", "codex-native-hook.js");
@@ -188,6 +190,8 @@ const TEAM_ENV_KEYS = [
188
190
  "OMX_TEAM_STATE_ROOT",
189
191
  "OMX_TEAM_LEADER_CWD",
190
192
  "OMX_SESSION_ID",
193
+ "OMX_ROOT",
194
+ "OMX_STATE_ROOT",
191
195
  "SESSION_ID",
192
196
  "OMX_QUESTION_RETURN_PANE",
193
197
  "OMX_LEADER_PANE_ID",
@@ -223,6 +227,8 @@ describe("codex native hook config", () => {
223
227
  "PreToolUse",
224
228
  "PostToolUse",
225
229
  "UserPromptSubmit",
230
+ "PreCompact",
231
+ "PostCompact",
226
232
  "Stop",
227
233
  ]);
228
234
 
@@ -437,14 +443,142 @@ describe("codex native hook dispatch", () => {
437
443
  }
438
444
  });
439
445
 
446
+ it("logs Stop dispatch failures without foreground stderr noise", async () => {
447
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-stop-dispatch-silent-"));
448
+ try {
449
+ const result = spawnSync(process.execPath, [nativeHookScriptPath()], {
450
+ cwd,
451
+ input: JSON.stringify({
452
+ hook_event_name: "Stop",
453
+ cwd,
454
+ session_id: "sess-cli-stop-dispatch-silent",
455
+ thread_id: "thread-cli-stop-dispatch-silent",
456
+ turn_id: "turn-cli-stop-dispatch-silent",
457
+ }),
458
+ encoding: "utf-8",
459
+ stdio: ["pipe", "pipe", "pipe"],
460
+ env: {
461
+ ...process.env,
462
+ NODE_ENV: "test",
463
+ OMX_NATIVE_HOOK_TEST_THROW_STOP_DISPATCH: "1",
464
+ },
465
+ });
466
+
467
+ assert.equal(result.status, 0, result.stderr || result.stdout);
468
+ assert.equal(result.stderr, "");
469
+ const output = parseSingleJsonStdout(result.stdout);
470
+ assert.equal(output.stopReason, "native_stop_dispatch_failure");
471
+
472
+ const logFiles = await readdir(join(cwd, ".omx", "logs"));
473
+ assert.equal(logFiles.some((name) => /^native-hook-\d{4}-\d{2}-\d{2}\.jsonl$/.test(name)), true);
474
+ } finally {
475
+ await rm(cwd, { recursive: true, force: true });
476
+ }
477
+ });
478
+
479
+ it("keeps non-Stop dispatch failures fail-closed without foreground stderr noise", async () => {
480
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-pretool-dispatch-silent-"));
481
+ try {
482
+ const result = spawnSync(process.execPath, [nativeHookScriptPath()], {
483
+ cwd,
484
+ input: JSON.stringify({
485
+ hook_event_name: "PreToolUse",
486
+ cwd,
487
+ session_id: "sess-cli-pretool-dispatch-silent",
488
+ thread_id: "thread-cli-pretool-dispatch-silent",
489
+ turn_id: "turn-cli-pretool-dispatch-silent",
490
+ tool_name: "Bash",
491
+ tool_input: { command: "pwd" },
492
+ }),
493
+ encoding: "utf-8",
494
+ stdio: ["pipe", "pipe", "pipe"],
495
+ env: {
496
+ ...process.env,
497
+ NODE_ENV: "test",
498
+ OMX_NATIVE_HOOK_TEST_THROW_DISPATCH: "1",
499
+ },
500
+ });
501
+
502
+ assert.equal(result.status, 1);
503
+ assert.equal(result.stdout, "");
504
+ assert.equal(result.stderr, "");
505
+
506
+ const logFiles = await readdir(join(cwd, ".omx", "logs"));
507
+ assert.equal(logFiles.some((name) => /^native-hook-\d{4}-\d{2}-\d{2}\.jsonl$/.test(name)), true);
508
+ } finally {
509
+ await rm(cwd, { recursive: true, force: true });
510
+ }
511
+ });
512
+
440
513
  it("maps Codex events onto OMX logical surfaces", () => {
441
514
  assert.equal(mapCodexHookEventToOmxEvent("SessionStart"), "session-start");
442
515
  assert.equal(mapCodexHookEventToOmxEvent("UserPromptSubmit"), "keyword-detector");
443
516
  assert.equal(mapCodexHookEventToOmxEvent("PreToolUse"), "pre-tool-use");
444
517
  assert.equal(mapCodexHookEventToOmxEvent("PostToolUse"), "post-tool-use");
518
+ assert.equal(mapCodexHookEventToOmxEvent("PreCompact"), "pre-compact");
519
+ assert.equal(mapCodexHookEventToOmxEvent("PostCompact"), "post-compact");
445
520
  assert.equal(mapCodexHookEventToOmxEvent("Stop"), "stop");
446
521
  });
447
522
 
523
+
524
+
525
+ it("returns bounded wiki context for PreCompact", async () => {
526
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-precompact-"));
527
+ try {
528
+ writePage(cwd, {
529
+ filename: "architecture.md",
530
+ frontmatter: {
531
+ title: "Architecture",
532
+ tags: ["architecture"],
533
+ created: "2026-05-08T00:00:00.000Z",
534
+ updated: "2026-05-08T00:00:00.000Z",
535
+ sources: [],
536
+ links: [],
537
+ category: "architecture",
538
+ confidence: "high",
539
+ schemaVersion: WIKI_SCHEMA_VERSION,
540
+ },
541
+ content: "\n# Architecture\n\nCompaction-relevant architecture note.\n",
542
+ });
543
+
544
+ const result = await dispatchCodexNativeHook({
545
+ hook_event_name: "PreCompact",
546
+ cwd,
547
+ session_id: "sess-precompact",
548
+ });
549
+
550
+ assert.equal(result.hookEventName, "PreCompact");
551
+ assert.equal(result.omxEventName, "pre-compact");
552
+ const additionalContext = (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } } | null)
553
+ ?.hookSpecificOutput?.additionalContext ?? "";
554
+ assert.match(additionalContext, /Wiki: 1 pages/);
555
+ assert.match(additionalContext, /architecture/);
556
+ } finally {
557
+ await rm(cwd, { recursive: true, force: true });
558
+ }
559
+ });
560
+
561
+ it("returns a PostCompact nudge to write compaction artifacts to omx_wiki", async () => {
562
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-postcompact-"));
563
+ try {
564
+ const result = await dispatchCodexNativeHook({
565
+ hook_event_name: "PostCompact",
566
+ cwd,
567
+ session_id: "sess-postcompact",
568
+ });
569
+
570
+ assert.equal(result.hookEventName, "PostCompact");
571
+ assert.equal(result.omxEventName, "post-compact");
572
+ const additionalContext = (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } } | null)
573
+ ?.hookSpecificOutput?.additionalContext ?? "";
574
+ assert.match(additionalContext, /PostCompact Nudge/);
575
+ assert.match(additionalContext, /omx_wiki/);
576
+ assert.match(additionalContext, /compaction artifacts/);
577
+ } finally {
578
+ await rm(cwd, { recursive: true, force: true });
579
+ }
580
+ });
581
+
448
582
  it("writes SessionStart state against the long-lived session owner pid and injects environment context", async () => {
449
583
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-start-"));
450
584
  try {
@@ -1032,7 +1166,12 @@ describe("codex native hook dispatch", () => {
1032
1166
  assert.ok(result.outputJson, "UserPromptSubmit should emit developer context");
1033
1167
  assert.match(JSON.stringify(result.outputJson), /skill: ralplan activated and initial state initialized at \.omx\/state\/sessions\/sess-1\/ralplan-state\.json; write subsequent updates via omx_state MCP\./);
1034
1168
 
1035
- const statePath = join(cwd, ".omx", "state", "skill-active-state.json");
1169
+ assert.equal(
1170
+ existsSync(join(cwd, ".omx", "state", "skill-active-state.json")),
1171
+ false,
1172
+ "session-scoped keyword activation should not write root skill-active-state.json",
1173
+ );
1174
+ const statePath = join(cwd, ".omx", "state", "sessions", "sess-1", "skill-active-state.json");
1036
1175
  assert.equal(existsSync(statePath), true);
1037
1176
  const state = JSON.parse(await readFile(statePath, "utf-8")) as {
1038
1177
  skill?: string;
@@ -8409,6 +8548,167 @@ exit 0
8409
8548
  }
8410
8549
  });
8411
8550
 
8551
+ it("clears stale root skill-active state when current session ralplan is terminal", async () => {
8552
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-root-skill-terminal-"));
8553
+ try {
8554
+ const stateDir = join(cwd, ".omx", "state");
8555
+ const sessionId = "sess-stop-terminal-ralplan";
8556
+ await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
8557
+ await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
8558
+ await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
8559
+ active: false,
8560
+ mode: "ralplan",
8561
+ current_phase: "completed",
8562
+ lifecycle_outcome: "finished",
8563
+ run_outcome: "finish",
8564
+ final_artifact: "proposed_plan",
8565
+ });
8566
+ await writeJson(join(stateDir, "skill-active-state.json"), {
8567
+ active: true,
8568
+ skill: "ultrawork",
8569
+ phase: "planning",
8570
+ source: "keyword-detector",
8571
+ active_skills: [
8572
+ { skill: "ultrawork", phase: "planning", active: true },
8573
+ ],
8574
+ });
8575
+
8576
+ const result = await dispatchCodexNativeHook(
8577
+ {
8578
+ hook_event_name: "Stop",
8579
+ cwd,
8580
+ session_id: sessionId,
8581
+ thread_id: "thread-stop-terminal-ralplan",
8582
+ turn_id: "turn-stop-terminal-ralplan-1",
8583
+ last_assistant_message: "Done.",
8584
+ },
8585
+ { cwd },
8586
+ );
8587
+
8588
+ assert.equal(result.omxEventName, "stop");
8589
+ assert.equal(result.outputJson, null);
8590
+
8591
+ const rootSkillState = JSON.parse(
8592
+ await readFile(join(stateDir, "skill-active-state.json"), "utf-8"),
8593
+ ) as { active?: boolean; active_skills?: unknown[]; reconciliation_reason?: string };
8594
+ assert.equal(rootSkillState.active, false);
8595
+ assert.deepEqual(rootSkillState.active_skills, []);
8596
+ assert.equal(rootSkillState.reconciliation_reason, "stop_hook_session_state_terminal");
8597
+ } finally {
8598
+ await rm(cwd, { recursive: true, force: true });
8599
+ }
8600
+ });
8601
+
8602
+ it("preserves legitimate session-scoped ultrawork blocking while reconciling root skill-active state", async () => {
8603
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-active-root-skill-session-mode-"));
8604
+ try {
8605
+ const stateDir = join(cwd, ".omx", "state");
8606
+ const sessionId = "sess-stop-active-ultrawork";
8607
+ await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
8608
+ await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
8609
+ await writeJson(join(stateDir, "sessions", sessionId, "ultrawork-state.json"), {
8610
+ active: true,
8611
+ mode: "ultrawork",
8612
+ current_phase: "executing",
8613
+ session_id: sessionId,
8614
+ });
8615
+ await writeJson(join(stateDir, "skill-active-state.json"), {
8616
+ active: true,
8617
+ skill: "ultrawork",
8618
+ phase: "planning",
8619
+ source: "keyword-detector",
8620
+ active_skills: [
8621
+ { skill: "ultrawork", phase: "planning", active: true, session_id: sessionId },
8622
+ ],
8623
+ });
8624
+
8625
+ const result = await dispatchCodexNativeHook(
8626
+ {
8627
+ hook_event_name: "Stop",
8628
+ cwd,
8629
+ session_id: sessionId,
8630
+ thread_id: "thread-stop-active-ultrawork",
8631
+ turn_id: "turn-stop-active-ultrawork-1",
8632
+ },
8633
+ { cwd },
8634
+ );
8635
+
8636
+ assert.equal(result.omxEventName, "stop");
8637
+ assert.deepEqual(result.outputJson, {
8638
+ decision: "block",
8639
+ reason: "OMX ultrawork is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.",
8640
+ stopReason: "ultrawork_executing",
8641
+ systemMessage: "OMX ultrawork is still active (phase: executing).",
8642
+ });
8643
+
8644
+ const rootSkillState = JSON.parse(
8645
+ await readFile(join(stateDir, "skill-active-state.json"), "utf-8"),
8646
+ ) as { active?: boolean; active_skills?: Array<{ skill?: string }> };
8647
+ assert.equal(rootSkillState.active, true);
8648
+ assert.deepEqual(rootSkillState.active_skills?.map((entry) => entry.skill), ["ultrawork"]);
8649
+ } finally {
8650
+ await rm(cwd, { recursive: true, force: true });
8651
+ }
8652
+ });
8653
+
8654
+ it("reconciles stale root skill-active state under OMX_ROOT boxed state", async () => {
8655
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-boxed-source-"));
8656
+ const omxRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-boxed-root-"));
8657
+ const previousOmxRoot = process.env.OMX_ROOT;
8658
+ try {
8659
+ process.env.OMX_ROOT = omxRoot;
8660
+ const stateDir = join(omxRoot, ".omx", "state");
8661
+ const sourceStateDir = join(cwd, ".omx", "state");
8662
+ const sessionId = "sess-stop-boxed-ralplan";
8663
+ await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
8664
+ await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
8665
+ await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
8666
+ active: false,
8667
+ mode: "ralplan",
8668
+ current_phase: "completed",
8669
+ lifecycle_outcome: "finished",
8670
+ run_outcome: "finish",
8671
+ });
8672
+ await writeJson(join(stateDir, "skill-active-state.json"), {
8673
+ active: true,
8674
+ skill: "ultrawork",
8675
+ phase: "planning",
8676
+ source: "keyword-detector",
8677
+ active_skills: [
8678
+ { skill: "ultrawork", phase: "planning", active: true },
8679
+ ],
8680
+ });
8681
+
8682
+ const result = await dispatchCodexNativeHook(
8683
+ {
8684
+ hook_event_name: "Stop",
8685
+ cwd,
8686
+ session_id: sessionId,
8687
+ thread_id: "thread-stop-boxed-ralplan",
8688
+ turn_id: "turn-stop-boxed-ralplan-1",
8689
+ last_assistant_message: "Done.",
8690
+ },
8691
+ { cwd },
8692
+ );
8693
+
8694
+ assert.equal(result.omxEventName, "stop");
8695
+ assert.equal(result.outputJson, null);
8696
+
8697
+ const boxedRootSkillState = JSON.parse(
8698
+ await readFile(join(stateDir, "skill-active-state.json"), "utf-8"),
8699
+ ) as { active?: boolean; active_skills?: unknown[]; reconciliation_reason?: string };
8700
+ assert.equal(boxedRootSkillState.active, false);
8701
+ assert.deepEqual(boxedRootSkillState.active_skills, []);
8702
+ assert.equal(boxedRootSkillState.reconciliation_reason, "stop_hook_session_state_terminal");
8703
+ assert.equal(existsSync(join(sourceStateDir, "skill-active-state.json")), false);
8704
+ } finally {
8705
+ if (previousOmxRoot === undefined) delete process.env.OMX_ROOT;
8706
+ else process.env.OMX_ROOT = previousOmxRoot;
8707
+ await rm(cwd, { recursive: true, force: true });
8708
+ await rm(omxRoot, { recursive: true, force: true });
8709
+ }
8710
+ });
8711
+
8412
8712
  it("auto-continues native Stop for permission-seeking prompts even outside OMX runtime", async () => {
8413
8713
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-plain-session-"));
8414
8714
  try {
@@ -0,0 +1,73 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+
7
+ import {
8
+ getScopedStatePath,
9
+ readScopedJsonIfExists,
10
+ resolveScopedStateDir,
11
+ writeScopedJson,
12
+ } from '../notify-hook/state-io.js';
13
+
14
+ describe('notify-hook state I/O session authority', () => {
15
+ it('uses an explicit session id before the current session pointer', async () => {
16
+ const wd = await mkdtemp(join(tmpdir(), 'omx-notify-state-io-'));
17
+ try {
18
+ const stateDir = join(wd, '.omx', 'state');
19
+ await mkdir(join(stateDir, 'sessions', 'sess-current'), { recursive: true });
20
+ await mkdir(join(stateDir, 'sessions', 'sess-explicit'), { recursive: true });
21
+ await writeFile(
22
+ join(stateDir, 'session.json'),
23
+ JSON.stringify({ session_id: 'sess-current', cwd: wd }, null, 2),
24
+ 'utf-8',
25
+ );
26
+
27
+ assert.equal(
28
+ await resolveScopedStateDir(stateDir, 'sess-explicit'),
29
+ join(stateDir, 'sessions', 'sess-explicit'),
30
+ );
31
+ assert.equal(
32
+ await getScopedStatePath(stateDir, 'hud-state.json', 'sess-explicit'),
33
+ join(stateDir, 'sessions', 'sess-explicit', 'hud-state.json'),
34
+ );
35
+
36
+ await writeScopedJson(stateDir, 'hud-state.json', 'sess-explicit', { turn_count: 3 });
37
+ const explicitHud = JSON.parse(
38
+ await readFile(join(stateDir, 'sessions', 'sess-explicit', 'hud-state.json'), 'utf-8'),
39
+ ) as { turn_count?: unknown };
40
+ assert.equal(explicitHud.turn_count, 3);
41
+ } finally {
42
+ await rm(wd, { recursive: true, force: true });
43
+ }
44
+ });
45
+
46
+ it('does not read current-session data when an explicit session has no state file', async () => {
47
+ const wd = await mkdtemp(join(tmpdir(), 'omx-notify-state-io-missing-'));
48
+ try {
49
+ const stateDir = join(wd, '.omx', 'state');
50
+ await mkdir(join(stateDir, 'sessions', 'sess-current'), { recursive: true });
51
+ await writeFile(
52
+ join(stateDir, 'session.json'),
53
+ JSON.stringify({ session_id: 'sess-current', cwd: wd }, null, 2),
54
+ 'utf-8',
55
+ );
56
+ await writeFile(
57
+ join(stateDir, 'sessions', 'sess-current', 'auto-nudge-state.json'),
58
+ JSON.stringify({ count: 9 }, null, 2),
59
+ 'utf-8',
60
+ );
61
+
62
+ const value = await readScopedJsonIfExists(
63
+ stateDir,
64
+ 'auto-nudge-state.json',
65
+ 'sess-explicit',
66
+ null,
67
+ );
68
+ assert.equal(value, null);
69
+ } finally {
70
+ await rm(wd, { recursive: true, force: true });
71
+ }
72
+ });
73
+ });
@@ -6,6 +6,8 @@ type CodexHookEventName =
6
6
  | "PreToolUse"
7
7
  | "PostToolUse"
8
8
  | "UserPromptSubmit"
9
+ | "PreCompact"
10
+ | "PostCompact"
9
11
  | "Stop";
10
12
 
11
13
  type CodexHookPayload = Record<string, unknown>;