oh-my-codex 0.20.2 → 0.20.5

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 (805) hide show
  1. package/Cargo.lock +36 -18
  2. package/Cargo.toml +1 -1
  3. package/README.md +28 -2
  4. package/crates/omx-runtime/Cargo.toml +4 -0
  5. package/crates/omx-runtime/src/main.rs +415 -2
  6. package/crates/omx-runtime/tests/execution.rs +483 -0
  7. package/crates/omx-runtime-core/src/dispatch.rs +96 -10
  8. package/crates/omx-runtime-core/src/engine.rs +839 -29
  9. package/crates/omx-runtime-core/src/lib.rs +8 -0
  10. package/dist/adapt/__tests__/herdr-target.test.d.ts +2 -0
  11. package/dist/adapt/__tests__/herdr-target.test.d.ts.map +1 -0
  12. package/dist/adapt/__tests__/herdr-target.test.js +72 -0
  13. package/dist/adapt/__tests__/herdr-target.test.js.map +1 -0
  14. package/dist/adapt/contracts.d.ts +1 -1
  15. package/dist/adapt/contracts.d.ts.map +1 -1
  16. package/dist/adapt/contracts.js +1 -1
  17. package/dist/adapt/contracts.js.map +1 -1
  18. package/dist/adapt/herdr/__tests__/authority.test.d.ts +2 -0
  19. package/dist/adapt/herdr/__tests__/authority.test.d.ts.map +1 -0
  20. package/dist/adapt/herdr/__tests__/authority.test.js +49 -0
  21. package/dist/adapt/herdr/__tests__/authority.test.js.map +1 -0
  22. package/dist/adapt/herdr/__tests__/bridge.test.d.ts +2 -0
  23. package/dist/adapt/herdr/__tests__/bridge.test.d.ts.map +1 -0
  24. package/dist/adapt/herdr/__tests__/bridge.test.js +176 -0
  25. package/dist/adapt/herdr/__tests__/bridge.test.js.map +1 -0
  26. package/dist/adapt/herdr/__tests__/sanitize.test.d.ts +2 -0
  27. package/dist/adapt/herdr/__tests__/sanitize.test.d.ts.map +1 -0
  28. package/dist/adapt/herdr/__tests__/sanitize.test.js +45 -0
  29. package/dist/adapt/herdr/__tests__/sanitize.test.js.map +1 -0
  30. package/dist/adapt/herdr/__tests__/semantic.test.d.ts +2 -0
  31. package/dist/adapt/herdr/__tests__/semantic.test.d.ts.map +1 -0
  32. package/dist/adapt/herdr/__tests__/semantic.test.js +130 -0
  33. package/dist/adapt/herdr/__tests__/semantic.test.js.map +1 -0
  34. package/dist/adapt/herdr/__tests__/seq-store.test.d.ts +2 -0
  35. package/dist/adapt/herdr/__tests__/seq-store.test.d.ts.map +1 -0
  36. package/dist/adapt/herdr/__tests__/seq-store.test.js +54 -0
  37. package/dist/adapt/herdr/__tests__/seq-store.test.js.map +1 -0
  38. package/dist/adapt/herdr/__tests__/socket-live.test.d.ts +2 -0
  39. package/dist/adapt/herdr/__tests__/socket-live.test.d.ts.map +1 -0
  40. package/dist/adapt/herdr/__tests__/socket-live.test.js +104 -0
  41. package/dist/adapt/herdr/__tests__/socket-live.test.js.map +1 -0
  42. package/dist/adapt/herdr/__tests__/team-map.test.d.ts +2 -0
  43. package/dist/adapt/herdr/__tests__/team-map.test.d.ts.map +1 -0
  44. package/dist/adapt/herdr/__tests__/team-map.test.js +46 -0
  45. package/dist/adapt/herdr/__tests__/team-map.test.js.map +1 -0
  46. package/dist/adapt/herdr/__tests__/transport.test.d.ts +2 -0
  47. package/dist/adapt/herdr/__tests__/transport.test.d.ts.map +1 -0
  48. package/dist/adapt/herdr/__tests__/transport.test.js +160 -0
  49. package/dist/adapt/herdr/__tests__/transport.test.js.map +1 -0
  50. package/dist/adapt/herdr/__tests__/wiring.test.d.ts +2 -0
  51. package/dist/adapt/herdr/__tests__/wiring.test.d.ts.map +1 -0
  52. package/dist/adapt/herdr/__tests__/wiring.test.js +118 -0
  53. package/dist/adapt/herdr/__tests__/wiring.test.js.map +1 -0
  54. package/dist/adapt/herdr/authority.d.ts +34 -0
  55. package/dist/adapt/herdr/authority.d.ts.map +1 -0
  56. package/dist/adapt/herdr/authority.js +71 -0
  57. package/dist/adapt/herdr/authority.js.map +1 -0
  58. package/dist/adapt/herdr/bridge.d.ts +84 -0
  59. package/dist/adapt/herdr/bridge.d.ts.map +1 -0
  60. package/dist/adapt/herdr/bridge.js +222 -0
  61. package/dist/adapt/herdr/bridge.js.map +1 -0
  62. package/dist/adapt/herdr/sanitize.d.ts +20 -0
  63. package/dist/adapt/herdr/sanitize.d.ts.map +1 -0
  64. package/dist/adapt/herdr/sanitize.js +70 -0
  65. package/dist/adapt/herdr/sanitize.js.map +1 -0
  66. package/dist/adapt/herdr/semantic.d.ts +67 -0
  67. package/dist/adapt/herdr/semantic.d.ts.map +1 -0
  68. package/dist/adapt/herdr/semantic.js +170 -0
  69. package/dist/adapt/herdr/semantic.js.map +1 -0
  70. package/dist/adapt/herdr/seq-store.d.ts +32 -0
  71. package/dist/adapt/herdr/seq-store.d.ts.map +1 -0
  72. package/dist/adapt/herdr/seq-store.js +126 -0
  73. package/dist/adapt/herdr/seq-store.js.map +1 -0
  74. package/dist/adapt/herdr/team-map.d.ts +30 -0
  75. package/dist/adapt/herdr/team-map.d.ts.map +1 -0
  76. package/dist/adapt/herdr/team-map.js +37 -0
  77. package/dist/adapt/herdr/team-map.js.map +1 -0
  78. package/dist/adapt/herdr/transport.d.ts +115 -0
  79. package/dist/adapt/herdr/transport.d.ts.map +1 -0
  80. package/dist/adapt/herdr/transport.js +260 -0
  81. package/dist/adapt/herdr/transport.js.map +1 -0
  82. package/dist/adapt/herdr/wiring.d.ts +24 -0
  83. package/dist/adapt/herdr/wiring.d.ts.map +1 -0
  84. package/dist/adapt/herdr/wiring.js +62 -0
  85. package/dist/adapt/herdr/wiring.js.map +1 -0
  86. package/dist/adapt/herdr.d.ts +19 -0
  87. package/dist/adapt/herdr.d.ts.map +1 -0
  88. package/dist/adapt/herdr.js +115 -0
  89. package/dist/adapt/herdr.js.map +1 -0
  90. package/dist/adapt/index.d.ts +4 -0
  91. package/dist/adapt/index.d.ts.map +1 -1
  92. package/dist/adapt/index.js +48 -0
  93. package/dist/adapt/index.js.map +1 -1
  94. package/dist/adapt/registry.d.ts.map +1 -1
  95. package/dist/adapt/registry.js +11 -0
  96. package/dist/adapt/registry.js.map +1 -1
  97. package/dist/agents/__tests__/definitions.test.js +81 -0
  98. package/dist/agents/__tests__/definitions.test.js.map +1 -1
  99. package/dist/agents/__tests__/native-config.test.js +25 -0
  100. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  101. package/dist/agents/native-config.d.ts +2 -1
  102. package/dist/agents/native-config.d.ts.map +1 -1
  103. package/dist/agents/native-config.js.map +1 -1
  104. package/dist/auth/__tests__/hotswap.test.js +60 -1
  105. package/dist/auth/__tests__/hotswap.test.js.map +1 -1
  106. package/dist/auth/__tests__/storage.test.js +33 -1
  107. package/dist/auth/__tests__/storage.test.js.map +1 -1
  108. package/dist/auth/hotswap.d.ts +22 -2
  109. package/dist/auth/hotswap.d.ts.map +1 -1
  110. package/dist/auth/hotswap.js +107 -22
  111. package/dist/auth/hotswap.js.map +1 -1
  112. package/dist/auth/storage.js +1 -1
  113. package/dist/auth/storage.js.map +1 -1
  114. package/dist/autopilot/__tests__/ralplan-gate.test.js +86 -1333
  115. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
  116. package/dist/autopilot/ralplan-gate.d.ts.map +1 -1
  117. package/dist/autopilot/ralplan-gate.js +7 -6
  118. package/dist/autopilot/ralplan-gate.js.map +1 -1
  119. package/dist/cli/__tests__/agents-init.test.js +4 -23
  120. package/dist/cli/__tests__/agents-init.test.js.map +1 -1
  121. package/dist/cli/__tests__/api.test.js +61 -24
  122. package/dist/cli/__tests__/api.test.js.map +1 -1
  123. package/dist/cli/__tests__/auth.test.js +63 -7
  124. package/dist/cli/__tests__/auth.test.js.map +1 -1
  125. package/dist/cli/__tests__/codex-feature-probe.test.js +66 -1
  126. package/dist/cli/__tests__/codex-feature-probe.test.js.map +1 -1
  127. package/dist/cli/__tests__/codex-plugin-layout.test.js +150 -2
  128. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  129. package/dist/cli/__tests__/detached-leader-teardown.test.d.ts +2 -0
  130. package/dist/cli/__tests__/detached-leader-teardown.test.d.ts.map +1 -0
  131. package/dist/cli/__tests__/detached-leader-teardown.test.js +473 -0
  132. package/dist/cli/__tests__/detached-leader-teardown.test.js.map +1 -0
  133. package/dist/cli/__tests__/doctor-process-identity-readiness.test.d.ts +2 -0
  134. package/dist/cli/__tests__/doctor-process-identity-readiness.test.d.ts.map +1 -0
  135. package/dist/cli/__tests__/doctor-process-identity-readiness.test.js +103 -0
  136. package/dist/cli/__tests__/doctor-process-identity-readiness.test.js.map +1 -0
  137. package/dist/cli/__tests__/doctor-state-root-binding.test.d.ts +2 -0
  138. package/dist/cli/__tests__/doctor-state-root-binding.test.d.ts.map +1 -0
  139. package/dist/cli/__tests__/doctor-state-root-binding.test.js +139 -0
  140. package/dist/cli/__tests__/doctor-state-root-binding.test.js.map +1 -0
  141. package/dist/cli/__tests__/doctor-warning-copy.test.js +135 -1
  142. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  143. package/dist/cli/__tests__/exec.test.js +211 -0
  144. package/dist/cli/__tests__/exec.test.js.map +1 -1
  145. package/dist/cli/__tests__/index.test.js +878 -463
  146. package/dist/cli/__tests__/index.test.js.map +1 -1
  147. package/dist/cli/__tests__/launch-fallback.test.js +1290 -127
  148. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  149. package/dist/cli/__tests__/mcp-parity.test.js +8 -4
  150. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  151. package/dist/cli/__tests__/native-assets.test.js +398 -16
  152. package/dist/cli/__tests__/native-assets.test.js.map +1 -1
  153. package/dist/cli/__tests__/native-hook-claim-journal.test.js +132 -6
  154. package/dist/cli/__tests__/native-hook-claim-journal.test.js.map +1 -1
  155. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +3 -3
  156. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  157. package/dist/cli/__tests__/ralph.test.js +3 -3
  158. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  159. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js +49 -164
  160. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js.map +1 -1
  161. package/dist/cli/__tests__/ralplan-child-binding-3212.test.d.ts +2 -0
  162. package/dist/cli/__tests__/ralplan-child-binding-3212.test.d.ts.map +1 -0
  163. package/dist/cli/__tests__/ralplan-child-binding-3212.test.js +56 -0
  164. package/dist/cli/__tests__/ralplan-child-binding-3212.test.js.map +1 -0
  165. package/dist/cli/__tests__/ralplan.test.js +107 -109
  166. package/dist/cli/__tests__/ralplan.test.js.map +1 -1
  167. package/dist/cli/__tests__/resume.test.js +11 -10
  168. package/dist/cli/__tests__/resume.test.js.map +1 -1
  169. package/dist/cli/__tests__/session-scoped-runtime.test.js +1118 -43
  170. package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
  171. package/dist/cli/__tests__/session-search-help.test.js +25 -1
  172. package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
  173. package/dist/cli/__tests__/setup-agents-overwrite.test.js +8 -55
  174. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  175. package/dist/cli/__tests__/setup-install-mode.test.js +224 -7
  176. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  177. package/dist/cli/__tests__/sparkshell-cli.test.js +93 -41
  178. package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
  179. package/dist/cli/__tests__/team.test.js +150 -8
  180. package/dist/cli/__tests__/team.test.js.map +1 -1
  181. package/dist/cli/__tests__/ultragoal.test.js +117 -2
  182. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  183. package/dist/cli/__tests__/uninstall.test.js +95 -4
  184. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  185. package/dist/cli/__tests__/update.test.js +669 -177
  186. package/dist/cli/__tests__/update.test.js.map +1 -1
  187. package/dist/cli/__tests__/windows-popup-loop-contract.test.js +33 -2
  188. package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
  189. package/dist/cli/api.d.ts.map +1 -1
  190. package/dist/cli/api.js +9 -10
  191. package/dist/cli/api.js.map +1 -1
  192. package/dist/cli/codex-feature-probe.d.ts +16 -0
  193. package/dist/cli/codex-feature-probe.d.ts.map +1 -1
  194. package/dist/cli/codex-feature-probe.js +44 -0
  195. package/dist/cli/codex-feature-probe.js.map +1 -1
  196. package/dist/cli/codex-home.d.ts.map +1 -1
  197. package/dist/cli/codex-home.js +7 -4
  198. package/dist/cli/codex-home.js.map +1 -1
  199. package/dist/cli/doctor.d.ts +20 -0
  200. package/dist/cli/doctor.d.ts.map +1 -1
  201. package/dist/cli/doctor.js +290 -3
  202. package/dist/cli/doctor.js.map +1 -1
  203. package/dist/cli/index.d.ts +234 -12
  204. package/dist/cli/index.d.ts.map +1 -1
  205. package/dist/cli/index.js +2409 -746
  206. package/dist/cli/index.js.map +1 -1
  207. package/dist/cli/mcp-parity.js +2 -2
  208. package/dist/cli/mcp-parity.js.map +1 -1
  209. package/dist/cli/native-assets.d.ts +25 -0
  210. package/dist/cli/native-assets.d.ts.map +1 -1
  211. package/dist/cli/native-assets.js +448 -51
  212. package/dist/cli/native-assets.js.map +1 -1
  213. package/dist/cli/native-hook-claim-journal.d.ts +20 -5
  214. package/dist/cli/native-hook-claim-journal.d.ts.map +1 -1
  215. package/dist/cli/native-hook-claim-journal.js +67 -32
  216. package/dist/cli/native-hook-claim-journal.js.map +1 -1
  217. package/dist/cli/package-manager-ownership.d.ts +63 -0
  218. package/dist/cli/package-manager-ownership.d.ts.map +1 -0
  219. package/dist/cli/package-manager-ownership.js +323 -0
  220. package/dist/cli/package-manager-ownership.js.map +1 -0
  221. package/dist/cli/ralph.js +2 -2
  222. package/dist/cli/ralph.js.map +1 -1
  223. package/dist/cli/ralplan.d.ts +5 -7
  224. package/dist/cli/ralplan.d.ts.map +1 -1
  225. package/dist/cli/ralplan.js +83 -134
  226. package/dist/cli/ralplan.js.map +1 -1
  227. package/dist/cli/session-search.d.ts +5 -0
  228. package/dist/cli/session-search.d.ts.map +1 -1
  229. package/dist/cli/session-search.js +87 -0
  230. package/dist/cli/session-search.js.map +1 -1
  231. package/dist/cli/setup-preferences.d.ts +15 -0
  232. package/dist/cli/setup-preferences.d.ts.map +1 -1
  233. package/dist/cli/setup-preferences.js +27 -1
  234. package/dist/cli/setup-preferences.js.map +1 -1
  235. package/dist/cli/setup.d.ts +8 -0
  236. package/dist/cli/setup.d.ts.map +1 -1
  237. package/dist/cli/setup.js +119 -47
  238. package/dist/cli/setup.js.map +1 -1
  239. package/dist/cli/sparkshell.d.ts.map +1 -1
  240. package/dist/cli/sparkshell.js +52 -9
  241. package/dist/cli/sparkshell.js.map +1 -1
  242. package/dist/cli/ultragoal.d.ts.map +1 -1
  243. package/dist/cli/ultragoal.js +2 -2
  244. package/dist/cli/ultragoal.js.map +1 -1
  245. package/dist/cli/uninstall.d.ts +9 -0
  246. package/dist/cli/uninstall.d.ts.map +1 -1
  247. package/dist/cli/uninstall.js +71 -30
  248. package/dist/cli/uninstall.js.map +1 -1
  249. package/dist/cli/update-worker.d.ts +2 -0
  250. package/dist/cli/update-worker.d.ts.map +1 -0
  251. package/dist/cli/update-worker.js +142 -0
  252. package/dist/cli/update-worker.js.map +1 -0
  253. package/dist/cli/update.d.ts +14 -22
  254. package/dist/cli/update.d.ts.map +1 -1
  255. package/dist/cli/update.js +192 -241
  256. package/dist/cli/update.js.map +1 -1
  257. package/dist/compat/__tests__/rust-runtime-compat.test.js +1 -1
  258. package/dist/config/__tests__/generator-idempotent.test.js +503 -30
  259. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  260. package/dist/config/__tests__/generator-notify.test.js +5 -3
  261. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  262. package/dist/config/__tests__/models.test.js +67 -3
  263. package/dist/config/__tests__/models.test.js.map +1 -1
  264. package/dist/config/generator.d.ts +27 -21
  265. package/dist/config/generator.d.ts.map +1 -1
  266. package/dist/config/generator.js +610 -152
  267. package/dist/config/generator.js.map +1 -1
  268. package/dist/config/models.d.ts +12 -2
  269. package/dist/config/models.d.ts.map +1 -1
  270. package/dist/config/models.js +15 -1
  271. package/dist/config/models.js.map +1 -1
  272. package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js +25 -0
  273. package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js.map +1 -1
  274. package/dist/goal-workflows/codex-goal-snapshot.d.ts +1 -1
  275. package/dist/goal-workflows/codex-goal-snapshot.d.ts.map +1 -1
  276. package/dist/goal-workflows/codex-goal-snapshot.js +2 -0
  277. package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -1
  278. package/dist/hooks/__tests__/agents-overlay.test.js +5 -3
  279. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  280. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +27 -4
  281. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  282. package/dist/hooks/__tests__/issue-3293-autopilot-handoff.test.d.ts +2 -0
  283. package/dist/hooks/__tests__/issue-3293-autopilot-handoff.test.d.ts.map +1 -0
  284. package/dist/hooks/__tests__/issue-3293-autopilot-handoff.test.js +172 -0
  285. package/dist/hooks/__tests__/issue-3293-autopilot-handoff.test.js.map +1 -0
  286. package/dist/hooks/__tests__/keyword-detector.test.js +176 -37
  287. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  288. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +402 -545
  289. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  290. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +39 -4
  291. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  292. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +161 -26
  293. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  294. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +27 -0
  295. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
  296. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +10 -118
  297. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  298. package/dist/hooks/__tests__/notify-hook-regression-205.test.js +7 -0
  299. package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
  300. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +533 -14
  301. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  302. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +159 -24
  303. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  304. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +482 -32
  305. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  306. package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.js +2 -1
  307. package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.js.map +1 -1
  308. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +68 -1
  309. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  310. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +7 -1
  311. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
  312. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +44 -4
  313. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  314. package/dist/hooks/__tests__/prompt-guidance-contract.test.js +9 -0
  315. package/dist/hooks/__tests__/prompt-guidance-contract.test.js.map +1 -1
  316. package/dist/hooks/__tests__/session.test.js +3130 -365
  317. package/dist/hooks/__tests__/session.test.js.map +1 -1
  318. package/dist/hooks/agents-overlay.js +1 -1
  319. package/dist/hooks/agents-overlay.js.map +1 -1
  320. package/dist/hooks/extensibility/__tests__/dispatcher.test.js +108 -2
  321. package/dist/hooks/extensibility/__tests__/dispatcher.test.js.map +1 -1
  322. package/dist/hooks/extensibility/__tests__/sdk.test.js +151 -1
  323. package/dist/hooks/extensibility/__tests__/sdk.test.js.map +1 -1
  324. package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
  325. package/dist/hooks/extensibility/dispatcher.js +20 -3
  326. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  327. package/dist/hooks/extensibility/plugin-runner.js +1 -0
  328. package/dist/hooks/extensibility/plugin-runner.js.map +1 -1
  329. package/dist/hooks/extensibility/runtime.d.ts.map +1 -1
  330. package/dist/hooks/extensibility/runtime.js.map +1 -1
  331. package/dist/hooks/extensibility/sdk/paths.d.ts +1 -1
  332. package/dist/hooks/extensibility/sdk/paths.d.ts.map +1 -1
  333. package/dist/hooks/extensibility/sdk/paths.js +2 -2
  334. package/dist/hooks/extensibility/sdk/paths.js.map +1 -1
  335. package/dist/hooks/extensibility/sdk/runtime-state.d.ts +1 -1
  336. package/dist/hooks/extensibility/sdk/runtime-state.d.ts.map +1 -1
  337. package/dist/hooks/extensibility/sdk/runtime-state.js +55 -15
  338. package/dist/hooks/extensibility/sdk/runtime-state.js.map +1 -1
  339. package/dist/hooks/extensibility/sdk.d.ts +1 -0
  340. package/dist/hooks/extensibility/sdk.d.ts.map +1 -1
  341. package/dist/hooks/extensibility/sdk.js +1 -1
  342. package/dist/hooks/extensibility/sdk.js.map +1 -1
  343. package/dist/hooks/extensibility/types.d.ts +7 -0
  344. package/dist/hooks/extensibility/types.d.ts.map +1 -1
  345. package/dist/hooks/keyword-detector.d.ts +10 -3
  346. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  347. package/dist/hooks/keyword-detector.js +116 -20
  348. package/dist/hooks/keyword-detector.js.map +1 -1
  349. package/dist/hooks/session.d.ts +204 -14
  350. package/dist/hooks/session.d.ts.map +1 -1
  351. package/dist/hooks/session.js +1500 -223
  352. package/dist/hooks/session.js.map +1 -1
  353. package/dist/hud/__tests__/hud-tmux-injection.test.js +6 -6
  354. package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
  355. package/dist/hud/__tests__/index.test.js +26 -16
  356. package/dist/hud/__tests__/index.test.js.map +1 -1
  357. package/dist/hud/__tests__/reconcile.test.js +83 -78
  358. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  359. package/dist/hud/__tests__/state.test.js +36 -31
  360. package/dist/hud/__tests__/state.test.js.map +1 -1
  361. package/dist/hud/__tests__/tmux.test.js +155 -198
  362. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  363. package/dist/hud/tmux.d.ts +42 -4
  364. package/dist/hud/tmux.d.ts.map +1 -1
  365. package/dist/hud/tmux.js +1017 -139
  366. package/dist/hud/tmux.js.map +1 -1
  367. package/dist/leader/__tests__/contract.test.js +124 -37
  368. package/dist/leader/__tests__/contract.test.js.map +1 -1
  369. package/dist/leader/contract.d.ts +17 -6
  370. package/dist/leader/contract.d.ts.map +1 -1
  371. package/dist/leader/contract.js +102 -26
  372. package/dist/leader/contract.js.map +1 -1
  373. package/dist/mcp/__tests__/code-intel-server.test.js +31 -3
  374. package/dist/mcp/__tests__/code-intel-server.test.js.map +1 -1
  375. package/dist/mcp/__tests__/hermes-bridge.test.js +17 -0
  376. package/dist/mcp/__tests__/hermes-bridge.test.js.map +1 -1
  377. package/dist/mcp/__tests__/server-lifecycle.test.js +4 -4
  378. package/dist/mcp/__tests__/state-paths.test.js +735 -4
  379. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  380. package/dist/mcp/__tests__/state-server.test.js +1 -1
  381. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  382. package/dist/mcp/__tests__/trace-server.test.js +2 -2
  383. package/dist/mcp/__tests__/trace-server.test.js.map +1 -1
  384. package/dist/mcp/__tests__/wiki-server.test.js +18 -0
  385. package/dist/mcp/__tests__/wiki-server.test.js.map +1 -1
  386. package/dist/mcp/code-intel-server.d.ts.map +1 -1
  387. package/dist/mcp/code-intel-server.js +13 -3
  388. package/dist/mcp/code-intel-server.js.map +1 -1
  389. package/dist/mcp/hermes-bridge.d.ts.map +1 -1
  390. package/dist/mcp/hermes-bridge.js +2 -2
  391. package/dist/mcp/hermes-bridge.js.map +1 -1
  392. package/dist/mcp/state-paths.d.ts +78 -4
  393. package/dist/mcp/state-paths.d.ts.map +1 -1
  394. package/dist/mcp/state-paths.js +543 -40
  395. package/dist/mcp/state-paths.js.map +1 -1
  396. package/dist/mcp/state-server.d.ts +4 -4
  397. package/dist/mcp/state-server.d.ts.map +1 -1
  398. package/dist/mcp/wiki-server.d.ts +11 -0
  399. package/dist/mcp/wiki-server.d.ts.map +1 -1
  400. package/dist/mcp/wiki-server.js +9 -2
  401. package/dist/mcp/wiki-server.js.map +1 -1
  402. package/dist/modes/__tests__/base-autopilot-gates.test.js +36 -7
  403. package/dist/modes/__tests__/base-autopilot-gates.test.js.map +1 -1
  404. package/dist/modes/__tests__/base-multi-state-compat.test.js +1 -1
  405. package/dist/modes/__tests__/base-multi-state-compat.test.js.map +1 -1
  406. package/dist/modes/__tests__/base-session-scope.test.js +17 -8
  407. package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
  408. package/dist/modes/__tests__/base-tmux-pane.test.js +4 -4
  409. package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
  410. package/dist/modes/base.d.ts +5 -5
  411. package/dist/modes/base.d.ts.map +1 -1
  412. package/dist/modes/base.js +76 -21
  413. package/dist/modes/base.js.map +1 -1
  414. package/dist/native-assets/archive.d.ts +17 -0
  415. package/dist/native-assets/archive.d.ts.map +1 -0
  416. package/dist/native-assets/archive.js +238 -0
  417. package/dist/native-assets/archive.js.map +1 -0
  418. package/dist/native-assets/policy.d.ts +42 -0
  419. package/dist/native-assets/policy.d.ts.map +1 -0
  420. package/dist/native-assets/policy.js +155 -0
  421. package/dist/native-assets/policy.js.map +1 -0
  422. package/dist/notifications/temp-contract.d.ts.map +1 -1
  423. package/dist/notifications/temp-contract.js +4 -0
  424. package/dist/notifications/temp-contract.js.map +1 -1
  425. package/dist/pipeline/__tests__/orchestrator.test.js +164 -42
  426. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  427. package/dist/pipeline/__tests__/stages.test.js +72 -155
  428. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  429. package/dist/pipeline/orchestrator.d.ts.map +1 -1
  430. package/dist/pipeline/orchestrator.js +124 -18
  431. package/dist/pipeline/orchestrator.js.map +1 -1
  432. package/dist/pipeline/stages/ralplan.d.ts.map +1 -1
  433. package/dist/pipeline/stages/ralplan.js +36 -8
  434. package/dist/pipeline/stages/ralplan.js.map +1 -1
  435. package/dist/ralplan/__tests__/consensus-gate.test.js +73 -1316
  436. package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
  437. package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts +2 -0
  438. package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts.map +1 -0
  439. package/dist/ralplan/__tests__/documented-leader-preflight.test.js +335 -0
  440. package/dist/ralplan/__tests__/documented-leader-preflight.test.js.map +1 -0
  441. package/dist/ralplan/__tests__/runtime.test.js +85 -101
  442. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  443. package/dist/ralplan/consensus-gate.d.ts +9 -1
  444. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  445. package/dist/ralplan/consensus-gate.js +82 -338
  446. package/dist/ralplan/consensus-gate.js.map +1 -1
  447. package/dist/ralplan/documented-leader-preflight.d.ts +41 -0
  448. package/dist/ralplan/documented-leader-preflight.d.ts.map +1 -0
  449. package/dist/ralplan/documented-leader-preflight.js +360 -0
  450. package/dist/ralplan/documented-leader-preflight.js.map +1 -0
  451. package/dist/ralplan/runtime.d.ts +2 -1
  452. package/dist/ralplan/runtime.d.ts.map +1 -1
  453. package/dist/ralplan/runtime.js +67 -168
  454. package/dist/ralplan/runtime.js.map +1 -1
  455. package/dist/runtime/__tests__/bridge.test.js +42 -1
  456. package/dist/runtime/__tests__/bridge.test.js.map +1 -1
  457. package/dist/runtime/bridge.d.ts +12 -0
  458. package/dist/runtime/bridge.d.ts.map +1 -1
  459. package/dist/runtime/bridge.js +52 -1
  460. package/dist/runtime/bridge.js.map +1 -1
  461. package/dist/runtime/run-state.d.ts +5 -1
  462. package/dist/runtime/run-state.d.ts.map +1 -1
  463. package/dist/runtime/run-state.js +15 -8
  464. package/dist/runtime/run-state.js.map +1 -1
  465. package/dist/scripts/__tests__/codex-native-hook.test.js +16323 -8978
  466. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  467. package/dist/scripts/__tests__/issue-3293-callsite-parity.test.d.ts +2 -0
  468. package/dist/scripts/__tests__/issue-3293-callsite-parity.test.d.ts.map +1 -0
  469. package/dist/scripts/__tests__/issue-3293-callsite-parity.test.js +225 -0
  470. package/dist/scripts/__tests__/issue-3293-callsite-parity.test.js.map +1 -0
  471. package/dist/scripts/__tests__/issue-3293-cancel-transaction.test.d.ts +2 -0
  472. package/dist/scripts/__tests__/issue-3293-cancel-transaction.test.d.ts.map +1 -0
  473. package/dist/scripts/__tests__/issue-3293-cancel-transaction.test.js +188 -0
  474. package/dist/scripts/__tests__/issue-3293-cancel-transaction.test.js.map +1 -0
  475. package/dist/scripts/__tests__/issue-3293-hook-owned-cancel.test.d.ts +2 -0
  476. package/dist/scripts/__tests__/issue-3293-hook-owned-cancel.test.d.ts.map +1 -0
  477. package/dist/scripts/__tests__/issue-3293-hook-owned-cancel.test.js +259 -0
  478. package/dist/scripts/__tests__/issue-3293-hook-owned-cancel.test.js.map +1 -0
  479. package/dist/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.d.ts +2 -0
  480. package/dist/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.d.ts.map +1 -0
  481. package/dist/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.js +491 -0
  482. package/dist/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.js.map +1 -0
  483. package/dist/scripts/__tests__/issue-3358-ultragoal-cancel-lifecycle.test.d.ts +2 -0
  484. package/dist/scripts/__tests__/issue-3358-ultragoal-cancel-lifecycle.test.d.ts.map +1 -0
  485. package/dist/scripts/__tests__/issue-3358-ultragoal-cancel-lifecycle.test.js +708 -0
  486. package/dist/scripts/__tests__/issue-3358-ultragoal-cancel-lifecycle.test.js.map +1 -0
  487. package/dist/scripts/__tests__/issue-3369-autopilot-ralplan-state-machine.test.d.ts +2 -0
  488. package/dist/scripts/__tests__/issue-3369-autopilot-ralplan-state-machine.test.d.ts.map +1 -0
  489. package/dist/scripts/__tests__/issue-3369-autopilot-ralplan-state-machine.test.js +208 -0
  490. package/dist/scripts/__tests__/issue-3369-autopilot-ralplan-state-machine.test.js.map +1 -0
  491. package/dist/scripts/__tests__/notify-state-io.test.js +26 -0
  492. package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
  493. package/dist/scripts/__tests__/notify-tmux-injection.test.js +23 -1
  494. package/dist/scripts/__tests__/notify-tmux-injection.test.js.map +1 -1
  495. package/dist/scripts/__tests__/postinstall.test.js +19 -1
  496. package/dist/scripts/__tests__/postinstall.test.js.map +1 -1
  497. package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts +2 -0
  498. package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts.map +1 -0
  499. package/dist/scripts/__tests__/reasoning-artifact-contract.test.js +108 -0
  500. package/dist/scripts/__tests__/reasoning-artifact-contract.test.js.map +1 -0
  501. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js +63 -173
  502. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js.map +1 -1
  503. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js +74 -159
  504. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js.map +1 -1
  505. package/dist/scripts/__tests__/smoke-packed-install.test.js +404 -13
  506. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  507. package/dist/scripts/__tests__/verify-native-release-assets.test.d.ts +2 -0
  508. package/dist/scripts/__tests__/verify-native-release-assets.test.d.ts.map +1 -0
  509. package/dist/scripts/__tests__/verify-native-release-assets.test.js +235 -0
  510. package/dist/scripts/__tests__/verify-native-release-assets.test.js.map +1 -0
  511. package/dist/scripts/codex-native-hook.d.ts +53 -1
  512. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  513. package/dist/scripts/codex-native-hook.js +14448 -1480
  514. package/dist/scripts/codex-native-hook.js.map +1 -1
  515. package/dist/scripts/codex-native-pre-post.d.ts +9 -0
  516. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  517. package/dist/scripts/codex-native-pre-post.js +35 -12
  518. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  519. package/dist/scripts/generate-native-release-manifest.js +80 -51
  520. package/dist/scripts/generate-native-release-manifest.js.map +1 -1
  521. package/dist/scripts/notify-fallback-watcher.js +78 -115
  522. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  523. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  524. package/dist/scripts/notify-hook/auto-nudge.js +74 -3
  525. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  526. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  527. package/dist/scripts/notify-hook/state-io.js +5 -0
  528. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  529. package/dist/scripts/notify-hook/team-dispatch.d.ts +9 -0
  530. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  531. package/dist/scripts/notify-hook/team-dispatch.js +234 -79
  532. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  533. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  534. package/dist/scripts/notify-hook/team-leader-nudge.js +71 -16
  535. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  536. package/dist/scripts/notify-hook/team-tmux-guard.d.ts +12 -4
  537. package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
  538. package/dist/scripts/notify-hook/team-tmux-guard.js +365 -44
  539. package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
  540. package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
  541. package/dist/scripts/notify-hook/team-worker-stop.js +42 -21
  542. package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
  543. package/dist/scripts/notify-hook/team-worker.d.ts +3 -0
  544. package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
  545. package/dist/scripts/notify-hook/team-worker.js +82 -33
  546. package/dist/scripts/notify-hook/team-worker.js.map +1 -1
  547. package/dist/scripts/notify-hook/tmux-injection.d.ts +1 -0
  548. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  549. package/dist/scripts/notify-hook/tmux-injection.js +32 -3
  550. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  551. package/dist/scripts/notify-hook.js +79 -82
  552. package/dist/scripts/notify-hook.js.map +1 -1
  553. package/dist/scripts/postinstall-advisory.d.ts +14 -0
  554. package/dist/scripts/postinstall-advisory.d.ts.map +1 -0
  555. package/dist/scripts/postinstall-advisory.js +49 -0
  556. package/dist/scripts/postinstall-advisory.js.map +1 -0
  557. package/dist/scripts/postinstall.d.ts +1 -1
  558. package/dist/scripts/postinstall.d.ts.map +1 -1
  559. package/dist/scripts/postinstall.js +1 -1
  560. package/dist/scripts/postinstall.js.map +1 -1
  561. package/dist/scripts/smoke-packed-install.d.ts +80 -4
  562. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  563. package/dist/scripts/smoke-packed-install.js +3162 -26
  564. package/dist/scripts/smoke-packed-install.js.map +1 -1
  565. package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
  566. package/dist/scripts/sync-plugin-mirror.js +16 -1
  567. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  568. package/dist/scripts/verify-native-release-assets.js +37 -23
  569. package/dist/scripts/verify-native-release-assets.js.map +1 -1
  570. package/dist/state/__tests__/mode-state-context.test.js +61 -0
  571. package/dist/state/__tests__/mode-state-context.test.js.map +1 -1
  572. package/dist/state/__tests__/operations.test.js +462 -850
  573. package/dist/state/__tests__/operations.test.js.map +1 -1
  574. package/dist/state/__tests__/skill-active.test.js +400 -2
  575. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  576. package/dist/state/mode-state-context.d.ts +8 -0
  577. package/dist/state/mode-state-context.d.ts.map +1 -1
  578. package/dist/state/mode-state-context.js +73 -0
  579. package/dist/state/mode-state-context.js.map +1 -1
  580. package/dist/state/operations.d.ts +3 -0
  581. package/dist/state/operations.d.ts.map +1 -1
  582. package/dist/state/operations.js +144 -40
  583. package/dist/state/operations.js.map +1 -1
  584. package/dist/state/skill-active.d.ts +18 -1
  585. package/dist/state/skill-active.d.ts.map +1 -1
  586. package/dist/state/skill-active.js +420 -23
  587. package/dist/state/skill-active.js.map +1 -1
  588. package/dist/state/workflow-transition-reconcile.d.ts +3 -0
  589. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  590. package/dist/state/workflow-transition-reconcile.js +10 -4
  591. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  592. package/dist/state/workflow-transition.d.ts.map +1 -1
  593. package/dist/state/workflow-transition.js +3 -1
  594. package/dist/state/workflow-transition.js.map +1 -1
  595. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js +53 -247
  596. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js.map +1 -1
  597. package/dist/subagents/__tests__/tracker.test.js +837 -1048
  598. package/dist/subagents/__tests__/tracker.test.js.map +1 -1
  599. package/dist/subagents/tracker.d.ts +58 -135
  600. package/dist/subagents/tracker.d.ts.map +1 -1
  601. package/dist/subagents/tracker.js +811 -632
  602. package/dist/subagents/tracker.js.map +1 -1
  603. package/dist/team/__tests__/api-interop.test.js +91 -1
  604. package/dist/team/__tests__/api-interop.test.js.map +1 -1
  605. package/dist/team/__tests__/delivery-e2e-smoke.test.js +58 -17
  606. package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
  607. package/dist/team/__tests__/idle-nudge.test.js +45 -15
  608. package/dist/team/__tests__/idle-nudge.test.js.map +1 -1
  609. package/dist/team/__tests__/mcp-comm.test.js +197 -30
  610. package/dist/team/__tests__/mcp-comm.test.js.map +1 -1
  611. package/dist/team/__tests__/model-contract.test.js +110 -3
  612. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  613. package/dist/team/__tests__/notice-ledger.test.d.ts +2 -0
  614. package/dist/team/__tests__/notice-ledger.test.d.ts.map +1 -0
  615. package/dist/team/__tests__/notice-ledger.test.js +131 -0
  616. package/dist/team/__tests__/notice-ledger.test.js.map +1 -0
  617. package/dist/team/__tests__/pane-teardown-residue-e2e.test.d.ts +2 -0
  618. package/dist/team/__tests__/pane-teardown-residue-e2e.test.d.ts.map +1 -0
  619. package/dist/team/__tests__/pane-teardown-residue-e2e.test.js +410 -0
  620. package/dist/team/__tests__/pane-teardown-residue-e2e.test.js.map +1 -0
  621. package/dist/team/__tests__/phase-controller.test.js +20 -0
  622. package/dist/team/__tests__/phase-controller.test.js.map +1 -1
  623. package/dist/team/__tests__/runtime-cli.test.js +9 -0
  624. package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
  625. package/dist/team/__tests__/runtime.test.js +3963 -428
  626. package/dist/team/__tests__/runtime.test.js.map +1 -1
  627. package/dist/team/__tests__/scaling.test.js +2405 -203
  628. package/dist/team/__tests__/scaling.test.js.map +1 -1
  629. package/dist/team/__tests__/state-root.test.js +117 -2
  630. package/dist/team/__tests__/state-root.test.js.map +1 -1
  631. package/dist/team/__tests__/state.test.js +804 -36
  632. package/dist/team/__tests__/state.test.js.map +1 -1
  633. package/dist/team/__tests__/team-ops-contract.test.js +6 -0
  634. package/dist/team/__tests__/team-ops-contract.test.js.map +1 -1
  635. package/dist/team/__tests__/tmux-session.test.js +4099 -1039
  636. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  637. package/dist/team/__tests__/tmux-source-authority-realtmux.test.d.ts +2 -0
  638. package/dist/team/__tests__/tmux-source-authority-realtmux.test.d.ts.map +1 -0
  639. package/dist/team/__tests__/tmux-source-authority-realtmux.test.js +149 -0
  640. package/dist/team/__tests__/tmux-source-authority-realtmux.test.js.map +1 -0
  641. package/dist/team/__tests__/tmux-test-fixture.d.ts +69 -0
  642. package/dist/team/__tests__/tmux-test-fixture.d.ts.map +1 -1
  643. package/dist/team/__tests__/tmux-test-fixture.js +242 -15
  644. package/dist/team/__tests__/tmux-test-fixture.js.map +1 -1
  645. package/dist/team/__tests__/tmux-test-fixture.test.js +177 -1
  646. package/dist/team/__tests__/tmux-test-fixture.test.js.map +1 -1
  647. package/dist/team/__tests__/worker-runtime-identity.test.js +53 -44
  648. package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
  649. package/dist/team/__tests__/worktree.test.js +6 -0
  650. package/dist/team/__tests__/worktree.test.js.map +1 -1
  651. package/dist/team/api-interop.d.ts.map +1 -1
  652. package/dist/team/api-interop.js +15 -9
  653. package/dist/team/api-interop.js.map +1 -1
  654. package/dist/team/exact-pane.d.ts +23 -0
  655. package/dist/team/exact-pane.d.ts.map +1 -0
  656. package/dist/team/exact-pane.js +133 -0
  657. package/dist/team/exact-pane.js.map +1 -0
  658. package/dist/team/idle-nudge.d.ts +12 -1
  659. package/dist/team/idle-nudge.d.ts.map +1 -1
  660. package/dist/team/idle-nudge.js +41 -14
  661. package/dist/team/idle-nudge.js.map +1 -1
  662. package/dist/team/model-contract.d.ts +2 -5
  663. package/dist/team/model-contract.d.ts.map +1 -1
  664. package/dist/team/model-contract.js +59 -17
  665. package/dist/team/model-contract.js.map +1 -1
  666. package/dist/team/notice-ledger.d.ts +54 -0
  667. package/dist/team/notice-ledger.d.ts.map +1 -0
  668. package/dist/team/notice-ledger.js +259 -0
  669. package/dist/team/notice-ledger.js.map +1 -0
  670. package/dist/team/phase-controller.d.ts.map +1 -1
  671. package/dist/team/phase-controller.js +2 -0
  672. package/dist/team/phase-controller.js.map +1 -1
  673. package/dist/team/runtime.d.ts +4 -1
  674. package/dist/team/runtime.d.ts.map +1 -1
  675. package/dist/team/runtime.js +1527 -291
  676. package/dist/team/runtime.js.map +1 -1
  677. package/dist/team/scaling.d.ts +13 -1
  678. package/dist/team/scaling.d.ts.map +1 -1
  679. package/dist/team/scaling.js +1503 -387
  680. package/dist/team/scaling.js.map +1 -1
  681. package/dist/team/state/dispatch.d.ts.map +1 -1
  682. package/dist/team/state/dispatch.js +26 -8
  683. package/dist/team/state/dispatch.js.map +1 -1
  684. package/dist/team/state/locks.d.ts.map +1 -1
  685. package/dist/team/state/locks.js +61 -13
  686. package/dist/team/state/locks.js.map +1 -1
  687. package/dist/team/state/monitor.d.ts +10 -0
  688. package/dist/team/state/monitor.d.ts.map +1 -1
  689. package/dist/team/state/monitor.js +13 -0
  690. package/dist/team/state/monitor.js.map +1 -1
  691. package/dist/team/state/tasks.d.ts.map +1 -1
  692. package/dist/team/state/tasks.js +4 -3
  693. package/dist/team/state/tasks.js.map +1 -1
  694. package/dist/team/state/types.d.ts +15 -0
  695. package/dist/team/state/types.d.ts.map +1 -1
  696. package/dist/team/state/types.js.map +1 -1
  697. package/dist/team/state-root.d.ts +12 -0
  698. package/dist/team/state-root.d.ts.map +1 -1
  699. package/dist/team/state-root.js +177 -0
  700. package/dist/team/state-root.js.map +1 -1
  701. package/dist/team/state.d.ts +85 -1
  702. package/dist/team/state.d.ts.map +1 -1
  703. package/dist/team/state.js +672 -129
  704. package/dist/team/state.js.map +1 -1
  705. package/dist/team/team-ops.d.ts +5 -0
  706. package/dist/team/team-ops.d.ts.map +1 -1
  707. package/dist/team/team-ops.js +4 -0
  708. package/dist/team/team-ops.js.map +1 -1
  709. package/dist/team/tmux-session.d.ts +163 -27
  710. package/dist/team/tmux-session.d.ts.map +1 -1
  711. package/dist/team/tmux-session.js +1682 -376
  712. package/dist/team/tmux-session.js.map +1 -1
  713. package/dist/team/worktree.d.ts.map +1 -1
  714. package/dist/team/worktree.js +4 -0
  715. package/dist/team/worktree.js.map +1 -1
  716. package/dist/ultragoal/__tests__/artifacts.test.js +950 -2
  717. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  718. package/dist/ultragoal/__tests__/docs-contract.test.js +1 -0
  719. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
  720. package/dist/ultragoal/artifacts.d.ts +35 -1
  721. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  722. package/dist/ultragoal/artifacts.js +232 -46
  723. package/dist/ultragoal/artifacts.js.map +1 -1
  724. package/dist/utils/__tests__/file-durability.test.d.ts +2 -0
  725. package/dist/utils/__tests__/file-durability.test.d.ts.map +1 -0
  726. package/dist/utils/__tests__/file-durability.test.js +114 -0
  727. package/dist/utils/__tests__/file-durability.test.js.map +1 -0
  728. package/dist/utils/__tests__/paths.test.js +21 -1
  729. package/dist/utils/__tests__/paths.test.js.map +1 -1
  730. package/dist/utils/file-durability.d.ts +25 -0
  731. package/dist/utils/file-durability.d.ts.map +1 -0
  732. package/dist/utils/file-durability.js +67 -0
  733. package/dist/utils/file-durability.js.map +1 -0
  734. package/dist/utils/paths.d.ts.map +1 -1
  735. package/dist/utils/paths.js +14 -6
  736. package/dist/utils/paths.js.map +1 -1
  737. package/dist/verification/__tests__/ci-rust-gates.test.js +49 -4
  738. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  739. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +3 -0
  740. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
  741. package/dist/verification/__tests__/explore-harness-release-workflow.test.js +60 -0
  742. package/dist/verification/__tests__/explore-harness-release-workflow.test.js.map +1 -1
  743. package/dist/verification/__tests__/native-release-manifest.test.js +157 -4
  744. package/dist/verification/__tests__/native-release-manifest.test.js.map +1 -1
  745. package/dist/wiki/lint.d.ts +4 -1
  746. package/dist/wiki/lint.d.ts.map +1 -1
  747. package/dist/wiki/lint.js +3 -2
  748. package/dist/wiki/lint.js.map +1 -1
  749. package/package.json +13 -5
  750. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  751. package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +105 -17
  752. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +13 -5
  753. package/plugins/oh-my-codex/skills/cancel/SKILL.md +30 -85
  754. package/plugins/oh-my-codex/skills/ralph/SKILL.md +6 -4
  755. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +16 -17
  756. package/plugins/oh-my-codex/skills/team/SKILL.md +13 -9
  757. package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +3 -2
  758. package/skills/autopilot/SKILL.md +13 -5
  759. package/skills/cancel/SKILL.md +30 -85
  760. package/skills/ralph/SKILL.md +6 -4
  761. package/skills/ralplan/SKILL.md +16 -17
  762. package/skills/team/SKILL.md +13 -9
  763. package/skills/ultragoal/SKILL.md +3 -2
  764. package/src/scripts/__tests__/codex-native-hook.test.ts +8831 -951
  765. package/src/scripts/__tests__/issue-3293-callsite-parity.test.ts +187 -0
  766. package/src/scripts/__tests__/issue-3293-cancel-transaction.test.ts +189 -0
  767. package/src/scripts/__tests__/issue-3293-hook-owned-cancel.test.ts +187 -0
  768. package/src/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.ts +591 -0
  769. package/src/scripts/__tests__/issue-3358-ultragoal-cancel-lifecycle.test.ts +701 -0
  770. package/src/scripts/__tests__/issue-3369-autopilot-ralplan-state-machine.test.ts +211 -0
  771. package/src/scripts/__tests__/notify-state-io.test.ts +29 -0
  772. package/src/scripts/__tests__/notify-tmux-injection.test.ts +26 -1
  773. package/src/scripts/__tests__/postinstall.test.ts +23 -1
  774. package/src/scripts/__tests__/reasoning-artifact-contract.test.ts +129 -0
  775. package/src/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.ts +67 -185
  776. package/src/scripts/__tests__/role-intent-durable-recovery-3181.test.ts +79 -178
  777. package/src/scripts/__tests__/smoke-packed-install.test.ts +525 -16
  778. package/src/scripts/__tests__/verify-native-release-assets.test.ts +240 -0
  779. package/src/scripts/codex-native-hook.ts +14238 -1429
  780. package/src/scripts/codex-native-pre-post.ts +43 -12
  781. package/src/scripts/generate-native-release-manifest.ts +83 -79
  782. package/src/scripts/notify-fallback-watcher.ts +91 -122
  783. package/src/scripts/notify-hook/auto-nudge.ts +71 -3
  784. package/src/scripts/notify-hook/state-io.ts +11 -0
  785. package/src/scripts/notify-hook/team-dispatch.ts +242 -75
  786. package/src/scripts/notify-hook/team-leader-nudge.ts +72 -22
  787. package/src/scripts/notify-hook/team-tmux-guard.ts +366 -39
  788. package/src/scripts/notify-hook/team-worker-stop.ts +43 -20
  789. package/src/scripts/notify-hook/team-worker.ts +82 -31
  790. package/src/scripts/notify-hook/tmux-injection.ts +33 -3
  791. package/src/scripts/notify-hook.ts +84 -82
  792. package/src/scripts/postinstall-advisory.ts +67 -0
  793. package/src/scripts/postinstall.ts +1 -1
  794. package/src/scripts/smoke-packed-install.ts +3608 -39
  795. package/src/scripts/sync-plugin-mirror.ts +17 -1
  796. package/src/scripts/verify-native-release-assets.ts +39 -31
  797. package/templates/AGENTS.md +1 -0
  798. package/dist/subagents/__tests__/adapted-role-binding.test.d.ts +0 -2
  799. package/dist/subagents/__tests__/adapted-role-binding.test.d.ts.map +0 -1
  800. package/dist/subagents/__tests__/adapted-role-binding.test.js +0 -988
  801. package/dist/subagents/__tests__/adapted-role-binding.test.js.map +0 -1
  802. package/dist/subagents/adapted-role-binding.d.ts +0 -17
  803. package/dist/subagents/adapted-role-binding.d.ts.map +0 -1
  804. package/dist/subagents/adapted-role-binding.js +0 -75
  805. package/dist/subagents/adapted-role-binding.js.map +0 -1
