oh-my-codex 0.18.0 → 0.18.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 (410) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +45 -19
  4. package/crates/omx-api/src/lib.rs +66 -9
  5. package/crates/omx-sparkshell/src/exec.rs +125 -3
  6. package/crates/omx-sparkshell/src/main.rs +126 -36
  7. package/crates/omx-sparkshell/tests/execution.rs +225 -1
  8. package/dist/agents/__tests__/definitions.test.js +14 -0
  9. package/dist/agents/__tests__/definitions.test.js.map +1 -1
  10. package/dist/agents/__tests__/native-config.test.js +19 -0
  11. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  12. package/dist/agents/definitions.d.ts.map +1 -1
  13. package/dist/agents/definitions.js +30 -0
  14. package/dist/agents/definitions.js.map +1 -1
  15. package/dist/agents/native-config.d.ts +1 -0
  16. package/dist/agents/native-config.d.ts.map +1 -1
  17. package/dist/agents/native-config.js +4 -0
  18. package/dist/agents/native-config.js.map +1 -1
  19. package/dist/catalog/__tests__/generator.test.js +4 -0
  20. package/dist/catalog/__tests__/generator.test.js.map +1 -1
  21. package/dist/cli/__tests__/codex-plugin-layout.test.js +15 -7
  22. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  23. package/dist/cli/__tests__/doctor-warning-copy.test.js +137 -8
  24. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  25. package/dist/cli/__tests__/index.test.js +203 -15
  26. package/dist/cli/__tests__/index.test.js.map +1 -1
  27. package/dist/cli/__tests__/install-docs-contract.test.d.ts +2 -0
  28. package/dist/cli/__tests__/install-docs-contract.test.d.ts.map +1 -0
  29. package/dist/cli/__tests__/install-docs-contract.test.js +55 -0
  30. package/dist/cli/__tests__/install-docs-contract.test.js.map +1 -0
  31. package/dist/cli/__tests__/launch-fallback.test.js +163 -0
  32. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  33. package/dist/cli/__tests__/question.test.js +29 -43
  34. package/dist/cli/__tests__/question.test.js.map +1 -1
  35. package/dist/cli/__tests__/setup-install-mode.test.js +94 -35
  36. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  37. package/dist/cli/__tests__/sparkshell-cli.test.js +20 -1
  38. package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
  39. package/dist/cli/__tests__/sparkshell-packaging.test.js +1 -0
  40. package/dist/cli/__tests__/sparkshell-packaging.test.js.map +1 -1
  41. package/dist/cli/__tests__/ultragoal.test.js +227 -4
  42. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  43. package/dist/cli/__tests__/update.test.js +72 -1
  44. package/dist/cli/__tests__/update.test.js.map +1 -1
  45. package/dist/cli/codex-feature-probe.d.ts +5 -0
  46. package/dist/cli/codex-feature-probe.d.ts.map +1 -1
  47. package/dist/cli/codex-feature-probe.js +13 -7
  48. package/dist/cli/codex-feature-probe.js.map +1 -1
  49. package/dist/cli/doctor.d.ts +7 -0
  50. package/dist/cli/doctor.d.ts.map +1 -1
  51. package/dist/cli/doctor.js +297 -17
  52. package/dist/cli/doctor.js.map +1 -1
  53. package/dist/cli/index.d.ts +9 -1
  54. package/dist/cli/index.d.ts.map +1 -1
  55. package/dist/cli/index.js +465 -110
  56. package/dist/cli/index.js.map +1 -1
  57. package/dist/cli/plugin-marketplace.d.ts +2 -0
  58. package/dist/cli/plugin-marketplace.d.ts.map +1 -1
  59. package/dist/cli/plugin-marketplace.js +15 -1
  60. package/dist/cli/plugin-marketplace.js.map +1 -1
  61. package/dist/cli/setup.d.ts.map +1 -1
  62. package/dist/cli/setup.js +71 -11
  63. package/dist/cli/setup.js.map +1 -1
  64. package/dist/cli/sparkshell.d.ts +7 -1
  65. package/dist/cli/sparkshell.d.ts.map +1 -1
  66. package/dist/cli/sparkshell.js +13 -3
  67. package/dist/cli/sparkshell.js.map +1 -1
  68. package/dist/cli/ultragoal.d.ts +1 -1
  69. package/dist/cli/ultragoal.d.ts.map +1 -1
  70. package/dist/cli/ultragoal.js +184 -10
  71. package/dist/cli/ultragoal.js.map +1 -1
  72. package/dist/cli/update.d.ts +2 -0
  73. package/dist/cli/update.d.ts.map +1 -1
  74. package/dist/cli/update.js +14 -3
  75. package/dist/cli/update.js.map +1 -1
  76. package/dist/compat/__tests__/doctor-contract.test.js +3 -0
  77. package/dist/compat/__tests__/doctor-contract.test.js.map +1 -1
  78. package/dist/config/__tests__/codex-feature-flags.test.js +11 -1
  79. package/dist/config/__tests__/codex-feature-flags.test.js.map +1 -1
  80. package/dist/config/__tests__/codex-hooks.test.js +22 -11
  81. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  82. package/dist/config/__tests__/commit-lore-guard.test.d.ts +2 -0
  83. package/dist/config/__tests__/commit-lore-guard.test.d.ts.map +1 -0
  84. package/dist/config/__tests__/commit-lore-guard.test.js +20 -0
  85. package/dist/config/__tests__/commit-lore-guard.test.js.map +1 -0
  86. package/dist/config/codex-feature-flags.d.ts +4 -0
  87. package/dist/config/codex-feature-flags.d.ts.map +1 -1
  88. package/dist/config/codex-feature-flags.js +4 -0
  89. package/dist/config/codex-feature-flags.js.map +1 -1
  90. package/dist/config/codex-hooks.d.ts +1 -0
  91. package/dist/config/codex-hooks.d.ts.map +1 -1
  92. package/dist/config/codex-hooks.js +8 -10
  93. package/dist/config/codex-hooks.js.map +1 -1
  94. package/dist/config/commit-lore-guard.d.ts +1 -0
  95. package/dist/config/commit-lore-guard.d.ts.map +1 -1
  96. package/dist/config/commit-lore-guard.js +29 -3
  97. package/dist/config/commit-lore-guard.js.map +1 -1
  98. package/dist/config/generator.d.ts +17 -1
  99. package/dist/config/generator.d.ts.map +1 -1
  100. package/dist/config/generator.js +124 -11
  101. package/dist/config/generator.js.map +1 -1
  102. package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js +21 -0
  103. package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js.map +1 -1
  104. package/dist/goal-workflows/codex-goal-snapshot.d.ts +4 -0
  105. package/dist/goal-workflows/codex-goal-snapshot.d.ts.map +1 -1
  106. package/dist/goal-workflows/codex-goal-snapshot.js +50 -3
  107. package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -1
  108. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +27 -6
  109. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  110. package/dist/hooks/__tests__/consensus-execution-handoff.test.d.ts +1 -1
  111. package/dist/hooks/__tests__/consensus-execution-handoff.test.js +13 -11
  112. package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
  113. package/dist/hooks/__tests__/deep-interview-contract.test.js +4 -3
  114. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  115. package/dist/hooks/__tests__/keyword-detector.test.js +173 -17
  116. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  117. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +33 -0
  118. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  119. package/dist/hooks/__tests__/prometheus-strict-contract.test.d.ts +2 -0
  120. package/dist/hooks/__tests__/prometheus-strict-contract.test.d.ts.map +1 -0
  121. package/dist/hooks/__tests__/prometheus-strict-contract.test.js +320 -0
  122. package/dist/hooks/__tests__/prometheus-strict-contract.test.js.map +1 -0
  123. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js +12 -0
  124. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js.map +1 -1
  125. package/dist/hooks/__tests__/research-workflow-boundaries.test.d.ts +2 -0
  126. package/dist/hooks/__tests__/research-workflow-boundaries.test.d.ts.map +1 -0
  127. package/dist/hooks/__tests__/research-workflow-boundaries.test.js +35 -0
  128. package/dist/hooks/__tests__/research-workflow-boundaries.test.js.map +1 -0
  129. package/dist/hooks/extensibility/__tests__/dispatcher.test.js +26 -3
  130. package/dist/hooks/extensibility/__tests__/dispatcher.test.js.map +1 -1
  131. package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
  132. package/dist/hooks/extensibility/dispatcher.js +29 -14
  133. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  134. package/dist/hooks/keyword-detector.d.ts +1 -1
  135. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  136. package/dist/hooks/keyword-detector.js +36 -9
  137. package/dist/hooks/keyword-detector.js.map +1 -1
  138. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  139. package/dist/hooks/keyword-registry.js +1 -0
  140. package/dist/hooks/keyword-registry.js.map +1 -1
  141. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  142. package/dist/hooks/prompt-guidance-contract.js +14 -2
  143. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  144. package/dist/hud/__tests__/hud-tmux-injection.test.js +36 -8
  145. package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
  146. package/dist/hud/__tests__/reconcile.test.js +122 -11
  147. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  148. package/dist/hud/__tests__/render.test.js +84 -0
  149. package/dist/hud/__tests__/render.test.js.map +1 -1
  150. package/dist/hud/__tests__/resource-leak-watch.test.d.ts +2 -0
  151. package/dist/hud/__tests__/resource-leak-watch.test.d.ts.map +1 -0
  152. package/dist/hud/__tests__/resource-leak-watch.test.js +28 -0
  153. package/dist/hud/__tests__/resource-leak-watch.test.js.map +1 -0
  154. package/dist/hud/__tests__/state.test.js +51 -1
  155. package/dist/hud/__tests__/state.test.js.map +1 -1
  156. package/dist/hud/__tests__/tmux.test.js +69 -23
  157. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  158. package/dist/hud/index.d.ts +2 -2
  159. package/dist/hud/index.d.ts.map +1 -1
  160. package/dist/hud/index.js +17 -6
  161. package/dist/hud/index.js.map +1 -1
  162. package/dist/hud/reconcile.d.ts.map +1 -1
  163. package/dist/hud/reconcile.js +6 -3
  164. package/dist/hud/reconcile.js.map +1 -1
  165. package/dist/hud/render.d.ts.map +1 -1
  166. package/dist/hud/render.js +26 -0
  167. package/dist/hud/render.js.map +1 -1
  168. package/dist/hud/state.d.ts +2 -1
  169. package/dist/hud/state.d.ts.map +1 -1
  170. package/dist/hud/state.js +62 -1
  171. package/dist/hud/state.js.map +1 -1
  172. package/dist/hud/tmux.d.ts +10 -3
  173. package/dist/hud/tmux.d.ts.map +1 -1
  174. package/dist/hud/tmux.js +60 -11
  175. package/dist/hud/tmux.js.map +1 -1
  176. package/dist/hud/types.d.ts +22 -0
  177. package/dist/hud/types.d.ts.map +1 -1
  178. package/dist/hud/types.js.map +1 -1
  179. package/dist/notifications/__tests__/http-client-resource.test.d.ts +2 -0
  180. package/dist/notifications/__tests__/http-client-resource.test.d.ts.map +1 -0
  181. package/dist/notifications/__tests__/http-client-resource.test.js +41 -0
  182. package/dist/notifications/__tests__/http-client-resource.test.js.map +1 -0
  183. package/dist/notifications/__tests__/verbosity.test.js +20 -0
  184. package/dist/notifications/__tests__/verbosity.test.js.map +1 -1
  185. package/dist/notifications/config.d.ts.map +1 -1
  186. package/dist/notifications/config.js +6 -3
  187. package/dist/notifications/config.js.map +1 -1
  188. package/dist/notifications/http-client.d.ts.map +1 -1
  189. package/dist/notifications/http-client.js +78 -27
  190. package/dist/notifications/http-client.js.map +1 -1
  191. package/dist/notifications/types.d.ts +2 -0
  192. package/dist/notifications/types.d.ts.map +1 -1
  193. package/dist/openclaw/__tests__/dispatcher.test.js +49 -1
  194. package/dist/openclaw/__tests__/dispatcher.test.js.map +1 -1
  195. package/dist/openclaw/dispatcher.d.ts +7 -4
  196. package/dist/openclaw/dispatcher.d.ts.map +1 -1
  197. package/dist/openclaw/dispatcher.js +32 -69
  198. package/dist/openclaw/dispatcher.js.map +1 -1
  199. package/dist/pipeline/__tests__/orchestrator.test.js +128 -4
  200. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  201. package/dist/pipeline/__tests__/stages.test.js +460 -9
  202. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  203. package/dist/pipeline/index.d.ts +8 -2
  204. package/dist/pipeline/index.d.ts.map +1 -1
  205. package/dist/pipeline/index.js +5 -2
  206. package/dist/pipeline/index.js.map +1 -1
  207. package/dist/pipeline/orchestrator.d.ts +5 -4
  208. package/dist/pipeline/orchestrator.d.ts.map +1 -1
  209. package/dist/pipeline/orchestrator.js +85 -17
  210. package/dist/pipeline/orchestrator.js.map +1 -1
  211. package/dist/pipeline/stages/code-review.d.ts +2 -2
  212. package/dist/pipeline/stages/code-review.d.ts.map +1 -1
  213. package/dist/pipeline/stages/code-review.js +5 -3
  214. package/dist/pipeline/stages/code-review.js.map +1 -1
  215. package/dist/pipeline/stages/deep-interview.d.ts +15 -0
  216. package/dist/pipeline/stages/deep-interview.d.ts.map +1 -0
  217. package/dist/pipeline/stages/deep-interview.js +32 -0
  218. package/dist/pipeline/stages/deep-interview.js.map +1 -0
  219. package/dist/pipeline/stages/ralph-verify.d.ts +5 -5
  220. package/dist/pipeline/stages/ralph-verify.d.ts.map +1 -1
  221. package/dist/pipeline/stages/ralph-verify.js +2 -2
  222. package/dist/pipeline/stages/ralph-verify.js.map +1 -1
  223. package/dist/pipeline/stages/ralplan.d.ts.map +1 -1
  224. package/dist/pipeline/stages/ralplan.js +41 -6
  225. package/dist/pipeline/stages/ralplan.js.map +1 -1
  226. package/dist/pipeline/stages/ultragoal.d.ts +19 -0
  227. package/dist/pipeline/stages/ultragoal.d.ts.map +1 -0
  228. package/dist/pipeline/stages/ultragoal.js +38 -0
  229. package/dist/pipeline/stages/ultragoal.js.map +1 -0
  230. package/dist/pipeline/stages/ultraqa.d.ts +30 -0
  231. package/dist/pipeline/stages/ultraqa.d.ts.map +1 -0
  232. package/dist/pipeline/stages/ultraqa.js +46 -0
  233. package/dist/pipeline/stages/ultraqa.js.map +1 -0
  234. package/dist/pipeline/types.d.ts +8 -6
  235. package/dist/pipeline/types.d.ts.map +1 -1
  236. package/dist/pipeline/types.js +2 -2
  237. package/dist/question/__tests__/ui.test.js +43 -10
  238. package/dist/question/__tests__/ui.test.js.map +1 -1
  239. package/dist/question/ui.d.ts +12 -0
  240. package/dist/question/ui.d.ts.map +1 -1
  241. package/dist/question/ui.js +83 -46
  242. package/dist/question/ui.js.map +1 -1
  243. package/dist/ralplan/__tests__/runtime.test.js +200 -10
  244. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  245. package/dist/ralplan/consensus-gate.d.ts +23 -0
  246. package/dist/ralplan/consensus-gate.d.ts.map +1 -0
  247. package/dist/ralplan/consensus-gate.js +212 -0
  248. package/dist/ralplan/consensus-gate.js.map +1 -0
  249. package/dist/ralplan/runtime.d.ts +25 -0
  250. package/dist/ralplan/runtime.d.ts.map +1 -1
  251. package/dist/ralplan/runtime.js +144 -8
  252. package/dist/ralplan/runtime.js.map +1 -1
  253. package/dist/scripts/__tests__/codex-native-hook.test.js +1358 -79
  254. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  255. package/dist/scripts/__tests__/docs-site-contract.test.d.ts +2 -0
  256. package/dist/scripts/__tests__/docs-site-contract.test.d.ts.map +1 -0
  257. package/dist/scripts/__tests__/docs-site-contract.test.js +42 -0
  258. package/dist/scripts/__tests__/docs-site-contract.test.js.map +1 -0
  259. package/dist/scripts/__tests__/notify-dispatcher.test.js +115 -2
  260. package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -1
  261. package/dist/scripts/__tests__/run-test-files.test.js +57 -0
  262. package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
  263. package/dist/scripts/__tests__/smoke-packed-install.test.js +23 -1
  264. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  265. package/dist/scripts/__tests__/verify-native-agents.test.js +18 -3
  266. package/dist/scripts/__tests__/verify-native-agents.test.js.map +1 -1
  267. package/dist/scripts/cleanup-explore-harness.js +1 -0
  268. package/dist/scripts/cleanup-explore-harness.js.map +1 -1
  269. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  270. package/dist/scripts/codex-native-hook.js +372 -44
  271. package/dist/scripts/codex-native-hook.js.map +1 -1
  272. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  273. package/dist/scripts/codex-native-pre-post.js +9 -1
  274. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  275. package/dist/scripts/notify-dispatcher.js +188 -4
  276. package/dist/scripts/notify-dispatcher.js.map +1 -1
  277. package/dist/scripts/notify-hook/process-runner.d.ts.map +1 -1
  278. package/dist/scripts/notify-hook/process-runner.js +39 -17
  279. package/dist/scripts/notify-hook/process-runner.js.map +1 -1
  280. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  281. package/dist/scripts/notify-hook/team-dispatch.js +9 -5
  282. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  283. package/dist/scripts/notify-hook/team-tmux-guard.d.ts +1 -1
  284. package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
  285. package/dist/scripts/notify-hook/team-tmux-guard.js +7 -1
  286. package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
  287. package/dist/scripts/run-test-files.js +13 -0
  288. package/dist/scripts/run-test-files.js.map +1 -1
  289. package/dist/scripts/smoke-packed-install.d.ts +3 -0
  290. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  291. package/dist/scripts/smoke-packed-install.js +99 -1
  292. package/dist/scripts/smoke-packed-install.js.map +1 -1
  293. package/dist/scripts/sync-plugin-mirror.js +2 -2
  294. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  295. package/dist/scripts/verify-native-agents.js +2 -2
  296. package/dist/scripts/verify-native-agents.js.map +1 -1
  297. package/dist/sidecar/__tests__/resource-leak-watch.test.d.ts +2 -0
  298. package/dist/sidecar/__tests__/resource-leak-watch.test.d.ts.map +1 -0
  299. package/dist/sidecar/__tests__/resource-leak-watch.test.js +38 -0
  300. package/dist/sidecar/__tests__/resource-leak-watch.test.js.map +1 -0
  301. package/dist/sidecar/index.d.ts +1 -1
  302. package/dist/sidecar/index.d.ts.map +1 -1
  303. package/dist/sidecar/index.js +29 -12
  304. package/dist/sidecar/index.js.map +1 -1
  305. package/dist/state/__tests__/operations-ralph-phase.test.js +88 -1
  306. package/dist/state/__tests__/operations-ralph-phase.test.js.map +1 -1
  307. package/dist/state/__tests__/workflow-transition.test.js +6 -0
  308. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  309. package/dist/state/operations.d.ts.map +1 -1
  310. package/dist/state/operations.js +11 -0
  311. package/dist/state/operations.js.map +1 -1
  312. package/dist/state/workflow-transition.d.ts +1 -1
  313. package/dist/state/workflow-transition.d.ts.map +1 -1
  314. package/dist/state/workflow-transition.js +7 -0
  315. package/dist/state/workflow-transition.js.map +1 -1
  316. package/dist/subagents/tracker.d.ts.map +1 -1
  317. package/dist/subagents/tracker.js +4 -3
  318. package/dist/subagents/tracker.js.map +1 -1
  319. package/dist/team/__tests__/runtime.test.js +36 -44
  320. package/dist/team/__tests__/runtime.test.js.map +1 -1
  321. package/dist/team/__tests__/tmux-session.test.js +163 -15
  322. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  323. package/dist/team/runtime.d.ts.map +1 -1
  324. package/dist/team/runtime.js +10 -20
  325. package/dist/team/runtime.js.map +1 -1
  326. package/dist/team/tmux-session.d.ts.map +1 -1
  327. package/dist/team/tmux-session.js +51 -21
  328. package/dist/team/tmux-session.js.map +1 -1
  329. package/dist/ultragoal/__tests__/artifacts.test.js +764 -10
  330. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  331. package/dist/ultragoal/__tests__/docs-contract.test.js +57 -1
  332. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
  333. package/dist/ultragoal/__tests__/steering-fixtures.d.ts +68 -0
  334. package/dist/ultragoal/__tests__/steering-fixtures.d.ts.map +1 -0
  335. package/dist/ultragoal/__tests__/steering-fixtures.js +259 -0
  336. package/dist/ultragoal/__tests__/steering-fixtures.js.map +1 -0
  337. package/dist/ultragoal/__tests__/steering-fixtures.test.d.ts +2 -0
  338. package/dist/ultragoal/__tests__/steering-fixtures.test.d.ts.map +1 -0
  339. package/dist/ultragoal/__tests__/steering-fixtures.test.js +65 -0
  340. package/dist/ultragoal/__tests__/steering-fixtures.test.js.map +1 -0
  341. package/dist/ultragoal/artifacts.d.ts +97 -2
  342. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  343. package/dist/ultragoal/artifacts.js +837 -256
  344. package/dist/ultragoal/artifacts.js.map +1 -1
  345. package/dist/utils/__tests__/sleep-resource.test.d.ts +2 -0
  346. package/dist/utils/__tests__/sleep-resource.test.d.ts.map +1 -0
  347. package/dist/utils/__tests__/sleep-resource.test.js +39 -0
  348. package/dist/utils/__tests__/sleep-resource.test.js.map +1 -0
  349. package/dist/utils/sleep.d.ts.map +1 -1
  350. package/dist/utils/sleep.js +17 -6
  351. package/dist/utils/sleep.js.map +1 -1
  352. package/package.json +2 -1
  353. package/plugins/oh-my-codex/.codex-plugin/plugin.json +4 -3
  354. package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +56 -0
  355. package/plugins/oh-my-codex/hooks/hooks.json +77 -0
  356. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +92 -50
  357. package/plugins/oh-my-codex/skills/autoresearch/SKILL.md +4 -0
  358. package/plugins/oh-my-codex/skills/autoresearch-goal/SKILL.md +1 -1
  359. package/plugins/oh-my-codex/skills/best-practice-research/SKILL.md +1 -1
  360. package/plugins/oh-my-codex/skills/cancel/SKILL.md +2 -2
  361. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +8 -8
  362. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +1 -1
  363. package/plugins/oh-my-codex/skills/pipeline/SKILL.md +23 -12
  364. package/plugins/oh-my-codex/skills/plan/SKILL.md +8 -8
  365. package/plugins/oh-my-codex/skills/prometheus-strict/README.md +35 -0
  366. package/plugins/oh-my-codex/skills/prometheus-strict/SKILL.md +219 -0
  367. package/plugins/oh-my-codex/skills/ralph/SKILL.md +7 -0
  368. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +22 -7
  369. package/plugins/oh-my-codex/skills/team/SKILL.md +1 -1
  370. package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +38 -4
  371. package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +1 -1
  372. package/prompts/planner.md +1 -1
  373. package/prompts/prometheus-strict-metis.md +274 -0
  374. package/prompts/prometheus-strict-momus.md +82 -0
  375. package/prompts/prometheus-strict-oracle.md +107 -0
  376. package/prompts/researcher.md +22 -3
  377. package/skills/autopilot/SKILL.md +92 -50
  378. package/skills/autoresearch/SKILL.md +4 -0
  379. package/skills/autoresearch-goal/SKILL.md +1 -1
  380. package/skills/best-practice-research/SKILL.md +1 -1
  381. package/skills/cancel/SKILL.md +2 -2
  382. package/skills/deep-interview/SKILL.md +8 -8
  383. package/skills/omx-setup/SKILL.md +1 -1
  384. package/skills/pipeline/SKILL.md +23 -12
  385. package/skills/plan/SKILL.md +8 -8
  386. package/skills/prometheus-strict/README.md +35 -0
  387. package/skills/prometheus-strict/SKILL.md +219 -0
  388. package/skills/ralph/SKILL.md +7 -0
  389. package/skills/ralplan/SKILL.md +22 -7
  390. package/skills/team/SKILL.md +1 -1
  391. package/skills/ultragoal/SKILL.md +38 -4
  392. package/skills/ultrawork/SKILL.md +1 -1
  393. package/src/scripts/__tests__/codex-native-hook.test.ts +1757 -210
  394. package/src/scripts/__tests__/docs-site-contract.test.ts +47 -0
  395. package/src/scripts/__tests__/notify-dispatcher.test.ts +132 -3
  396. package/src/scripts/__tests__/run-test-files.test.ts +67 -0
  397. package/src/scripts/__tests__/smoke-packed-install.test.ts +31 -0
  398. package/src/scripts/__tests__/verify-native-agents.test.ts +23 -3
  399. package/src/scripts/cleanup-explore-harness.ts +1 -0
  400. package/src/scripts/codex-native-hook.ts +393 -40
  401. package/src/scripts/codex-native-pre-post.ts +16 -1
  402. package/src/scripts/notify-dispatcher.ts +202 -4
  403. package/src/scripts/notify-hook/process-runner.ts +40 -16
  404. package/src/scripts/notify-hook/team-dispatch.ts +9 -5
  405. package/src/scripts/notify-hook/team-tmux-guard.ts +7 -0
  406. package/src/scripts/run-test-files.ts +13 -0
  407. package/src/scripts/smoke-packed-install.ts +105 -0
  408. package/src/scripts/sync-plugin-mirror.ts +3 -3
  409. package/src/scripts/verify-native-agents.ts +2 -2
  410. package/templates/catalog-manifest.json +22 -0