@@ -1,13 +1,15 @@
1
1
  import { afterEach, describe, it, mock } from "node:test";
2
2
  import assert from "node:assert/strict";
3
3
  import { existsSync, mkdirSync, readFileSync, utimesSync } from "node:fs";
4
+ import { spawnSync } from "node:child_process";
4
5
  import { chmod, lstat, mkdir, mkdtemp, readFile, readdir as fsReaddir, rm, stat, symlink, writeFile } from "node:fs/promises";
5
6
  import { delimiter, dirname, join } from "node:path";
6
7
  import { tmpdir } from "node:os";
7
8
  import { fileURLToPath } from "node:url";
8
- import { once } from "node:events";
9
9
  import TOML from "@iarna/toml";
10
- import { HELP, normalizeCodexLaunchArgs, buildTmuxShellCommand, buildTmuxPaneCommand, shouldSourceTmuxPaneShellRc, buildWindowsPromptCommand, buildTmuxSessionName, resolveCliInvocation, resolveUpdateChannelArg, commandOwnsLocalHelp, resolveCodexLaunchPolicy, resolveEffectiveLeaderLaunchPolicyOverride, resolveEnvLaunchPolicyOverride, resolveLeaderLaunchPolicyOverride, classifyCodexExecFailure, resolveSignalExitCode, parseTmuxPaneSnapshot, findHudWatchPaneIds, buildHudPaneCleanupTargets, readTopLevelTomlString, upsertTopLevelTomlString, collectInheritableTeamWorkerArgs, resolveTeamWorkerLaunchArgsEnv, injectModelInstructionsBypassArgs, resolveWorkerSparkModel, resolveSetupInstallModeArg, resolveSetupMcpModeArg, resolveSetupScopeArg, resolveSetupTeamModeArg, resolveSetupAgentsMergePolicyArg, resolveLaunchConfigRepairOptions, readPersistedSetupPreferences, readPersistedSetupScope, resolveCodexConfigPathForLaunch, resolveCodexHomeForLaunch, resolveProjectLocalCodexHomeForLaunch, shouldAutoIsolateMadmaxLaunch, createMadmaxIsolatedRoot, buildMadmaxDetachedLaunchContextKey, withMadmaxDetachedContextLock, resolveOmxRootForLaunch, resolveDisposableWorktreeOmxRootForLaunch, prepareCodexHomeForLaunch, captureMadmaxWorktreeRuntimeContext, persistProjectLaunchRuntimeAuthState, persistProjectLaunchRuntimeProjectTrustState, cleanupRuntimeCodexHome, runtimeCodexHomePath, buildDetachedSessionBootstrapSteps, buildDetachedTmuxSessionName, buildDetachedSessionFinalizeSteps, shouldAttachDetachedTmuxSession, buildDetachedSessionRollbackSteps, detectDetachedSessionWindowIndex, resolveNotifyTempContract, buildNotifyTempStartupMessages, buildNotifyFallbackWatcherEnv, shouldEnableNotifyFallbackWatcher, reapStaleNotifyFallbackWatcher, cleanupLaunchOrphanedMcpProcesses, reapPostLaunchOrphanedMcpProcesses, cleanupPostLaunchModeStateFiles, resolveBackgroundHelperLaunchMode, shouldDetachBackgroundHelper, resolveNotifyFallbackWatcherScript, resolveHookDerivedWatcherScript, resolveNotifyHookScript, buildDetachedWindowsBootstrapScript, acquireTmuxExtendedKeysLease, resolveNativeSessionName, releaseTmuxExtendedKeysLease, withTmuxExtendedKeys, serializeDetachedSessionParentEnv, buildInsideTmuxHudHookEnv, registerInsideTmuxHudResizeHook, buildDetachedHudHookEnv, registerDetachedHudLayoutReconcileHook, ensureOmxRuntimeCommandShim, omxRuntimeCommandShimPath, prependOmxRuntimeCommandShimToEnv, CODEX_SQLITE_HOME_ENV, DETACHED_TMUX_HISTORY_LIMIT, isExistingTmuxWindowTooCrampedForLaunchHud, } from "../index.js";
10
+ import { HELP, normalizeCodexLaunchArgs, buildTmuxShellCommand, buildTmuxPaneCommand, shouldSourceTmuxPaneShellRc, buildWindowsPromptCommand, buildWindowsDetachedChildCommand, buildTmuxSessionName, resolveCliInvocation, parseResumeCodexHomeSelection, isResumeCodexLaunch, CODEX_GLOBAL_OPTIONS_WITH_SPLIT_VALUE, isCodexVersionRequest, resolveUpdateChannelArg, commandOwnsLocalHelp, resolveCodexLaunchPolicy, resolveEffectiveLeaderLaunchPolicyOverride, resolveEnvLaunchPolicyOverride, resolveLeaderLaunchPolicyOverride, classifyCodexExecFailure, resolveSignalExitCode, parseTmuxPaneSnapshot, findHudWatchPaneIds, buildHudPaneCleanupTargets, readTopLevelTomlString, upsertTopLevelTomlString, collectInheritableTeamWorkerArgs, resolveTeamWorkerLaunchArgsEnv, injectModelInstructionsBypassArgs, resolveWorkerSparkModel, resolveSetupInstallModeArg, resolveSetupMcpModeArg, resolveSetupScopeArg, resolveSetupTeamModeArg, resolveSetupAgentsMergePolicyArg, resolveLaunchConfigRepairOptions, readPersistedSetupPreferences, readPersistedSetupScope, resolveCodexConfigPathForLaunch, resolveCodexHomeForLaunch, resolveProjectLocalCodexHomeForLaunch, shouldAutoIsolateMadmaxLaunch, createMadmaxIsolatedRoot, buildMadmaxDetachedLaunchContextKey, withMadmaxDetachedContextLock, executeDetachedLaunchStateMachine, resolveOmxRootForLaunch, resolveDisposableWorktreeOmxRootForLaunch, prepareCodexHomeForLaunch, captureMadmaxWorktreeRuntimeContext, persistProjectLaunchRuntimeAuthState, persistProjectLaunchRuntimeProjectTrustState, cleanupRuntimeCodexHome, runtimeCodexHomePath, buildDetachedSessionBootstrapSteps, buildDetachedTmuxSessionName, buildDetachedSessionFinalizeSteps, shouldAttachDetachedTmuxSession, buildDetachedSessionRollbackSteps, detectDetachedSessionWindowIndex, resolveNotifyTempContract, buildNotifyTempStartupMessages, buildNotifyFallbackWatcherEnv, shouldEnableNotifyFallbackWatcher, reapStaleNotifyFallbackWatcher, cleanupLaunchOrphanedMcpProcesses, reapPostLaunchOrphanedMcpProcesses, cleanupPostLaunchModeStateFiles, resolveBackgroundHelperLaunchMode, shouldDetachBackgroundHelper, resolveNotifyFallbackWatcherScript, resolveHookDerivedWatcherScript, resolveNotifyHookScript, acquireTmuxExtendedKeysLease, resolveNativeSessionName, releaseTmuxExtendedKeysLease, withTmuxExtendedKeys, serializeDetachedSessionParentEnv, buildInsideTmuxHudHookEnv, registerInsideTmuxHudResizeHook, buildDetachedHudHookEnv, registerDetachedHudLayoutReconcileHook, ensureOmxRuntimeCommandShim, omxRuntimeCommandShimPath, prependOmxRuntimeCommandShimToEnv, CODEX_SQLITE_HOME_ENV, DETACHED_TMUX_HISTORY_LIMIT, isExistingTmuxWindowTooCrampedForLaunchHud, guardDetachedHudDeferredMutation, DETACHED_LAUNCH_CONTROL_PLANE_KEYS, buildDetachedLaunchControlPlane, } from "../index.js";
11
+ import { buildResumeArgsWithPreservedFlags, stripHotswapArg } from "../../auth/hotswap.js";
12
+ import { writeSkillActiveStateCopiesForStateDir } from '../../state/skill-active.js';
11
13
  import { mergeConfig, repairConfigIfNeeded } from "../../config/generator.js";