package/dist/cli/index.js CHANGED
@@ -7,6 +7,7 @@ import { basename, dirname, join, posix, win32 } from "path";
7
7
  import { existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from "fs";
8
8
  import { copyFile, cp, lstat, mkdir, readFile, readdir, rm, symlink, writeFile } from "fs/promises";
9
9
  import { constants as osConstants, homedir } from "os";
10
+ import { createHash } from "crypto";
10
11
  import { setup, SETUP_MCP_MODES, SETUP_SCOPES, } from "./setup.js";
11
12
  import { uninstall } from "./uninstall.js";
12
13
  import { version } from "./version.js";
@@ -48,11 +49,11 @@ import { readSessionState, writeSessionStart, writeSessionEnd, resetSessionMetri
48
49
  import { buildClientAttachedReconcileHookName, buildReconcileHudResizeArgs, buildRegisterClientAttachedReconcileArgs, buildRegisterResizeHookArgs, buildResizeHookName, buildResizeHookTarget, buildScheduleDelayedHudResizeArgs, buildUnregisterClientAttachedReconcileArgs, buildUnregisterResizeHookArgs, enableMouseScrolling, isMsysOrGitBash, isNativeWindows, isTmuxAvailable, mitigateCopyModeUnderlineArtifacts, } from "../team/tmux-session.js";
49
50
  import { getPackageRoot } from "../utils/package.js";
50
51
  import { codexConfigPath, omxRoot, rememberOmxLaunchContext, resolveOmxEntryPath } from "../utils/paths.js";
51
- import { cleanCodexModelAvailabilityNuxIfNeeded, extractSharedMcpRegistryServersFromConfig, repairConfigIfNeeded, upsertManagedCodexHookTrustState } from "../config/generator.js";
52
+ import { cleanCodexModelAvailabilityNuxIfNeeded, extractSharedMcpRegistryServersFromConfig, repairConfigIfNeeded, syncProjectScopeTrustStateFromRuntime } from "../config/generator.js";
52
53
  import { OMX_FIRST_PARTY_MCP_SERVER_NAMES } from "../config/omx-first-party-mcp.js";
53
54
  import { HUD_TMUX_HEIGHT_LINES } from "../hud/constants.js";
54
55
  import { OMX_TMUX_HUD_OWNER_ENV } from "../hud/reconcile.js";
55
- import { createHudWatchPane as createSharedHudWatchPane, killTmuxPane as killSharedTmuxPane, listCurrentWindowHudPaneIds, parsePaneIdFromTmuxOutput, registerHudResizeHook, } from "../hud/tmux.js";
56
+ import { createHudWatchPane as createSharedHudWatchPane, killTmuxPane as killSharedTmuxPane, listCurrentWindowHudPaneIds, OMX_TMUX_HUD_LEADER_PANE_ENV, parsePaneIdFromTmuxOutput, registerHudResizeHook, } from "../hud/tmux.js";
56
57
  export { parseTmuxPaneSnapshot, isHudWatchPane, findHudWatchPaneIds } from "../hud/tmux.js";
57
58
  rememberOmxLaunchContext();
58
59
  import { classifySpawnError, resolveTmuxBinaryForPlatform, spawnPlatformCommandSync, } from "../utils/platform-command.js";
@@ -478,6 +479,32 @@ async function linkOrCopyCodexHomeEntry(source, destination) {
478
479
  function isCodexSqliteArtifact(entryName) {
479
480
  return /^(?:state|logs)_\d+\.sqlite(?:-(?:shm|wal))?$/.test(entryName);
480
481
  }
482
+ const PROJECT_LAUNCH_PERSISTED_RUNTIME_ENTRY_NAMES = new Set([
483
+ // Codex CLI writes browser/OTP login state here when CODEX_HOME points at
484
+ // the per-session mirror. Persist only the opaque file itself; never parse or
485
+ // log the contents.
486
+ "auth.json",
487
+ ]);
488
+ // Mirroring these files into the runtime CODEX_HOME would cause Codex to load
489
+ // them as user-scope config alongside the canonical project-scope copies under
490
+ // <cwd>/.codex, duplicating every native hook and asking the user to re-trust
491
+ // hooks on every launch. See GH issue #2470.
492
+ const PROJECT_LAUNCH_RUNTIME_SKIPPED_ENTRY_NAMES = new Set(["hooks.json"]);
493
+ function shouldPersistProjectLaunchRuntimeEntry(entryName) {
494
+ return PROJECT_LAUNCH_PERSISTED_RUNTIME_ENTRY_NAMES.has(entryName);
495
+ }
496
+ export async function persistProjectLaunchRuntimeAuthState(runtimeCodexHome, projectCodexHome) {
497
+ if (!runtimeCodexHome || !projectCodexHome)
498
+ return;
499
+ if (!existsSync(runtimeCodexHome))
500
+ return;
501
+ await mkdir(projectCodexHome, { recursive: true });
502
+ for (const entry of await readdir(runtimeCodexHome, { withFileTypes: true })) {
503
+ if (!shouldPersistProjectLaunchRuntimeEntry(entry.name) || !entry.isFile())
504
+ continue;
505
+ await copyFile(join(runtimeCodexHome, entry.name), join(projectCodexHome, entry.name));
506
+ }
507
+ }
481
508
  /**
482
509
  * Project-scope setup keeps durable Codex config under <repo>/.codex, but the
483
510
  * Codex TUI also stores model-availability NUX counters in CODEX_HOME/config.toml.
@@ -493,22 +520,16 @@ export async function prepareRuntimeCodexHomeForProjectLaunch(cwd, sessionId, pr
493
520
  for (const entry of await readdir(projectCodexHome, { withFileTypes: true })) {
494
521
  if (isCodexSqliteArtifact(entry.name))
495
522
  continue;
523
+ if (PROJECT_LAUNCH_RUNTIME_SKIPPED_ENTRY_NAMES.has(entry.name))
524
+ continue;
496
525
  const source = join(projectCodexHome, entry.name);
497
526
  const destination = join(runtimeCodexHome, entry.name);
498
- if (entry.name === "config.toml" || entry.name === "hooks.json") {
527
+ if (entry.name === "config.toml") {
499
528
  await copyFile(source, destination);
500
529
  continue;
501
530
  }
502
531
  await linkOrCopyCodexHomeEntry(source, destination);
503
532
  }
504
- const runtimeConfigPath = join(runtimeCodexHome, "config.toml");
505
- const runtimeHooksPath = join(runtimeCodexHome, "hooks.json");
506
- if (existsSync(runtimeConfigPath) && existsSync(runtimeHooksPath)) {
507
- const runtimeConfig = await readFile(runtimeConfigPath, "utf-8");
508
- if (runtimeConfig.includes("# OMX-owned Codex hook trust state")) {
509
- await writeFile(runtimeConfigPath, upsertManagedCodexHookTrustState(runtimeConfig, getPackageRoot(), runtimeHooksPath), "utf-8");
510
- }
511
- }
512
533
  return runtimeCodexHome;
513
534
  }
514
535
  function resolveProjectSqliteHomeForLaunch(projectCodexHome, env) {
@@ -533,9 +554,29 @@ export async function prepareCodexHomeForLaunch(cwd, sessionId, env = process.en
533
554
  projectLocalCodexHomeForCleanup,
534
555
  };
535
556
  }
536
- async function cleanupRuntimeCodexHome(runtimeCodexHomeForCleanup) {
557
+ export async function persistProjectLaunchRuntimeProjectTrustState(runtimeCodexHome, projectCodexHome) {
558
+ if (!runtimeCodexHome || !projectCodexHome)
559
+ return;
560
+ const runtimeConfigPath = join(runtimeCodexHome, "config.toml");
561
+ if (!existsSync(runtimeConfigPath))
562
+ return;
563
+ const projectConfigPath = join(projectCodexHome, "config.toml");
564
+ const runtimeConfig = await readFile(runtimeConfigPath, "utf-8");
565
+ const projectConfig = existsSync(projectConfigPath)
566
+ ? await readFile(projectConfigPath, "utf-8")
567
+ : "";
568
+ const projectHooksPath = join(projectCodexHome, "hooks.json");
569
+ const nextProjectConfig = syncProjectScopeTrustStateFromRuntime(projectConfig, runtimeConfig, projectHooksPath);
570
+ if (nextProjectConfig !== projectConfig) {
571
+ await mkdir(projectCodexHome, { recursive: true });
572
+ await writeFile(projectConfigPath, nextProjectConfig, "utf-8");
573
+ }
574
+ }
575
+ async function cleanupRuntimeCodexHome(runtimeCodexHomeForCleanup, projectCodexHomeForPersistence) {
537
576
  if (!runtimeCodexHomeForCleanup)
538
577
  return;
578
+ await persistProjectLaunchRuntimeAuthState(runtimeCodexHomeForCleanup, projectCodexHomeForPersistence);
579
+ await persistProjectLaunchRuntimeProjectTrustState(runtimeCodexHomeForCleanup, projectCodexHomeForPersistence);
539
580
  await rm(runtimeCodexHomeForCleanup, { recursive: true, force: true });
540
581
  }
541
582
  function execTmuxFileSync(args, options) {
@@ -800,22 +841,262 @@ export function shouldAutoIsolateMadmaxLaunch(command, launchArgs, env = process
800
841
  function sanitizeRunIdSegment(value) {
801
842
  return value.replace(/[^a-zA-Z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
802
843
  }
844
+ const MADMAX_DETACHED_ACTIVE_DIR = "active-detached";
845
+ const MADMAX_DETACHED_LOCK_STALE_MS = 30_000;
846
+ const MADMAX_DETACHED_LOCK_RETRY_MS = 50;
847
+ const MADMAX_DETACHED_LOCK_MAX_ATTEMPTS = 100;
848
+ const OMX_MADMAX_DETACHED_CONTEXT_ENV = "OMX_MADMAX_DETACHED_CONTEXT";
849
+ function resolveMadmaxRunsRoot(env = process.env) {
850
+ return env.OMX_RUNS_DIR || join(homedir(), ".omx-runs");
851
+ }
852
+ function canonicalizeLaunchCwd(cwd) {
853
+ try {
854
+ return execFileSync("git", ["rev-parse", "--show-toplevel"], {
855
+ cwd,
856
+ encoding: "utf-8",
857
+ stdio: ["ignore", "pipe", "ignore"],
858
+ }).trim() || cwd;
859
+ }
860
+ catch {
861
+ return cwd;
862
+ }
863
+ }
864
+ function normalizeMadmaxDetachedLaunchArgv(argv) {
865
+ const passthrough = [];
866
+ const semanticFlags = new Set();
867
+ let reasoningFlag = null;
868
+ let afterEndOfOptions = false;
869
+ for (const arg of argv) {
870
+ if (afterEndOfOptions) {
871
+ passthrough.push(arg);
872
+ continue;
873
+ }
874
+ if (arg === "--") {
875
+ afterEndOfOptions = true;
876
+ passthrough.push(arg);
877
+ continue;
878
+ }
879
+ if (arg === "--tmux" || arg === "--direct") {
880
+ continue;
881
+ }
882
+ if (arg === MADMAX_FLAG ||
883
+ arg === MADMAX_SPARK_FLAG) {
884
+ semanticFlags.add(arg);
885
+ continue;
886
+ }
887
+ if (arg === HIGH_REASONING_FLAG || arg === XHIGH_REASONING_FLAG) {
888
+ reasoningFlag = arg;
889
+ continue;
890
+ }
891
+ passthrough.push(arg);
892
+ }
893
+ return [
894
+ ...Array.from(semanticFlags).sort(),
895
+ ...(reasoningFlag ? [reasoningFlag] : []),
896
+ ...passthrough,
897
+ ];
898
+ }
899
+ export function buildMadmaxDetachedLaunchContextKey(sourceCwd, argv, runIdentity = "") {
900
+ // The boxed run root is part of the lock identity for auto-isolated madmax
901
+ // launches. That lets independent `omx --madmax --high` sessions share the
902
+ // same source cwd/argv without contending on one active-detached lock, while
903
+ // callers that intentionally reuse the same boxed context keep one key.
904
+ const payload = JSON.stringify({
905
+ source_cwd: canonicalizeLaunchCwd(sourceCwd),
906
+ argv: normalizeMadmaxDetachedLaunchArgv(argv),
907
+ run_identity: runIdentity,
908
+ });
909
+ return createHash("sha256").update(payload).digest("hex").slice(0, 32);
910
+ }
911
+ function madmaxDetachedActiveRecordPath(runsRoot, contextKey) {
912
+ return join(runsRoot, MADMAX_DETACHED_ACTIVE_DIR, `${contextKey}.json`);
913
+ }
914
+ function readMadmaxDetachedActiveRecord(recordPath) {
915
+ if (!existsSync(recordPath))
916
+ return null;
917
+ try {
918
+ const parsed = JSON.parse(readFileSync(recordPath, "utf-8"));
919
+ if (parsed.version !== 1 ||
920
+ typeof parsed.context_key !== "string" ||
921
+ typeof parsed.source_cwd !== "string" ||
922
+ typeof parsed.run_dir !== "string" ||
923
+ typeof parsed.tmux_session_name !== "string" ||
924
+ !Array.isArray(parsed.argv) ||
925
+ !parsed.argv.every((arg) => typeof arg === "string")) {
926
+ return null;
927
+ }
928
+ return {
929
+ version: 1,
930
+ context_key: parsed.context_key,
931
+ created_at: typeof parsed.created_at === "string" ? parsed.created_at : "",
932
+ source_cwd: parsed.source_cwd,
933
+ argv: [...parsed.argv],
934
+ run_dir: parsed.run_dir,
935
+ tmux_session_name: parsed.tmux_session_name,
936
+ };
937
+ }
938
+ catch {
939
+ return null;
940
+ }
941
+ }
942
+ function detachedTmuxSessionExists(sessionName) {
943
+ try {
944
+ execTmuxFileSync(["has-session", "-t", sessionName], { stdio: "ignore" });
945
+ return true;
946
+ }
947
+ catch {
948
+ return false;
949
+ }
950
+ }
951
+ function readMadmaxDetachedLockOwner(lockPath) {
952
+ try {
953
+ const parsed = JSON.parse(readFileSync(join(lockPath, "owner.json"), "utf-8"));
954
+ if (parsed.version !== 1 ||
955
+ typeof parsed.pid !== "number" ||
956
+ !Number.isSafeInteger(parsed.pid) ||
957
+ parsed.pid <= 0 ||
958
+ typeof parsed.context_key !== "string" ||
959
+ typeof parsed.acquired_at !== "string") {
960
+ return null;
961
+ }
962
+ return {
963
+ version: 1,
964
+ pid: parsed.pid,
965
+ context_key: parsed.context_key,
966
+ acquired_at: parsed.acquired_at,
967
+ };
968
+ }
969
+ catch {
970
+ return null;
971
+ }
972
+ }
973
+ function readMadmaxDetachedLockPid(lockPath) {
974
+ const owner = readMadmaxDetachedLockOwner(lockPath);
975
+ if (owner)
976
+ return owner.pid;
977
+ try {
978
+ const holderPid = Number.parseInt(readFileSync(join(lockPath, "pid"), "utf-8").trim(), 10);
979
+ return Number.isSafeInteger(holderPid) && holderPid > 0 ? holderPid : null;
980
+ }
981
+ catch {
982
+ return null;
983
+ }
984
+ }
985
+ function inspectMadmaxDetachedContextLock(lockPath) {
986
+ const lockStat = statSync(lockPath, { throwIfNoEntry: false });
987
+ if (!lockStat) {
988
+ return { stale: false, diagnostic: "lock disappeared while waiting" };
989
+ }
990
+ const ageMs = Math.max(0, Date.now() - lockStat.mtimeMs);
991
+ const owner = readMadmaxDetachedLockOwner(lockPath);
992
+ const holderPid = owner?.pid ?? readMadmaxDetachedLockPid(lockPath);
993
+ if (holderPid) {
994
+ if (!isProcessAlive(holderPid)) {
995
+ return {
996
+ stale: true,
997
+ diagnostic: `stale holder pid ${holderPid} is not running; lock age ${Math.round(ageMs)}ms`,
998
+ };
999
+ }
1000
+ const ownerContext = owner ? `, owner context ${owner.context_key}` : ", legacy pid-only lock";
1001
+ const sameDirectoryGuidance = "Another madmax detached launch is active for this directory; close the existing madmax session or use --worktree for concurrent work. Multiple madmax sessions in one directory are unsafe";
1002
+ return {
1003
+ stale: false,
1004
+ diagnostic: `holder pid ${holderPid} is still running${ownerContext}; lock age ${Math.round(ageMs)}ms. ${sameDirectoryGuidance}`,
1005
+ };
1006
+ }
1007
+ if (ageMs > MADMAX_DETACHED_LOCK_STALE_MS) {
1008
+ return {
1009
+ stale: true,
1010
+ diagnostic: `legacy lock has no readable owner pid and is older than ${MADMAX_DETACHED_LOCK_STALE_MS}ms; lock age ${Math.round(ageMs)}ms`,
1011
+ };
1012
+ }
1013
+ return {
1014
+ stale: false,
1015
+ diagnostic: `lock has no readable owner pid yet; lock age ${Math.round(ageMs)}ms`,
1016
+ };
1017
+ }
1018
+ export function withMadmaxDetachedContextLock(runsRoot, contextKey, run, options = {}) {
1019
+ const lockPath = join(runsRoot, MADMAX_DETACHED_ACTIVE_DIR, `${contextKey}.lock`);
1020
+ const maxAttempts = options.maxAttempts ?? MADMAX_DETACHED_LOCK_MAX_ATTEMPTS;
1021
+ const retryMs = options.retryMs ?? MADMAX_DETACHED_LOCK_RETRY_MS;
1022
+ let lastDiagnostic = "lock was busy";
1023
+ mkdirSync(dirname(lockPath), { recursive: true });
1024
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
1025
+ try {
1026
+ mkdirSync(lockPath);
1027
+ try {
1028
+ const owner = {
1029
+ version: 1,
1030
+ pid: process.pid,
1031
+ context_key: contextKey,
1032
+ acquired_at: new Date().toISOString(),
1033
+ };
1034
+ writeFileSync(join(lockPath, "owner.json"), `${JSON.stringify(owner, null, 2)}\n`, { mode: 0o600 });
1035
+ writeFileSync(join(lockPath, "pid"), String(process.pid));
1036
+ return run();
1037
+ }
1038
+ finally {
1039
+ rmSync(lockPath, { recursive: true, force: true });
1040
+ }
1041
+ }
1042
+ catch (err) {
1043
+ const code = err && typeof err === "object" && "code" in err
1044
+ ? String(err.code)
1045
+ : "";
1046
+ if (code !== "EEXIST")
1047
+ throw err;
1048
+ const inspection = inspectMadmaxDetachedContextLock(lockPath);
1049
+ lastDiagnostic = inspection.diagnostic;
1050
+ if (inspection.stale) {
1051
+ rmSync(lockPath, { recursive: true, force: true });
1052
+ continue;
1053
+ }
1054
+ blockMs(retryMs);
1055
+ }
1056
+ }
1057
+ throw new MadmaxDetachedGuardError(`timed out waiting for madmax detached launch context lock: ${lockPath} (${lastDiagnostic})`);
1058
+ }
1059
+ function isMadmaxDetachedGuardEnabled(env) {
1060
+ return env.OMXBOX_ACTIVE === "1" && typeof env[OMX_MADMAX_DETACHED_CONTEXT_ENV] === "string";
1061
+ }
1062
+ function cleanupCurrentMadmaxReuseRunRoot(env, runsRoot) {
1063
+ const runRoot = env.OMX_ROOT;
1064
+ if (!runRoot || !env.OMXBOX_ACTIVE)
1065
+ return;
1066
+ const normalizedRunsRoot = runsRoot.endsWith("/") ? runsRoot : `${runsRoot}/`;
1067
+ if (runRoot !== runsRoot && !runRoot.startsWith(normalizedRunsRoot))
1068
+ return;
1069
+ rmSync(runRoot, { recursive: true, force: true });
1070
+ }
1071
+ function writeMadmaxDetachedActiveRecord(recordPath, record) {
1072
+ mkdirSync(dirname(recordPath), { recursive: true });
1073
+ writeFileSync(recordPath, `${JSON.stringify(record, null, 2)}\n`, { mode: 0o600 });
1074
+ }
1075
+ class MadmaxDetachedReuseError extends Error {
1076
+ failClosed = true;
1077
+ }
1078
+ class MadmaxDetachedGuardError extends Error {
1079
+ failClosed = true;
1080
+ }
803
1081
  export function createMadmaxIsolatedRoot(sourceCwd, argv, env = process.env) {
804
- const runsRoot = env.OMX_RUNS_DIR || join(homedir(), ".omx-runs");
1082
+ const runsRoot = resolveMadmaxRunsRoot(env);
805
1083
  mkdirSync(runsRoot, { recursive: true });
806
1084
  const stamp = new Date().toISOString().replace(/[-:TZ.]/g, "").slice(0, 14);
807
1085
  const suffix = Math.random().toString(16).slice(2, 6);
808
1086
  const runDir = join(runsRoot, sanitizeRunIdSegment(`run-${stamp}-${suffix}`));
809
1087
  mkdirSync(runDir, { recursive: false });
1088
+ const detachedLaunchContext = buildMadmaxDetachedLaunchContextKey(sourceCwd, argv, runDir);
810
1089
  const metadata = {
811
1090
  launcher: "omx --madmax",
812
1091
  created_at: new Date().toISOString(),
813
1092
  cwd: runDir,
814
1093
  source_cwd: sourceCwd,
815
1094
  argv,
1095
+ detached_launch_context: detachedLaunchContext,
816
1096
  };
817
1097
  writeFileSync(join(runDir, ".omxbox-run.json"), `${JSON.stringify(metadata, null, 2)}\n`);
818
1098
  writeFileSync(join(runsRoot, "registry.jsonl"), `${JSON.stringify(metadata)}\n`, { flag: "a" });
1099
+ env[OMX_MADMAX_DETACHED_CONTEXT_ENV] = detachedLaunchContext;
819
1100
  return runDir;
820
1101
  }
821
1102
  function activateMadmaxIsolationIfNeeded(command, launchArgs, cwd, env = process.env) {
@@ -1028,6 +1309,11 @@ export async function main(args) {
1028
1309
  case "reasoning":
1029
1310
  await reasoningCommand(args.slice(1));
1030
1311
  break;
1312
+ case "codex-native-hook": {
1313
+ const { runCodexNativeHookCli } = await import("../scripts/codex-native-hook.js");
1314
+ await runCodexNativeHookCli();
1315
+ break;
1316
+ }
1031
1317
  case "help":
1032
1318
  case "--help":
1033
1319
  case "-h":
@@ -1227,7 +1513,7 @@ export async function launchWithHud(args) {
1227
1513
  // ── Phase 3: postLaunch ─────────────────────────────────────────────
1228
1514
  if (!postLaunchHandledExternally) {
1229
1515
  await postLaunch(cwd, sessionId, codexHomeOverride, enableNotifyFallbackAuthority, projectLocalCodexHomeForCleanup);
1230
- await cleanupRuntimeCodexHome(preparedCodexHome.runtimeCodexHomeForCleanup).catch(logCliOperationFailure);
1516
+ await cleanupRuntimeCodexHome(preparedCodexHome.runtimeCodexHomeForCleanup, projectLocalCodexHomeForCleanup).catch(logCliOperationFailure);
1231
1517
  }
1232
1518
  }
1233
1519
  }
@@ -1319,7 +1605,7 @@ export async function execWithOverlay(args) {
1319
1605
  }
1320
1606
  finally {
1321
1607
  await postLaunch(cwd, sessionId, codexHomeOverride, true, projectLocalCodexHomeForCleanup);
1322
- await cleanupRuntimeCodexHome(preparedCodexHome.runtimeCodexHomeForCleanup).catch(logCliOperationFailure);
1608
+ await cleanupRuntimeCodexHome(preparedCodexHome.runtimeCodexHomeForCleanup, projectLocalCodexHomeForCleanup).catch(logCliOperationFailure);
1323
1609
  }
1324
1610
  }
1325
1611
  export function normalizeCodexLaunchArgs(args) {
@@ -2649,12 +2935,19 @@ function runCodex(cwd, args, sessionId, workerDefaultModel, codexHomeOverride, s
2649
2935
  if (!omxBin) {
2650
2936
  throw new Error("Unable to resolve OMX launcher path for tmux HUD bootstrap");
2651
2937
  }
2938
+ const omxRootOverride = resolveOmxRootForLaunch(cwd, process.env);
2939
+ const currentPaneId = process.env.TMUX_PANE;
2940
+ const hudEnvArgs = [
2941
+ `OMX_SESSION_ID=${sessionId}`,
2942
+ `${OMX_TMUX_HUD_OWNER_ENV}=1`,
2943
+ ...(currentPaneId ? [`${OMX_TMUX_HUD_LEADER_PANE_ENV}=${currentPaneId}`] : []),
2944
+ ...(omxRootOverride ? [`OMX_ROOT=${omxRootOverride}`] : []),
2945
+ ];
2652
2946
  const hudCmd = nativeWindows
2653
2947
  ? buildWindowsPromptCommand("node", [omxBin, "hud", "--watch"])
2654
- : buildTmuxPaneCommand("env", [`OMX_SESSION_ID=${sessionId}`, `${OMX_TMUX_HUD_OWNER_ENV}=1`, "node", omxBin, "hud", "--watch"]);
2948
+ : buildTmuxPaneCommand("env", [...hudEnvArgs, "node", omxBin, "hud", "--watch"]);
2655
2949
  const inheritLeaderFlags = process.env[TEAM_INHERIT_LEADER_FLAGS_ENV] !== "0";
2656
2950
  const workerLaunchArgs = resolveTeamWorkerLaunchArgsEnv(process.env[TEAM_WORKER_LAUNCH_ARGS_ENV], launchArgs, inheritLeaderFlags, workerDefaultModel);
2657
- const omxRootOverride = resolveOmxRootForLaunch(cwd, process.env);
2658
2951
  const codexBaseEnv = {
2659
2952
  ...process.env,
2660
2953
  ...(codexHomeOverride ? { CODEX_HOME: codexHomeOverride } : {}),
@@ -2679,14 +2972,16 @@ function runCodex(cwd, args, sessionId, workerDefaultModel, codexHomeOverride, s
2679
2972
  }
2680
2973
  if (launchPolicy === "inside-tmux") {
2681
2974
  // Already in tmux: launch codex in current pane, HUD in bottom split
2682
- const currentPaneId = process.env.TMUX_PANE;
2683
- const staleHudPaneIds = listHudWatchPaneIdsInCurrentWindow(currentPaneId);
2975
+ const staleHudPaneIds = listHudWatchPaneIdsInCurrentWindow(currentPaneId, { sessionId, leaderPaneId: currentPaneId });
2684
2976
  for (const paneId of staleHudPaneIds) {
2685
2977
  killTmuxPane(paneId);
2686
2978
  }
2687
2979
  let hudPaneId = null;
2688
2980
  try {
2689
- hudPaneId = createHudWatchPane(cwd, hudCmd);
2981
+ hudPaneId = createHudWatchPane(cwd, hudCmd, {
2982
+ heightLines: HUD_TMUX_HEIGHT_LINES,
2983
+ targetPaneId: currentPaneId,
2984
+ });
2690
2985
  if (hudPaneId && currentPaneId) {
2691
2986
  registerHudResizeHook(hudPaneId, currentPaneId, HUD_TMUX_HEIGHT_LINES);
2692
2987
  }
@@ -2750,126 +3045,186 @@ function runCodex(cwd, args, sessionId, workerDefaultModel, codexHomeOverride, s
2750
3045
  ? buildWindowsPromptCommand("codex", launchArgs)
2751
3046
  : null;
2752
3047
  const sessionName = buildDetachedTmuxSessionName(cwd, sessionId);
2753
- void writeSessionStart(cwd, sessionId, { tmuxSessionName: sessionName }).catch((err) => {
2754
- logCliOperationFailure(err);
2755
- // Non-fatal: managed tmux recovery can still use compatibility fallback.
2756
- });
2757
- let createdDetachedSession = false;
2758
- let registeredHookTarget = null;
2759
- let registeredHookName = null;
2760
- let registeredClientAttachedHookName = null;
2761
- let detachedParentEnvFilePath;
2762
- try {
2763
- // This path is the user-shell interactive launch: OMX creates a tmux
2764
- // session and immediately attaches the user's terminal to it. If a tmux
2765
- // server already exists, `new-session -e` only forwards explicit values,
2766
- // so provider-specific parent-shell keys would disappear. Source a
2767
- // private env file inside the leader shell instead of putting every
2768
- // parent env value on the tmux command line or in logs.
2769
- if (!nativeWindows) {
2770
- detachedParentEnvFilePath = writeDetachedSessionParentEnvFile(cwd, sessionId, codexEnvWithNotify);
3048
+ const launchDetachedSession = () => {
3049
+ const contextKey = process.env[OMX_MADMAX_DETACHED_CONTEXT_ENV]?.trim();
3050
+ const runsRoot = resolveMadmaxRunsRoot(process.env);
3051
+ const activeRecordPath = contextKey
3052
+ ? madmaxDetachedActiveRecordPath(runsRoot, contextKey)
3053
+ : null;
3054
+ const activeRecord = activeRecordPath
3055
+ ? readMadmaxDetachedActiveRecord(activeRecordPath)
3056
+ : null;
3057
+ if (activeRecord &&
3058
+ activeRecord.context_key === contextKey &&
3059
+ detachedTmuxSessionExists(activeRecord.tmux_session_name)) {
3060
+ cleanupCurrentMadmaxReuseRunRoot(process.env, runsRoot);
3061
+ process.stderr.write(`[omx] madmax detached launch already active for this context; attaching ${activeRecord.tmux_session_name} instead of starting a duplicate.\n`);
3062
+ try {
3063
+ execTmuxFileSync(["attach-session", "-t", activeRecord.tmux_session_name], {
3064
+ stdio: "inherit",
3065
+ });
3066
+ }
3067
+ catch (err) {
3068
+ logCliOperationFailure(err);
3069
+ throw new MadmaxDetachedReuseError(`refusing duplicate madmax detached launch: existing session ${activeRecord.tmux_session_name} is active but attach failed`);
3070
+ }
3071
+ return { postLaunchHandledExternally: true };
2771
3072
  }
2772
- const bootstrapSteps = buildDetachedSessionBootstrapSteps(sessionName, cwd, codexCmd, hudCmd, workerLaunchArgs, codexHomeOverride, notifyTempContractRaw, nativeWindows, sessionId, projectLocalCodexHomeForCleanup, runtimeCodexHomeForCleanup, omxRootOverride, process.env, sqliteHomeOverride, detachedParentEnvFilePath);
2773
- for (const step of bootstrapSteps) {
2774
- const output = execTmuxFileSync(step.args, {
2775
- stdio: "pipe",
2776
- encoding: "utf-8",
2777
- });
2778
- if (step.name === "new-session") {
2779
- createdDetachedSession = true;
2780
- parsePaneIdFromTmuxOutput(output || "");
3073
+ if (activeRecordPath && activeRecord) {
3074
+ rmSync(activeRecordPath, { force: true });
3075
+ }
3076
+ void writeSessionStart(cwd, sessionId, { tmuxSessionName: sessionName }).catch((err) => {
3077
+ logCliOperationFailure(err);
3078
+ // Non-fatal: managed tmux recovery can still use compatibility fallback.
3079
+ });
3080
+ let createdDetachedSession = false;
3081
+ let registeredHookTarget = null;
3082
+ let registeredHookName = null;
3083
+ let registeredClientAttachedHookName = null;
3084
+ let detachedParentEnvFilePath;
3085
+ try {
3086
+ // This path is the user-shell interactive launch: OMX creates a tmux
3087
+ // session and immediately attaches the user's terminal to it. If a tmux
3088
+ // server already exists, `new-session -e` only forwards explicit values,
3089
+ // so provider-specific parent-shell keys would disappear. Source a
3090
+ // private env file inside the leader shell instead of putting every
3091
+ // parent env value on the tmux command line or in logs.
3092
+ if (!nativeWindows) {
3093
+ detachedParentEnvFilePath = writeDetachedSessionParentEnvFile(cwd, sessionId, codexEnvWithNotify);
2781
3094
  }
2782
- if (step.name === "split-and-capture-hud-pane") {
2783
- const hudPaneId = parsePaneIdFromTmuxOutput(output || "");
2784
- const hookWindowIndex = hudPaneId
2785
- ? detectDetachedSessionWindowIndex(sessionName)
2786
- : null;
2787
- const hookTarget = hudPaneId && hookWindowIndex
2788
- ? buildResizeHookTarget(sessionName, hookWindowIndex)
2789
- : null;
2790
- const hookName = hudPaneId && hookWindowIndex
2791
- ? buildResizeHookName("launch", sessionName, hookWindowIndex, hudPaneId)
2792
- : null;
2793
- const clientAttachedHookName = hudPaneId && hookWindowIndex
2794
- ? buildClientAttachedReconcileHookName("launch", sessionName, hookWindowIndex, hudPaneId)
2795
- : null;
2796
- const finalizeSteps = buildDetachedSessionFinalizeSteps(sessionName, hudPaneId, hookWindowIndex, process.env.OMX_MOUSE !== "0", nativeWindows);
2797
- if (nativeWindows && detachedWindowsCodexCmd) {
2798
- scheduleDetachedWindowsCodexLaunch(sessionName, detachedWindowsCodexCmd);
3095
+ const bootstrapSteps = buildDetachedSessionBootstrapSteps(sessionName, cwd, codexCmd, hudCmd, workerLaunchArgs, codexHomeOverride, notifyTempContractRaw, nativeWindows, sessionId, projectLocalCodexHomeForCleanup, runtimeCodexHomeForCleanup, omxRootOverride, process.env, sqliteHomeOverride, detachedParentEnvFilePath);
3096
+ for (const step of bootstrapSteps) {
3097
+ const output = execTmuxFileSync(step.args, {
3098
+ stdio: "pipe",
3099
+ encoding: "utf-8",
3100
+ });
3101
+ if (step.name === "new-session") {
3102
+ createdDetachedSession = true;
3103
+ if (activeRecordPath && contextKey) {
3104
+ writeMadmaxDetachedActiveRecord(activeRecordPath, {
3105
+ version: 1,
3106
+ context_key: contextKey,
3107
+ created_at: new Date().toISOString(),
3108
+ source_cwd: process.env.OMX_SOURCE_CWD || cwd,
3109
+ argv: args,
3110
+ run_dir: process.env.OMX_ROOT || cwd,
3111
+ tmux_session_name: sessionName,
3112
+ });
3113
+ }
3114
+ parsePaneIdFromTmuxOutput(output || "");
2799
3115
  }
2800
- for (const finalizeStep of finalizeSteps) {
2801
- if (finalizeStep.name === "sanitize-copy-mode-style") {
3116
+ if (step.name === "split-and-capture-hud-pane") {
3117
+ const hudPaneId = parsePaneIdFromTmuxOutput(output || "");
3118
+ const hookWindowIndex = hudPaneId
3119
+ ? detectDetachedSessionWindowIndex(sessionName)
3120
+ : null;
3121
+ const hookTarget = hudPaneId && hookWindowIndex
3122
+ ? buildResizeHookTarget(sessionName, hookWindowIndex)
3123
+ : null;
3124
+ const hookName = hudPaneId && hookWindowIndex
3125
+ ? buildResizeHookName("launch", sessionName, hookWindowIndex, hudPaneId)
3126
+ : null;
3127
+ const clientAttachedHookName = hudPaneId && hookWindowIndex
3128
+ ? buildClientAttachedReconcileHookName("launch", sessionName, hookWindowIndex, hudPaneId)
3129
+ : null;
3130
+ const finalizeSteps = buildDetachedSessionFinalizeSteps(sessionName, hudPaneId, hookWindowIndex, process.env.OMX_MOUSE !== "0", nativeWindows);
3131
+ if (nativeWindows && detachedWindowsCodexCmd) {
3132
+ scheduleDetachedWindowsCodexLaunch(sessionName, detachedWindowsCodexCmd);
3133
+ }
3134
+ for (const finalizeStep of finalizeSteps) {
3135
+ if (finalizeStep.name === "sanitize-copy-mode-style") {
3136
+ try {
3137
+ mitigateCopyModeUnderlineArtifacts(sessionName);
3138
+ }
3139
+ catch (err) {
3140
+ logCliOperationFailure(err);
3141
+ }
3142
+ continue;
3143
+ }
3144
+ const stdio = finalizeStep.name === "attach-session" ? "inherit" : "ignore";
2802
3145
  try {
2803
- mitigateCopyModeUnderlineArtifacts(sessionName);
3146
+ const startedAtMs = Date.now();
3147
+ execTmuxFileSync(finalizeStep.args, { stdio });
3148
+ if (finalizeStep.name === "attach-session") {
3149
+ assertDetachedAttachDidNotNoop(sessionName, Date.now() - startedAtMs, process.env);
3150
+ }
2804
3151
  }
2805
3152
  catch (err) {
2806
3153
  logCliOperationFailure(err);
3154
+ if (finalizeStep.name === "attach-session")
3155
+ throw new Error("failed to attach detached tmux session");
3156
+ continue;
2807
3157
  }
2808
- continue;
2809
- }
2810
- const stdio = finalizeStep.name === "attach-session" ? "inherit" : "ignore";
2811
- try {
2812
- const startedAtMs = Date.now();
2813
- execTmuxFileSync(finalizeStep.args, { stdio });
2814
- if (finalizeStep.name === "attach-session") {
2815
- assertDetachedAttachDidNotNoop(sessionName, Date.now() - startedAtMs, process.env);
3158
+ if (finalizeStep.name === "register-resize-hook" &&
3159
+ hookTarget &&
3160
+ hookName) {
3161
+ registeredHookTarget = hookTarget;
3162
+ registeredHookName = hookName;
3163
+ }
3164
+ if (finalizeStep.name === "register-client-attached-reconcile" &&
3165
+ clientAttachedHookName) {
3166
+ registeredClientAttachedHookName = clientAttachedHookName;
2816
3167
  }
2817
3168
  }
2818
- catch (err) {
2819
- logCliOperationFailure(err);
2820
- if (finalizeStep.name === "attach-session")
2821
- throw new Error("failed to attach detached tmux session");
2822
- continue;
2823
- }
2824
- if (finalizeStep.name === "register-resize-hook" &&
2825
- hookTarget &&
2826
- hookName) {
2827
- registeredHookTarget = hookTarget;
2828
- registeredHookName = hookName;
3169
+ }
3170
+ }
3171
+ return { postLaunchHandledExternally: !nativeWindows };
3172
+ }
3173
+ catch (err) {
3174
+ if (detachedParentEnvFilePath) {
3175
+ rmSync(detachedParentEnvFilePath, { force: true });
3176
+ }
3177
+ if (activeRecordPath) {
3178
+ rmSync(activeRecordPath, { force: true });
3179
+ }
3180
+ if (createdDetachedSession) {
3181
+ const rollbackSteps = buildDetachedSessionRollbackSteps(sessionName, registeredHookTarget, registeredHookName, registeredClientAttachedHookName);
3182
+ for (const rollbackStep of rollbackSteps) {
3183
+ try {
3184
+ execTmuxFileSync(rollbackStep.args, { stdio: "ignore" });
2829
3185
  }
2830
- if (finalizeStep.name === "register-client-attached-reconcile" &&
2831
- clientAttachedHookName) {
2832
- registeredClientAttachedHookName = clientAttachedHookName;
3186
+ catch (rollbackErr) {
3187
+ logCliOperationFailure(rollbackErr);
3188
+ // best-effort rollback only
2833
3189
  }
2834
3190
  }
2835
3191
  }
3192
+ throw err;
3193
+ }
3194
+ };
3195
+ const contextKey = process.env[OMX_MADMAX_DETACHED_CONTEXT_ENV]?.trim();
3196
+ const runsRoot = resolveMadmaxRunsRoot(process.env);
3197
+ try {
3198
+ if (isMadmaxDetachedGuardEnabled(process.env) && contextKey) {
3199
+ return withMadmaxDetachedContextLock(runsRoot, contextKey, launchDetachedSession);
2836
3200
  }
2837
- return { postLaunchHandledExternally: !nativeWindows };
3201
+ return launchDetachedSession();
2838
3202
  }
2839
3203
  catch (err) {
2840
- logCliOperationFailure(err);
2841
- if (detachedParentEnvFilePath) {
2842
- rmSync(detachedParentEnvFilePath, { force: true });
2843
- }
2844
- if (createdDetachedSession) {
2845
- const rollbackSteps = buildDetachedSessionRollbackSteps(sessionName, registeredHookTarget, registeredHookName, registeredClientAttachedHookName);
2846
- for (const rollbackStep of rollbackSteps) {
2847
- try {
2848
- execTmuxFileSync(rollbackStep.args, { stdio: "ignore" });
2849
- }
2850
- catch (err) {
2851
- logCliOperationFailure(err);
2852
- // best-effort rollback only
2853
- }
2854
- }
3204
+ if (err instanceof MadmaxDetachedReuseError || err instanceof MadmaxDetachedGuardError) {
3205
+ throw err;
2855
3206
  }
3207
+ logCliOperationFailure(err);
2856
3208
  // tmux not available or failed, just run codex directly
2857
3209
  runCodexBlocking(cwd, launchArgs, codexEnvWithNotify);
2858
3210
  return { postLaunchHandledExternally: false };
2859
3211
  }
2860
3212
  }
2861
3213
  }
2862
- function listHudWatchPaneIdsInCurrentWindow(currentPaneId) {
3214
+ function listHudWatchPaneIdsInCurrentWindow(currentPaneId, owner = {}) {
2863
3215
  try {
2864
- return listCurrentWindowHudPaneIds(currentPaneId);
3216
+ return listCurrentWindowHudPaneIds(currentPaneId, undefined, owner);
2865
3217
  }
2866
3218
  catch (err) {
2867
3219
  logCliOperationFailure(err);
2868
3220
  return [];
2869
3221
  }
2870
3222
  }
2871
- function createHudWatchPane(cwd, hudCmd) {
2872
- return createSharedHudWatchPane(cwd, hudCmd, { heightLines: HUD_TMUX_HEIGHT_LINES });
3223
+ function createHudWatchPane(cwd, hudCmd, options = {}) {
3224
+ return createSharedHudWatchPane(cwd, hudCmd, {
3225
+ heightLines: options.heightLines ?? HUD_TMUX_HEIGHT_LINES,
3226
+ targetPaneId: options.targetPaneId,
3227
+ });
2873
3228
  }
2874
3229
  function killTmuxPane(paneId) {
2875
3230
  if (!paneId.startsWith("%"))
@@ -3109,7 +3464,7 @@ async function postLaunch(cwd, sessionId, codexHomeOverride, enableNotifyFallbac
3109
3464
  }
3110
3465
  export async function runDetachedSessionPostLaunch(cwd, sessionId, codexHomeOverride, projectLocalCodexHomeForCleanup, runtimeCodexHomeForCleanup) {
3111
3466
  await postLaunch(cwd, sessionId, codexHomeOverride, false, projectLocalCodexHomeForCleanup);
3112
- await cleanupRuntimeCodexHome(runtimeCodexHomeForCleanup).catch(logCliOperationFailure);
3467
+ await cleanupRuntimeCodexHome(runtimeCodexHomeForCleanup, projectLocalCodexHomeForCleanup).catch(logCliOperationFailure);
3113
3468
  }
3114
3469
  async function emitNativeHookEvent(cwd, event, opts = {}) {
3115
3470
  const payload = buildHookEvent(event, {