12
14
  import { ensureReusableNodeModules } from "../../utils/repo-deps.js";
13
15
  import { readAllState } from "../../hud/state.js";
@@ -16,6 +18,7 @@ import { HUD_TMUX_HEIGHT_LINES, HUD_TMUX_MIN_LAUNCH_WINDOW_HEIGHT_LINES } from "
16
18
  import { createHudWatchPane as createSharedHudWatchPane, listCurrentWindowHudPaneIds } from "../../hud/tmux.js";
17
19
  import { DEFAULT_FRONTIER_MODEL, getTeamLowComplexityModel, } from "../../config/models.js";
18
20
  import { splitWorkerLaunchArgs } from "../../team/model-contract.js";
21
+ import { buildRegisterResizeHookArgs } from "../../team/tmux-session.js";
19
22
  const testDir = dirname(fileURLToPath(import.meta.url));
20
23
  const repoRoot = join(testDir, "..", "..", "..");
21
24
  function normalizeDarwinTmpPath(value) {
@@ -66,6 +69,7 @@ describe("madmax state isolation", () => {
66
69
  const runDir = createMadmaxIsolatedRoot(wd, ["--madmax", "--high"], env);
67
70
  env.OMX_ROOT = runDir;
68
71
  env.OMXBOX_ACTIVE = "1";
72
+ env.OMX_SOURCE_CWD = wd;
69
73
  assert.equal(shouldAutoIsolateMadmaxLaunch("launch", ["--madmax", "--high"], env, wd), false);
70
74
  assert.equal(shouldAutoIsolateMadmaxLaunch("launch", ["--madmax", "--xhigh"], env, wd), true, "changed launch semantics must not reuse an inherited boxed root");
71
75
  }
@@ -77,29 +81,40 @@ describe("madmax state isolation", () => {
77
81
  it("preserves explicit no-box behavior", () => {
78
82
  assert.equal(shouldAutoIsolateMadmaxLaunch("launch", ["--madmax"], { OMX_NO_BOX: "1" }, "/repo"), false);
79
83
  });
80
- it("captures madmax worktree context from parsed worktree state, not remaining args", () => {
84
+ it("captures madmax worktree context from parsed worktree state, not remaining args", async () => {
81
85
  const sourceCwd = "/repo/source";
82
86
  const worktreeCwd = "/repo/.worktrees/session";
83
- const runDir = "/runs/run-issue-3043";
84
- const context = captureMadmaxWorktreeRuntimeContext({
85
- originalLaunchArgs: ["--madmax", "--worktree", "--version"],
86
- worktreeEnabled: true,
87
- sourceCwd,
88
- worktreeCwd,
89
- env: {
90
- OMX_ROOT: runDir,
91
- OMXBOX_ACTIVE: "1",
92
- OMX_SOURCE_CWD: sourceCwd,
93
- OMX_MADMAX_DETACHED_CONTEXT: "ctx-3043",
94
- },
95
- });
96
- assert.deepEqual(context, {
97
- omxRoot: runDir,
98
- sourceCwd,
99
- worktreeCwd,
100
- madmaxDetachedContext: "ctx-3043",
101
- boxedActive: true,
102
- });
87
+ const runDir = await mkdtemp(join(tmpdir(), "omx-run-issue-3043-"));
88
+ try {
89
+ const detachedContext = buildMadmaxDetachedLaunchContextKey(sourceCwd, ["--madmax", "--worktree", "--version"], runDir);
90
+ await writeFile(join(runDir, ".omxbox-run.json"), JSON.stringify({
91
+ cwd: runDir,
92
+ source_cwd: sourceCwd,
93
+ detached_launch_context: detachedContext,
94
+ }));
95
+ const context = captureMadmaxWorktreeRuntimeContext({
96
+ originalLaunchArgs: ["--madmax", "--worktree", "--version"],
97
+ worktreeEnabled: true,
98
+ sourceCwd,
99
+ worktreeCwd,
100
+ env: {
101
+ OMX_ROOT: runDir,
102
+ OMXBOX_ACTIVE: "1",
103
+ OMX_SOURCE_CWD: sourceCwd,
104
+ OMX_MADMAX_DETACHED_CONTEXT: detachedContext,
105
+ },
106
+ });
107
+ assert.deepEqual(context, {
108
+ omxRoot: runDir,
109
+ sourceCwd,
110
+ worktreeCwd,
111
+ madmaxDetachedContext: detachedContext,
112
+ boxedActive: true,
113
+ });
114
+ }
115
+ finally {
116
+ await rm(runDir, { recursive: true, force: true });
117
+ }
103
118
  });
104
119
  it("does not capture ordinary worktree or unboxed madmax launches", () => {
105
120
  assert.equal(captureMadmaxWorktreeRuntimeContext({
@@ -195,7 +210,7 @@ describe("madmax state isolation", () => {
195
210
  mkdirSync(lockPath, { recursive: true });
196
211
  await writeFile(join(lockPath, "pid"), "2147483647");
197
212
  let ran = false;
198
- const result = withMadmaxDetachedContextLock(runs, contextKey, () => {
213
+ const result = await withMadmaxDetachedContextLock(runs, contextKey, () => {
199
214
  ran = true;
200
215
  return "acquired";
201
216
  }, { maxAttempts: 2, retryMs: 0 });
@@ -220,7 +235,7 @@ describe("madmax state isolation", () => {
220
235
  acquired_at: new Date().toISOString(),
221
236
  })}\n`);
222
237
  await writeFile(join(lockPath, "pid"), String(process.pid));
223
- assert.throws(() => withMadmaxDetachedContextLock(runs, contextKey, () => "should-not-run", { maxAttempts: 1, retryMs: 0 }), (err) => {
238
+ await assert.rejects(() => withMadmaxDetachedContextLock(runs, contextKey, () => "should-not-run", { maxAttempts: 1, retryMs: 0 }), (err) => {
224
239
  assert.ok(err instanceof Error);
225
240
  assert.match(err.message, /timed out waiting for madmax detached launch context lock/);
226
241
  assert.match(err.message, new RegExp(`holder pid ${process.pid} is still running`));
@@ -237,6 +252,135 @@ describe("madmax state isolation", () => {
237
252
  }
238
253
  });
239
254
  });
255
+ describe("detached launch state machine", () => {
256
+ function createDependencies(events, failAt) {
257
+ const step = async (name) => {
258
+ events.push(name);
259
+ if (failAt === name)
260
+ throw new Error(name);
261
+ };
262
+ return {
263
+ establish: async () => { await step("D1"); return "binding"; },
264
+ complete: async () => { await step("D2"); return { kind: "success" }; },
265
+ createInertSession: async () => { await step("D3"); return "inert"; },
266
+ capturePane: async () => { await step("D4"); return "%1"; },
267
+ updateNameMetadata: async () => { await step("D5"); return "committed-released"; },
268
+ updatePaneMetadata: async () => { await step("D6"); return "committed-released"; },
269
+ publishActiveRecord: async () => {
270
+ await step("D7");
271
+ return { bytes: "record", digest: "digest", nonce: "nonce" };
272
+ },
273
+ finalizeSetupFailure: async () => { await step("finalize-setup-failure"); },
274
+ releaseBarrier: async () => { await step("D9"); },
275
+ abortAndAwaitFinalization: async () => ({
276
+ acknowledged: true,
277
+ nonce: "nonce",
278
+ sessionId: "session",
279
+ sessionName: "session-name",
280
+ leaderPid: 123,
281
+ kind: "failed",
282
+ }),
283
+ attachOrReturn: async () => { await step("D10"); },
284
+ rollback: async () => { events.push("rollback"); },
285
+ };
286
+ }
287
+ it("uses the frozen D0 outcome without rediscovering transport or reuse", async () => {
288
+ const events = [];
289
+ const result = await executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: true, report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, createDependencies(events));
290
+ assert.equal(result.kind, "attached");
291
+ assert.deepEqual(events, ["D1", "D2", "D3", "D4", "D5", "D6", "D7", "D9", "D10"]);
292
+ });
293
+ for (const platform of ["posix", "native-windows"]) {
294
+ it(`${platform}: releases only after atomic record and starts exactly once`, async () => {
295
+ const events = [];
296
+ const result = await executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: platform === "posix", report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, createDependencies(events));
297
+ assert.equal(result.kind, platform === "posix" ? "attached" : "returned");
298
+ assert.deepEqual(events, ["D1", "D2", "D3", "D4", "D5", "D6", "D7", "D9", "D10"]);
299
+ });
300
+ }
301
+ for (const failure of ["D1", "D2", "D3", "D4", "D5", "D6", "D7"]) {
302
+ it(`rolls back verified pre-release ownership when ${failure} fails`, async () => {
303
+ const events = [];
304
+ await assert.rejects(executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: true, report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, createDependencies(events, failure)), (err) => {
305
+ assert.ok(err instanceof Error);
306
+ assert.equal(err.report?.transitions.at(-1), failure);
307
+ return true;
308
+ });
309
+ assert.ok(events.includes("rollback"));
310
+ assert.equal(events.includes("D10"), false);
311
+ });
312
+ }
313
+ it("finalizes and closes exactly once before D2 rollback without transport effects", async () => {
314
+ const events = [];
315
+ const deps = createDependencies(events);
316
+ deps.complete = async () => {
317
+ events.push("D2");
318
+ return { kind: "failure", operation: "session-instructions", error: new Error("instructions") };
319
+ };
320
+ await assert.rejects(() => executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: true, report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, deps), /preLaunch session-instructions failed: instructions/);
321
+ assert.deepEqual(events, ["D1", "D2", "finalize-setup-failure", "rollback"]);
322
+ });
323
+ it("rolls back proven pane authority when leader parsing fails before a report PID", async () => {
324
+ const events = [];
325
+ const deps = createDependencies(events, "D2");
326
+ deps.abortAndAwaitFinalization = async () => ({ acknowledged: false, rollbackAuthorized: true });
327
+ await assert.rejects(executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: true, report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, deps));
328
+ assert.equal(events.includes("rollback"), true);
329
+ });
330
+ it("never rolls back, deletes records, or falls back after D10 attach failure", async () => {
331
+ const events = [];
332
+ await assert.rejects(executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: true, report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, createDependencies(events, "D10")), (err) => {
333
+ assert.ok(err instanceof Error);
334
+ assert.equal(err.report?.transitions.at(-1), "D10");
335
+ return true;
336
+ });
337
+ assert.equal(events.includes("rollback"), false);
338
+ assert.deepEqual(events.slice(-2), ["D9", "D10"]);
339
+ });
340
+ it("preserves leader authority when D9 publication fails without an authenticated finalization acknowledgement", async () => {
341
+ const events = [];
342
+ const deps = createDependencies(events, "D9");
343
+ deps.abortAndAwaitFinalization = async () => {
344
+ events.push("abort-request");
345
+ return { acknowledged: false };
346
+ };
347
+ await assert.rejects(() => executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: true, report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, deps));
348
+ assert.deepEqual(events.slice(-2), ["D9", "abort-request"]);
349
+ assert.equal(events.includes("rollback"), false);
350
+ });
351
+ it("permits rollback only after an authenticated D9 abort acknowledgement", async () => {
352
+ const events = [];
353
+ const deps = createDependencies(events, "D9");
354
+ deps.abortAndAwaitFinalization = async () => ({
355
+ acknowledged: true,
356
+ nonce: "nonce",
357
+ sessionId: "session",
358
+ sessionName: "session-name",
359
+ leaderPid: 123,
360
+ kind: "failed",
361
+ });
362
+ await assert.rejects(() => executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: true, report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, deps));
363
+ assert.equal(events.includes("rollback"), true);
364
+ });
365
+ it("fails closed when a purported D9 acknowledgement omits its finalized authority binding", async () => {
366
+ const events = [];
367
+ const deps = createDependencies(events, "D9");
368
+ deps.abortAndAwaitFinalization = async () => ({ acknowledged: true });
369
+ await assert.rejects(() => executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: true, report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, deps));
370
+ assert.equal(events.includes("rollback"), false);
371
+ });
372
+ it("preserves the authority-owning leader on a pre-D9 failure without finalized acknowledgement", async () => {
373
+ const events = [];
374
+ const deps = createDependencies(events, "D4");
375
+ deps.abortAndAwaitFinalization = async () => {
376
+ events.push("abort-request");
377
+ return { acknowledged: false };
378
+ };
379
+ await assert.rejects(() => executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: true, report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, deps));
380
+ assert.deepEqual(events.slice(-2), ["D4", "abort-request"]);
381
+ assert.equal(events.includes("rollback"), false);
382
+ });
383
+ });
240
384
  describe("resolveOmxRootForLaunch", () => {
241
385
  it("preserves POSIX absolute OMX_ROOT", () => {
242
386
  assert.equal(resolveOmxRootForLaunch("/repo", { OMX_ROOT: "/var/tmp/omx" }), "/var/tmp/omx");
@@ -322,15 +466,223 @@ describe("normalizeCodexLaunchArgs", () => {
322
466
  'model_reasoning_effort="xhigh"',
323
467
  ]);
324
468
  });
469
+ it("adds reasoning overrides before a literal -- marker", () => {
470
+ assert.deepEqual(normalizeCodexLaunchArgs(["--xhigh", "--", "--max"]), [
471
+ "-c",
472
+ 'model_reasoning_effort="xhigh"',
473
+ "--",
474
+ "--max",
475
+ ]);
476
+ });
477
+ it("adds bypass and reasoning overrides before preserving raw marker suffix args", () => {
478
+ assert.deepEqual(normalizeCodexLaunchArgs([
479
+ "--madmax",
480
+ "--xhigh",
481
+ "--",
482
+ "-c",
483
+ 'model_reasoning_effort="ultra"',
484
+ "--max",
485
+ ]), [
486
+ "--dangerously-bypass-approvals-and-sandbox",
487
+ "-c",
488
+ 'model_reasoning_effort="xhigh"',
489
+ "--",
490
+ "-c",
491
+ 'model_reasoning_effort="ultra"',
492
+ "--max",
493
+ ]);
494
+ });
325
495
  it("uses the last reasoning shorthand when both are present", () => {
326
496
  assert.deepEqual(normalizeCodexLaunchArgs(["--high", "--xhigh"]), [
327
497
  "-c",
328
498
  'model_reasoning_effort="xhigh"',
329
499
  ]);
330
500
  });
331
- it("rejects ambiguous max and ultra reasoning shorthands", () => {
332
- assert.throws(() => normalizeCodexLaunchArgs(["--max"]), /canonical highest reasoning effort is "xhigh".*"max" and "ultra" are not accepted aliases/);
333
- assert.throws(() => normalizeCodexLaunchArgs(["--ultra"]), /canonical highest reasoning effort is "xhigh".*"max" and "ultra" are not accepted aliases/);
501
+ it("rejects pre-marker max and ultra reasoning shorthands with approved guidance", () => {
502
+ const errors = [
503
+ [
504
+ "--max",
505
+ 'Unsupported OMX launch shorthand "--max".\nNo --max shorthand exists; use agentReasoning for per-agent "max" or pass -c model_reasoning_effort=... directly to Codex.\nRun "omx help" for usage.',
506
+ ],
507
+ [
508
+ "--ultra",
509
+ 'Unsupported OMX launch shorthand "--ultra".\n"ultra" is not an OMX root or per-agent reasoning value and is not an alias for "max".\nRun "omx help" for usage.',
510
+ ],
511
+ ];
512
+ for (const [flag, message] of errors) {
513
+ assert.throws(() => normalizeCodexLaunchArgs([flag]), { message });
514
+ }
515
+ });
516
+ it("preserves literal max and ultra after -- with raw -c arguments", () => {
517
+ const args = [
518
+ "-c",
519
+ "model_reasoning_effort=MAX",
520
+ "--",
521
+ "--max",
522
+ "--ultra",
523
+ "-c",
524
+ 'model_reasoning_effort="ultra"',
525
+ "-c",
526
+ "model_reasoning_effort=future",
527
+ ];
528
+ assert.deepEqual(normalizeCodexLaunchArgs(args), args);
529
+ });
530
+ it("preserves post-marker OMX flags as literal Codex arguments", () => {
531
+ const args = [
532
+ "--",
533
+ "--worktree",
534
+ "post-marker-branch",
535
+ "--notify-temp",
536
+ "--discord",
537
+ "--custom",
538
+ "openclaw:ops",
539
+ "--spark",
540
+ "resume",
541
+ "--project",
542
+ "--codex-home",
543
+ "/tmp/literal-codex-home",
544
+ "--version",
545
+ ];
546
+ const notifyTempResult = resolveNotifyTempContract(args, {});
547
+ assert.equal(notifyTempResult.contract.active, false);
548
+ assert.deepEqual(notifyTempResult.contract.canonicalSelectors, []);
549
+ assert.deepEqual(notifyTempResult.passthroughArgs, args);
550
+ assert.equal(resolveWorkerSparkModel(notifyTempResult.passthroughArgs), undefined);
551
+ assert.equal(isResumeCodexLaunch(args), false);
552
+ assert.equal(isCodexVersionRequest(args), false);
553
+ assert.deepEqual(parseResumeCodexHomeSelection(args), {
554
+ args,
555
+ explicitCodexHome: undefined,
556
+ projectOnly: false,
557
+ });
558
+ assert.deepEqual(normalizeCodexLaunchArgs(notifyTempResult.passthroughArgs), args);
559
+ });
560
+ it("parses resume-owned selectors only before the end-of-options marker", () => {
561
+ assert.deepEqual(parseResumeCodexHomeSelection([
562
+ "resume",
563
+ "--codex-home",
564
+ "/tmp/selected-codex-home",
565
+ "--project",
566
+ "--",
567
+ "--codex-home",
568
+ "/tmp/literal-codex-home",
569
+ "--project",
570
+ ]), {
571
+ args: [
572
+ "resume",
573
+ "--",
574
+ "--codex-home",
575
+ "/tmp/literal-codex-home",
576
+ "--project",
577
+ ],
578
+ explicitCodexHome: "/tmp/selected-codex-home",
579
+ projectOnly: true,
580
+ });
581
+ assert.equal(isResumeCodexLaunch(["resume", "--", "literal"]), true);
582
+ assert.equal(isCodexVersionRequest(["--version", "--", "literal"]), true);
583
+ });
584
+ it("removes hotswap only before the end-of-options marker", () => {
585
+ assert.deepEqual(stripHotswapArg(["--hotswap", "--", "--hotswap", "literal"]), ["--", "--hotswap", "literal"]);
586
+ });
587
+ it("preserves Codex launch authority and the first literal suffix when building quota resume args", () => {
588
+ assert.deepEqual(buildResumeArgsWithPreservedFlags([
589
+ "--model", "gpt-review",
590
+ "--model=gpt-review-fast",
591
+ "--config", "developer_instructions=enabled",
592
+ "--config=developer_instructions=overridden",
593
+ "--add-dir", "src",
594
+ "--remote", "ws://127.0.0.1:4500",
595
+ "--remote=ws://127.0.0.1:4501",
596
+ "--remote-auth-token-env", "CODEX_REMOTE_TOKEN",
597
+ "--remote-auth-token-env=CODEX_REMOTE_TOKEN_BACKUP",
598
+ "-i", "one.png",
599
+ "--image", "two.png",
600
+ "-i", "three.png,four.png",
601
+ "--image=five.png,six.png",
602
+ "-iseven.png,eight.png",
603
+ "--image=resume", "resume",
604
+ "--oss",
605
+ "--dangerously-bypass-approvals-and-sandbox",
606
+ "--model", "gpt-resume",
607
+ "resume", "old-session",
608
+ "--last", "--all", "--include-non-interactive",
609
+ "--", "--hotswap", "--last", "--all", "--include-non-interactive", "--model", "opaque-model", "literal suffix",
610
+ ], "session-123"), [
611
+ "resume", "session-123",
612
+ "--model", "gpt-review",
613
+ "--model=gpt-review-fast",
614
+ "--config", "developer_instructions=enabled",
615
+ "--config=developer_instructions=overridden",
616
+ "--add-dir", "src",
617
+ "--remote", "ws://127.0.0.1:4500",
618
+ "--remote=ws://127.0.0.1:4501",
619
+ "--remote-auth-token-env", "CODEX_REMOTE_TOKEN",
620
+ "--remote-auth-token-env=CODEX_REMOTE_TOKEN_BACKUP",
621
+ "-i", "one.png",
622
+ "--image", "two.png",
623
+ "-i", "three.png,four.png",
624
+ "--image=five.png,six.png",
625
+ "-iseven.png,eight.png",
626
+ "--image=resume",
627
+ "--oss",
628
+ "--dangerously-bypass-approvals-and-sandbox",
629
+ "--model", "gpt-resume",
630
+ "--", "--hotswap", "--last", "--all", "--include-non-interactive", "--model", "opaque-model", "literal suffix",
631
+ ]);
632
+ });
633
+ it("removes resume selectors only when synthesizing an explicit session", () => {
634
+ for (const selectors of [
635
+ ["--last"],
636
+ ["--all"],
637
+ ["--include-non-interactive"],
638
+ ["--last", "--all"],
639
+ ["--all", "--include-non-interactive"],
640
+ ["--last", "--all", "--include-non-interactive"],
641
+ ]) {
642
+ assert.deepEqual(buildResumeArgsWithPreservedFlags([
643
+ "resume",
644
+ ...selectors,
645
+ "--model", "gpt-review",
646
+ "--remote", "ws://127.0.0.1:4500",
647
+ "--", ...selectors, "opaque suffix",
648
+ ], "session-123"), [
649
+ "resume", "session-123",
650
+ "--model", "gpt-review",
651
+ "--remote", "ws://127.0.0.1:4500",
652
+ "--", ...selectors, "opaque suffix",
653
+ ], JSON.stringify(selectors));
654
+ }
655
+ });
656
+ it("treats only split image values as variadic", () => {
657
+ assert.deepEqual([...CODEX_GLOBAL_OPTIONS_WITH_SPLIT_VALUE], [
658
+ ["-a", "single"], ["--ask-for-approval", "single"], ["-c", "single"],
659
+ ["--config", "single"], ["-C", "single"], ["--cd", "single"],
660
+ ["-i", "variadic"], ["--image", "variadic"], ["-m", "single"],
661
+ ["--model", "single"], ["-p", "single"], ["--profile", "single"],
662
+ ["-s", "single"], ["--sandbox", "single"], ["--add-dir", "single"],
663
+ ["--disable", "single"], ["--enable", "single"], ["--local-provider", "single"],
664
+ ["--remote", "single"], ["--remote-auth-token-env", "single"],
665
+ ]);
666
+ for (const args of [
667
+ ["--model", "resume"], ["--remote", "resume"],
668
+ ["--image", "resume"], ["-i", "resume"], ["--image=resume"], ["-iresume"], ["-i=resume"],
669
+ ["-i", "one.png", "resume"], ["--image", "one.png", "resume"],
670
+ ["-i", "one.png", "-i", "two.png", "resume"],
671
+ ["--image", "one.png", "--image", "two.png", "resume"],
672
+ ["-i", "one.png,two.png", "resume"], ["--image", "one.png,two.png", "resume"],
673
+ ["exec", "resume"], ["--image", "one.png", "--", "resume"],
674
+ ]) {
675
+ assert.equal(isResumeCodexLaunch(args), false, JSON.stringify(args));
676
+ }
677
+ for (const args of [
678
+ ["--model", "gpt-review", "resume"],
679
+ ["--image=one.png", "resume"], ["-ione.png", "resume"], ["-i=one.png", "resume"],
680
+ ["--image", "one.png", "--model", "gpt-review", "resume"],
681
+ ["--image=one.png", "--model=gpt-review", "resume"],
682
+ ["-ione.png", "--remote", "ws://127.0.0.1:4500", "resume"],
683
+ ]) {
684
+ assert.equal(isResumeCodexLaunch(args), true, JSON.stringify(args));
685
+ }
334
686
  });
335
687
  it("maps --xhigh --madmax to codex-native flags only", () => {
336
688
  assert.deepEqual(normalizeCodexLaunchArgs(["--xhigh", "--madmax"]), [
@@ -484,6 +836,13 @@ describe("resolveNotifyTempContract", () => {
484
836
  ]);
485
837
  assert.equal(parsed.contract.warnings.length >= 1, true);
486
838
  });
839
+ it("does not activate or consume selectors after --", () => {
840
+ const args = ["--", "--notify-temp", "--discord", "--custom", "openclaw:ops"];
841
+ const parsed = resolveNotifyTempContract(args, {});
842
+ assert.equal(parsed.contract.active, false);
843
+ assert.deepEqual(parsed.contract.selectors, []);
844
+ assert.deepEqual(parsed.passthroughArgs, args);
845
+ });
487
846
  it("activates from OMX_NOTIFY_TEMP=1 env parity", () => {
488
847
  const parsed = resolveNotifyTempContract(["--model", "gpt-5"], {
489
848
  OMX_NOTIFY_TEMP: "1",
@@ -634,6 +993,81 @@ describe("cleanupPostLaunchModeStateFiles", () => {
634
993
  }
635
994
  assert.deepEqual(warnings, []);
636
995
  });
996
+ it('routes root skill-active cleanup through the locked root writer', async () => {
997
+ const wd = await mkdtemp(join(tmpdir(), 'omx-postlaunch-root-writer-'));
998
+ const sessionId = 'sess-root-writer';
999
+ const stateDir = join(wd, '.omx', 'state');
1000
+ const rootState = {
1001
+ version: 1,
1002
+ active: true,
1003
+ skill: 'ralph',
1004
+ session_id: sessionId,
1005
+ active_skills: [{ skill: 'ralph', phase: 'executing', active: true, session_id: sessionId }],
1006
+ };
1007
+ await mkdir(join(stateDir, 'sessions', sessionId), { recursive: true });
1008
+ await writeFile(join(stateDir, 'skill-active-state.json'), `${JSON.stringify(rootState, null, 2)}\n`, 'utf8');
1009
+ let writerCalls = 0;
1010
+ await cleanupPostLaunchModeStateFiles(wd, sessionId, {
1011
+ writeRootState: async (rootDir, update) => {
1012
+ writerCalls += 1;
1013
+ const concurrentRoot = {
1014
+ ...rootState,
1015
+ active_skills: [
1016
+ ...rootState.active_skills,
1017
+ { skill: 'team', phase: 'running', active: true, session_id: 'sess-concurrent' },
1018
+ ],
1019
+ };
1020
+ const nextState = update(concurrentRoot);
1021
+ assert.ok(nextState);
1022
+ await writeSkillActiveStateCopiesForStateDir(rootDir, nextState);
1023
+ },
1024
+ });
1025
+ assert.equal(writerCalls, 1);
1026
+ const persisted = JSON.parse(await readFile(join(stateDir, 'skill-active-state.json'), 'utf8'));
1027
+ assert.equal(persisted.active, true);
1028
+ assert.deepEqual(persisted.active_skills, [{ skill: 'team', phase: 'running', active: true, session_id: 'sess-concurrent' }]);
1029
+ });
1030
+ it('preserves a process-level session update before root scrub cleanup', async () => {
1031
+ const wd = await mkdtemp(join(tmpdir(), 'omx-postlaunch-process-scrub-'));
1032
+ const sessionId = 'sess-process-scrub';
1033
+ const stateDir = join(wd, '.omx', 'state');
1034
+ const rootPath = join(stateDir, 'skill-active-state.json');
1035
+ const rootState = {
1036
+ version: 1,
1037
+ active: true,
1038
+ skill: 'ralph',
1039
+ session_id: sessionId,
1040
+ active_skills: [{ skill: 'ralph', phase: 'executing', active: true, session_id: sessionId }],
1041
+ };
1042
+ try {
1043
+ await mkdir(join(stateDir, 'sessions', sessionId), { recursive: true });
1044
+ await writeFile(rootPath, `${JSON.stringify(rootState, null, 2)}\n`, 'utf8');
1045
+ const updateScript = `
1046
+ import { readFile, writeFile } from 'node:fs/promises';
1047
+ import { writeSkillActiveStateCopiesForStateDir } from './dist/state/skill-active.js';
1048
+ const stateDir = process.env.STATE_DIR;
1049
+ const current = JSON.parse(await readFile(process.env.ROOT_PATH, 'utf8'));
1050
+ current.active_skills.push({ skill: 'team', phase: 'running', active: true, session_id: 'sess-process-concurrent' });
1051
+ await writeSkillActiveStateCopiesForStateDir(stateDir, current, undefined, current);
1052
+ `;
1053
+ const update = spawnSync(process.execPath, ['--input-type=module', '-e', updateScript], {
1054
+ cwd: repoRoot,
1055
+ env: { ...process.env, STATE_DIR: stateDir, ROOT_PATH: rootPath },
1056
+ encoding: 'utf8',
1057
+ });
1058
+ assert.equal(update.status, 0, update.stderr);
1059
+ const scrub = spawnSync(process.execPath, ['--input-type=module', '-e', `
1060
+ import { cleanupPostLaunchModeStateFiles } from './dist/cli/index.js';
1061
+ await cleanupPostLaunchModeStateFiles(${JSON.stringify(wd)}, ${JSON.stringify(sessionId)});
1062
+ `], { cwd: repoRoot, env: { ...process.env }, encoding: 'utf8' });
1063
+ assert.equal(scrub.status, 0, scrub.stderr);
1064
+ const persisted = JSON.parse(await readFile(rootPath, 'utf8'));
1065
+ assert.deepEqual(persisted.active_skills, [{ skill: 'team', phase: 'running', active: true, session_id: 'sess-process-concurrent' }]);
1066
+ }
1067
+ finally {
1068
+ await rm(wd, { recursive: true, force: true });
1069
+ }
1070
+ });
637
1071
  it("normalizes stale terminal deep-interview locks during postLaunch cleanup", async () => {
638
1072
  const wd = await mkdtemp(join(tmpdir(), "omx-postlaunch-di-terminal-locks-"));
639
1073
  const sessionId = "sess-postlaunch-di-terminal-locks";
@@ -1141,6 +1575,10 @@ describe("resolveWorkerSparkModel", () => {
1141
1575
  it("returns undefined for empty args", () => {
1142
1576
  assert.equal(resolveWorkerSparkModel([]), undefined);
1143
1577
  });
1578
+ it("returns undefined for spark flags after --", () => {
1579
+ assert.equal(resolveWorkerSparkModel(["--", "--spark"]), undefined);
1580
+ assert.equal(resolveWorkerSparkModel(["--", "--madmax-spark"]), undefined);
1581
+ });
1144
1582
  it("reads low-complexity team model from config when codexHomeOverride is provided", async () => {
1145
1583
  // Intentional legacy model fixture: verifies an explicit user override is routed to workers unchanged.
1146
1584
  const codexHome = await mkdtemp(join(tmpdir(), "omx-codex-home-"));
@@ -1312,6 +1750,17 @@ describe("resolveCliInvocation", () => {
1312
1750
  assert.match(HELP, /omx update --stable\s+Install\/rollback to npm stable \(oh-my-codex@latest\), then refresh setup/);
1313
1751
  assert.match(HELP, /omx update --dev\s+Install the upstream dev branch, then refresh setup/);
1314
1752
  });
1753
+ it("scopes Ralplan authority preflight in top-level help", () => {
1754
+ assert.match(HELP, /omx ralplan\s+Adapted Ralplan authority support; preflight applies only when native role routing is unavailable\s+and adapted Ralplan authority is requested/);
1755
+ assert.doesNotMatch(HELP, /Record validated role intents/);
1756
+ });
1757
+ it("advertises only the four supported root reasoning modes", () => {
1758
+ assert.match(HELP, /omx reasoning Show or set model reasoning effort \(low\|medium\|high\|xhigh\)/);
1759
+ assert.match(HELP, /--high\s+Launch Codex with high reasoning effort/);
1760
+ assert.match(HELP, /--xhigh\s+Launch Codex with xhigh reasoning effort/);
1761
+ assert.doesNotMatch(HELP, /--max/);
1762
+ assert.doesNotMatch(HELP, /--ultra/);
1763
+ });
1315
1764
  it("advertises concise launch policy controls in top-level help", () => {
1316
1765
  assert.match(HELP, /--direct\s+Launch the interactive leader directly/);
1317
1766
  assert.match(HELP, /OMX_LAUNCH_POLICY=auto[\s\S]*Use the default policy/);
@@ -1830,6 +2279,10 @@ describe("project launch scope helpers", () => {
1830
2279
  'trusted_hash = "sha256:setup-owned"',
1831
2280
  "# End OMX-owned Codex hook trust state",
1832
2281
  "",
2282
+ "# User-owned project trust source must remain external during launch repair.",
2283
+ `[projects."${wd}"] # retained external ownership`,
2284
+ 'trust_level = "trusted"',
2285
+ "",
1833
2286
  "# OMX-synced Codex project trust state (from runtime CODEX_HOME)",
1834
2287
  `[projects."${wd}"]`,
1835
2288
  'trust_level = "trusted"',
@@ -1850,6 +2303,9 @@ describe("project launch scope helpers", () => {
1850
2303
  assert.equal(countMatches(runtimeConfig, new RegExp(`^${escapedProjectHookTrustHeader}$`, "gm")), 1);
1851
2304
  assert.ok(runtimeConfig.includes(`[projects."${wd}"]`));
1852
2305
  assert.ok(repairedProjectConfig.includes(`[projects."${wd}"]`));
2306
+ assert.match(repairedProjectConfig, /User-owned project trust source must remain external/);
2307
+ assert.match(repairedProjectConfig, new RegExp(`^${escapeRegExp(`[projects."${wd}"]`)} # retained external ownership$`, "m"));
2308
+ assert.equal(countMatches(repairedProjectConfig, new RegExp(`^${escapeRegExp(`[projects."${wd}"]`)}(?:\\s|$)`, "gm")), 1, "launch repair must remove only the marker-owned duplicate before the runtime mirror is written");
1853
2309
  }
1854
2310
  finally {
1855
2311
  await rm(wd, { recursive: true, force: true });
@@ -1997,6 +2453,47 @@ describe("project launch scope helpers", () => {
1997
2453
  await rm(wd, { recursive: true, force: true });
1998
2454
  }
1999
2455
  });
2456
+ it("walks upward to find a project-scoped setup for launch resolution", async () => {
2457
+ const wd = await mkdtemp(join(tmpdir(), "omx-launch-scope-"));
2458
+ try {
2459
+ await mkdir(join(wd, ".omx"), { recursive: true });
2460
+ await writeFile(join(wd, ".omx", "setup-scope.json"), JSON.stringify({ scope: "project" }));
2461
+ const nested = join(wd, "packages", "app", "src");
2462
+ await mkdir(nested, { recursive: true });
2463
+ assert.equal(resolveCodexHomeForLaunch(nested, {}), join(wd, ".codex"));
2464
+ assert.equal(resolveCodexConfigPathForLaunch(nested, {}), join(wd, ".codex", "config.toml"));
2465
+ }
2466
+ finally {
2467
+ await rm(wd, { recursive: true, force: true });
2468
+ }
2469
+ });
2470
+ it("does not redirect a launch outside any setup root into project CODEX_HOME", async () => {
2471
+ const wd = await mkdtemp(join(tmpdir(), "omx-launch-scope-"));
2472
+ try {
2473
+ await mkdir(join(wd, ".omx"), { recursive: true });
2474
+ await writeFile(join(wd, ".omx", "setup-scope.json"), JSON.stringify({ scope: "project" }));
2475
+ const outside = join(wd, "..", `outside-${Date.now()}`);
2476
+ await mkdir(outside, { recursive: true });
2477
+ assert.equal(resolveCodexHomeForLaunch(outside, {}), undefined);
2478
+ }
2479
+ finally {
2480
+ await rm(wd, { recursive: true, force: true });
2481
+ }
2482
+ });
2483
+ it("keeps a user-scope setup on the user config when launching from a subdirectory", async () => {
2484
+ const wd = await mkdtemp(join(tmpdir(), "omx-launch-scope-"));
2485
+ try {
2486
+ await mkdir(join(wd, ".omx"), { recursive: true });
2487
+ await writeFile(join(wd, ".omx", "setup-scope.json"), JSON.stringify({ scope: "user" }));
2488
+ const nested = join(wd, "sub");
2489
+ await mkdir(nested, { recursive: true });
2490
+ assert.equal(resolveCodexHomeForLaunch(nested, {}), undefined);
2491
+ assert.equal(resolveProjectLocalCodexHomeForLaunch(nested, {}), undefined);
2492
+ }
2493
+ finally {
2494
+ await rm(wd, { recursive: true, force: true });
2495
+ }
2496
+ });
2000
2497
  });
2001
2498
  describe("pointer launch aborts", () => {
2002
2499
  it("does not launch, tag, or retain a runtime home when a pointer lock is held", async () => {
@@ -2086,7 +2583,7 @@ describe("pointer launch aborts", () => {
2086
2583
  },
2087
2584
  });
2088
2585
  assert.equal(result.status, 1, result.stderr || result.stdout);
2089
- assert.match(result.stderr, /session_pointer_context_failure/);
2586
+ assert.match(result.stderr, /Unable to establish launch directory capability/);
2090
2587
  assert.equal(existsSync(codexLog), false);
2091
2588
  assert.equal(existsSync(tmuxLog), false);
2092
2589
  assert.equal(existsSync(join(wd, ".omx", "state", "sessions")), false);
@@ -2221,20 +2718,23 @@ describe("tmux HUD pane helpers", () => {
2221
2718
  it("buildHudPaneCleanupTargets is a no-op guard when leaderPaneId is absent", () => {
2222
2719
  assert.deepEqual(buildHudPaneCleanupTargets(["%3"], "%4"), ["%3", "%4"]);
2223
2720
  });
2224
- it("listCurrentWindowHudPaneIds scopes tmux pane listing to the emitting pane", () => {
2721
+ it("listCurrentWindowHudPaneIds excludes snapshots without complete current ownership", () => {
2225
2722
  const calls = [];
2226
- const panes = listCurrentWindowHudPaneIds("%leader", (args) => {
2723
+ const panes = listCurrentWindowHudPaneIds("%1", (args) => {
2227
2724
  calls.push(args);
2725
+ if (args.at(-1) === "#{pane_id}")
2726
+ return "%1\n%2\n";
2228
2727
  return [
2229
- "%leader\tcodex\tcodex",
2230
- "%hud\tnode\tnode /tmp/bin/omx.js hud --watch",
2728
+ "%1\x1fcodex\x1f0\x1f0\x1f100\x1f40\x1f39\x1f100\x1f40\x1fcodex\x1f/repo\x1f0\x1f101",
2729
+ "%2\x1fnode\x1f0\x1f40\x1f100\x1f3\x1f42\x1f100\x1f43\x1fexec env OMX_TMUX_HUD_OWNER=1 OMX_TMUX_HUD_LEADER_PANE='%1' node /tmp/bin/dist/cli/omx.js hud --watch\x1f/repo\x1f0\x1f202",
2231
2730
  ].join("\n");
2232
2731
  });
2233
- assert.deepEqual(panes, ["%hud"]);
2234
- assert.deepEqual(calls[0], [
2732
+ assert.deepEqual(panes, []);
2733
+ assert.deepEqual(calls[0], ["list-panes", "-t", "%1", "-F", "#{pane_id}"]);
2734
+ assert.deepEqual(calls[1], [
2235
2735
  "list-panes",
2236
2736
  "-t",
2237
- "%leader",
2737
+ "%1",
2238
2738
  "-F",
2239
2739
  [
2240
2740
  "#{pane_id}",
@@ -2248,31 +2748,67 @@ describe("tmux HUD pane helpers", () => {
2248
2748
  "#{window_height}",
2249
2749
  "#{pane_start_command}",
2250
2750
  "#{pane_current_path}",
2751
+ "#{pane_dead}",
2752
+ "#{pane_pid}",
2251
2753
  ].join("\x1f"),
2252
2754
  ]);
2253
2755
  });
2254
- it("createHudWatchPane splits from the emitting pane target when provided", () => {
2756
+ it("createHudWatchPane rejects an incomplete synthetic source incarnation", () => {
2255
2757
  const calls = [];
2256
- const paneId = createSharedHudWatchPane("/repo", "node /repo/dist/cli/omx.js hud --watch", { heightLines: 3, targetPaneId: "%leader" }, (args) => {
2758
+ const options = new Map();
2759
+ let splitCreated = false;
2760
+ let splitMarker = "";
2761
+ const paneId = createSharedHudWatchPane("/repo", "node /repo/dist/cli/omx.js hud --watch", { heightLines: 3, targetPaneId: "%1" }, (args) => {
2257
2762
  calls.push(args);
2258
- return "%hud\n";
2763
+ if (args[0] === "display-message") {
2764
+ const format = args.at(-1);
2765
+ if (format === "#{pane_id}\t#{pane_dead}\t#{pane_pid}\t#{session_id}\t#{window_id}") {
2766
+ return "%1\t0\t101\t$7\t@1";
2767
+ }
2768
+ if (format === "#{session_id}\t#{window_id}")
2769
+ return "$7\t@1";
2770
+ }
2771
+ if (args[0] === "list-panes") {
2772
+ const format = args.at(-1);
2773
+ const panes = splitCreated ? ["%1", "%2"] : ["%1"];
2774
+ if (format === "#{pane_id}")
2775
+ return `${panes.join("\n")}\n`;
2776
+ if (format === "#{pane_id}\t#{pane_start_command}") {
2777
+ return [
2778
+ "%1\tcodex",
2779
+ `%2\tOMX_TMUX_SPLIT_OPERATION_MARKER='${splitMarker}'; export OMX_TMUX_SPLIT_OPERATION_MARKER; exec env OMX_TMUX_HUD_OWNER=1 OMX_TMUX_HUD_LEADER_PANE='%1' node /repo/dist/cli/omx.js hud --watch`,
2780
+ ].join("\n");
2781
+ }
2782
+ if (format === "#{pane_id}\t#{pane_dead}\t#{pane_pid}") {
2783
+ return "%1\t0\t101\n%2\t0\t202\n";
2784
+ }
2785
+ }
2786
+ if (args[0] === "set-option") {
2787
+ options.set(args[2], args[3]);
2788
+ return "";
2789
+ }
2790
+ if (args[0] === "show-options")
2791
+ return `${options.get(args.at(-1)) ?? ""}\n`;
2792
+ if (args[0] === "if-shell") {
2793
+ assert.equal(args[1], "-F");
2794
+ assert.equal(args[2], "-t");
2795
+ assert.equal(args[3], "%1");
2796
+ assert.match(args[4], /#\{pane_id\},%1/);
2797
+ assert.match(args[4], /#\{pane_pid\},101/);
2798
+ assert.match(args[4], /#\{session_id\},\$1/);
2799
+ assert.match(args[4], /#\{window_id\},@1/);
2800
+ splitCreated = true;
2801
+ const marker = /OMX_TMUX_SPLIT_OPERATION_MARKER='([\w-]+)'/.exec(args[5]);
2802
+ const receipt = /display-message -p (__omx_hud_split_[\w-]+)/.exec(args[5]);
2803
+ assert.ok(marker, "guarded split must bind an operation marker");
2804
+ assert.ok(receipt, "guarded split must emit an exact receipt");
2805
+ splitMarker = marker[1];
2806
+ return `${receipt[1]}\n`;
2807
+ }
2808
+ throw new Error(`unexpected tmux command: ${args.join(" ")}`);
2259
2809
  });
2260
- assert.equal(paneId, "%hud");
2261
- assert.deepEqual(calls[0], [
2262
- "split-window",
2263
- "-v",
2264
- "-l",
2265
- "3",
2266
- "-d",
2267
- "-t",
2268
- "%leader",
2269
- "-c",
2270
- "/repo",
2271
- "-P",
2272
- "-F",
2273
- "#{pane_id}",
2274
- "node /repo/dist/cli/omx.js hud --watch",
2275
- ]);
2810
+ assert.equal(paneId, null);
2811
+ assert.equal(calls.some((args) => args[0] === "if-shell"), false);
2276
2812
  });
2277
2813
  });
2278
2814
  describe("detached tmux new-session sequencing", () => {
@@ -2341,7 +2877,7 @@ describe("detached tmux new-session sequencing", () => {
2341
2877
  assert.equal(newSession.args.includes("-e") &&
2342
2878
  newSession.args.some((arg) => arg === "OMX_ROOT=/tmp/omx-root"), true);
2343
2879
  });
2344
- it("buildDetachedSessionBootstrapSteps forwards boxed env to detached tmux session", () => {
2880
+ it("clears unverified boxed env from detached tmux sessions", () => {
2345
2881
  const steps = buildDetachedSessionBootstrapSteps("omx-demo", "/tmp/boxed-runtime", "'codex' '--model' 'gpt-5'", "'node' '/tmp/omx.js' 'hud' '--watch'", null, undefined, null, false, "sess-detached-managed", undefined, undefined, "/tmp/boxed-runtime", {
2346
2882
  OMXBOX_ACTIVE: "1",
2347
2883
  OMX_SOURCE_CWD: "/tmp/source-project",
@@ -2349,11 +2885,11 @@ describe("detached tmux new-session sequencing", () => {
2349
2885
  });
2350
2886
  const newSession = steps.find((step) => step.name === "new-session");
2351
2887
  assert.ok(newSession);
2352
- assert.equal(newSession.args.some((arg) => arg === "OMX_ROOT=/tmp/boxed-runtime"), true);
2888
+ assert.equal(newSession.args.some((arg) => arg === "OMX_ROOT=/tmp/boxed-runtime"), false);
2353
2889
  assert.equal(newSession.args.some((arg) => arg === "OMX_STATE_ROOT=/tmp/boxed-state-root"), false);
2354
2890
  assert.equal(newSession.args.some((arg) => arg === "OMX_TMUX_HUD_OWNER=1"), true);
2355
- assert.equal(newSession.args.some((arg) => arg === "OMXBOX_ACTIVE=1"), true);
2356
- assert.equal(newSession.args.some((arg) => arg === "OMX_SOURCE_CWD=/tmp/source-project"), true);
2891
+ assert.equal(newSession.args.some((arg) => arg === "OMXBOX_ACTIVE=1"), false);
2892
+ assert.equal(newSession.args.some((arg) => arg === "OMX_SOURCE_CWD=/tmp/source-project"), false);
2357
2893
  });
2358
2894
  it("buildDetachedSessionBootstrapSteps preserves OMX_STATE_ROOT identity when no root override is explicit", () => {
2359
2895
  const steps = buildDetachedSessionBootstrapSteps("omx-demo", "/tmp/project", "'codex' '--model' 'gpt-5'", "'node' '/tmp/omx.js' 'hud' '--watch'", null, undefined, null, false, "sess-detached-managed", undefined, undefined, "/tmp/state-root", { OMX_STATE_ROOT: "/tmp/state-root" });
@@ -2645,13 +3181,15 @@ exit 0
2645
3181
  it("runCodex builds inside-tmux HUD command through explicit runtime-root resolver", async () => {
2646
3182
  const source = await readFile(join(repoRoot, 'src', 'cli', 'index.ts'), 'utf-8');
2647
3183
  assert.match(source, /const hudRuntimeRoot: HudRuntimeRootForLaunch = runtimeContext\s*\? \{ omxRoot: runtimeContext\.omxRoot, rootSource: 'omx-root-env' \}\s*: resolveHudRuntimeRootForLaunch\(cwd, process\.env\);/);
2648
- assert.match(source, /const hudRuntimeEnv = \{\s*\.\.\.buildHudRuntimeEnv\(\{\s*sessionId,\s*leaderPaneId: currentPaneId,\s*\.\.\.hudRuntimeRoot,\s*\}\)\.env,\s*\.\.\.runtimeEnvOverlay,\s*\};\s*const hudEnvArgs = Object\.entries\(hudRuntimeEnv\)\.map\(\(\[key, value\]\) => `\$\{key\}=\$\{value\}`\)/);
2649
- assert.match(source, /if \(env\.OMX_TEAM_STATE_ROOT\?\.trim\(\)\) return 'team-env';\s*if \(env\.OMX_ROOT\?\.trim\(\) \|\| omxRootOverride\) return 'omx-root-env';\s*if \(env\.OMX_STATE_ROOT\?\.trim\(\)\) return 'omx-state-root-env';/);
3184
+ assert.match(source, /const hudRuntimeEnv = launchOwnedControlPlane\s*\? Object\.fromEntries\([\s\S]*?\)\s*:\s*\{\s*\.\.\.buildHudRuntimeEnv\(/);
3185
+ assert.match(source, /const restoreInsideTmuxControlPlane = insideTmuxControlPlane\s*\? applyLaunchOwnedControlPlane\(insideTmuxControlPlane\)/);
3186
+ assert.match(source, /const restoreInsideTmuxControlPlane[\s\S]*?launch = await preLaunch/);
3187
+ assert.match(source, /\.\.\.runtimeEnvOverlay,\s*\.\.\.launchOwnedControlPlaneEnv/);
2650
3188
  assert.match(source, /buildTmuxPaneCommand\("env",\s*\[\.\.\.hudEnvArgs,\s*"node",\s*omxBin,\s*"hud",\s*"--watch"\]\)/);
2651
3189
  });
2652
3190
  it("runCodex registers a HUD resize hook immediately for inside-tmux launches", async () => {
2653
3191
  const source = await readFile(join(repoRoot, 'src', 'cli', 'index.ts'), 'utf-8');
2654
- assert.match(source, /registerInsideTmuxHudResizeHook\(\{\s*hudPaneId,\s*currentPaneId,\s*cwd,\s*sessionId,\s*omxRootOverride,\s*baseEnv: runtimeHookEnv,\s*\}\)/);
3192
+ assert.match(source, /registerInsideTmuxHudResizeHook\(\{\s*hudPaneId,\s*currentPaneId,\s*cwd,\s*sessionId,\s*omxRootOverride: selectedOmxRootOverride,\s*baseEnv: runtimeHookEnv,\s*\}\)/);
2655
3193
  assert.match(source, /if \(currentPaneId\) \{\s*unregisterHudResizeHook\(currentPaneId\);\s*\}/);
2656
3194
  });
2657
3195
  it("buildInsideTmuxHudHookEnv tags hook commands with session, owner, leader, and local root", () => {
@@ -2710,6 +3248,59 @@ exit 0
2710
3248
  assert.equal(env.OMX_ROOT, "/repo");
2711
3249
  assert.equal(env.OMX_ENTRY_PATH, "/repo/dist/cli/omx.js");
2712
3250
  });
3251
+ describe("detached HUD deferred mutation guard", () => {
3252
+ const leader = {
3253
+ paneId: "%11",
3254
+ panePid: 1100,
3255
+ sessionName: "omx-detached",
3256
+ sessionId: "$1",
3257
+ sessionCreated: "1700000000",
3258
+ windowId: "@1",
3259
+ windowIndex: "0",
3260
+ ownerId: "instance-1",
3261
+ };
3262
+ const hud = {
3263
+ paneId: "%77",
3264
+ panePid: 7700,
3265
+ sessionId: "$1",
3266
+ windowId: "@1",
3267
+ operationMarker: "operation-1",
3268
+ };
3269
+ it("throws fail-closed when a deferred HUD payload has format escapes but no resize sink", () => {
3270
+ const sinkFree = ["run-shell", "-b",
3271
+ "tmux list-panes -a -F '#{pane_id}\t#{pane_dead}\t#{pane_pid}' >/dev/null 2>&1 || true"];
3272
+ assert.throws(() => guardDetachedHudDeferredMutation(leader, hud, sinkFree), /detached deferred HUD mutation lacks a recognized resize sink/);
3273
+ });
3274
+ it("guards immediate and delayed resize sinks as bare tmux command-list commands", () => {
3275
+ const args = buildRegisterResizeHookArgs("omx-detached:0", "omx_resize_detached", "%77", 2, 7700, "instance-1");
3276
+ const guarded = guardDetachedHudDeferredMutation(leader, hud, args);
3277
+ const hookCommand = guarded.at(-1);
3278
+ assert.match(hookCommand, /resize-pane -t %77 -y 2 ; display-message/);
3279
+ assert.doesNotMatch(hookCommand, /tmux resize-pane -t %77 -y 2 ; display-message/);
3280
+ assert.match(hookCommand, /tmux if-shell -F/);
3281
+ assert.match(hookCommand, /##\{pane_id\}/);
3282
+ assert.equal(countMatches(hookCommand, /tmux if-shell -F/g), 2);
3283
+ assert.equal(countMatches(hookCommand, /run-shell -b/g), 1);
3284
+ });
3285
+ it("escapes injected authority conditions so they survive the outer run-shell format pass", () => {
3286
+ const args = buildRegisterResizeHookArgs("omx-detached:0", "omx_resize_detached", "%77", 2, 7700, "instance-1");
3287
+ const hookCommand = guardDetachedHudDeferredMutation(leader, hud, args).at(-1);
3288
+ // run-shell applies exactly one outer tmux format pass to the stored payload.
3289
+ // Unescaped, tmux pre-expands these conditions against the hook's own pane and
3290
+ // collapses them to constants, denying the resize before the sink is parsed.
3291
+ for (const condition of [
3292
+ "##{==:##{pane_id},%11}",
3293
+ "##{==:##{pane_pid},1100}",
3294
+ "##{==:##{@omx_instance_id},instance-1}",
3295
+ "##{==:##{pane_id},%77}",
3296
+ "##{m:*OMX_DETACHED_HUD_OPERATION=operation-1*,##{pane_start_command}}",
3297
+ ]) {
3298
+ assert.ok(hookCommand.includes(condition), `stored payload must escape ${condition}`);
3299
+ }
3300
+ assert.doesNotMatch(hookCommand, /(^|[^#])#\{==:#\{pane_id\},%77\}/);
3301
+ assert.doesNotMatch(hookCommand, /(^|[^#])#\{&&:/);
3302
+ });
3303
+ });
2713
3304
  it("registerDetachedHudLayoutReconcileHook reads TMUX from the detached leader pane before registering", () => {
2714
3305
  const calls = [];
2715
3306
  const readTargets = [];
@@ -2759,412 +3350,28 @@ exit 0
2759
3350
  },
2760
3351
  }), false);
2761
3352
  });
2762
- it("buildDetachedSessionBootstrapSteps starts native Windows detached sessions with powershell", () => {
2763
- const hudCmd = buildWindowsPromptCommand("node", [
2764
- "omx.js",
2765
- "hud",
2766
- "--watch",
2767
- ]);
2768
- const steps = buildDetachedSessionBootstrapSteps("omx-demo", "C:/project", "'codex' '--dangerously-bypass-approvals-and-sandbox'", hudCmd, "--model gpt-5", "C:/codex-home", null, true);
2769
- assert.equal(steps[0]?.name, "new-session");
2770
- assert.equal(steps[0]?.args.at(-1), "powershell.exe");
2771
- assert.equal(steps[1]?.name, "split-and-capture-hud-pane");
2772
- assert.equal(steps[1]?.args.at(-1), hudCmd);
2773
- });
2774
- it("buildDetachedWindowsBootstrapScript targets the resolved tmux-compatible command", () => {
2775
- const script = buildDetachedWindowsBootstrapScript("omx-demo", "powershell.exe -NoLogo -NoExit -EncodedCommand abc", 2500, "C:\\Program Files\\psmux\\psmux.exe");
2776
- assert.match(script, /const tmuxCommand = "C:\\\\Program Files\\\\psmux\\\\psmux\.exe";/);
2777
- assert.match(script, /execFileSync\(tmuxCommand, \['send-keys'/);
2778
- assert.doesNotMatch(script, /execFileSync\('tmux'/);
2779
- });
2780
- it("buildDetachedSessionBootstrapSteps kills detached tmux session on normal shell exit", () => {
2781
- const steps = buildDetachedSessionBootstrapSteps("omx-demo", "/tmp/project", "'codex' '--model' 'gpt-5'", "'node' '/tmp/omx.js' 'hud' '--watch'", null);
3353
+ it("buildDetachedSessionBootstrapSteps starts native Windows with the persistent internal leader", () => {
3354
+ const steps = buildDetachedSessionBootstrapSteps("omx-demo", "C:/project", buildWindowsDetachedChildCommand("codex", ["--model", "gpt-5"]), "'node' 'omx.js' 'hud' '--watch'", null, undefined, null, true, "omx-session-123", "C:/project/.codex", "C:/project/.omx/runtime/codex-home/omx-session-123", undefined, process.env, undefined, undefined, undefined, "C:/project/.omx/runtime/detached-release/omx-session-123.release", "C:/project/dist/cli/omx.js");
2782
3355
  const leaderCmd = steps[0]?.args.at(-1);
2783
3356
  assert.equal(typeof leaderCmd, "string");
2784
- assert.match(leaderCmd, /^\/bin\/sh -c '/);
2785
- assert.doesNotMatch(leaderCmd, /^\/bin\/sh -lc '/);
2786
- assert.match(leaderCmd, /acquireTmuxExtendedKeysLease/);
2787
- assert.match(leaderCmd, /omx_detached_session_cleanup\(\)/);
2788
- assert.match(leaderCmd, /trap omx_detached_session_cleanup 0 INT TERM HUP;/);
2789
- assert.match(leaderCmd, /exec 3<&0;/);
2790
- assert.match(leaderCmd, /omx_codex_pid=\$!;/);
2791
- assert.match(leaderCmd, /<\&3 &/);
2792
- assert.match(leaderCmd, /wait "\$omx_codex_pid";/);
2793
- assert.match(leaderCmd, /kill -TERM "\$omx_codex_pid"/);
2794
- assert.match(leaderCmd, /releaseTmuxExtendedKeysLease/);
2795
- assert.match(leaderCmd, /if \[ "\$status" -eq 0 \]; then/);
2796
- assert.match(leaderCmd, /tmux kill-session -t/);
2797
- assert.match(leaderCmd, /"omx-demo"/);
2798
- assert.match(leaderCmd, /codex exited immediately with code 0/);
2799
- assert.match(leaderCmd, /codex exited with code/);
2800
- assert.match(leaderCmd, /detached tmux session is being kept open/);
2801
- assert.match(leaderCmd, /exit \$status/);
2802
- });
2803
- it("buildDetachedSessionBootstrapSteps finalizes postLaunch inside the detached leader when a session id is available", () => {
2804
- const steps = buildDetachedSessionBootstrapSteps("omx-demo", "/tmp/project", "'codex' '--model' 'gpt-5'", "'node' '/tmp/omx.js' 'hud' '--watch'", null, "/tmp/codex-home", null, false, "omx-session-123", "/tmp/project/.codex-project", "/tmp/project/.omx/runtime/codex-home/omx-session-123");
3357
+ assert.match(leaderCmd, /powershell\.exe -NoLogo -NoProfile -NonInteractive -Command/);
3358
+ assert.match(leaderCmd, /__detached-session-leader/);
3359
+ assert.doesNotMatch(leaderCmd, /__detached-post-launch|Test-Path -LiteralPath/);
3360
+ });
3361
+ it("buildDetachedSessionBootstrapSteps gives the POSIX leader persistent lifecycle ownership", () => {
3362
+ const releaseMarkerPath = "/tmp/project/.omx/runtime/detached-release/omx-session-123.release";
3363
+ const steps = buildDetachedSessionBootstrapSteps("omx-demo", "/tmp/project", "'codex' '--model' 'gpt-5'", "'node' '/tmp/omx.js' 'hud' '--watch'", null, "/tmp/codex-home", null, false, "omx-session-123", "/tmp/project/.codex-project", "/tmp/project/.omx/runtime/codex-home/omx-session-123", undefined, process.env, undefined, undefined, undefined, releaseMarkerPath);
2805
3364
  const leaderCmd = steps[0]?.args.at(-1);
2806
3365
  assert.equal(typeof leaderCmd, "string");
2807
- assert.match(leaderCmd, /runDetachedSessionPostLaunch/);
2808
- assert.match(leaderCmd, /omx-session-123/);
2809
- assert.match(leaderCmd, /\/tmp\/codex-home/);
2810
- assert.match(leaderCmd, /\/tmp\/project\/\.codex-project/);
2811
- assert.match(leaderCmd, /\/tmp\/project\/\.omx\/runtime\/codex-home\/omx-session-123/);
2812
- const helperIndex = leaderCmd.indexOf("runDetachedSessionPostLaunch");
2813
- const signalGateIndex = leaderCmd.indexOf('if [ "$status" -eq 0 ]');
2814
- assert.ok(helperIndex >= 0);
2815
- assert.ok(signalGateIndex >= 0);
2816
- assert.ok(helperIndex < signalGateIndex, "detached postLaunch helper must run before the signal-derived tmux kill-session gate");
2817
- });
2818
- it("detached leader command keeps stdin open for the Codex child", async () => {
2819
- const cwd = await mkdtemp(join(tmpdir(), "omx-detached-leader-stdin-"));
2820
- const fakeBin = join(cwd, "bin");
2821
- const stdinLogPath = join(cwd, "stdin.log");
2822
- try {
2823
- await mkdir(fakeBin, { recursive: true });
2824
- await writeFile(join(fakeBin, "codex"), `#!/bin/sh
2825
- if IFS= read -r line; then
2826
- printf 'stdin:%s\n' "$line" > "${stdinLogPath}"
2827
- else
2828
- printf 'stdin:EOF\n' > "${stdinLogPath}"
2829
- fi
2830
- `);
2831
- await chmod(join(fakeBin, "codex"), 0o755);
2832
- await writeFile(join(fakeBin, "tmux"), `#!/bin/sh
2833
- case "$1" in
2834
- display-message)
2835
- if [ "$3" = '#{socket_path}' ] || [ "$4" = '#{socket_path}' ]; then
2836
- printf '/tmp/tmux-test.sock\n'
2837
- else
2838
- printf '0\n'
2839
- fi
2840
- ;;
2841
- show-options)
2842
- printf 'off\n'
2843
- ;;
2844
- set-option|kill-session)
2845
- ;;
2846
- esac
2847
- exit 0
2848
- `);
2849
- await chmod(join(fakeBin, "tmux"), 0o755);
2850
- const steps = buildDetachedSessionBootstrapSteps("omx-demo", cwd, buildTmuxPaneCommand("codex", [], "/bin/sh"), "'node' '/tmp/omx.js' 'hud' '--watch'", null);
2851
- const leaderCmd = steps[0]?.args.at(-1);
2852
- assert.equal(typeof leaderCmd, "string");
2853
- const result = (await import("node:child_process")).spawnSync("/bin/sh", ["-c", leaderCmd], {
2854
- cwd,
2855
- env: {
2856
- ...process.env,
2857
- PATH: `${fakeBin}:/usr/bin:/bin`,
2858
- HOME: cwd,
2859
- },
2860
- input: "hello from leader\n",
2861
- encoding: "utf-8",
2862
- });
2863
- assert.equal(result.status, 0, result.stderr || result.stdout);
2864
- const stdinLog = await readFile(stdinLogPath, "utf-8");
2865
- assert.match(stdinLog, /stdin:hello from leader/);
2866
- }
2867
- finally {
2868
- await rm(cwd, { recursive: true, force: true });
2869
- }
2870
- });
2871
- it("detached leader command preserves cwd and cleanup without shell-quote breakage", async () => {
2872
- const cwd = await mkdtemp(join(tmpdir(), "omx-detached-leader-"));
2873
- const fakeBin = join(cwd, "bin");
2874
- const logPath = join(cwd, "leader.log");
2875
- try {
2876
- await mkdir(fakeBin, { recursive: true });
2877
- await writeFile(join(fakeBin, "codex"), `#!/bin/sh
2878
- printf 'codex:%s\\n' "$*" >> "${logPath}"
2879
- printf 'codex-pwd:%s\\n' "$(pwd)" >> "${logPath}"
2880
- printf 'codex-bridge-env:%s\\n' "\${OMX_HERMES_MCP_BRIDGE-unset}" >> "${logPath}"
2881
- exit 0
2882
- `);
2883
- await chmod(join(fakeBin, "codex"), 0o755);
2884
- await writeFile(join(cwd, ".profile"), "cd ..\n");
2885
- await writeFile(join(fakeBin, "tmux"), `#!/bin/sh
2886
- printf 'tmux:%s\\n' "$*" >> "${logPath}"
2887
- case "$1" in
2888
- display-message)
2889
- if [ "$3" = '#{socket_path}' ] || [ "$4" = '#{socket_path}' ]; then
2890
- printf '/tmp/tmux-test.sock\\n'
2891
- else
2892
- printf '0\\n'
2893
- fi
2894
- ;;
2895
- show-options)
2896
- printf 'off\\n'
2897
- ;;
2898
- set-option|kill-session)
2899
- ;;
2900
- esac
2901
- exit 0
2902
- `);
2903
- await chmod(join(fakeBin, "tmux"), 0o755);
2904
- const steps = buildDetachedSessionBootstrapSteps("omx-demo", cwd, buildTmuxPaneCommand("codex", ["--dangerously-bypass-approvals-and-sandbox"], "/bin/sh"), "'node' '/tmp/omx.js' 'hud' '--watch'", null);
2905
- const leaderCmd = steps[0]?.args.at(-1);
2906
- assert.equal(typeof leaderCmd, "string");
2907
- (await import("node:child_process")).execFileSync("/bin/sh", ["-c", leaderCmd], {
2908
- cwd,
2909
- env: {
2910
- ...process.env,
2911
- PATH: `${fakeBin}:/usr/bin:/bin`,
2912
- HOME: cwd,
2913
- OMX_HERMES_MCP_BRIDGE: "1",
2914
- },
2915
- stdio: "ignore",
2916
- });
2917
- const log = await readFile(logPath, "utf-8");
2918
- assert.match(log, /codex:--dangerously-bypass-approvals-and-sandbox/);
2919
- assert.match(normalizeDarwinTmpPath(log), new RegExp(`codex-pwd:${escapeRegExp(normalizeDarwinTmpPath(cwd))}`));
2920
- assert.match(log, /codex-bridge-env:unset/);
2921
- assert.match(log, /tmux:display-message -p #\{socket_path\}/);
2922
- assert.match(log, /tmux:show-options -sv extended-keys/);
2923
- assert.match(log, /tmux:set-option -sq extended-keys always/);
2924
- assert.match(log, /tmux:set-option -sq extended-keys off/);
2925
- assert.match(log, /tmux:kill-session -t omx-demo/);
2926
- }
2927
- finally {
2928
- await rm(cwd, { recursive: true, force: true });
2929
- }
2930
- });
2931
- it("detached leader command preserves the detached tmux session on signal-derived exits", async () => {
2932
- const cwd = await mkdtemp(join(tmpdir(), "omx-detached-leader-signal-"));
2933
- const fakeBin = join(cwd, "bin");
2934
- const logPath = join(cwd, "leader.log");
2935
- try {
2936
- await mkdir(fakeBin, { recursive: true });
2937
- await writeFile(join(fakeBin, "codex"), `#!/bin/sh
2938
- printf 'codex:%s\\n' "$*" >> "${logPath}"
2939
- exit 143
2940
- `);
2941
- await chmod(join(fakeBin, "codex"), 0o755);
2942
- await writeFile(join(fakeBin, "tmux"), `#!/bin/sh
2943
- printf 'tmux:%s\\n' "$*" >> "${logPath}"
2944
- case "$1" in
2945
- display-message)
2946
- if [ "$3" = '#{socket_path}' ] || [ "$4" = '#{socket_path}' ]; then
2947
- printf '/tmp/tmux-test.sock\\n'
2948
- else
2949
- printf '0\\n'
2950
- fi
2951
- ;;
2952
- show-options)
2953
- printf 'off\\n'
2954
- ;;
2955
- set-option|kill-session)
2956
- ;;
2957
- esac
2958
- exit 0
2959
- `);
2960
- await chmod(join(fakeBin, "tmux"), 0o755);
2961
- const steps = buildDetachedSessionBootstrapSteps("omx-demo", cwd, buildTmuxPaneCommand("codex", ["--dangerously-bypass-approvals-and-sandbox"], "/bin/sh"), "'node' '/tmp/omx.js' 'hud' '--watch'", null);
2962
- const leaderCmd = steps[0]?.args.at(-1);
2963
- assert.equal(typeof leaderCmd, "string");
2964
- const result = (await import("node:child_process")).spawnSync("/bin/sh", ["-c", leaderCmd], {
2965
- cwd,
2966
- env: {
2967
- ...process.env,
2968
- PATH: `${fakeBin}:/usr/bin:/bin`,
2969
- HOME: cwd,
2970
- },
2971
- encoding: "utf-8",
2972
- });
2973
- assert.equal(result.status, 143);
2974
- const log = await readFile(logPath, "utf-8");
2975
- assert.match(log, /codex:--dangerously-bypass-approvals-and-sandbox/);
2976
- assert.match(log, /tmux:display-message -p #\{socket_path\}/);
2977
- assert.match(log, /tmux:show-options -sv extended-keys/);
2978
- assert.match(log, /tmux:set-option -sq extended-keys always/);
2979
- assert.match(log, /tmux:set-option -sq extended-keys off/);
2980
- assert.doesNotMatch(log, /tmux:kill-session -t omx-demo/);
2981
- }
2982
- finally {
2983
- await rm(cwd, { recursive: true, force: true });
2984
- }
2985
- });
2986
- it("detached leader command keeps child startup errors visible instead of killing the session", async () => {
2987
- const cwd = await mkdtemp(join(tmpdir(), "omx-detached-leader-error-"));
2988
- const fakeBin = join(cwd, "bin");
2989
- const logPath = join(cwd, "leader.log");
2990
- try {
2991
- await mkdir(fakeBin, { recursive: true });
2992
- await writeFile(join(fakeBin, "codex"), `#!/bin/sh
2993
- printf 'codex-stderr: unsupported startup flag\\n' >&2
2994
- exit 42
2995
- `);
2996
- await chmod(join(fakeBin, "codex"), 0o755);
2997
- await writeFile(join(fakeBin, "tmux"), `#!/bin/sh
2998
- printf 'tmux:%s\\n' "$*" >> "${logPath}"
2999
- case "$1" in
3000
- display-message)
3001
- if [ "$3" = '#{socket_path}' ] || [ "$4" = '#{socket_path}' ]; then
3002
- printf '/tmp/tmux-test.sock\\n'
3003
- else
3004
- printf '0\\n'
3005
- fi
3006
- ;;
3007
- show-options)
3008
- printf 'off\\n'
3009
- ;;
3010
- set-option|kill-session)
3011
- ;;
3012
- esac
3013
- exit 0
3014
- `);
3015
- await chmod(join(fakeBin, "tmux"), 0o755);
3016
- const steps = buildDetachedSessionBootstrapSteps("omx-demo", cwd, buildTmuxPaneCommand("codex", ["--bad-startup-flag"], "/bin/sh"), "'node' '/tmp/omx.js' 'hud' '--watch'", null);
3017
- const leaderCmd = steps[0]?.args.at(-1);
3018
- assert.equal(typeof leaderCmd, "string");
3019
- const result = (await import("node:child_process")).spawnSync("/bin/sh", ["-c", leaderCmd], {
3020
- cwd,
3021
- env: {
3022
- ...process.env,
3023
- PATH: `${fakeBin}:/usr/bin:/bin`,
3024
- HOME: cwd,
3025
- },
3026
- input: "\n",
3027
- encoding: "utf-8",
3028
- });
3029
- assert.equal(result.status, 42);
3030
- assert.match(result.stderr, /codex-stderr: unsupported startup flag/);
3031
- assert.match(result.stderr, /codex exited with code 42 during startup/);
3032
- assert.match(result.stderr, /detached tmux session is being kept open/);
3033
- const log = await readFile(logPath, "utf-8");
3034
- assert.doesNotMatch(log, /tmux:kill-session -t omx-demo/);
3035
- }
3036
- finally {
3037
- await rm(cwd, { recursive: true, force: true });
3038
- }
3039
- });
3040
- it("detached leader command keeps immediate zero-code exits visible instead of silently closing", async () => {
3041
- const cwd = await mkdtemp(join(tmpdir(), "omx-detached-leader-zero-"));
3042
- const fakeBin = join(cwd, "bin");
3043
- const logPath = join(cwd, "leader.log");
3044
- try {
3045
- await mkdir(fakeBin, { recursive: true });
3046
- await writeFile(join(fakeBin, "codex"), `#!/bin/sh
3047
- printf 'codex-started-then-quit\\n' >&2
3048
- exit 0
3049
- `);
3050
- await chmod(join(fakeBin, "codex"), 0o755);
3051
- await writeFile(join(fakeBin, "tmux"), `#!/bin/sh
3052
- printf 'tmux:%s\\n' "$*" >> "${logPath}"
3053
- case "$1" in
3054
- display-message)
3055
- if [ "$3" = '#{socket_path}' ] || [ "$4" = '#{socket_path}' ]; then
3056
- printf '/tmp/tmux-test.sock\\n'
3057
- else
3058
- printf '0\\n'
3059
- fi
3060
- ;;
3061
- show-options)
3062
- printf 'off\\n'
3063
- ;;
3064
- set-option|kill-session)
3065
- ;;
3066
- esac
3067
- exit 0
3068
- `);
3069
- await chmod(join(fakeBin, "tmux"), 0o755);
3070
- const steps = buildDetachedSessionBootstrapSteps("omx-demo", cwd, buildTmuxPaneCommand("codex", [], "/bin/sh"), "'node' '/tmp/omx.js' 'hud' '--watch'", null);
3071
- const leaderCmd = steps[0]?.args.at(-1);
3072
- assert.equal(typeof leaderCmd, "string");
3073
- const result = (await import("node:child_process")).spawnSync("/bin/sh", ["-c", leaderCmd], {
3074
- cwd,
3075
- env: {
3076
- ...process.env,
3077
- PATH: `${fakeBin}:/usr/bin:/bin`,
3078
- HOME: cwd,
3079
- },
3080
- input: "\n",
3081
- encoding: "utf-8",
3082
- });
3083
- assert.equal(result.status, 0, result.stderr || result.stdout);
3084
- assert.match(result.stderr, /codex-started-then-quit/);
3085
- assert.match(result.stderr, /codex exited immediately with code 0 during startup/);
3086
- assert.match(result.stderr, /detached tmux session is being kept open/);
3087
- const log = await readFile(logPath, "utf-8");
3088
- assert.match(log, /tmux:kill-session -t omx-demo/);
3089
- }
3090
- finally {
3091
- await rm(cwd, { recursive: true, force: true });
3092
- }
3366
+ assert.match(leaderCmd, /^\/bin\/sh -c '/);
3367
+ assert.match(leaderCmd, /__detached-session-leader/);
3368
+ assert.doesNotMatch(leaderCmd, /__detached-post-launch|omx_codex_pid|while \[ ! -f/);
3093
3369
  });
3094
- it("detached leader command terminates codex child on external SIGHUP", async () => {
3095
- const cwd = await mkdtemp(join(tmpdir(), "omx-detached-leader-hup-"));
3096
- const fakeBin = join(cwd, "bin");
3097
- const pidFile = join(cwd, "codex.pid");
3098
- try {
3099
- await mkdir(fakeBin, { recursive: true });
3100
- await writeFile(join(fakeBin, "codex"), `#!/bin/sh
3101
- echo $$ > "${pidFile}"
3102
- trap '' HUP
3103
- while true; do sleep 1; done
3104
- `);
3105
- await chmod(join(fakeBin, "codex"), 0o755);
3106
- await writeFile(join(fakeBin, "tmux"), `#!/bin/sh
3107
- case "$1" in
3108
- display-message)
3109
- if [ "$3" = '#{socket_path}' ] || [ "$4" = '#{socket_path}' ]; then
3110
- printf '/tmp/tmux-test.sock\\n'
3111
- else
3112
- printf '0\\n'
3113
- fi
3114
- ;;
3115
- show-options) printf 'off\\n' ;;
3116
- set-option|kill-session) ;;
3117
- esac
3118
- exit 0
3119
- `);
3120
- await chmod(join(fakeBin, "tmux"), 0o755);
3121
- const steps = buildDetachedSessionBootstrapSteps("omx-demo", cwd, buildTmuxPaneCommand("codex", [], "/bin/sh"), "'node' '/tmp/omx.js' 'hud' '--watch'", null);
3122
- const leaderCmd = steps[0]?.args.at(-1);
3123
- assert.equal(typeof leaderCmd, "string");
3124
- const { spawn } = await import("node:child_process");
3125
- const child = spawn("/bin/sh", ["-c", `exec ${leaderCmd}`], {
3126
- cwd,
3127
- env: {
3128
- ...process.env,
3129
- PATH: `${fakeBin}:/usr/bin:/bin`,
3130
- HOME: cwd,
3131
- },
3132
- stdio: "ignore",
3133
- detached: true,
3134
- });
3135
- try {
3136
- for (let i = 0; i < 50; i += 1) {
3137
- if (existsSync(pidFile))
3138
- break;
3139
- await new Promise((resolve) => setTimeout(resolve, 100));
3140
- }
3141
- assert.ok(existsSync(pidFile), "codex pid file not written");
3142
- const codexPid = Number.parseInt((await readFile(pidFile, "utf-8")).trim(), 10);
3143
- assert.ok(codexPid > 0, "codex pid must be positive");
3144
- assert.doesNotThrow(() => process.kill(codexPid, 0), "codex must be alive before signal");
3145
- const leaderExit = once(child, "exit");
3146
- process.kill(child.pid, "SIGHUP");
3147
- await Promise.race([
3148
- leaderExit,
3149
- new Promise((_, reject) => setTimeout(() => reject(new Error("leader did not exit after SIGHUP")), 3000)),
3150
- ]);
3151
- assert.throws(() => process.kill(codexPid, 0), (err) => typeof err === "object" &&
3152
- err !== null &&
3153
- "code" in err &&
3154
- err.code === "ESRCH", "codex child must be terminated after leader SIGHUP");
3155
- }
3156
- finally {
3157
- try {
3158
- process.kill(child.pid, "SIGKILL");
3159
- }
3160
- catch {
3161
- /* already dead */
3162
- }
3163
- }
3164
- }
3165
- finally {
3166
- await rm(cwd, { recursive: true, force: true });
3167
- }
3370
+ it("keeps capability data out of the detached leader command payload", () => {
3371
+ const steps = buildDetachedSessionBootstrapSteps("omx-demo", "/tmp/project", "codex", "hud", null, undefined, null, false, "omx-session-123", undefined, undefined, undefined, process.env);
3372
+ const leaderCmd = steps[0]?.args.at(-1) ?? "";
3373
+ assert.match(leaderCmd, /__detached-session-leader/);
3374
+ assert.doesNotMatch(leaderCmd, /launch_lineage_token|directoryIdentity|FileHandle/);
3168
3375
  });
3169
3376
  it("withTmuxExtendedKeys enables tmux extended keys during codex launch and restores them afterwards", async () => {
3170
3377
  const cwd = await mkdtemp(join(tmpdir(), "omx-tmux-lease-wrapper-"));
@@ -3719,6 +3926,45 @@ describe("buildWindowsPromptCommand", () => {
3719
3926
  assert.equal(decoded, "$ErrorActionPreference = 'Stop'; & { & 'codex' '--dangerously-bypass-approvals-and-sandbox' '-c' 'model_reasoning_effort=\"high\"' 'it''s' }");
3720
3927
  });
3721
3928
  });
3929
+ describe("buildWindowsDetachedChildCommand", () => {
3930
+ it("exits with the Codex child status instead of retaining an interactive shell", () => {
3931
+ const result = buildWindowsDetachedChildCommand("codex", ["--model", "gpt-5"]);
3932
+ const prefix = "powershell.exe -NoLogo -NoProfile -NonInteractive -EncodedCommand ";
3933
+ assert.ok(result.startsWith(prefix));
3934
+ assert.doesNotMatch(result, /-NoExit/);
3935
+ assert.equal(Buffer.from(result.slice(prefix.length), "base64").toString("utf16le"), "$ErrorActionPreference = 'Stop'; & 'codex' '--model' 'gpt-5'; exit $LASTEXITCODE");
3936
+ });
3937
+ });
3938
+ describe("Windows detached leader environment", () => {
3939
+ it("serializes inherited parent values into the detached Windows leader command", () => {
3940
+ const steps = buildDetachedSessionBootstrapSteps("omx-test", "C:/project", "codex", "hud", null, undefined, null, true, "session-1", undefined, undefined, undefined, {
3941
+ PATH: "C:/runtime-shim;C:/parent/bin",
3942
+ CUSTOM_VALUE: "retained",
3943
+ CODEX_HOME: "C:/project/.codex-runtime",
3944
+ CODEX_SQLITE_HOME: "C:/project/.codex-sqlite",
3945
+ OMX_ROOT: "C:/project/.omx-run",
3946
+ OMX_CODEX_LAUNCH_ID: "launch-id",
3947
+ OMX_NOTIFY_TEMP_CONTRACT: "{\"active\":true}",
3948
+ OMX_TEAM_WORKER_LAUNCH_ARGS: "[\"--model\",\"gpt-5\"]",
3949
+ TMUX: "foreign",
3950
+ TMUX_PANE: "%9",
3951
+ TERM: "xterm-256color",
3952
+ });
3953
+ const command = steps[0]?.args.at(-1) ?? "";
3954
+ const encodedPayload = command.match(/__detached-session-leader ''([^']+)''/)?.[1];
3955
+ assert.ok(encodedPayload);
3956
+ const payload = JSON.parse(Buffer.from(encodedPayload, "base64url").toString("utf8"));
3957
+ assert.deepEqual(payload.parentEnv, {
3958
+ CODEX_HOME: "C:/project/.codex-runtime",
3959
+ CODEX_SQLITE_HOME: "C:/project/.codex-sqlite",
3960
+ CUSTOM_VALUE: "retained",
3961
+ OMX_CODEX_LAUNCH_ID: "launch-id",
3962
+ OMX_NOTIFY_TEMP_CONTRACT: "{\"active\":true}",
3963
+ OMX_TEAM_WORKER_LAUNCH_ARGS: "[\"--model\",\"gpt-5\"]",
3964
+ PATH: "C:/runtime-shim;C:/parent/bin",
3965
+ });
3966
+ });
3967
+ });
3722
3968
  describe("buildTmuxSessionName", () => {
3723
3969
  it("uses detached fallback quietly outside git repos", () => {
3724
3970
  const name = buildTmuxSessionName("/tmp/My Repo", "omx-1770992424158-abc123");
@@ -3940,6 +4186,26 @@ describe("injectModelInstructionsBypassArgs", () => {
3940
4186
  'model_instructions_file="/tmp/my-project/AGENTS.md"',
3941
4187
  ]);
3942
4188
  });
4189
+ it("inserts model instructions before the end-of-options marker", () => {
4190
+ const args = injectModelInstructionsBypassArgs("/tmp/my-project", ["--", "--spark", "literal"], {});
4191
+ assert.deepEqual(args, [
4192
+ "-c",
4193
+ 'model_instructions_file="/tmp/my-project/AGENTS.md"',
4194
+ "--",
4195
+ "--spark",
4196
+ "literal",
4197
+ ]);
4198
+ });
4199
+ it("does not treat a post-marker model instructions token as an OMX override", () => {
4200
+ const args = injectModelInstructionsBypassArgs("/tmp/my-project", ["--", "-c", 'model_instructions_file="/tmp/literal.md"'], {});
4201
+ assert.deepEqual(args, [
4202
+ "-c",
4203
+ 'model_instructions_file="/tmp/my-project/AGENTS.md"',
4204
+ "--",
4205
+ "-c",
4206
+ 'model_instructions_file="/tmp/literal.md"',
4207
+ ]);
4208
+ });
3943
4209
  it("does not append when bypass is disabled via env", () => {
3944
4210
  const args = injectModelInstructionsBypassArgs("/tmp/my-project", ["--model", "gpt-5"], { OMX_BYPASS_DEFAULT_SYSTEM_PROMPT: "0" });
3945
4211
  assert.deepEqual(args, ["--model", "gpt-5"]);
@@ -4002,4 +4268,153 @@ describe("isExistingTmuxWindowTooCrampedForLaunchHud (#2754)", () => {
4002
4268
  assert.equal(isExistingTmuxWindowTooCrampedForLaunchHud(39, 40), true);
4003
4269
  });
4004
4270
  });
4271
+ describe("detached control-plane binding", () => {
4272
+ it("bounds inside-tmux lifecycle propagation to the finite launch-owned control plane", async () => {
4273
+ const source = await readFile(join(repoRoot, "src", "cli", "index.ts"), "utf8");
4274
+ assert.match(source, /if \(launchPolicy === "inside-tmux"\) \{[\s\S]*?buildDetachedLaunchControlPlane\(/);
4275
+ assert.match(source, /const restoreInsideTmuxControlPlane = insideTmuxControlPlane\s*\? applyLaunchOwnedControlPlane\(insideTmuxControlPlane\)/);
4276
+ assert.match(source, /const codexBaseEnv = prependOmxRuntimeCommandShimToEnv\([\s\S]*?\.\.\.launchOwnedControlPlaneEnv/);
4277
+ assert.match(source, /const runtimeHookEnv = launchOwnedControlPlane\s*\?[\s\S]*?\.\.\.launchOwnedControlPlaneEnv/);
4278
+ });
4279
+ it("emits one stable assignment for every managed key and clears poisoned identity", () => {
4280
+ const env = {
4281
+ OMX_ROOT: "",
4282
+ OMX_STATE_ROOT: "",
4283
+ OMX_TEAM_STATE_ROOT: "",
4284
+ OMX_RUNS_DIR: "/foreign/runs",
4285
+ OMX_TEAM_LEADER_CWD: "/foreign",
4286
+ OMX_TEAM_WORKER: "foreign/worker-1",
4287
+ OMX_TEAM_INTERNAL_WORKER: "foreign/worker-1",
4288
+ };
4289
+ const controlPlane = buildDetachedLaunchControlPlane({ cwd: "/tmp/project", sessionId: "sess-binding", env });
4290
+ assert.deepEqual([...controlPlane.keys], [...DETACHED_LAUNCH_CONTROL_PLANE_KEYS]);
4291
+ assert.deepEqual(Object.keys(controlPlane.values), [...DETACHED_LAUNCH_CONTROL_PLANE_KEYS]);
4292
+ assert.equal(controlPlane.values.OMX_SESSION_ID, "sess-binding");
4293
+ assert.equal(controlPlane.values.OMX_TMUX_HUD_OWNER, "1");
4294
+ for (const key of [
4295
+ "CODEX_SESSION_ID", "SESSION_ID", "OMX_TMUX_HUD_LEADER_PANE", "OMX_ROOT", "OMX_STATE_ROOT",
4296
+ "OMX_TEAM_STATE_ROOT", "OMXBOX_ACTIVE", "OMX_SOURCE_CWD", "OMX_MADMAX_DETACHED_CONTEXT", "OMX_RUNS_DIR",
4297
+ "OMX_TEAM_LEADER_CWD", "OMX_TEAM_WORKER", "OMX_TEAM_INTERNAL_WORKER",
4298
+ ])
4299
+ assert.equal(controlPlane.values[key], "", `${key} must be cleared`);
4300
+ const steps = buildDetachedSessionBootstrapSteps("omx-binding", "/tmp/project", "codex", "hud", null, undefined, null, false, "sess-binding", undefined, undefined, undefined, env, undefined, undefined, undefined, undefined, undefined, undefined, controlPlane);
4301
+ const newSession = steps.find((step) => step.name === "new-session");
4302
+ assert.ok(newSession);
4303
+ const managedAssignments = newSession.args.filter((arg) => DETACHED_LAUNCH_CONTROL_PLANE_KEYS.some((key) => arg.startsWith(`${key}=`)));
4304
+ assert.equal(managedAssignments.length, DETACHED_LAUNCH_CONTROL_PLANE_KEYS.length);
4305
+ assert.deepEqual(managedAssignments.map((assignment) => assignment.slice(0, assignment.indexOf("="))), [...DETACHED_LAUNCH_CONTROL_PLANE_KEYS]);
4306
+ });
4307
+ it("preserves selected explicit roots and valid Madmax runs identity", () => {
4308
+ const explicit = buildDetachedLaunchControlPlane({
4309
+ cwd: "/tmp/project", sessionId: "sess-explicit",
4310
+ env: { OMX_ROOT: "/tmp/selected-root", OMX_STATE_ROOT: "/tmp/stale-state" },
4311
+ });
4312
+ assert.equal(explicit.values.OMX_ROOT, "/tmp/selected-root");
4313
+ assert.equal(explicit.values.OMX_STATE_ROOT, "");
4314
+ assert.equal(explicit.values.OMX_RUNS_DIR, "");
4315
+ const madmax = buildDetachedLaunchControlPlane({
4316
+ cwd: "/tmp/project", sessionId: "sess-madmax",
4317
+ omxRootOverride: "/tmp/run-root",
4318
+ verifiedMadmaxContext: {
4319
+ root: "/tmp/run-root",
4320
+ sourceCwd: "/tmp/project",
4321
+ context: "ctx-madmax",
4322
+ runsRoot: "/tmp/outer-runs",
4323
+ },
4324
+ env: {
4325
+ OMX_ROOT: "/tmp/run-root", OMXBOX_ACTIVE: "1", OMX_SOURCE_CWD: "/tmp/project",
4326
+ OMX_MADMAX_DETACHED_CONTEXT: "ctx-madmax", OMX_RUNS_DIR: "/tmp/outer-runs",
4327
+ },
4328
+ });
4329
+ assert.deepEqual([madmax.values.OMX_ROOT, madmax.values.OMXBOX_ACTIVE, madmax.values.OMX_SOURCE_CWD,
4330
+ madmax.values.OMX_MADMAX_DETACHED_CONTEXT, madmax.values.OMX_RUNS_DIR], ["/tmp/run-root", "1", "/tmp/project", "ctx-madmax", "/tmp/outer-runs"]);
4331
+ const poisoned = buildDetachedLaunchControlPlane({
4332
+ cwd: "/tmp/project", sessionId: "sess-poisoned", omxRootOverride: "/foreign/root",
4333
+ env: {
4334
+ OMX_ROOT: "/foreign/root", OMXBOX_ACTIVE: "1", OMX_SOURCE_CWD: "/foreign/source",
4335
+ OMX_MADMAX_DETACHED_CONTEXT: "poisoned-context", OMX_RUNS_DIR: "/foreign/runs",
4336
+ },
4337
+ });
4338
+ assert.deepEqual([poisoned.values.OMX_ROOT, poisoned.values.OMX_SOURCE_CWD,
4339
+ poisoned.values.OMX_MADMAX_DETACHED_CONTEXT, poisoned.values.OMX_RUNS_DIR], ["", "", "", ""]);
4340
+ });
4341
+ it("preserves an explicit team root while clearing unverified boxed lineage and worker tuple", () => {
4342
+ const cases = [
4343
+ {
4344
+ name: "boxed guard without worker tuple",
4345
+ env: {
4346
+ OMX_TEAM_STATE_ROOT: "/tmp/explicit-team-root",
4347
+ OMX_ROOT: "/tmp/poison-root",
4348
+ OMX_STATE_ROOT: "/tmp/poison-state-root",
4349
+ OMXBOX_ACTIVE: "1",
4350
+ OMX_SOURCE_CWD: "/tmp/poison-source",
4351
+ OMX_MADMAX_DETACHED_CONTEXT: "poison-context",
4352
+ OMX_RUNS_DIR: "/tmp/poison-runs",
4353
+ },
4354
+ },
4355
+ {
4356
+ name: "boxed guard with poisoned worker tuple",
4357
+ env: {
4358
+ OMX_TEAM_STATE_ROOT: "/tmp/explicit-team-root",
4359
+ OMX_ROOT: "/tmp/poison-root",
4360
+ OMX_STATE_ROOT: "/tmp/poison-state-root",
4361
+ OMXBOX_ACTIVE: "1",
4362
+ OMX_SOURCE_CWD: "/tmp/poison-source",
4363
+ OMX_MADMAX_DETACHED_CONTEXT: "poison-context",
4364
+ OMX_RUNS_DIR: "/tmp/poison-runs",
4365
+ OMX_TEAM_LEADER_CWD: "/tmp/poison-leader",
4366
+ OMX_TEAM_WORKER: "poison-team/worker-1",
4367
+ OMX_TEAM_INTERNAL_WORKER: "poison-team/worker-2",
4368
+ },
4369
+ },
4370
+ ];
4371
+ for (const testCase of cases) {
4372
+ const controlPlane = buildDetachedLaunchControlPlane({
4373
+ cwd: "/tmp/project",
4374
+ sessionId: `sess-${testCase.name.replaceAll(" ", "-")}`,
4375
+ env: testCase.env,
4376
+ });
4377
+ assert.equal(controlPlane.values.OMX_TEAM_STATE_ROOT, "/tmp/explicit-team-root", testCase.name);
4378
+ for (const key of [
4379
+ "OMX_ROOT", "OMX_STATE_ROOT", "OMXBOX_ACTIVE", "OMX_SOURCE_CWD",
4380
+ "OMX_MADMAX_DETACHED_CONTEXT", "OMX_RUNS_DIR", "OMX_TEAM_LEADER_CWD",
4381
+ "OMX_TEAM_WORKER", "OMX_TEAM_INTERNAL_WORKER",
4382
+ ]) {
4383
+ assert.equal(controlPlane.values[key], "", `${testCase.name}: ${key} must be cleared`);
4384
+ }
4385
+ }
4386
+ });
4387
+ it("keeps unrelated parent configuration while filtering exact managed replay names", () => {
4388
+ const serialized = serializeDetachedSessionParentEnv({
4389
+ OMX_ROOT: "/foreign/root", omx_root: "/foreign/mixed-case-root",
4390
+ OMX_TEAM_WORKER: "foreign/worker", Omx_Notify_Fallback: "1",
4391
+ PROVIDER_SENTINEL: "provider-value", Path: "/system/path",
4392
+ });
4393
+ assert.doesNotMatch(serialized, /export OMX_ROOT=/);
4394
+ assert.doesNotMatch(serialized, /export OMX_TEAM_WORKER=/);
4395
+ assert.match(serialized, /export omx_root='/);
4396
+ assert.match(serialized, /export PROVIDER_SENTINEL='provider-value'/);
4397
+ assert.match(serialized, /export Path='\/system\/path'/);
4398
+ assert.match(serialized, /export Omx_Notify_Fallback='1'/);
4399
+ });
4400
+ it("filters every managed Windows case variant while preserving unrelated configuration", () => {
4401
+ const env = {
4402
+ oMx_RoOt: "/foreign/root",
4403
+ OmX_TeAm_WorkEr: "foreign/worker",
4404
+ OMX_PROVIDER_URL: "https://provider.invalid",
4405
+ Path: "/system/path",
4406
+ Omx_Notify_Fallback: "1",
4407
+ };
4408
+ const steps = buildDetachedSessionBootstrapSteps("omx-windows-binding", "/tmp/project", "codex", "hud", null, undefined, null, true, "sess-windows", undefined, undefined, undefined, env);
4409
+ const command = steps[0]?.args.at(-1) ?? "";
4410
+ const encoded = /__detached-session-leader [^A-Za-z0-9_-]*([A-Za-z0-9_-]{40,})/.exec(command)?.[1];
4411
+ assert.ok(encoded);
4412
+ const payload = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
4413
+ assert.equal(payload.parentEnv?.oMx_RoOt, undefined);
4414
+ assert.equal(payload.parentEnv?.OmX_TeAm_WorkEr, undefined);
4415
+ assert.equal(payload.parentEnv?.OMX_PROVIDER_URL, "https://provider.invalid");
4416
+ assert.equal(payload.parentEnv?.Path, "/system/path");
4417
+ assert.equal(payload.parentEnv?.Omx_Notify_Fallback, "1");
4418
+ });
4419
+ });
4005
4420
  //# sourceMappingURL=index.test.js.map