oh-my-codex 0.20.1 → 0.20.3

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 (791) hide show
  1. package/Cargo.lock +7 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +36 -1
  4. package/crates/omx-runtime/Cargo.toml +1 -0
  5. package/crates/omx-runtime/src/main.rs +25 -2
  6. package/crates/omx-runtime/tests/execution.rs +219 -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__/hermes.test.js.map +1 -1
  11. package/dist/adapt/contracts.d.ts.map +1 -1
  12. package/dist/agents/__tests__/definitions.test.js +81 -0
  13. package/dist/agents/__tests__/definitions.test.js.map +1 -1
  14. package/dist/agents/__tests__/native-config.test.js +25 -0
  15. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  16. package/dist/agents/native-config.d.ts +2 -1
  17. package/dist/agents/native-config.d.ts.map +1 -1
  18. package/dist/agents/native-config.js.map +1 -1
  19. package/dist/auth/__tests__/hotswap.test.d.ts +2 -0
  20. package/dist/auth/__tests__/hotswap.test.d.ts.map +1 -0
  21. package/dist/auth/__tests__/hotswap.test.js +183 -0
  22. package/dist/auth/__tests__/hotswap.test.js.map +1 -0
  23. package/dist/auth/hotswap.d.ts +6 -0
  24. package/dist/auth/hotswap.d.ts.map +1 -1
  25. package/dist/auth/hotswap.js +102 -19
  26. package/dist/auth/hotswap.js.map +1 -1
  27. package/dist/auth/paths.js.map +1 -1
  28. package/dist/auth/storage.js.map +1 -1
  29. package/dist/autopilot/__tests__/ralplan-gate.test.js +5 -5
  30. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
  31. package/dist/autopilot/completion-gate.js.map +1 -1
  32. package/dist/autopilot/fsm.d.ts +1 -1
  33. package/dist/autopilot/fsm.d.ts.map +1 -1
  34. package/dist/autoresearch/goal.js.map +1 -1
  35. package/dist/autoresearch/runtime.js.map +1 -1
  36. package/dist/capabilities/lockfile.js.map +1 -1
  37. package/dist/catalog/reader.js.map +1 -1
  38. package/dist/catalog/skill-mirror.js.map +1 -1
  39. package/dist/cli/__tests__/adapt-help.test.js.map +1 -1
  40. package/dist/cli/__tests__/agents-init.test.js.map +1 -1
  41. package/dist/cli/__tests__/agents.test.js.map +1 -1
  42. package/dist/cli/__tests__/api.test.js.map +1 -1
  43. package/dist/cli/__tests__/ask.test.js.map +1 -1
  44. package/dist/cli/__tests__/auth.test.js +60 -6
  45. package/dist/cli/__tests__/auth.test.js.map +1 -1
  46. package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
  47. package/dist/cli/__tests__/autoresearch.test.js.map +1 -1
  48. package/dist/cli/__tests__/capabilities.test.js.map +1 -1
  49. package/dist/cli/__tests__/codex-plugin-layout.test.js +80 -0
  50. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  51. package/dist/cli/__tests__/doctor-context-window-warning.test.js.map +1 -1
  52. package/dist/cli/__tests__/doctor-invalid-config.test.js +47 -4
  53. package/dist/cli/__tests__/doctor-invalid-config.test.js.map +1 -1
  54. package/dist/cli/__tests__/doctor-team.test.js.map +1 -1
  55. package/dist/cli/__tests__/doctor-warning-copy.test.js +752 -15
  56. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  57. package/dist/cli/__tests__/exec.test.js +136 -0
  58. package/dist/cli/__tests__/exec.test.js.map +1 -1
  59. package/dist/cli/__tests__/imagegen-continuation.test.js.map +1 -1
  60. package/dist/cli/__tests__/index.test.js +473 -14
  61. package/dist/cli/__tests__/index.test.js.map +1 -1
  62. package/dist/cli/__tests__/launch-fallback.test.js +336 -4
  63. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  64. package/dist/cli/__tests__/mcp-parity.test.js +87 -12
  65. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  66. package/dist/cli/__tests__/native-hook-claim-journal.test.d.ts +2 -0
  67. package/dist/cli/__tests__/native-hook-claim-journal.test.d.ts.map +1 -0
  68. package/dist/cli/__tests__/native-hook-claim-journal.test.js +206 -0
  69. package/dist/cli/__tests__/native-hook-claim-journal.test.js.map +1 -0
  70. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  71. package/dist/cli/__tests__/question.test.js.map +1 -1
  72. package/dist/cli/__tests__/ralph-deslop-contract.test.js.map +1 -1
  73. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +6 -0
  74. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  75. package/dist/cli/__tests__/ralph-prd-deep-interview.test.js.map +1 -1
  76. package/dist/cli/__tests__/ralph-prd-smoke.test.js.map +1 -1
  77. package/dist/cli/__tests__/ralph.test.js +8 -3
  78. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  79. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.d.ts +2 -0
  80. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.d.ts.map +1 -0
  81. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js +72 -0
  82. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js.map +1 -0
  83. package/dist/cli/__tests__/ralplan.test.d.ts +2 -0
  84. package/dist/cli/__tests__/ralplan.test.d.ts.map +1 -0
  85. package/dist/cli/__tests__/ralplan.test.js +55 -0
  86. package/dist/cli/__tests__/ralplan.test.js.map +1 -0
  87. package/dist/cli/__tests__/resume.test.js +17 -11
  88. package/dist/cli/__tests__/resume.test.js.map +1 -1
  89. package/dist/cli/__tests__/session-scoped-runtime.test.js +25 -0
  90. package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
  91. package/dist/cli/__tests__/session-search-help.test.js +25 -1
  92. package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
  93. package/dist/cli/__tests__/session-search.test.js.map +1 -1
  94. package/dist/cli/__tests__/setup-agents-overwrite.test.js +79 -0
  95. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  96. package/dist/cli/__tests__/setup-gh-star.test.js.map +1 -1
  97. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +77 -3
  98. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +1 -1
  99. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.d.ts +2 -0
  100. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.d.ts.map +1 -0
  101. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.js +285 -0
  102. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.js.map +1 -0
  103. package/dist/cli/__tests__/setup-install-mode.test.js +2579 -294
  104. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  105. package/dist/cli/__tests__/setup-preferences.test.d.ts +2 -0
  106. package/dist/cli/__tests__/setup-preferences.test.d.ts.map +1 -0
  107. package/dist/cli/__tests__/setup-preferences.test.js +79 -0
  108. package/dist/cli/__tests__/setup-preferences.test.js.map +1 -0
  109. package/dist/cli/__tests__/setup-refresh.test.js +33 -2
  110. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  111. package/dist/cli/__tests__/setup-scope.test.js +46 -3
  112. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  113. package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
  114. package/dist/cli/__tests__/state.test.js +37 -0
  115. package/dist/cli/__tests__/state.test.js.map +1 -1
  116. package/dist/cli/__tests__/team.test.js +105 -7
  117. package/dist/cli/__tests__/team.test.js.map +1 -1
  118. package/dist/cli/__tests__/ultragoal.test.js +61 -1
  119. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  120. package/dist/cli/__tests__/uninstall.test.js +1817 -24
  121. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  122. package/dist/cli/__tests__/update.test.js +58 -0
  123. package/dist/cli/__tests__/update.test.js.map +1 -1
  124. package/dist/cli/__tests__/url.test.js.map +1 -1
  125. package/dist/cli/__tests__/windows-popup-loop-contract.test.js +2 -1
  126. package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
  127. package/dist/cli/adapt.js.map +1 -1
  128. package/dist/cli/agents-init.js.map +1 -1
  129. package/dist/cli/agents.js.map +1 -1
  130. package/dist/cli/api.js.map +1 -1
  131. package/dist/cli/ask.d.ts +1 -1
  132. package/dist/cli/ask.d.ts.map +1 -1
  133. package/dist/cli/ask.js.map +1 -1
  134. package/dist/cli/auth.js.map +1 -1
  135. package/dist/cli/autoresearch-guided.js.map +1 -1
  136. package/dist/cli/autoresearch-intake.js.map +1 -1
  137. package/dist/cli/capabilities.js.map +1 -1
  138. package/dist/cli/cleanup.js.map +1 -1
  139. package/dist/cli/codex-feature-probe.js.map +1 -1
  140. package/dist/cli/codex-home.js.map +1 -1
  141. package/dist/cli/doctor.d.ts +21 -0
  142. package/dist/cli/doctor.d.ts.map +1 -1
  143. package/dist/cli/doctor.js +607 -130
  144. package/dist/cli/doctor.js.map +1 -1
  145. package/dist/cli/explore.js.map +1 -1
  146. package/dist/cli/index.d.ts +14 -3
  147. package/dist/cli/index.d.ts.map +1 -1
  148. package/dist/cli/index.js +245 -57
  149. package/dist/cli/index.js.map +1 -1
  150. package/dist/cli/mcp-parity.js +2 -2
  151. package/dist/cli/mcp-parity.js.map +1 -1
  152. package/dist/cli/mcp-serve.js.map +1 -1
  153. package/dist/cli/mission.js.map +1 -1
  154. package/dist/cli/native-assets.js.map +1 -1
  155. package/dist/cli/native-hook-claim-journal.d.ts +29 -0
  156. package/dist/cli/native-hook-claim-journal.d.ts.map +1 -0
  157. package/dist/cli/native-hook-claim-journal.js +252 -0
  158. package/dist/cli/native-hook-claim-journal.js.map +1 -0
  159. package/dist/cli/omx.js.map +1 -1
  160. package/dist/cli/plugin-marketplace.js.map +1 -1
  161. package/dist/cli/project-runtime-codex-homes.js.map +1 -1
  162. package/dist/cli/question.js.map +1 -1
  163. package/dist/cli/ralph.d.ts.map +1 -1
  164. package/dist/cli/ralph.js +3 -2
  165. package/dist/cli/ralph.js.map +1 -1
  166. package/dist/cli/ralplan.d.ts +10 -0
  167. package/dist/cli/ralplan.d.ts.map +1 -0
  168. package/dist/cli/ralplan.js +110 -0
  169. package/dist/cli/ralplan.js.map +1 -0
  170. package/dist/cli/session-search.d.ts +5 -0
  171. package/dist/cli/session-search.d.ts.map +1 -1
  172. package/dist/cli/session-search.js +87 -0
  173. package/dist/cli/session-search.js.map +1 -1
  174. package/dist/cli/setup-preferences.d.ts +11 -0
  175. package/dist/cli/setup-preferences.d.ts.map +1 -1
  176. package/dist/cli/setup-preferences.js +32 -0
  177. package/dist/cli/setup-preferences.js.map +1 -1
  178. package/dist/cli/setup.d.ts +58 -0
  179. package/dist/cli/setup.d.ts.map +1 -1
  180. package/dist/cli/setup.js +1450 -398
  181. package/dist/cli/setup.js.map +1 -1
  182. package/dist/cli/sparkshell.js.map +1 -1
  183. package/dist/cli/star-prompt.js.map +1 -1
  184. package/dist/cli/state.d.ts.map +1 -1
  185. package/dist/cli/state.js +2 -1
  186. package/dist/cli/state.js.map +1 -1
  187. package/dist/cli/team.js.map +1 -1
  188. package/dist/cli/ultragoal.d.ts +5 -1
  189. package/dist/cli/ultragoal.d.ts.map +1 -1
  190. package/dist/cli/ultragoal.js +34 -2
  191. package/dist/cli/ultragoal.js.map +1 -1
  192. package/dist/cli/uninstall.d.ts +17 -0
  193. package/dist/cli/uninstall.d.ts.map +1 -1
  194. package/dist/cli/uninstall.js +986 -95
  195. package/dist/cli/uninstall.js.map +1 -1
  196. package/dist/cli/update.d.ts.map +1 -1
  197. package/dist/cli/update.js +8 -1
  198. package/dist/cli/update.js.map +1 -1
  199. package/dist/cli/url.js.map +1 -1
  200. package/dist/compat/__tests__/doctor-contract.test.js +5 -1
  201. package/dist/compat/__tests__/doctor-contract.test.js.map +1 -1
  202. package/dist/compat/__tests__/rust-runtime-compat.test.js +1 -1
  203. package/dist/compat/__tests__/rust-runtime-compat.test.js.map +1 -1
  204. package/dist/config/__tests__/codex-hooks.test.js +1287 -11
  205. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  206. package/dist/config/__tests__/generator-idempotent.test.js +1179 -32
  207. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  208. package/dist/config/__tests__/generator-notify.test.js +6 -1
  209. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  210. package/dist/config/__tests__/models.test.js +67 -3
  211. package/dist/config/__tests__/models.test.js.map +1 -1
  212. package/dist/config/codex-feature-flags.d.ts.map +1 -1
  213. package/dist/config/codex-feature-flags.js.map +1 -1
  214. package/dist/config/codex-hooks.d.ts +124 -1
  215. package/dist/config/codex-hooks.d.ts.map +1 -1
  216. package/dist/config/codex-hooks.js +2142 -265
  217. package/dist/config/codex-hooks.js.map +1 -1
  218. package/dist/config/commit-lore-guard.js.map +1 -1
  219. package/dist/config/generator.d.ts +23 -34
  220. package/dist/config/generator.d.ts.map +1 -1
  221. package/dist/config/generator.js +1480 -246
  222. package/dist/config/generator.js.map +1 -1
  223. package/dist/config/mcp-registry.js.map +1 -1
  224. package/dist/config/models.d.ts +14 -4
  225. package/dist/config/models.d.ts.map +1 -1
  226. package/dist/config/models.js +15 -1
  227. package/dist/config/models.js.map +1 -1
  228. package/dist/config/omx-first-party-mcp.js.map +1 -1
  229. package/dist/config/team-mode.d.ts.map +1 -1
  230. package/dist/document-refresh/__tests__/enforcer.test.js.map +1 -1
  231. package/dist/document-refresh/enforcer.js.map +1 -1
  232. package/dist/exec/followup.js.map +1 -1
  233. package/dist/goal-workflows/artifacts.js.map +1 -1
  234. package/dist/hooks/__tests__/agents-overlay.test.js +7 -4
  235. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  236. package/dist/hooks/__tests__/analyze-routing-contract.test.js.map +1 -1
  237. package/dist/hooks/__tests__/analyze-skill-contract.test.js.map +1 -1
  238. package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
  239. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +9 -3
  240. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  241. package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
  242. package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
  243. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  244. package/dist/hooks/__tests__/design-skill.test.js.map +1 -1
  245. package/dist/hooks/__tests__/keyword-detector.test.js +1376 -6
  246. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  247. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +252 -543
  248. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  249. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +38 -4
  250. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  251. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +765 -10
  252. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  253. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +2 -0
  254. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
  255. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +142 -1
  256. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  257. package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
  258. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js +48 -2
  259. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js.map +1 -1
  260. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +61 -44
  261. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
  262. package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
  263. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js +3 -2
  264. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js.map +1 -1
  265. package/dist/hooks/__tests__/notify-hook-session-scope.test.js +243 -4
  266. package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
  267. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +531 -12
  268. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  269. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +163 -24
  270. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  271. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +482 -32
  272. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  273. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +68 -1
  274. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  275. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +7 -1
  276. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
  277. package/dist/hooks/__tests__/notify-hook-visual-verdict.test.js.map +1 -1
  278. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +43 -4
  279. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  280. package/dist/hooks/__tests__/openclaw-setup-contract.test.js.map +1 -1
  281. package/dist/hooks/__tests__/pre-context-gate-skills.test.js.map +1 -1
  282. package/dist/hooks/__tests__/prometheus-strict-contract.test.js.map +1 -1
  283. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js.map +1 -1
  284. package/dist/hooks/__tests__/prompt-guidance-test-helpers.js.map +1 -1
  285. package/dist/hooks/__tests__/prompt-session-provenance.test.d.ts +2 -0
  286. package/dist/hooks/__tests__/prompt-session-provenance.test.d.ts.map +1 -0
  287. package/dist/hooks/__tests__/prompt-session-provenance.test.js +82 -0
  288. package/dist/hooks/__tests__/prompt-session-provenance.test.js.map +1 -0
  289. package/dist/hooks/__tests__/session.test.js +878 -11
  290. package/dist/hooks/__tests__/session.test.js.map +1 -1
  291. package/dist/hooks/__tests__/skill-catalog-hygiene.test.js.map +1 -1
  292. package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.js.map +1 -1
  293. package/dist/hooks/__tests__/visual-ralph-skill.test.js.map +1 -1
  294. package/dist/hooks/__tests__/visual-verdict-loop.test.js.map +1 -1
  295. package/dist/hooks/agents-overlay.js +5 -5
  296. package/dist/hooks/agents-overlay.js.map +1 -1
  297. package/dist/hooks/code-simplifier/index.js.map +1 -1
  298. package/dist/hooks/explore-routing.js.map +1 -1
  299. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  300. package/dist/hooks/extensibility/events.js.map +1 -1
  301. package/dist/hooks/extensibility/loader.js.map +1 -1
  302. package/dist/hooks/extensibility/plugin-runner-stdin.js.map +1 -1
  303. package/dist/hooks/extensibility/sdk/logging.js.map +1 -1
  304. package/dist/hooks/keyword-detector.d.ts +33 -7
  305. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  306. package/dist/hooks/keyword-detector.js +2228 -119
  307. package/dist/hooks/keyword-detector.js.map +1 -1
  308. package/dist/hooks/keyword-registry.d.ts +12 -0
  309. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  310. package/dist/hooks/keyword-registry.js +41 -0
  311. package/dist/hooks/keyword-registry.js.map +1 -1
  312. package/dist/hooks/prompt-session-provenance.d.ts +87 -0
  313. package/dist/hooks/prompt-session-provenance.d.ts.map +1 -0
  314. package/dist/hooks/prompt-session-provenance.js +180 -0
  315. package/dist/hooks/prompt-session-provenance.js.map +1 -0
  316. package/dist/hooks/session.d.ts +163 -34
  317. package/dist/hooks/session.d.ts.map +1 -1
  318. package/dist/hooks/session.js +1395 -317
  319. package/dist/hooks/session.js.map +1 -1
  320. package/dist/hooks/task-size-detector.js.map +1 -1
  321. package/dist/hud/__tests__/state.test.js.map +1 -1
  322. package/dist/hud/authority.js.map +1 -1
  323. package/dist/hud/constants.js.map +1 -1
  324. package/dist/hud/index.js.map +1 -1
  325. package/dist/hud/reconcile.js.map +1 -1
  326. package/dist/hud/render.js.map +1 -1
  327. package/dist/hud/state.js.map +1 -1
  328. package/dist/hud/tmux.js.map +1 -1
  329. package/dist/leader/__tests__/contract.test.js +153 -1
  330. package/dist/leader/__tests__/contract.test.js.map +1 -1
  331. package/dist/leader/contract.d.ts +42 -5
  332. package/dist/leader/contract.d.ts.map +1 -1
  333. package/dist/leader/contract.js +184 -3
  334. package/dist/leader/contract.js.map +1 -1
  335. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  336. package/dist/mcp/__tests__/code-intel-server.test.js +31 -3
  337. package/dist/mcp/__tests__/code-intel-server.test.js.map +1 -1
  338. package/dist/mcp/__tests__/server-lifecycle.test.js +15 -2
  339. package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
  340. package/dist/mcp/__tests__/state-paths.test.js +98 -1
  341. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  342. package/dist/mcp/__tests__/wiki-server.test.js +18 -0
  343. package/dist/mcp/__tests__/wiki-server.test.js.map +1 -1
  344. package/dist/mcp/bootstrap.js.map +1 -1
  345. package/dist/mcp/code-intel-server.d.ts +58 -58
  346. package/dist/mcp/code-intel-server.d.ts.map +1 -1
  347. package/dist/mcp/code-intel-server.js +13 -3
  348. package/dist/mcp/code-intel-server.js.map +1 -1
  349. package/dist/mcp/hermes-bridge.js.map +1 -1
  350. package/dist/mcp/hermes-server.d.ts +70 -70
  351. package/dist/mcp/hermes-server.d.ts.map +1 -1
  352. package/dist/mcp/lifecycle-telemetry.js.map +1 -1
  353. package/dist/mcp/memory-server.d.ts +34 -34
  354. package/dist/mcp/memory-server.d.ts.map +1 -1
  355. package/dist/mcp/memory-server.js.map +1 -1
  356. package/dist/mcp/state-paths.d.ts +14 -0
  357. package/dist/mcp/state-paths.d.ts.map +1 -1
  358. package/dist/mcp/state-paths.js +96 -20
  359. package/dist/mcp/state-paths.js.map +1 -1
  360. package/dist/mcp/state-server.d.ts +47 -47
  361. package/dist/mcp/state-server.d.ts.map +1 -1
  362. package/dist/mcp/trace-server.d.ts +4 -4
  363. package/dist/mcp/trace-server.d.ts.map +1 -1
  364. package/dist/mcp/trace-server.js.map +1 -1
  365. package/dist/mcp/wiki-server.d.ts +34 -23
  366. package/dist/mcp/wiki-server.d.ts.map +1 -1
  367. package/dist/mcp/wiki-server.js +9 -2
  368. package/dist/mcp/wiki-server.js.map +1 -1
  369. package/dist/modes/__tests__/base-session-scope.test.js +53 -1
  370. package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
  371. package/dist/modes/__tests__/base-tmux-pane.test.js +4 -4
  372. package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
  373. package/dist/modes/base.d.ts.map +1 -1
  374. package/dist/modes/base.js +5 -5
  375. package/dist/modes/base.js.map +1 -1
  376. package/dist/notifications/__tests__/lifecycle-dedupe.test.js.map +1 -1
  377. package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
  378. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js +177 -85
  379. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js.map +1 -1
  380. package/dist/notifications/__tests__/session-registry.test.js.map +1 -1
  381. package/dist/notifications/__tests__/template-engine.test.js.map +1 -1
  382. package/dist/notifications/__tests__/verbosity.test.js.map +1 -1
  383. package/dist/notifications/http-client.js.map +1 -1
  384. package/dist/notifications/index.d.ts +4 -1
  385. package/dist/notifications/index.d.ts.map +1 -1
  386. package/dist/notifications/index.js +3 -9
  387. package/dist/notifications/index.js.map +1 -1
  388. package/dist/notifications/lifecycle-dedupe.js.map +1 -1
  389. package/dist/notifications/reply-listener.d.ts.map +1 -1
  390. package/dist/notifications/reply-listener.js.map +1 -1
  391. package/dist/notifications/session-registry.js.map +1 -1
  392. package/dist/notifications/session-status.js.map +1 -1
  393. package/dist/notifications/temp-contract.d.ts.map +1 -1
  394. package/dist/notifications/temp-contract.js +4 -0
  395. package/dist/notifications/temp-contract.js.map +1 -1
  396. package/dist/notifications/tmux-detector.js.map +1 -1
  397. package/dist/notifications/tmux.js.map +1 -1
  398. package/dist/openclaw/config.js.map +1 -1
  399. package/dist/openclaw/dispatcher.js.map +1 -1
  400. package/dist/pipeline/__tests__/orchestrator.test.js +4 -4
  401. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  402. package/dist/pipeline/__tests__/stages.test.js +16 -13
  403. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  404. package/dist/pipeline/stages/code-review.js.map +1 -1
  405. package/dist/pipeline/stages/ralph-verify.js.map +1 -1
  406. package/dist/pipeline/stages/ralplan.js.map +1 -1
  407. package/dist/pipeline/stages/team-exec.js.map +1 -1
  408. package/dist/pipeline/stages/ultragoal.js.map +1 -1
  409. package/dist/pipeline/stages/ultraqa.js.map +1 -1
  410. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js.map +1 -1
  411. package/dist/planning/artifact-names.js.map +1 -1
  412. package/dist/planning/artifacts.js.map +1 -1
  413. package/dist/question/__tests__/state.test.js.map +1 -1
  414. package/dist/question/__tests__/ui.test.js.map +1 -1
  415. package/dist/question/autopilot-wait.js.map +1 -1
  416. package/dist/question/client.d.ts.map +1 -1
  417. package/dist/question/client.js.map +1 -1
  418. package/dist/question/deep-interview.js.map +1 -1
  419. package/dist/question/events.js.map +1 -1
  420. package/dist/question/renderer.js.map +1 -1
  421. package/dist/question/state.d.ts.map +1 -1
  422. package/dist/question/state.js.map +1 -1
  423. package/dist/question/ui.js.map +1 -1
  424. package/dist/ralph/contract.d.ts +1 -1
  425. package/dist/ralph/contract.d.ts.map +1 -1
  426. package/dist/ralplan/__tests__/consensus-gate.test.js +527 -8
  427. package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
  428. package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts +2 -0
  429. package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts.map +1 -0
  430. package/dist/ralplan/__tests__/documented-leader-preflight.test.js +46 -0
  431. package/dist/ralplan/__tests__/documented-leader-preflight.test.js.map +1 -0
  432. package/dist/ralplan/__tests__/runtime.test.js +116 -14
  433. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  434. package/dist/ralplan/consensus-gate.d.ts +3 -3
  435. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  436. package/dist/ralplan/consensus-gate.js +173 -70
  437. package/dist/ralplan/consensus-gate.js.map +1 -1
  438. package/dist/ralplan/documented-leader-preflight.d.ts +34 -0
  439. package/dist/ralplan/documented-leader-preflight.d.ts.map +1 -0
  440. package/dist/ralplan/documented-leader-preflight.js +58 -0
  441. package/dist/ralplan/documented-leader-preflight.js.map +1 -0
  442. package/dist/ralplan/runtime.d.ts +3 -2
  443. package/dist/ralplan/runtime.d.ts.map +1 -1
  444. package/dist/ralplan/runtime.js +12 -9
  445. package/dist/ralplan/runtime.js.map +1 -1
  446. package/dist/runtime/__tests__/bridge.test.js +42 -1
  447. package/dist/runtime/__tests__/bridge.test.js.map +1 -1
  448. package/dist/runtime/bridge.d.ts +12 -0
  449. package/dist/runtime/bridge.d.ts.map +1 -1
  450. package/dist/runtime/bridge.js +52 -1
  451. package/dist/runtime/bridge.js.map +1 -1
  452. package/dist/runtime/process-tree.js.map +1 -1
  453. package/dist/runtime/run-loop.js.map +1 -1
  454. package/dist/runtime/run-outcome.d.ts +3 -3
  455. package/dist/runtime/run-outcome.d.ts.map +1 -1
  456. package/dist/runtime/run-outcome.js.map +1 -1
  457. package/dist/runtime/run-state.js.map +1 -1
  458. package/dist/scripts/__tests__/codex-native-hook.test.js +6657 -995
  459. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  460. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.d.ts +2 -0
  461. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.d.ts.map +1 -0
  462. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.js +634 -0
  463. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.js.map +1 -0
  464. package/dist/scripts/__tests__/generate-release-body.test.js.map +1 -1
  465. package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -1
  466. package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -1
  467. package/dist/scripts/__tests__/notify-fallback-delivery.test.d.ts +2 -0
  468. package/dist/scripts/__tests__/notify-fallback-delivery.test.d.ts.map +1 -0
  469. package/dist/scripts/__tests__/notify-fallback-delivery.test.js +174 -0
  470. package/dist/scripts/__tests__/notify-fallback-delivery.test.js.map +1 -0
  471. package/dist/scripts/__tests__/notify-state-io.test.js +57 -1
  472. package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
  473. package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts +2 -0
  474. package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts.map +1 -0
  475. package/dist/scripts/__tests__/reasoning-artifact-contract.test.js +108 -0
  476. package/dist/scripts/__tests__/reasoning-artifact-contract.test.js.map +1 -0
  477. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.d.ts +2 -0
  478. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.d.ts.map +1 -0
  479. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js +78 -0
  480. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js.map +1 -0
  481. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.d.ts +2 -0
  482. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.d.ts.map +1 -0
  483. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js +55 -0
  484. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js.map +1 -0
  485. package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
  486. package/dist/scripts/__tests__/smoke-packed-install.test.js +1100 -4
  487. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  488. package/dist/scripts/__tests__/test-reply-listener-live.test.js.map +1 -1
  489. package/dist/scripts/build-api.js.map +1 -1
  490. package/dist/scripts/build-explore-harness.js.map +1 -1
  491. package/dist/scripts/build-sparkshell.js.map +1 -1
  492. package/dist/scripts/cleanup-explore-harness.js.map +1 -1
  493. package/dist/scripts/codex-execution-surface.js.map +1 -1
  494. package/dist/scripts/codex-native-hook.d.ts +4 -1
  495. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  496. package/dist/scripts/codex-native-hook.js +11577 -1184
  497. package/dist/scripts/codex-native-hook.js.map +1 -1
  498. package/dist/scripts/generate-native-release-manifest.js.map +1 -1
  499. package/dist/scripts/generate-release-body.js.map +1 -1
  500. package/dist/scripts/hook-payload-guard.d.ts.map +1 -1
  501. package/dist/scripts/hook-payload-guard.js.map +1 -1
  502. package/dist/scripts/notify-fallback-delivery.d.ts +48 -0
  503. package/dist/scripts/notify-fallback-delivery.d.ts.map +1 -0
  504. package/dist/scripts/notify-fallback-delivery.js +419 -0
  505. package/dist/scripts/notify-fallback-delivery.js.map +1 -0
  506. package/dist/scripts/notify-fallback-watcher.js +218 -138
  507. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  508. package/dist/scripts/notify-hook/auto-nudge.d.ts +18 -9
  509. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  510. package/dist/scripts/notify-hook/auto-nudge.js +112 -15
  511. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  512. package/dist/scripts/notify-hook/managed-tmux.d.ts +23 -0
  513. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  514. package/dist/scripts/notify-hook/managed-tmux.js +122 -12
  515. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  516. package/dist/scripts/notify-hook/orchestration-intent.d.ts +7 -7
  517. package/dist/scripts/notify-hook/orchestration-intent.d.ts.map +1 -1
  518. package/dist/scripts/notify-hook/ralph-session-resume.d.ts +5 -2
  519. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
  520. package/dist/scripts/notify-hook/ralph-session-resume.js +33 -37
  521. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  522. package/dist/scripts/notify-hook/state-io.d.ts +17 -0
  523. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  524. package/dist/scripts/notify-hook/state-io.js +73 -11
  525. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  526. package/dist/scripts/notify-hook/team-dispatch.d.ts +10 -1
  527. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  528. package/dist/scripts/notify-hook/team-dispatch.js +234 -79
  529. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  530. package/dist/scripts/notify-hook/team-leader-nudge.d.ts +2 -2
  531. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  532. package/dist/scripts/notify-hook/team-leader-nudge.js +39 -13
  533. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  534. package/dist/scripts/notify-hook/team-tmux-guard.d.ts +16 -8
  535. package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
  536. package/dist/scripts/notify-hook/team-tmux-guard.js +365 -44
  537. package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
  538. package/dist/scripts/notify-hook/team-worker-posttooluse.js.map +1 -1
  539. package/dist/scripts/notify-hook/team-worker-stop.d.ts +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 +19 -18
  542. package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
  543. package/dist/scripts/notify-hook/team-worker.d.ts +5 -2
  544. package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
  545. package/dist/scripts/notify-hook/team-worker.js +37 -28
  546. package/dist/scripts/notify-hook/team-worker.js.map +1 -1
  547. package/dist/scripts/notify-hook/tmux-injection.d.ts +8 -1
  548. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  549. package/dist/scripts/notify-hook/tmux-injection.js +53 -18
  550. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  551. package/dist/scripts/notify-hook/visual-verdict.d.ts +1 -1
  552. package/dist/scripts/notify-hook/visual-verdict.d.ts.map +1 -1
  553. package/dist/scripts/notify-hook/visual-verdict.js +15 -13
  554. package/dist/scripts/notify-hook/visual-verdict.js.map +1 -1
  555. package/dist/scripts/notify-hook.d.ts +24 -1
  556. package/dist/scripts/notify-hook.d.ts.map +1 -1
  557. package/dist/scripts/notify-hook.js +267 -90
  558. package/dist/scripts/notify-hook.js.map +1 -1
  559. package/dist/scripts/postinstall.js.map +1 -1
  560. package/dist/scripts/prompt-inventory.js.map +1 -1
  561. package/dist/scripts/smoke-packed-install.d.ts +927 -5
  562. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  563. package/dist/scripts/smoke-packed-install.js +4317 -13
  564. package/dist/scripts/smoke-packed-install.js.map +1 -1
  565. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  566. package/dist/scripts/test-reply-listener-live.js.map +1 -1
  567. package/dist/scripts/test-sparkshell.js.map +1 -1
  568. package/dist/scripts/verify-native-agents.js.map +1 -1
  569. package/dist/session-history/friction.js.map +1 -1
  570. package/dist/session-history/search.js.map +1 -1
  571. package/dist/sidecar/__tests__/boundary.test.js.map +1 -1
  572. package/dist/sidecar/collector.js.map +1 -1
  573. package/dist/sidecar/index.js.map +1 -1
  574. package/dist/sidecar/render.js.map +1 -1
  575. package/dist/sidecar/tmux.js.map +1 -1
  576. package/dist/state/__tests__/mode-state-context.test.js +54 -0
  577. package/dist/state/__tests__/mode-state-context.test.js.map +1 -1
  578. package/dist/state/__tests__/operations.test.js +66 -15
  579. package/dist/state/__tests__/operations.test.js.map +1 -1
  580. package/dist/state/__tests__/skill-active.test.js +19 -1
  581. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  582. package/dist/state/__tests__/workflow-transition.test.js +61 -0
  583. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  584. package/dist/state/mode-state-context.d.ts +5 -0
  585. package/dist/state/mode-state-context.d.ts.map +1 -1
  586. package/dist/state/mode-state-context.js +59 -0
  587. package/dist/state/mode-state-context.js.map +1 -1
  588. package/dist/state/operations.d.ts +1 -1
  589. package/dist/state/operations.d.ts.map +1 -1
  590. package/dist/state/operations.js +49 -55
  591. package/dist/state/operations.js.map +1 -1
  592. package/dist/state/paths.d.ts +1 -1
  593. package/dist/state/paths.d.ts.map +1 -1
  594. package/dist/state/paths.js +1 -1
  595. package/dist/state/paths.js.map +1 -1
  596. package/dist/state/skill-active.d.ts +13 -1
  597. package/dist/state/skill-active.d.ts.map +1 -1
  598. package/dist/state/skill-active.js +43 -4
  599. package/dist/state/skill-active.js.map +1 -1
  600. package/dist/state/terminal-normalization.js.map +1 -1
  601. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  602. package/dist/state/workflow-transition-reconcile.js +20 -4
  603. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  604. package/dist/state/workflow-transition.d.ts +1 -1
  605. package/dist/state/workflow-transition.d.ts.map +1 -1
  606. package/dist/state/workflow-transition.js.map +1 -1
  607. package/dist/subagents/__tests__/leader-bootstrap-3181.test.d.ts +2 -0
  608. package/dist/subagents/__tests__/leader-bootstrap-3181.test.d.ts.map +1 -0
  609. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js +52 -0
  610. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js.map +1 -0
  611. package/dist/subagents/__tests__/tracker.test.js +1550 -10
  612. package/dist/subagents/__tests__/tracker.test.js.map +1 -1
  613. package/dist/subagents/role-routing-marker.d.ts +10 -0
  614. package/dist/subagents/role-routing-marker.d.ts.map +1 -0
  615. package/dist/subagents/role-routing-marker.js +136 -0
  616. package/dist/subagents/role-routing-marker.js.map +1 -0
  617. package/dist/subagents/tracker.d.ts +91 -0
  618. package/dist/subagents/tracker.d.ts.map +1 -1
  619. package/dist/subagents/tracker.js +806 -62
  620. package/dist/subagents/tracker.js.map +1 -1
  621. package/dist/team/__tests__/api-interop.test.js +91 -1
  622. package/dist/team/__tests__/api-interop.test.js.map +1 -1
  623. package/dist/team/__tests__/approved-execution.test.js.map +1 -1
  624. package/dist/team/__tests__/delivery-e2e-smoke.test.js +52 -10
  625. package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
  626. package/dist/team/__tests__/idle-nudge.test.js +45 -15
  627. package/dist/team/__tests__/idle-nudge.test.js.map +1 -1
  628. package/dist/team/__tests__/mcp-comm.test.js +93 -27
  629. package/dist/team/__tests__/mcp-comm.test.js.map +1 -1
  630. package/dist/team/__tests__/model-contract.test.js +341 -4
  631. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  632. package/dist/team/__tests__/role-router.test.js.map +1 -1
  633. package/dist/team/__tests__/runtime-cli.test.js +9 -0
  634. package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
  635. package/dist/team/__tests__/runtime.test.js +3624 -355
  636. package/dist/team/__tests__/runtime.test.js.map +1 -1
  637. package/dist/team/__tests__/scaling.test.js +2516 -118
  638. package/dist/team/__tests__/scaling.test.js.map +1 -1
  639. package/dist/team/__tests__/state-root.test.js.map +1 -1
  640. package/dist/team/__tests__/state.test.js +804 -36
  641. package/dist/team/__tests__/state.test.js.map +1 -1
  642. package/dist/team/__tests__/team-ops-contract.test.js +6 -0
  643. package/dist/team/__tests__/team-ops-contract.test.js.map +1 -1
  644. package/dist/team/__tests__/tmux-session.test.js +3664 -969
  645. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  646. package/dist/team/__tests__/tmux-test-fixture.js.map +1 -1
  647. package/dist/team/__tests__/worker-runtime-identity.test.js +38 -2
  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 +2 -2
  652. package/dist/team/api-interop.d.ts.map +1 -1
  653. package/dist/team/api-interop.js +15 -9
  654. package/dist/team/api-interop.js.map +1 -1
  655. package/dist/team/commit-hygiene.js.map +1 -1
  656. package/dist/team/contracts.d.ts +5 -5
  657. package/dist/team/contracts.d.ts.map +1 -1
  658. package/dist/team/delivery-log.js.map +1 -1
  659. package/dist/team/exact-pane.d.ts +23 -0
  660. package/dist/team/exact-pane.d.ts.map +1 -0
  661. package/dist/team/exact-pane.js +133 -0
  662. package/dist/team/exact-pane.js.map +1 -0
  663. package/dist/team/followup-planner.js.map +1 -1
  664. package/dist/team/goal-workflow.js.map +1 -1
  665. package/dist/team/idle-nudge.d.ts +12 -1
  666. package/dist/team/idle-nudge.d.ts.map +1 -1
  667. package/dist/team/idle-nudge.js +41 -14
  668. package/dist/team/idle-nudge.js.map +1 -1
  669. package/dist/team/model-contract.d.ts +18 -2
  670. package/dist/team/model-contract.d.ts.map +1 -1
  671. package/dist/team/model-contract.js +314 -35
  672. package/dist/team/model-contract.js.map +1 -1
  673. package/dist/team/orchestrator.js.map +1 -1
  674. package/dist/team/pane-status.js.map +1 -1
  675. package/dist/team/phase-controller.js.map +1 -1
  676. package/dist/team/reminder-intents.d.ts +1 -1
  677. package/dist/team/reminder-intents.d.ts.map +1 -1
  678. package/dist/team/reminder-intents.js.map +1 -1
  679. package/dist/team/runtime-cli.js.map +1 -1
  680. package/dist/team/runtime.d.ts +1 -0
  681. package/dist/team/runtime.d.ts.map +1 -1
  682. package/dist/team/runtime.js +1326 -231
  683. package/dist/team/runtime.js.map +1 -1
  684. package/dist/team/scaling.d.ts +22 -8
  685. package/dist/team/scaling.d.ts.map +1 -1
  686. package/dist/team/scaling.js +1463 -376
  687. package/dist/team/scaling.js.map +1 -1
  688. package/dist/team/state/dispatch-lock.js.map +1 -1
  689. package/dist/team/state/dispatch.d.ts.map +1 -1
  690. package/dist/team/state/dispatch.js +26 -8
  691. package/dist/team/state/dispatch.js.map +1 -1
  692. package/dist/team/state/events.js.map +1 -1
  693. package/dist/team/state/locks.d.ts.map +1 -1
  694. package/dist/team/state/locks.js +61 -13
  695. package/dist/team/state/locks.js.map +1 -1
  696. package/dist/team/state/tasks.d.ts.map +1 -1
  697. package/dist/team/state/tasks.js +4 -3
  698. package/dist/team/state/tasks.js.map +1 -1
  699. package/dist/team/state-root.js.map +1 -1
  700. package/dist/team/state.d.ts +67 -1
  701. package/dist/team/state.d.ts.map +1 -1
  702. package/dist/team/state.js +636 -129
  703. package/dist/team/state.js.map +1 -1
  704. package/dist/team/team-identity.d.ts.map +1 -1
  705. package/dist/team/team-identity.js.map +1 -1
  706. package/dist/team/team-ops.d.ts +5 -0
  707. package/dist/team/team-ops.d.ts.map +1 -1
  708. package/dist/team/team-ops.js +4 -0
  709. package/dist/team/team-ops.js.map +1 -1
  710. package/dist/team/tmux-session.d.ts +125 -23
  711. package/dist/team/tmux-session.d.ts.map +1 -1
  712. package/dist/team/tmux-session.js +1340 -333
  713. package/dist/team/tmux-session.js.map +1 -1
  714. package/dist/team/worker-bootstrap.js.map +1 -1
  715. package/dist/team/worktree.d.ts.map +1 -1
  716. package/dist/team/worktree.js +4 -0
  717. package/dist/team/worktree.js.map +1 -1
  718. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  719. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
  720. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  721. package/dist/ultragoal/artifacts.js +4 -1
  722. package/dist/ultragoal/artifacts.js.map +1 -1
  723. package/dist/url-reader/index.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 +57 -0
  727. package/dist/utils/__tests__/file-durability.test.js.map +1 -0
  728. package/dist/utils/agents-model-table.js.map +1 -1
  729. package/dist/utils/file-durability.d.ts +15 -0
  730. package/dist/utils/file-durability.d.ts.map +1 -0
  731. package/dist/utils/file-durability.js +37 -0
  732. package/dist/utils/file-durability.js.map +1 -0
  733. package/dist/utils/package.js.map +1 -1
  734. package/dist/utils/paths.js.map +1 -1
  735. package/dist/utils/platform-command.js.map +1 -1
  736. package/dist/utils/repo-deps.js.map +1 -1
  737. package/dist/utils/version.js.map +1 -1
  738. package/dist/verification/__tests__/ci-rust-gates.test.js +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/verifier.js.map +1 -1
  742. package/dist/visual/constants.d.ts +1 -1
  743. package/dist/visual/constants.d.ts.map +1 -1
  744. package/dist/vscode/__tests__/index.test.js.map +1 -1
  745. package/dist/vscode/index.js.map +1 -1
  746. package/dist/wiki/__tests__/cjk-tokenize.test.js.map +1 -1
  747. package/dist/wiki/__tests__/lint.test.js.map +1 -1
  748. package/dist/wiki/__tests__/query.test.js.map +1 -1
  749. package/dist/wiki/__tests__/storage.test.js.map +1 -1
  750. package/dist/wiki/__tests__/test-helpers.d.ts.map +1 -1
  751. package/dist/wiki/lint.d.ts +4 -1
  752. package/dist/wiki/lint.d.ts.map +1 -1
  753. package/dist/wiki/lint.js +3 -2
  754. package/dist/wiki/lint.js.map +1 -1
  755. package/dist/wiki/query.js.map +1 -1
  756. package/dist/wiki/storage.js.map +1 -1
  757. package/package.json +2 -2
  758. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  759. package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +24 -0
  760. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +10 -5
  761. package/plugins/oh-my-codex/skills/ralph/SKILL.md +6 -2
  762. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +6 -2
  763. package/plugins/oh-my-codex/skills/team/SKILL.md +13 -9
  764. package/skills/omx-setup/SKILL.md +10 -5
  765. package/skills/ralph/SKILL.md +6 -2
  766. package/skills/ralplan/SKILL.md +6 -2
  767. package/skills/team/SKILL.md +13 -9
  768. package/src/scripts/__tests__/codex-native-hook.test.ts +20048 -10827
  769. package/src/scripts/__tests__/fixtures/issue-3138-installed-smoke.ts +734 -0
  770. package/src/scripts/__tests__/notify-fallback-delivery.test.ts +177 -0
  771. package/src/scripts/__tests__/notify-state-io.test.ts +73 -0
  772. package/src/scripts/__tests__/reasoning-artifact-contract.test.ts +129 -0
  773. package/src/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.ts +78 -0
  774. package/src/scripts/__tests__/role-intent-durable-recovery-3181.test.ts +60 -0
  775. package/src/scripts/__tests__/smoke-packed-install.test.ts +1309 -4
  776. package/src/scripts/codex-native-hook.ts +11429 -1163
  777. package/src/scripts/notify-fallback-delivery.ts +416 -0
  778. package/src/scripts/notify-fallback-watcher.ts +216 -155
  779. package/src/scripts/notify-hook/auto-nudge.ts +113 -14
  780. package/src/scripts/notify-hook/managed-tmux.ts +149 -12
  781. package/src/scripts/notify-hook/ralph-session-resume.ts +37 -36
  782. package/src/scripts/notify-hook/state-io.ts +100 -13
  783. package/src/scripts/notify-hook/team-dispatch.ts +242 -75
  784. package/src/scripts/notify-hook/team-leader-nudge.ts +40 -18
  785. package/src/scripts/notify-hook/team-tmux-guard.ts +366 -39
  786. package/src/scripts/notify-hook/team-worker-stop.ts +20 -16
  787. package/src/scripts/notify-hook/team-worker.ts +39 -26
  788. package/src/scripts/notify-hook/tmux-injection.ts +66 -23
  789. package/src/scripts/notify-hook/visual-verdict.ts +22 -12
  790. package/src/scripts/notify-hook.ts +336 -90
  791. package/src/scripts/smoke-packed-install.ts +5154 -149
@@ -1,10 +1,15 @@
1
- import { mkdtempSync, realpathSync, rmSync, } from 'node:fs';
2
- import { mkdirSync } from 'node:fs';
1
+ import assert from 'node:assert/strict';
2
+ import { chmodSync, existsSync, linkSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, realpathSync, rmSync, statSync, symlinkSync, writeFileSync, } from 'node:fs';
3
3
  import { tmpdir } from 'node:os';
4
- import { join } from 'node:path';
5
- import { spawnSync } from 'node:child_process';
4
+ import { delimiter, dirname, join, resolve } from 'node:path';
5
+ import { spawn, spawnSync } from 'node:child_process';
6
6
  import { pathToFileURL } from 'node:url';
7
+ import { TextDecoder } from 'node:util';
8
+ import TOML, { parse as parseToml } from '@iarna/toml';
9
+ import { isManagedCodexHookCommand, planManagedCodexHooksRemoval } from '../config/codex-hooks.js';
10
+ import { spawnPlatformCommand, spawnPlatformCommandSync, } from '../utils/platform-command.js';
7
11
  import { ensureReusableNodeModules, } from '../utils/repo-deps.js';
12
+ import { escapeTomlString } from '../utils/toml.js';
8
13
  export { hasUsableNodeModules, resolveGitCommonDir, resolveReusableNodeModulesSource, } from '../utils/repo-deps.js';
9
14
  export const PACKED_INSTALL_SMOKE_CORE_COMMANDS = [
10
15
  ['--help'],
@@ -12,7 +17,7 @@ export const PACKED_INSTALL_SMOKE_CORE_COMMANDS = [
12
17
  ['api', '--help'],
13
18
  ['sparkshell', '--help'],
14
19
  ];
15
- export const PACKED_INSTALL_NATIVE_HOOK_SMOKE_EVENTS = [
20
+ export const MANAGED_CODEX_HOOK_EVENTS = [
16
21
  'SessionStart',
17
22
  'PreToolUse',
18
23
  'PostToolUse',
@@ -21,12 +26,1674 @@ export const PACKED_INSTALL_NATIVE_HOOK_SMOKE_EVENTS = [
21
26
  'PostCompact',
22
27
  'Stop',
23
28
  ];
29
+ export const PACKED_INSTALL_NATIVE_HOOK_REGRESSION_PROMPTS = [
30
+ { name: 'directive-use-ralplan', prompt: 'use $ralplan plan this', expectedSkill: 'ralplan', expectedStopBlock: true },
31
+ { name: 'directive-please-use-ralplan', prompt: 'please use $ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
32
+ { name: 'directive-run-ralplan', prompt: 'run $ralplan plan this', expectedSkill: 'ralplan', expectedStopBlock: true },
33
+ { name: 'directive-list-use-ralplan', prompt: '- use $ralplan plan this', expectedSkill: 'ralplan', expectedStopBlock: true },
34
+ { name: 'directive-documentation-then-command', prompt: 'use $ralplan is the consensus-planning command\n$autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
35
+ { name: 'directive-documentation-then-implicit-command', prompt: 'use $ralplan is the consensus-planning command\nUse autopilot mode.', expectedSkill: 'autopilot', expectedStopBlock: true },
36
+ { name: 'directive-documentation-trailing-prose-then-command', prompt: 'use $ralplan is the workflow command for planning\n$autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
37
+ { name: 'directive-documentation-implicit-prose', prompt: 'use $ralplan is the workflow command for autopilot mode', expectedSkill: null, expectedStopBlock: false },
38
+ { name: 'directive-documentation-alias-prose', prompt: 'use $ralplan is the consensus-planning command\nAutopilot mode is its alias.', expectedSkill: null, expectedStopBlock: false },
39
+ { name: 'directive-coordinated-documentation-then-command', prompt: '- use $ralplan and $autopilot are workflow commands\n$ralph execute it', expectedSkill: 'ralph', expectedStopBlock: true },
40
+ { name: 'directive-documentation-semicolon-directive', prompt: 'use $ralplan is the consensus-planning command; use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
41
+ { name: 'directive-two-documentation-blocks', prompt: 'use $ralplan is the consensus-planning command\nuse $autopilot is the autonomous workflow command\n$ralph execute it', expectedSkill: 'ralph', expectedStopBlock: true },
42
+ { name: 'directive-documentation-embedded-token-then-command', prompt: 'use $ralplan is the workflow command for $team\n$autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
43
+ { name: 'doc-task-noun', prompt: 'use $ralplan is the workflow command; use $autopilot update the documentation', expectedSkill: 'autopilot', expectedStopBlock: true },
44
+ { name: 'doc-implicit-followup', prompt: 'use $ralplan is the consensus-planning command; use autopilot mode.', expectedSkill: 'autopilot', expectedStopBlock: true },
45
+ { name: 'doc-transition-followup', prompt: 'use $ralplan is the consensus-planning command; then use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
46
+ { name: 'doc-explicit-alias', prompt: 'use $ralplan is the consensus-planning command; $team is its alias', expectedSkill: null, expectedStopBlock: false },
47
+ { name: 'doc-implicit-chain', prompt: 'use $ralplan is the consensus-planning command\nAutopilot mode is its alias.\n$ralph execute it', expectedSkill: 'ralph', expectedStopBlock: true },
48
+ { name: 'doc-fullwidth-separator', prompt: 'use $ralplan,$autopilot are workflow commands', expectedSkill: null, expectedStopBlock: false },
49
+ { name: 'doc-compact-slash', prompt: 'use $ralplan/$autopilot are workflow commands\n$ralph execute it', expectedSkill: 'ralph', expectedStopBlock: true },
50
+ { name: 'reference-prompts-followup', prompt: '[docs]: /target "title\nUse /prompts:architect"\n$ralph execute it', expectedSkill: 'ralph', expectedStopBlock: true },
51
+ { name: 'doc-period-implicit', prompt: 'use $ralplan is the consensus-planning command. Use autopilot mode.', expectedSkill: 'autopilot', expectedStopBlock: true },
52
+ { name: 'doc-bare-implicit', prompt: 'use $ralplan is the consensus-planning command; autopilot mode.', expectedSkill: 'autopilot', expectedStopBlock: true },
53
+ { name: 'doc-fullwidth-semicolon', prompt: 'use $ralplan is the consensus-planning command; use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
54
+ { name: 'doc-but-followup', prompt: 'use $ralplan is the workflow command; but use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
55
+ { name: 'doc-fullwidth-oxford', prompt: 'use $ralplan, $autopilot, and $team are workflow commands', expectedSkill: null, expectedStopBlock: false },
56
+ { name: 'reference-zero-title', prompt: '[docs]: ./target\n(autopilot mode)', expectedSkill: null, expectedStopBlock: false },
57
+ { name: 'doc-also-alias-explicit', prompt: 'use $ralplan is the consensus-planning command; $team is also its alias', expectedSkill: null, expectedStopBlock: false },
58
+ { name: 'doc-also-alias-implicit', prompt: 'use $ralplan is the consensus-planning command\nAutopilot mode is also its alias.', expectedSkill: null, expectedStopBlock: false },
59
+ { name: 'doc-embedded-mention', prompt: 'use $ralplan is the workflow command; $team appears in the documentation.', expectedSkill: null, expectedStopBlock: false },
60
+ { name: 'chained-negation', prompt: '$ralplan; $autopilot is prohibited', expectedSkill: 'ralplan', expectedStopBlock: true, expectedDeferredSkills: [] },
61
+ { name: 'long-negation', prompt: `$ralplan; $autopilot${' '.repeat(193)}is prohibited`, expectedSkill: 'ralplan', expectedStopBlock: true, expectedDeferredSkills: [] },
62
+ { name: 'doc-arabic-comma', prompt: 'use $ralplan، $autopilot are workflow commands', expectedSkill: null, expectedStopBlock: false },
63
+ { name: 'arabic-negation', prompt: '$ralplan، $autopilot are prohibited', expectedSkill: null, expectedStopBlock: false },
64
+ { name: 'implicit-arabic-negation', prompt: 'Autopilot mode، deep interview are prohibited.', expectedSkill: null, expectedStopBlock: false },
65
+ { name: 'fullwidth-frame-reset', prompt: 'For instance: manual mode is slower。 Use autopilot mode.', expectedSkill: 'autopilot', expectedStopBlock: true },
66
+ { name: 'doc-abbreviation', prompt: 'use $ralplan is the workflow command, e.g. use $autopilot in examples.', expectedSkill: null, expectedStopBlock: false },
67
+ { name: 'implicit-doc-mention', prompt: 'use $ralplan is the workflow command; autopilot mode appears in the documentation.', expectedSkill: null, expectedStopBlock: false },
68
+ { name: 'implicit-doc-chain', prompt: 'use $ralplan is the workflow command; autopilot mode is its alias; $ralph execute it', expectedSkill: 'ralph', expectedStopBlock: true },
69
+ { name: 'long-command-gap', prompt: `use $ralplan is the workflow command; use${' '.repeat(161)}$autopilot build it`, expectedSkill: 'autopilot', expectedStopBlock: true },
70
+ { name: 'ideographic-negation', prompt: '$ralplan、 $autopilot are prohibited', expectedSkill: null, expectedStopBlock: false },
71
+ { name: 'implicit-ideo-negation', prompt: 'Autopilot mode、 deep interview are prohibited.', expectedSkill: null, expectedStopBlock: false },
72
+ { name: 'doc-exclamation', prompt: 'use $ralplan is the consensus-planning command! run $autopilot', expectedSkill: 'autopilot', expectedStopBlock: true },
73
+ { name: 'doc-fullwidth-question', prompt: 'use $ralplan is the consensus-planning command? run $autopilot', expectedSkill: 'autopilot', expectedStopBlock: true },
74
+ { name: 'implicit-doc-predecessor', prompt: 'Autopilot mode is workflow documentation.\n$ralph execute it', expectedSkill: 'ralph', expectedStopBlock: true },
75
+ { name: 'confusable-use-verb', prompt: 'uſe $ralplan plan it', expectedSkill: null, expectedStopBlock: false },
76
+ { name: 'confusable-please', prompt: 'pleaſe use $ralplan plan it', expectedSkill: null, expectedStopBlock: false },
77
+ { name: 'confusable-prompts-token-then-command', prompt: '/promptſ:architect; use autopilot mode.', expectedSkill: 'autopilot', expectedStopBlock: true },
78
+ { name: 'reserved-em-dash-boundary', prompt: '/prompts:architect— use autopilot mode', expectedSkill: null, expectedStopBlock: false },
79
+ { name: 'reserved-fullwidth-comma-boundary', prompt: '/prompts:architect, use autopilot mode', expectedSkill: null, expectedStopBlock: false },
80
+ { name: 'confusable-implicit-verb', prompt: 'Do not use deep interview but uſe autopilot mode.', expectedSkill: null, expectedStopBlock: false },
81
+ { name: 'frame-fullwidth-colon', prompt: 'For instance: use autopilot mode.', expectedSkill: null, expectedStopBlock: false },
82
+ { name: 'frame-fullwidth-comma', prompt: 'For instance, use autopilot mode.', expectedSkill: null, expectedStopBlock: false },
83
+ { name: 'frame-arabic-comma', prompt: 'For instance، use autopilot mode.', expectedSkill: null, expectedStopBlock: false },
84
+ { name: 'frame-ideo-comma', prompt: 'For instance、 use autopilot mode.', expectedSkill: null, expectedStopBlock: false },
85
+ { name: 'doc-explicit-documented', prompt: 'use $ralplan is the workflow command; $autopilot is documented in the guide.', expectedSkill: null, expectedStopBlock: false },
86
+ { name: 'doc-explicit-described', prompt: '$autopilot is described in the manual.', expectedSkill: null, expectedStopBlock: false },
87
+ { name: 'doc-comma-followup', prompt: 'use $ralplan is the workflow command, but use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
88
+ { name: 'doc-fw-comma-followup', prompt: 'use $ralplan is the workflow command, but use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
89
+ { name: 'doc-arabic-followup', prompt: 'use $ralplan is the workflow command، but use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
90
+ { name: 'doc-ideo-followup', prompt: 'use $ralplan is the workflow command、 but use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
91
+ { name: 'neg-arabic-followup', prompt: 'Do not run $ralplan، use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
92
+ { name: 'neg-ideo-followup', prompt: 'Do not run $ralplan、 use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
93
+ { name: 'implicit-doc-prefix-next', prompt: 'The docs mention autopilot mode.\n$ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
94
+ { name: 'implicit-doc-prefix-line', prompt: 'The docs mention autopilot mode; use $ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
95
+ { name: 'implicit-doc-subject-line', prompt: 'Autopilot mode is workflow documentation; use $ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
96
+ { name: 'compact-explicit-negation', prompt: '$ralplan,$autopilot are prohibited', expectedSkill: null, expectedStopBlock: false },
97
+ { name: 'compact-implicit-negation', prompt: 'Autopilot mode،deep interview are prohibited.', expectedSkill: null, expectedStopBlock: false },
98
+ { name: 'doc-clause-local-prefix', prompt: '$ralplan; $autopilot is documented in the guide.', expectedSkill: 'ralplan', expectedStopBlock: true },
99
+ { name: 'doc-chain-described', prompt: 'use $ralplan is the workflow command; autopilot mode is documented in the guide; $team execute it', expectedSkill: 'team', expectedStopBlock: false, insideTmux: true },
100
+ { name: 'doc-chain-workflow', prompt: 'use $ralplan is the workflow command; autopilot mode is workflow documentation; use $ralph execute it', expectedSkill: 'ralph', expectedStopBlock: true },
101
+ { name: 'ref-inline-explicit', prompt: '[docs]: $ralplan\n$autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
102
+ { name: 'ref-inline-prompts', prompt: '[docs]: /prompts:architect\n$autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
103
+ { name: 'list-fullwidth-explicit-doc', prompt: '- $ralplan: consensus-planning workflow', expectedSkill: null, expectedStopBlock: false },
104
+ { name: 'list-fullwidth-implicit-doc', prompt: '- autopilot mode: autonomous workflow command', expectedSkill: null, expectedStopBlock: false },
105
+ { name: 'possessive-straight', prompt: "$ralplan's workflow is documented", expectedSkill: null, expectedStopBlock: false },
106
+ { name: 'possessive-curly', prompt: '$ralplan’s workflow is documented', expectedSkill: null, expectedStopBlock: false },
107
+ { name: 'possessive-fullwidth', prompt: '$ralplan's workflow is documented', expectedSkill: null, expectedStopBlock: false },
108
+ { name: 'possessive-prompts', prompt: "/prompts:architect's syntax is documented", expectedSkill: null, expectedStopBlock: false },
109
+ { name: 'malformed-prefix-kata', prompt: '$・autopilot mode', expectedSkill: null, expectedStopBlock: false },
110
+ { name: 'malformed-prefix-half', prompt: '$・autopilot mode', expectedSkill: null, expectedStopBlock: false },
111
+ { name: 'malformed-prefix-arabic', prompt: '$٪autopilot mode', expectedSkill: null, expectedStopBlock: false },
112
+ { name: 'malformed-prefix-division', prompt: '$∕autopilot mode', expectedSkill: null, expectedStopBlock: false },
113
+ { name: 'doc-but-directive', prompt: 'use $autopilot is documented but use $ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
114
+ { name: 'list-directive-fw-colon', prompt: '- use $ralplan: consensus-planning workflow', expectedSkill: null, expectedStopBlock: false },
115
+ { name: 'doc-arabic-question', prompt: 'use $ralplan is the workflow command؟ run $autopilot', expectedSkill: 'autopilot', expectedStopBlock: true },
116
+ { name: 'arabic-semicolon-negation', prompt: '$ralplan؛ $autopilot is prohibited', expectedSkill: 'ralplan', expectedStopBlock: true, expectedDeferredSkills: [] },
117
+ { name: 'confusable-postposed-transition', prompt: '$ralplan is prohibited but uſe autopilot mode.', expectedSkill: null, expectedStopBlock: false },
118
+ { name: 'mixed-negation', prompt: 'Autopilot mode and $ralplan are prohibited.', expectedSkill: null, expectedStopBlock: false },
119
+ { name: 'both-mixed-negation', prompt: 'Both autopilot mode and $ralplan are prohibited.', expectedSkill: null, expectedStopBlock: false },
120
+ { name: 'mixed-documentation', prompt: 'use $ralplan and autopilot mode are workflow commands', expectedSkill: null, expectedStopBlock: false },
121
+ { name: 'prose-doc-no-reopen', prompt: '$ralplan is prohibited because docs use $autopilot.', expectedSkill: null, expectedStopBlock: false },
122
+ { name: 'neg-fw-dot-reopen', prompt: 'Do not run $ralplan. use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
123
+ { name: 'neg-greek-q-reopen', prompt: 'Do not run $ralplan; use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
124
+ { name: 'unicode-attached-contrast', prompt: 'Do not use deep interview яbut use autopilot mode.', expectedSkill: null, expectedStopBlock: false },
125
+ { name: 'prefix-list-followup', prompt: 'Do not run $ralplan, $autopilot; use $team execute it', expectedSkill: 'team', expectedStopBlock: false, insideTmux: true },
126
+ { name: 'mixed-postposed-chain', prompt: '$ralplan, autopilot mode, $team are prohibited.', expectedSkill: null, expectedStopBlock: false },
127
+ { name: 'implicit-first-doc-chain', prompt: 'Autopilot mode and $ralplan are workflow commands; use $team execute it', expectedSkill: 'team', expectedStopBlock: false, insideTmux: true },
128
+ { name: 'both-mixed-doc-followup', prompt: 'Both autopilot mode and $ralplan are workflow commands; use $team execute it', expectedSkill: 'team', expectedStopBlock: false, insideTmux: true },
129
+ { name: 'doc-semicolon-preserves-earlier', prompt: 'Use autopilot mode; use $ralplan is the workflow command.', expectedSkill: 'autopilot', expectedStopBlock: true },
130
+ { name: 'doc-independent-comma', prompt: 'Use autopilot mode, and $ralplan is documented in the guide.', expectedSkill: 'autopilot', expectedStopBlock: true },
131
+ { name: 'reference-unclosed-quote-destination', prompt: '[docs]: "target\n$autopilot build it', expectedSkill: null, expectedStopBlock: false },
132
+ { name: 'reference-unclosed-inline-destination', prompt: '[docs]: `target\n$autopilot build it', expectedSkill: null, expectedStopBlock: false },
133
+ { name: 'mixed-prefix-negation-implicit', prompt: 'Do not run $ralplan and use autopilot mode.', expectedSkill: null, expectedStopBlock: false },
134
+ { name: 'repeated-postposed-followup', prompt: '$team is prohibited and is forbidden; use $ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
135
+ { name: 'doc-preserves-earlier', prompt: 'Use autopilot mode; "note"; use $ralplan is the workflow command.', expectedSkill: 'autopilot', expectedStopBlock: true },
136
+ { name: 'doc-colon-followup', prompt: 'use $ralplan is the workflow command: use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
137
+ { name: 'table-followup', prompt: 'Mode | Meaning\n--- | ---\nmanual | documentation\n$ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
138
+ { name: 'neg-advance-reopen', prompt: 'Do not run $ralplan but advance to $ultragoal', expectedSkill: 'ultragoal', expectedStopBlock: false },
139
+ { name: 'neg-jump-reopen', prompt: 'Do not run $ralplan but jump straight to $ultragoal', expectedSkill: 'ultragoal', expectedStopBlock: false },
140
+ { name: 'reference-plain-title', prompt: '[docs]: /target "title\nplain text"\n$ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
141
+ { name: 'reference-plain-destination', prompt: '[docs]: ./target\n$ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
142
+ { name: 'directive-use-the', prompt: 'Do not run $ralplan; use the $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
143
+ { name: 'directive-continue-after-quote', prompt: '"quoted"\ncontinue with $ralplan', expectedSkill: 'ralplan', expectedStopBlock: true },
144
+ { name: 'doc-advance-followup', prompt: 'use $ralplan is the workflow command; advance to $ultragoal', expectedSkill: 'ultragoal', expectedStopBlock: false },
145
+ { name: 'directive-run-code-review', prompt: 'run $code-review', expectedSkill: 'code-review', expectedStopBlock: false },
146
+ { name: 'directive-documentation', prompt: 'use $ralplan is the consensus-planning command', expectedSkill: null, expectedStopBlock: false },
147
+ { name: 'nested-bounded-child-unbounded-parent', prompt: '"`x`\n$ralplan plan it', expectedSkill: null, expectedStopBlock: false },
148
+ { name: 'first-contiguous-block-terminal', prompt: '$ralplan plan it\n"x"\n$autopilot build it', expectedSkill: 'ralplan', expectedStopBlock: true, expectedDeferredSkills: [] },
149
+ { name: 'leading-reserved-dominance', prompt: '/prompts:architect\n"x"\n$ralplan plan it', expectedSkill: null, expectedStopBlock: false },
150
+ { name: 'list-fence-root-opener', prompt: '- ```\n sample\n```\n$ralplan plan it', expectedSkill: null, expectedStopBlock: false },
151
+ { name: 'list-fence-relative-closer', prompt: '- ```\n sample\n ```\n$ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
152
+ { name: 'reference-multiline-title-explicit', prompt: '[docs]: /target "title\nuse /prompts:architect\n$ralplan plan it"', expectedSkill: null, expectedStopBlock: false },
153
+ { name: 'reference-multiline-title-implicit', prompt: '[docs]: /target "title\nuse autopilot mode"', expectedSkill: null, expectedStopBlock: false },
154
+ { name: 'reference-next-line-title', prompt: '[docs]: ./target\n (autopilot mode)', expectedSkill: null, expectedStopBlock: false },
155
+ { name: 'reference-next-line-destination-title', prompt: '[docs]:\n ./target\n (autopilot mode)', expectedSkill: null, expectedStopBlock: false },
156
+ { name: 'kelvin-case-fold-suffix', prompt: '$ultraworK execute', expectedSkill: null, expectedStopBlock: false },
157
+ { name: 'katakana-middle-dot-suffix', prompt: '$ralplan・suffix plan it', expectedSkill: null, expectedStopBlock: false },
158
+ { name: 'halfwidth-middle-dot-suffix', prompt: '$ralplan・suffix plan it', expectedSkill: null, expectedStopBlock: false },
159
+ { name: 'arabic-percent-suffix', prompt: '$ralplan٪docs', expectedSkill: null, expectedStopBlock: false },
160
+ { name: 'division-slash-suffix', prompt: '$ralplan∕config', expectedSkill: null, expectedStopBlock: false },
161
+ { name: 'unclosed-prompts-quote', prompt: '"Use /prompts:architect\n$ralplan plan it', expectedSkill: null, expectedStopBlock: false },
162
+ { name: 'malformed-prompts-suffix', prompt: '/prompts:architect한글\n$ralplan plan it', expectedSkill: null, expectedStopBlock: false },
163
+ { name: 'stale-predecessor-prose-directive', prompt: '> quoted context\nProse\n$ralplan implement this', expectedSkill: null, expectedStopBlock: false },
164
+ { name: 'stale-predecessor-reserved-directive', prompt: '> quoted context\n/prompts:architect\n$ralplan plan this', expectedSkill: null, expectedStopBlock: false },
165
+ { name: 'stale-predecessor-first-block-terminal', prompt: '> quoted context\n$ralplan plan it\nLater discussion.\n$autopilot build it', expectedSkill: 'ralplan', expectedStopBlock: true, expectedDeferredSkills: [] },
166
+ { name: 'stale-predecessor-unclosed-after-negation', prompt: 'Do not run $ralplan.\n"unclosed context\n$autopilot build it', expectedSkill: null, expectedStopBlock: false },
167
+ { name: 'stale-predecessor-reference-unclosed', prompt: '[$ralplan]: ./docs\n"unclosed context\n$autopilot build it', expectedSkill: null, expectedStopBlock: false },
168
+ { name: 'stale-directive-clause-prefix', prompt: '> quoted context\nProse\nUse $ralplan plan this', expectedSkill: null, expectedStopBlock: false },
169
+ { name: 'nested-reserved-predecessor-successor', prompt: '- Use /prompts:architect.\n$ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
170
+ { name: 'nested-fence-predecessor-successor', prompt: '- - ```\n quoted context\n ```\n$ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
171
+ { name: 'reference-multiline-destination-implicit', prompt: '[docs]:\nautopilot', expectedSkill: null, expectedStopBlock: false },
172
+ { name: 'middle-dot-suffix', prompt: '$ralplan·suffix plan it', expectedSkill: null, expectedStopBlock: false },
173
+ { name: 'percent-suffix', prompt: '$ralplan%docs', expectedSkill: null, expectedStopBlock: false },
174
+ { name: 'fullwidth-percent-suffix', prompt: '$ralplan%docs', expectedSkill: null, expectedStopBlock: false },
175
+ { name: 'g1a-ordered-multi-skill', prompt: '$ralplan, $autopilot; $team', expectedSkill: 'ralplan', expectedStopBlock: true, expectedDeferredSkills: ['autopilot', 'team'], expectedActiveSkills: ['ralplan'], insideTmux: true },
176
+ { name: 'g1c-duplicate-alias', prompt: '$autopilot $oh-my-codex:autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true, expectedDeferredSkills: [], expectedActiveSkills: ['autopilot'] },
177
+ { name: 'b3-longer-valid-fence', prompt: '```text\n$autopilot build it\n````\n$ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
178
+ { name: 'b4-shorter-invalid-fence', prompt: '````text\n$autopilot build it\n```\n$ralplan plan it', expectedSkill: null, expectedStopBlock: false },
179
+ { name: 'b5-different-marker-invalid-fence', prompt: '```text\n$autopilot build it\n~~~\n$ralplan plan it', expectedSkill: null, expectedStopBlock: false },
180
+ ];
181
+ export function buildPackedRegressionEnvironment(testCase, baseEnv = process.env) {
182
+ const insideTmux = testCase.insideTmux === true;
183
+ return {
184
+ ...baseEnv,
185
+ OMX_ROOT: '',
186
+ OMX_STATE_ROOT: '',
187
+ OMX_TEAM_STATE_ROOT: '',
188
+ OMX_SESSION_ID: '',
189
+ CODEX_SESSION_ID: '',
190
+ SESSION_ID: '',
191
+ OMX_TEAM_WORKER: '',
192
+ OMX_TEAM_INTERNAL_WORKER: '',
193
+ OMX_TEAM_LEADER_CWD: '',
194
+ OMX_TEAM_MODE: insideTmux ? 'enabled' : '',
195
+ OMX_QUESTION_RETURN_PANE: '',
196
+ OMX_LEADER_PANE_ID: '',
197
+ OMX_TMUX_HUD_OWNER: '',
198
+ TMUX: insideTmux ? '/tmp/tmux-pr3140-regression' : '',
199
+ TMUX_PANE: insideTmux ? '%3140' : '',
200
+ };
201
+ }
202
+ export const PACKED_INSTALL_NATIVE_HOOK_SMOKE_EVENTS = MANAGED_CODEX_HOOK_EVENTS;
203
+ export const CODEX_APP_SERVER_TIMEOUTS = {
204
+ versionProbeMs: 2_000,
205
+ initializeMs: 15_000,
206
+ requestMs: 10_000,
207
+ shutdownMs: 5_000,
208
+ };
209
+ const CODEX_TRUST_STATUSES = new Set([
210
+ 'managed',
211
+ 'untrusted',
212
+ 'trusted',
213
+ 'modified',
214
+ ]);
215
+ const CODEX_EVENT_LABELS = {
216
+ preToolUse: 'PreToolUse',
217
+ permissionRequest: 'PermissionRequest',
218
+ postToolUse: 'PostToolUse',
219
+ preCompact: 'PreCompact',
220
+ postCompact: 'PostCompact',
221
+ sessionStart: 'SessionStart',
222
+ userPromptSubmit: 'UserPromptSubmit',
223
+ subagentStart: 'SubagentStart',
224
+ subagentStop: 'SubagentStop',
225
+ stop: 'Stop',
226
+ };
227
+ const PINNED_CODEX_VERSION = '0.142.5';
228
+ const PINNED_CODEX_VERSION_OUTPUT = `codex-cli ${PINNED_CODEX_VERSION}`;
229
+ /** Sanitized Codex 0.144.5 PreToolUse shape: documented fields only, with no pointer or tracker state. */
230
+ export const PACKED_CODEX_01445_NO_POINTER_NO_TRACKER_FIXTURE = Object.freeze({
231
+ hook_event_name: 'PreToolUse',
232
+ session_id: 'packed-01445-session',
233
+ turn_id: 'packed-01445-turn',
234
+ tool_name: 'Bash',
235
+ tool_use_id: 'packed-01445-tool',
236
+ tool_input: { command: 'omx ralplan role-intent write --role architect --parent-thread "$CODEX_THREAD_ID" --json' },
237
+ });
238
+ const CODEX_APP_SERVER_MAX_STDOUT_FRAME_LENGTH = 1_048_576;
239
+ export class CodexExecutableNotFoundError extends Error {
240
+ constructor() {
241
+ super('codex executable was not found');
242
+ this.name = 'CodexExecutableNotFoundError';
243
+ }
244
+ }
245
+ function isRecord(value) {
246
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
247
+ }
248
+ function isNodeErrorWithCode(value, code) {
249
+ return value instanceof Error
250
+ && isRecord(value)
251
+ && value.code === code;
252
+ }
253
+ function compactDiagnostic(value, limit = 4_000) {
254
+ return value.length <= limit ? value : value.slice(-limit);
255
+ }
256
+ function protocolError(message, stderr = '') {
257
+ return new Error(`${message}${stderr.trim() ? `\nCodex stderr:\n${compactDiagnostic(stderr)}` : ''}`);
258
+ }
259
+ function requireString(value, label) {
260
+ if (typeof value !== 'string' || value.length === 0) {
261
+ throw new Error(`Codex hooks/list response is missing ${label}`);
262
+ }
263
+ return value;
264
+ }
265
+ function requireRecord(value, label) {
266
+ if (!isRecord(value))
267
+ throw new Error(`Codex response has malformed ${label}`);
268
+ return value;
269
+ }
270
+ function hasExactPinnedCodexVersionStdout(stdout) {
271
+ return stdout === PINNED_CODEX_VERSION_OUTPUT
272
+ || stdout === `${PINNED_CODEX_VERSION_OUTPUT}\n`
273
+ || stdout === `${PINNED_CODEX_VERSION_OUTPUT}\r\n`;
274
+ }
275
+ function hasOnlyBenignCodexVersionStderr(stderr) {
276
+ const diagnostics = stderr.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
277
+ return diagnostics.every((line) => /^(?:warning|note): /i.test(line));
278
+ }
279
+ function formatVersionProbeOutput(stdout, stderr) {
280
+ return [
281
+ `stdout=${JSON.stringify(compactDiagnostic(stdout))}`,
282
+ ...(stderr.length === 0 ? [] : [`stderr=${JSON.stringify(compactDiagnostic(stderr))}`]),
283
+ ].join(' ');
284
+ }
285
+ const CODEX_VERSION_PROBE_CANDIDATE_BUDGET = 32;
286
+ const CODEX_VERSION_PROBE_DEADLINE_MS = 5_000;
287
+ function inspectCodexCandidate(executable) {
288
+ try {
289
+ lstatSync(executable);
290
+ }
291
+ catch (error) {
292
+ if (isNodeErrorWithCode(error, 'ENOENT'))
293
+ return { kind: 'absent' };
294
+ return {
295
+ kind: 'uninspectable',
296
+ error: error instanceof Error ? error : new Error(String(error)),
297
+ };
298
+ }
299
+ try {
300
+ statSync(executable);
301
+ return { kind: 'present' };
302
+ }
303
+ catch (error) {
304
+ if (isNodeErrorWithCode(error, 'ENOENT'))
305
+ return { kind: 'dangling' };
306
+ return {
307
+ kind: 'uninspectable',
308
+ error: error instanceof Error ? error : new Error(String(error)),
309
+ };
310
+ }
311
+ }
312
+ function versionProbeDeadlineError() {
313
+ return new Error(`Codex version resolution exceeded the ${CODEX_VERSION_PROBE_DEADLINE_MS}ms global deadline`);
314
+ }
315
+ function* streamPathEntries(pathValue, deadline, platform) {
316
+ const pathDelimiter = platform === 'win32' ? ';' : delimiter;
317
+ let start = 0;
318
+ for (let index = 0; index < pathValue.length; index += 1) {
319
+ if ((index & 0x3ff) === 0 && Date.now() >= deadline)
320
+ throw versionProbeDeadlineError();
321
+ if (pathValue[index] !== pathDelimiter)
322
+ continue;
323
+ yield pathValue.slice(start, index);
324
+ start = index + 1;
325
+ }
326
+ if (Date.now() >= deadline)
327
+ throw versionProbeDeadlineError();
328
+ yield pathValue.slice(start);
329
+ }
330
+ function codexCandidatePaths(pathEntry, env, platform) {
331
+ if (platform !== 'win32')
332
+ return [join(pathEntry, 'codex')];
333
+ const pathext = String(env.PATHEXT ?? '.COM;.EXE;.BAT;.CMD;.PS1')
334
+ .split(';')
335
+ .map((extension) => extension.trim().toLowerCase())
336
+ .filter(Boolean);
337
+ return [...new Set([...pathext.map((extension) => join(pathEntry, `codex${extension}`)), join(pathEntry, 'codex')])];
338
+ }
339
+ function resolvePinnedCodexExecutable(cwd, env, seam = {}) {
340
+ const platform = seam.platform ?? process.platform;
341
+ const deadline = Date.now() + CODEX_VERSION_PROBE_DEADLINE_MS;
342
+ const pathValue = env.PATH ?? env.Path ?? '';
343
+ const observed = [];
344
+ const seenPathEntries = new Set();
345
+ for (const entry of streamPathEntries(pathValue, deadline, platform)) {
346
+ if (Date.now() >= deadline)
347
+ throw versionProbeDeadlineError();
348
+ const pathEntry = resolve(cwd, entry || '.');
349
+ if (seenPathEntries.has(pathEntry))
350
+ continue;
351
+ if (seenPathEntries.size === CODEX_VERSION_PROBE_CANDIDATE_BUDGET) {
352
+ throw new Error(`Codex version resolution exceeded the ${CODEX_VERSION_PROBE_CANDIDATE_BUDGET}-candidate PATH budget`);
353
+ }
354
+ seenPathEntries.add(pathEntry);
355
+ let pathEntryState = inspectCodexCandidate(pathEntry);
356
+ if (pathEntryState.kind === 'absent') {
357
+ pathEntryState = inspectCodexCandidate(pathEntry);
358
+ if (pathEntryState.kind === 'absent')
359
+ continue;
360
+ }
361
+ if (pathEntryState.kind === 'dangling') {
362
+ observed.push(`${pathEntry}: PATH entry exists but its target is unavailable (dangling or changed during probe)`);
363
+ continue;
364
+ }
365
+ if (pathEntryState.kind === 'uninspectable') {
366
+ observed.push(`${pathEntry}: PATH entry cannot be inspected (${pathEntryState.error.message})`);
367
+ continue;
368
+ }
369
+ let selected = null;
370
+ for (const executable of codexCandidatePaths(pathEntry, env, platform)) {
371
+ if (Date.now() >= deadline)
372
+ throw versionProbeDeadlineError();
373
+ let before = inspectCodexCandidate(executable);
374
+ if (before.kind === 'absent') {
375
+ before = inspectCodexCandidate(executable);
376
+ if (before.kind === 'absent')
377
+ continue;
378
+ }
379
+ selected = { executable, before };
380
+ break;
381
+ }
382
+ if (!selected)
383
+ continue;
384
+ const { executable, before } = selected;
385
+ if (before.kind === 'dangling') {
386
+ observed.push(`${executable}: candidate exists but its target is unavailable (dangling or changed during probe)`);
387
+ continue;
388
+ }
389
+ if (before.kind === 'uninspectable') {
390
+ observed.push(`${executable}: candidate cannot be inspected (${before.error.message})`);
391
+ continue;
392
+ }
393
+ const remainingMs = deadline - Date.now();
394
+ if (remainingMs <= 0)
395
+ throw versionProbeDeadlineError();
396
+ const { result } = spawnPlatformCommandSync(executable, ['--version'], {
397
+ cwd,
398
+ env,
399
+ encoding: 'utf-8',
400
+ timeout: Math.max(1, Math.min(CODEX_APP_SERVER_TIMEOUTS.versionProbeMs, remainingMs)),
401
+ killSignal: 'SIGKILL',
402
+ }, platform, env, undefined, seam.spawnSyncImpl ?? spawnSync);
403
+ if (Date.now() >= deadline)
404
+ throw versionProbeDeadlineError();
405
+ if (isNodeErrorWithCode(result.error, 'ENOENT')) {
406
+ const after = inspectCodexCandidate(executable);
407
+ observed.push(`${executable}: launch returned ENOENT after an existing candidate was observed (${after.kind})`);
408
+ continue;
409
+ }
410
+ if (isNodeErrorWithCode(result.error, 'ETIMEDOUT')) {
411
+ if (remainingMs <= CODEX_APP_SERVER_TIMEOUTS.versionProbeMs)
412
+ throw versionProbeDeadlineError();
413
+ observed.push(`${executable}: version probe timed out after ${CODEX_APP_SERVER_TIMEOUTS.versionProbeMs}ms and was force-terminated`);
414
+ continue;
415
+ }
416
+ if (result.error) {
417
+ observed.push(`${executable}: version probe failed (${result.error.message})`);
418
+ continue;
419
+ }
420
+ if (result.status !== 0) {
421
+ observed.push(`${executable}: exit ${String(result.status)}`);
422
+ continue;
423
+ }
424
+ const stdout = String(result.stdout ?? '');
425
+ const stderr = String(result.stderr ?? '');
426
+ if (hasExactPinnedCodexVersionStdout(stdout) && hasOnlyBenignCodexVersionStderr(stderr)) {
427
+ return { executable, version: PINNED_CODEX_VERSION_OUTPUT };
428
+ }
429
+ observed.push(`${executable}: ${formatVersionProbeOutput(stdout, stderr)}`);
430
+ }
431
+ if (Date.now() >= deadline)
432
+ throw versionProbeDeadlineError();
433
+ if (observed.length === 0)
434
+ throw new CodexExecutableNotFoundError();
435
+ throw new Error(`Unsupported installed Codex version for the ${PINNED_CODEX_VERSION} boundary:\n${observed.join('\n')}`);
436
+ }
437
+ /**
438
+ * Newline-delimited JSON-RPC client for the installed Codex app-server boundary.
439
+ * It accepts no protocol fallback: malformed messages, request errors, unexpected
440
+ * exits, and cleanup failures are test failures.
441
+ */
442
+ export class CodexAppServer {
443
+ child;
444
+ pending = new Map();
445
+ closePromise;
446
+ stdoutDecoder = new TextDecoder('utf-8', { fatal: true });
447
+ stderr = '';
448
+ stdoutBuffer = '';
449
+ failure = null;
450
+ closing = false;
451
+ spawned = false;
452
+ constructor(child) {
453
+ this.child = child;
454
+ this.closePromise = new Promise((resolveClose) => {
455
+ child.once('close', (code, signal) => {
456
+ const exit = { code, signal };
457
+ this.flushStdoutDecoder();
458
+ if (this.stdoutBuffer.length > 0) {
459
+ this.fail(protocolError(`codex app-server closed with unterminated JSON-RPC stdout: ${JSON.stringify(compactDiagnostic(this.stdoutBuffer))}`, this.stderr));
460
+ }
461
+ if (!this.closing) {
462
+ this.fail(protocolError(`codex app-server exited unexpectedly (code ${String(code)}, signal ${String(signal)})`, this.stderr));
463
+ }
464
+ resolveClose(exit);
465
+ });
466
+ });
467
+ child.once('spawn', () => {
468
+ this.spawned = true;
469
+ });
470
+ child.once('error', (error) => {
471
+ this.fail(error instanceof Error ? error : new Error(String(error)));
472
+ });
473
+ child.stdout.on('data', (chunk) => this.onStdout(chunk));
474
+ child.stderr.setEncoding('utf-8');
475
+ child.stderr.on('data', (chunk) => {
476
+ this.stderr = compactDiagnostic(this.stderr + chunk);
477
+ });
478
+ }
479
+ static async start(options) {
480
+ const seam = options.commandSeam;
481
+ const platform = seam?.platform ?? process.platform;
482
+ const { executable } = resolvePinnedCodexExecutable(options.cwd, options.env, seam);
483
+ const { child } = spawnPlatformCommand(executable, ['app-server', '--stdio'], {
484
+ cwd: options.cwd,
485
+ env: options.env,
486
+ stdio: 'pipe',
487
+ }, platform, options.env, undefined, seam?.spawnImpl ?? spawn);
488
+ const server = new CodexAppServer(child);
489
+ try {
490
+ await server.waitForStartup();
491
+ }
492
+ catch (error) {
493
+ try {
494
+ await server.close();
495
+ }
496
+ catch {
497
+ // Preserve the startup failure as the primary diagnostic.
498
+ }
499
+ throw error;
500
+ }
501
+ return server;
502
+ }
503
+ async waitForStartup() {
504
+ const started = new Promise((resolveStarted, rejectStarted) => {
505
+ if (this.spawned) {
506
+ resolveStarted();
507
+ return;
508
+ }
509
+ this.child.once('spawn', resolveStarted);
510
+ this.child.once('error', rejectStarted);
511
+ });
512
+ await this.withTimeout(started, CODEX_APP_SERVER_TIMEOUTS.initializeMs, 'codex app-server did not start');
513
+ }
514
+ onStdout(chunk) {
515
+ let decoded;
516
+ try {
517
+ decoded = this.stdoutDecoder.decode(chunk, { stream: true });
518
+ }
519
+ catch {
520
+ this.fail(protocolError('codex app-server emitted invalid UTF-8 stdout', this.stderr));
521
+ return;
522
+ }
523
+ this.onStdoutText(decoded);
524
+ }
525
+ flushStdoutDecoder() {
526
+ try {
527
+ const decoded = this.stdoutDecoder.decode();
528
+ if (decoded)
529
+ this.onStdoutText(decoded);
530
+ }
531
+ catch {
532
+ this.fail(protocolError('codex app-server emitted invalid UTF-8 stdout', this.stderr));
533
+ }
534
+ }
535
+ onStdoutText(chunk) {
536
+ this.stdoutBuffer += chunk;
537
+ while (true) {
538
+ const newline = this.stdoutBuffer.indexOf('\n');
539
+ const frameLength = Buffer.byteLength(newline < 0 ? this.stdoutBuffer : this.stdoutBuffer.slice(0, newline), 'utf-8');
540
+ if (frameLength > CODEX_APP_SERVER_MAX_STDOUT_FRAME_LENGTH) {
541
+ this.fail(protocolError('codex app-server emitted an oversized JSON-RPC stdout frame', this.stderr));
542
+ return;
543
+ }
544
+ if (newline < 0)
545
+ return;
546
+ const line = this.stdoutBuffer.slice(0, newline).trim();
547
+ this.stdoutBuffer = this.stdoutBuffer.slice(newline + 1);
548
+ if (!line)
549
+ continue;
550
+ let envelope;
551
+ try {
552
+ envelope = JSON.parse(line);
553
+ }
554
+ catch {
555
+ this.fail(protocolError(`codex app-server emitted malformed JSON-RPC stdout: ${line}`, this.stderr));
556
+ return;
557
+ }
558
+ if (!isRecord(envelope)) {
559
+ this.fail(protocolError('codex app-server emitted a non-object JSON-RPC envelope', this.stderr));
560
+ return;
561
+ }
562
+ this.onEnvelope(envelope);
563
+ }
564
+ }
565
+ isMethodBearingNotification(envelope) {
566
+ return !Object.hasOwn(envelope, 'id')
567
+ && typeof envelope.method === 'string'
568
+ && envelope.method.trim().length > 0
569
+ && !Object.hasOwn(envelope, 'result')
570
+ && !Object.hasOwn(envelope, 'error');
571
+ }
572
+ validateResponseEnvelope(envelope) {
573
+ if (Object.hasOwn(envelope, 'method') || Object.hasOwn(envelope, 'params')) {
574
+ return 'codex app-server response mixed request or notification fields with an id';
575
+ }
576
+ const hasResult = Object.hasOwn(envelope, 'result');
577
+ const hasError = Object.hasOwn(envelope, 'error');
578
+ if (hasResult === hasError) {
579
+ return 'codex app-server response must contain exactly one of result or error';
580
+ }
581
+ if (hasError) {
582
+ const error = envelope.error;
583
+ if (!isRecord(error)
584
+ || typeof error.code !== 'number'
585
+ || !Number.isFinite(error.code)
586
+ || typeof error.message !== 'string') {
587
+ return 'codex app-server response has malformed JSON-RPC error';
588
+ }
589
+ }
590
+ return null;
591
+ }
592
+ onEnvelope(envelope) {
593
+ if (!Object.hasOwn(envelope, 'id')) {
594
+ if (!this.isMethodBearingNotification(envelope)) {
595
+ this.fail(protocolError('codex app-server emitted an invalid idless JSON-RPC envelope', this.stderr));
596
+ }
597
+ return;
598
+ }
599
+ if (typeof envelope.id !== 'number') {
600
+ this.fail(protocolError('codex app-server response had a non-numeric id', this.stderr));
601
+ return;
602
+ }
603
+ const validationError = this.validateResponseEnvelope(envelope);
604
+ if (validationError) {
605
+ this.fail(protocolError(validationError, this.stderr));
606
+ return;
607
+ }
608
+ const pending = this.pending.get(envelope.id);
609
+ if (!pending) {
610
+ this.fail(protocolError(`codex app-server returned an unexpected response id ${envelope.id}`, this.stderr));
611
+ return;
612
+ }
613
+ this.pending.delete(envelope.id);
614
+ clearTimeout(pending.timeout);
615
+ if (Object.hasOwn(envelope, 'error')) {
616
+ pending.reject(protocolError(`codex app-server ${pending.method} returned JSON-RPC error ${JSON.stringify(envelope.error)}`, this.stderr));
617
+ return;
618
+ }
619
+ pending.resolve(envelope.result);
620
+ }
621
+ fail(error) {
622
+ if (this.failure)
623
+ return;
624
+ this.failure = error;
625
+ for (const [id, pending] of this.pending) {
626
+ this.pending.delete(id);
627
+ clearTimeout(pending.timeout);
628
+ pending.reject(error);
629
+ }
630
+ }
631
+ withTimeout(promise, timeoutMs, message) {
632
+ return new Promise((resolvePromise, rejectPromise) => {
633
+ const timeout = setTimeout(() => rejectPromise(protocolError(message, this.stderr)), timeoutMs);
634
+ promise.then((value) => {
635
+ clearTimeout(timeout);
636
+ resolvePromise(value);
637
+ }, (error) => {
638
+ clearTimeout(timeout);
639
+ rejectPromise(error);
640
+ });
641
+ });
642
+ }
643
+ async request(envelope, timeoutMs) {
644
+ if (this.failure)
645
+ throw this.failure;
646
+ if (!Number.isInteger(envelope.id) || envelope.id <= 0 || !envelope.method) {
647
+ throw new Error('Codex JSON-RPC request requires a positive numeric id and method');
648
+ }
649
+ if (this.pending.has(envelope.id)) {
650
+ throw new Error(`duplicate Codex JSON-RPC request id ${envelope.id}`);
651
+ }
652
+ const response = new Promise((resolveResponse, rejectResponse) => {
653
+ const timeout = setTimeout(() => {
654
+ this.pending.delete(envelope.id);
655
+ rejectResponse(protocolError(`codex app-server ${envelope.method} request ${envelope.id} timed out after ${timeoutMs}ms`, this.stderr));
656
+ }, timeoutMs);
657
+ this.pending.set(envelope.id, {
658
+ method: envelope.method,
659
+ resolve: resolveResponse,
660
+ reject: rejectResponse,
661
+ timeout,
662
+ });
663
+ });
664
+ this.child.stdin.write(`${JSON.stringify(envelope)}\n`, 'utf-8', (error) => {
665
+ if (error)
666
+ this.fail(error instanceof Error ? error : new Error(String(error)));
667
+ });
668
+ return await response;
669
+ }
670
+ notify(envelope) {
671
+ if (this.failure)
672
+ throw this.failure;
673
+ if (!envelope.method)
674
+ throw new Error('Codex JSON-RPC notification requires a method');
675
+ this.child.stdin.write(`${JSON.stringify(envelope)}\n`, 'utf-8', (error) => {
676
+ if (error)
677
+ this.fail(error instanceof Error ? error : new Error(String(error)));
678
+ });
679
+ }
680
+ async close() {
681
+ if (this.closing)
682
+ return;
683
+ this.closing = true;
684
+ this.child.stdin.end();
685
+ let exit;
686
+ try {
687
+ exit = await this.withTimeout(this.closePromise, CODEX_APP_SERVER_TIMEOUTS.shutdownMs, 'codex app-server did not exit after stdin closed');
688
+ }
689
+ catch (error) {
690
+ this.child.kill('SIGKILL');
691
+ try {
692
+ await this.withTimeout(this.closePromise, CODEX_APP_SERVER_TIMEOUTS.shutdownMs, 'codex app-server did not exit after SIGKILL');
693
+ }
694
+ catch {
695
+ // Preserve the original graceful-cleanup failure, which has better diagnostics.
696
+ }
697
+ throw error;
698
+ }
699
+ if (exit.code !== 0 || exit.signal !== null) {
700
+ throw protocolError(`codex app-server exited abnormally during cleanup (code ${String(exit.code)}, signal ${String(exit.signal)})`, this.stderr);
701
+ }
702
+ if (this.failure)
703
+ throw this.failure;
704
+ }
705
+ }
706
+ export function createCodexInitializeEnvelope(clientName) {
707
+ return {
708
+ id: 1,
709
+ method: 'initialize',
710
+ params: {
711
+ clientInfo: { name: clientName, version: '1.0.0' },
712
+ capabilities: null,
713
+ },
714
+ };
715
+ }
716
+ export const CODEX_INITIALIZED_ENVELOPE = { method: 'initialized' };
717
+ export function createCodexHooksListEnvelope(projectCwd) {
718
+ return {
719
+ id: 2,
720
+ method: 'hooks/list',
721
+ params: { cwds: [projectCwd] },
722
+ };
723
+ }
724
+ export function createCodexBatchWriteEnvelope(trustByKey) {
725
+ return {
726
+ id: 3,
727
+ method: 'config/batchWrite',
728
+ params: {
729
+ edits: [{
730
+ keyPath: 'hooks.state',
731
+ value: Object.fromEntries(Object.entries(trustByKey).map(([key, currentHash]) => [key, { trusted_hash: currentHash }])),
732
+ mergeStrategy: 'upsert',
733
+ }],
734
+ filePath: null,
735
+ expectedVersion: null,
736
+ reloadUserConfig: true,
737
+ },
738
+ };
739
+ }
740
+ export async function initializeCodexAppServer(server, clientName) {
741
+ await server.request(createCodexInitializeEnvelope(clientName), CODEX_APP_SERVER_TIMEOUTS.initializeMs);
742
+ server.notify(CODEX_INITIALIZED_ENVELOPE);
743
+ }
744
+ export async function listCodexHooks(server, projectCwd, hooksPath) {
745
+ const result = await server.request(createCodexHooksListEnvelope(projectCwd), CODEX_APP_SERVER_TIMEOUTS.requestMs);
746
+ return parseCodexHooksListResult(result, projectCwd, hooksPath);
747
+ }
748
+ export function parseCodexHooksListResult(result, projectCwd, hooksPath) {
749
+ const resultRecord = requireRecord(result, 'hooks/list result');
750
+ if (!Array.isArray(resultRecord.data) || resultRecord.data.length !== 1) {
751
+ throw new Error('Codex hooks/list response must contain exactly one data entry');
752
+ }
753
+ const entry = requireRecord(resultRecord.data[0], 'hooks/list data entry');
754
+ if (entry.cwd !== projectCwd) {
755
+ throw new Error(`Codex hooks/list response cwd mismatch: expected ${projectCwd}, got ${String(entry.cwd)}`);
756
+ }
757
+ if (!Array.isArray(entry.hooks) || !Array.isArray(entry.warnings) || !Array.isArray(entry.errors)) {
758
+ throw new Error('Codex hooks/list response must contain hooks, warnings, and errors arrays');
759
+ }
760
+ if (entry.warnings.length !== 0 || entry.errors.length !== 0) {
761
+ throw new Error(`Codex hooks/list reported warnings/errors: ${JSON.stringify({ warnings: entry.warnings, errors: entry.errors })}`);
762
+ }
763
+ const hooks = entry.hooks.map((value, index) => {
764
+ const hook = requireRecord(value, `hooks/list hook ${index}`);
765
+ const rawEvent = requireString(hook.eventName, `hooks[${index}].eventName`);
766
+ const event = CODEX_EVENT_LABELS[rawEvent] ?? rawEvent;
767
+ const command = requireString(hook.command, `hooks[${index}].command`);
768
+ const enabled = hook.enabled === undefined ? true : hook.enabled;
769
+ if (enabled !== true) {
770
+ throw new Error(`Codex hooks/list hook ${index} is not an enabled command handler`);
771
+ }
772
+ const sourcePath = requireString(hook.sourcePath, `hooks[${index}].sourcePath`);
773
+ const key = requireString(hook.key, `hooks[${index}].key`);
774
+ const currentHash = requireString(hook.currentHash, `hooks[${index}].currentHash`);
775
+ const displayOrder = hook.displayOrder;
776
+ if (typeof displayOrder !== 'number' || !Number.isSafeInteger(displayOrder) || displayOrder < 0) {
777
+ throw new Error(`Codex hooks/list response has invalid hooks[${index}].displayOrder`);
778
+ }
779
+ const trustStatus = requireString(hook.trustStatus, `hooks[${index}].trustStatus`);
780
+ if (!CODEX_TRUST_STATUSES.has(trustStatus)) {
781
+ throw new Error(`Codex hooks/list response has unsupported trustStatus ${trustStatus}`);
782
+ }
783
+ if (sourcePath !== hooksPath) {
784
+ throw new Error(`Codex hooks/list sourcePath mismatch: expected ${hooksPath}, got ${sourcePath}`);
785
+ }
786
+ return {
787
+ event,
788
+ command,
789
+ sourcePath,
790
+ key,
791
+ currentHash,
792
+ displayOrder,
793
+ trustStatus: trustStatus,
794
+ };
795
+ });
796
+ return {
797
+ cwd: projectCwd,
798
+ hooks,
799
+ warnings: entry.warnings,
800
+ errors: entry.errors,
801
+ };
802
+ }
803
+ export function managedCodexHooksByEvent(hooks) {
804
+ const result = {};
805
+ for (const event of MANAGED_CODEX_HOOK_EVENTS) {
806
+ const matching = hooks.filter((hook) => hook.event === event && isManagedCodexHookCommand(hook.command));
807
+ if (matching.length !== 1) {
808
+ throw new Error(`Expected exactly one OMX ${event} hook from Codex, received ${matching.length}`);
809
+ }
810
+ result[event] = matching[0];
811
+ }
812
+ return result;
813
+ }
814
+ export function generatedHookTrustState(configToml) {
815
+ const parsed = TOML.parse(configToml);
816
+ const state = parsed.hooks?.state;
817
+ if (!isRecord(state))
818
+ throw new Error('setup config.toml is missing hooks.state');
819
+ const trust = Object.create(null);
820
+ for (const [key, value] of Object.entries(state)) {
821
+ if (!isRecord(value)) {
822
+ throw new Error(`setup config.toml has invalid hooks.state entry ${key}: expected an object`);
823
+ }
824
+ const entryKeys = Object.keys(value);
825
+ if (entryKeys.length !== 1 || entryKeys[0] !== 'trusted_hash') {
826
+ throw new Error(`setup config.toml has invalid hooks.state entry ${key}: expected only trusted_hash`);
827
+ }
828
+ if (typeof value.trusted_hash !== 'string' || value.trusted_hash.trim().length === 0) {
829
+ throw new Error(`setup config.toml has invalid hooks.state entry ${key}: trusted_hash must be non-empty`);
830
+ }
831
+ trust[key] = value.trusted_hash;
832
+ }
833
+ return trust;
834
+ }
835
+ export function managedTrustByKey(hooks) {
836
+ return Object.fromEntries(Object.values(managedCodexHooksByEvent(hooks)).map((hook) => [hook.key, hook.currentHash]));
837
+ }
838
+ export function assertGeneratedTrustMatchesCodex(generatedTrust, hooks) {
839
+ const expected = managedTrustByKey(hooks);
840
+ const expectedKeys = Object.keys(expected).sort();
841
+ const actualKeys = Object.keys(generatedTrust).sort();
842
+ if (actualKeys.length !== MANAGED_CODEX_HOOK_EVENTS.length
843
+ || actualKeys.length !== expectedKeys.length
844
+ || actualKeys.some((key, index) => key !== expectedKeys[index])) {
845
+ throw new Error(`Generated trust must contain exactly the ${MANAGED_CODEX_HOOK_EVENTS.length} current OMX hooks with no stale keys`);
846
+ }
847
+ for (const [key, currentHash] of Object.entries(expected)) {
848
+ if (generatedTrust[key] !== currentHash) {
849
+ throw new Error(`Generated hook trust does not equal Codex hooks/list metadata for ${key}`);
850
+ }
851
+ }
852
+ }
853
+ export async function approveManagedHooksInCodex(server, hooks, expectedConfigPath) {
854
+ const result = await server.request(createCodexBatchWriteEnvelope(managedTrustByKey(hooks)), CODEX_APP_SERVER_TIMEOUTS.requestMs);
855
+ assertCodexBatchWriteResult(result, expectedConfigPath);
856
+ }
857
+ export function assertCodexBatchWriteResult(result, expectedConfigPath) {
858
+ const response = requireRecord(result, 'config/batchWrite result');
859
+ if (response.filePath !== expectedConfigPath) {
860
+ throw new Error(`Codex config/batchWrite wrote ${String(response.filePath)}, expected isolated user config ${expectedConfigPath}`);
861
+ }
862
+ if (response.status !== 'ok' && response.status !== 'okOverridden') {
863
+ throw new Error(`Codex config/batchWrite returned unsupported status ${String(response.status)}`);
864
+ }
865
+ if (!Object.hasOwn(response, 'version')) {
866
+ throw new Error('Codex config/batchWrite result is missing version');
867
+ }
868
+ }
869
+ export function hookMetadataSnapshot(hooks) {
870
+ return hooks
871
+ .map(({ event, command, key, currentHash, displayOrder, trustStatus }) => ({
872
+ event,
873
+ command,
874
+ key,
875
+ currentHash,
876
+ displayOrder,
877
+ trustStatus,
878
+ }))
879
+ .sort((left, right) => left.displayOrder - right.displayOrder);
880
+ }
881
+ export function appendForeignHookGroups(hooksContent, marker, options) {
882
+ const parsed = JSON.parse(hooksContent);
883
+ if (!isRecord(parsed.hooks))
884
+ throw new Error('hooks.json is missing hooks object');
885
+ const hooks = parsed.hooks;
886
+ const foreignCommand = (event, suffix = '') => `${JSON.stringify(process.execPath)} ${JSON.stringify(`${marker}-${event}${suffix}.js`)}`;
887
+ const foreignGroup = (event) => ({
888
+ ...(event === 'PreToolUse' ? { matcher: 'Bash' } : {}),
889
+ foreign_group_metadata: {
890
+ marker,
891
+ nested: { keep: ['foreign', event], depth: { value: 7 } },
892
+ },
893
+ hooks: [{
894
+ type: 'command',
895
+ command: foreignCommand(event),
896
+ statusMessage: `${marker} ${event}`,
897
+ foreign_handler_metadata: { marker, event, values: [1, { two: true }] },
898
+ }],
899
+ });
900
+ for (const event of ['PreToolUse', 'PostToolUse']) {
901
+ const existing = hooks[event];
902
+ if (existing !== undefined && !Array.isArray(existing)) {
903
+ throw new Error(`hooks.${event} is not an array`);
904
+ }
905
+ const entries = (existing ?? []);
906
+ if (options.appendGroups) {
907
+ entries.push(foreignGroup(event));
908
+ }
909
+ else {
910
+ const group = entries.find((entry) => isRecord(entry)
911
+ && Array.isArray(entry.hooks)
912
+ && entry.hooks.some((hook) => isRecord(hook) && typeof hook.command === 'string' && hook.command.includes(marker)));
913
+ if (!group) {
914
+ throw new Error(`missing pre-seeded foreign ${event} group`);
915
+ }
916
+ const groupHooks = group.hooks;
917
+ if (!Array.isArray(groupHooks)) {
918
+ throw new Error(`missing pre-seeded foreign ${event} handler array`);
919
+ }
920
+ groupHooks.push({
921
+ type: 'command',
922
+ command: foreignCommand(event, '-inserted'),
923
+ statusMessage: `${marker} inserted ${event}`,
924
+ foreign_handler_metadata: { marker, inserted: true, nested: { event } },
925
+ });
926
+ }
927
+ hooks[event] = entries;
928
+ }
929
+ return JSON.stringify(parsed, null, 2) + '\n';
930
+ }
931
+ export function appendDisplayOrderStableForeignHookGroups(hooksContent, marker, options) {
932
+ const parsed = JSON.parse(hooksContent);
933
+ if (!isRecord(parsed.hooks))
934
+ throw new Error('hooks.json is missing hooks object');
935
+ const hooks = parsed.hooks;
936
+ const event = 'PreToolUse';
937
+ const existing = hooks[event];
938
+ if (existing !== undefined && !Array.isArray(existing)) {
939
+ throw new Error(`hooks.${event} is not an array`);
940
+ }
941
+ const entries = (existing ?? []);
942
+ const foreignCommand = (groupLabel, suffix = '') => `${JSON.stringify(process.execPath)} ${JSON.stringify(`${marker}-${event}-${groupLabel}${suffix}.js`)}`;
943
+ if (options.appendGroups) {
944
+ for (const groupLabel of ['first', 'second']) {
945
+ entries.push({
946
+ ...(groupLabel === 'first' ? { matcher: 'Bash' } : {}),
947
+ foreign_group_metadata: {
948
+ marker,
949
+ groupLabel,
950
+ nested: { keep: ['foreign', event, groupLabel], depth: { value: 7 } },
951
+ },
952
+ hooks: [{
953
+ type: 'command',
954
+ command: foreignCommand(groupLabel),
955
+ statusMessage: `${marker} ${event} ${groupLabel}`,
956
+ foreign_handler_metadata: { marker, event, groupLabel, values: [1, { two: true }] },
957
+ }],
958
+ });
959
+ }
960
+ }
961
+ else {
962
+ const groupLabel = 'second';
963
+ const group = entries.find((entry) => isRecord(entry)
964
+ && Array.isArray(entry.hooks)
965
+ && entry.hooks.some((hook) => isRecord(hook)
966
+ && hook.command === foreignCommand(groupLabel)));
967
+ if (!group || !Array.isArray(group.hooks)) {
968
+ throw new Error(`missing pre-seeded foreign ${event} ${groupLabel} group`);
969
+ }
970
+ for (const insertionLabel of ['one', 'two']) {
971
+ group.hooks.push({
972
+ type: 'command',
973
+ command: foreignCommand(groupLabel, `-${insertionLabel}-inserted`),
974
+ statusMessage: `${marker} inserted ${event} ${insertionLabel}`,
975
+ foreign_handler_metadata: { marker, inserted: true, insertionLabel, nested: { event } },
976
+ });
977
+ }
978
+ }
979
+ hooks[event] = entries;
980
+ return JSON.stringify(parsed, null, 2) + '\n';
981
+ }
982
+ export function foreignHookGroupSnapshot(hooksContent, marker) {
983
+ const parsed = JSON.parse(hooksContent);
984
+ if (!isRecord(parsed.hooks))
985
+ throw new Error('hooks.json is missing hooks object');
986
+ const result = [];
987
+ for (const [event, entries] of Object.entries(parsed.hooks)) {
988
+ if (!Array.isArray(entries))
989
+ continue;
990
+ entries.forEach((entry, groupIndex) => {
991
+ if (!isRecord(entry) || !Array.isArray(entry.hooks))
992
+ return;
993
+ entry.hooks.forEach((hook, handlerIndex) => {
994
+ if (isRecord(hook) && typeof hook.command === 'string' && hook.command.includes(marker)) {
995
+ result.push({ event, groupIndex, handlerIndex, group: structuredClone(entry), handler: structuredClone(hook) });
996
+ }
997
+ });
998
+ });
999
+ }
1000
+ return result;
1001
+ }
1002
+ export function assertForeignHookGroupsPreserved(expected, actualContent, marker) {
1003
+ const actual = foreignHookGroupSnapshot(actualContent, marker);
1004
+ if (JSON.stringify(actual) !== JSON.stringify(expected)) {
1005
+ throw new Error(`Foreign hook groups or metadata changed: ${JSON.stringify({ expected, actual })}`);
1006
+ }
1007
+ }
1008
+ export function probeCodexVersion(cwd, env, seam) {
1009
+ return resolvePinnedCodexExecutable(cwd, env, seam).version;
1010
+ }
1011
+ export const PACKED_INSTALL_PLUGIN_MCP_TARGETS = [
1012
+ ['omx_state', 'state', 'omx-state'],
1013
+ ['omx_memory', 'memory', 'omx-memory'],
1014
+ ['omx_code_intel', 'code-intel', 'omx-code-intel'],
1015
+ ['omx_trace', 'trace', 'omx-trace'],
1016
+ ['omx_wiki', 'wiki', 'omx-wiki'],
1017
+ ['omx_hermes', 'hermes', 'omx-hermes'],
1018
+ ];
1019
+ const PACKED_INSTALL_OPERATIONAL_PROBE_TIMEOUT_MS = 5_000;
1020
+ function buildPackedProbeEnv(overrides = {}) {
1021
+ const env = { ...process.env };
1022
+ for (const key of Object.keys(env)) {
1023
+ const upper = key.toUpperCase();
1024
+ if (upper === 'NODE_OPTIONS' || upper.startsWith('OMX_') || upper.startsWith('CODEX_')) {
1025
+ delete env[key];
1026
+ }
1027
+ }
1028
+ return { ...env, ...overrides };
1029
+ }
1030
+ export const PACKED_INSTALL_REQUIRED_ARTIFACT_PATHS = [
1031
+ 'dist/config/models.js',
1032
+ 'dist/config/models.d.ts',
1033
+ 'dist/agents/definitions.js',
1034
+ 'dist/agents/definitions.d.ts',
1035
+ 'dist/agents/native-config.js',
1036
+ 'dist/agents/native-config.d.ts',
1037
+ 'dist/team/model-contract.js',
1038
+ 'dist/team/model-contract.d.ts',
1039
+ 'dist/cli/index.js',
1040
+ 'dist/cli/index.d.ts',
1041
+ 'dist/cli/omx.js',
1042
+ 'dist/cli/omx.d.ts',
1043
+ 'skills/team/SKILL.md',
1044
+ 'plugins/oh-my-codex/skills/team/SKILL.md',
1045
+ 'plugins/oh-my-codex/.codex-plugin/plugin.json',
1046
+ 'plugins/oh-my-codex/.mcp.json',
1047
+ 'plugins/oh-my-codex/.app.json',
1048
+ 'plugins/oh-my-codex/hooks/hooks.json',
1049
+ 'plugins/oh-my-codex/hooks/codex-native-hook.mjs',
1050
+ ];
1051
+ export const PACKED_INSTALL_FORBIDDEN_ARTIFACT_PATHS = [
1052
+ '.gjc/',
1053
+ 'docs/',
1054
+ '.omx/',
1055
+ ];
1056
+ function normalizedPackPath(path) {
1057
+ return path.replaceAll('\\', '/').replace(/^\.\//, '');
1058
+ }
1059
+ export function isForbiddenPackedInstallArtifact(path) {
1060
+ const normalized = normalizedPackPath(path);
1061
+ return PACKED_INSTALL_FORBIDDEN_ARTIFACT_PATHS.some((prefix) => normalized.startsWith(prefix))
1062
+ || normalized.endsWith('.tgz')
1063
+ || normalized.endsWith('.tmp')
1064
+ || normalized.endsWith('.temp')
1065
+ || /(^|\/)(?:tmp|temp)\//.test(normalized);
1066
+ }
1067
+ export function assertPackedInstallFileMetadata(files) {
1068
+ const paths = new Set(files.map((file) => normalizedPackPath(file.path)));
1069
+ for (const requiredPath of PACKED_INSTALL_REQUIRED_ARTIFACT_PATHS) {
1070
+ if (!paths.has(requiredPath)) {
1071
+ throw new Error(`npm pack is missing required artifact: ${requiredPath}`);
1072
+ }
1073
+ }
1074
+ for (const path of paths) {
1075
+ if (isForbiddenPackedInstallArtifact(path)) {
1076
+ throw new Error(`npm pack includes forbidden workspace artifact: ${path}`);
1077
+ }
1078
+ }
1079
+ }
1080
+ function assertTextIncludes(text, expected, surface) {
1081
+ if (!text.includes(expected)) {
1082
+ throw new Error(`${surface} is missing required text: ${expected}`);
1083
+ }
1084
+ }
1085
+ function assertTextMatches(text, pattern, surface) {
1086
+ if (!pattern.test(text)) {
1087
+ throw new Error(`${surface} does not satisfy required pattern: ${pattern}`);
1088
+ }
1089
+ }
1090
+ export function assertInstalledRootReasoningHelp(help) {
1091
+ assertTextIncludes(help, 'Usage: omx reasoning <low|medium|high|xhigh>', 'installed omx help');
1092
+ for (const unsupportedOption of ['--max', '--ultra']) {
1093
+ if (help.includes(unsupportedOption)) {
1094
+ throw new Error(`installed omx help must not advertise ${unsupportedOption}`);
1095
+ }
1096
+ }
1097
+ }
1098
+ export function assertInstalledRootReasoningRejection(mode, result, configBefore, configAfter) {
1099
+ if (result.status !== 1) {
1100
+ throw new Error(`omx reasoning ${mode} must exit 1, received ${String(result.status)}`);
1101
+ }
1102
+ if (String(result.stdout ?? '').trim() !== '') {
1103
+ throw new Error(`omx reasoning ${mode} must not emit success stdout`);
1104
+ }
1105
+ if (configBefore !== configAfter) {
1106
+ throw new Error(`omx reasoning ${mode} must not create or mutate config.toml`);
1107
+ }
1108
+ const stderr = String(result.stderr ?? '');
1109
+ const fragments = mode === 'max'
1110
+ ? [
1111
+ 'Reasoning mode "max" is not supported by "omx reasoning".',
1112
+ 'Per-agent "max" is configured with agentReasoning; direct -c model_reasoning_effort=... is passed to Codex and remains capability-dependent.',
1113
+ 'Invalid reasoning mode "max". Expected one of: low, medium, high, xhigh.',
1114
+ 'Usage: omx reasoning <low|medium|high|xhigh>',
1115
+ ]
1116
+ : [
1117
+ 'Reasoning mode "ultra" is not supported by OMX root or per-agent reasoning and is not an alias for "max".',
1118
+ 'Direct -c model_reasoning_effort=... remains opaque Codex passthrough.',
1119
+ 'Invalid reasoning mode "ultra". Expected one of: low, medium, high, xhigh.',
1120
+ 'Usage: omx reasoning <low|medium|high|xhigh>',
1121
+ ];
1122
+ for (const fragment of fragments) {
1123
+ assertTextIncludes(stderr, fragment, `omx reasoning ${mode} stderr`);
1124
+ }
1125
+ }
1126
+ export function assertInstalledReasoningRuntimeContract(models) {
1127
+ const expected = ['low', 'medium', 'high', 'xhigh'];
1128
+ const equalTuple = (name, actual, values) => {
1129
+ if (!Array.isArray(actual) || actual.length !== values.length || actual.some((value, index) => value !== values[index])) {
1130
+ throw new Error(`${name} must be exactly ${JSON.stringify(values)}`);
1131
+ }
1132
+ };
1133
+ equalTuple('CANONICAL_REASONING_EFFORTS', models.CANONICAL_REASONING_EFFORTS, expected);
1134
+ equalTuple('AMBIGUOUS_UNSUPPORTED_REASONING_EFFORTS', models.AMBIGUOUS_UNSUPPORTED_REASONING_EFFORTS, ['max', 'ultra']);
1135
+ equalTuple('PER_AGENT_REASONING_EFFORTS', models.PER_AGENT_REASONING_EFFORTS, [...expected, 'max']);
1136
+ equalTuple('ROOT_REASONING_EFFORTS', models.ROOT_REASONING_EFFORTS, expected);
1137
+ equalTuple('ROOT_UNSUPPORTED_REASONING_EFFORTS', models.ROOT_UNSUPPORTED_REASONING_EFFORTS, ['max', 'ultra']);
1138
+ const isLegacyUnsupported = models.isAmbiguousUnsupportedReasoningEffort;
1139
+ const isRootUnsupported = models.isUnsupportedRootReasoningEffort;
1140
+ const normalizeRootUnsupported = models.normalizeUnsupportedRootReasoningEffort;
1141
+ if (typeof isLegacyUnsupported !== 'function' || isLegacyUnsupported('MAX') !== true) {
1142
+ throw new Error('legacy unsupported reasoning helper must remain case-insensitive');
1143
+ }
1144
+ if (typeof isRootUnsupported !== 'function'
1145
+ || isRootUnsupported('MAX') !== true
1146
+ || isRootUnsupported('ultra') !== true
1147
+ || isRootUnsupported('xhigh') !== false) {
1148
+ throw new Error('root unsupported reasoning helper must be a case-insensitive max/ultra classifier');
1149
+ }
1150
+ if (typeof normalizeRootUnsupported !== 'function'
1151
+ || normalizeRootUnsupported(' MAX ') !== 'max'
1152
+ || normalizeRootUnsupported('Ultra') !== 'ultra'
1153
+ || normalizeRootUnsupported('xhigh') !== undefined) {
1154
+ throw new Error('root unsupported reasoning normalizer must canonicalize only max and ultra');
1155
+ }
1156
+ }
1157
+ export function assertInstalledReasoningDeclarationContract(declarations) {
1158
+ const { models, definitions, nativeConfig, team } = declarations;
1159
+ const tuple = (values) => values.map((value) => `["']${value}["']`).join('\\s*,\\s*');
1160
+ assertTextMatches(models, new RegExp(`export\\s+declare\\s+const\\s+CANONICAL_REASONING_EFFORTS\\s*:\\s*readonly\\s*\\[\\s*${tuple(['low', 'medium', 'high', 'xhigh'])}\\s*\\]\\s*;`), 'models declaration');
1161
+ assertTextIncludes(models, 'export type ConfiguredAgentReasoningEffort = (typeof CANONICAL_REASONING_EFFORTS)[number];', 'models declaration');
1162
+ assertTextMatches(models, new RegExp(`export\\s+declare\\s+const\\s+AMBIGUOUS_UNSUPPORTED_REASONING_EFFORTS\\s*:\\s*readonly\\s*\\[\\s*${tuple(['max', 'ultra'])}\\s*\\]\\s*;`), 'models declaration');
1163
+ assertTextMatches(models, new RegExp(`export\\s+declare\\s+const\\s+PER_AGENT_REASONING_EFFORTS\\s*:\\s*readonly\\s*\\[\\s*${tuple(['low', 'medium', 'high', 'xhigh', 'max'])}\\s*\\]\\s*;`), 'models declaration');
1164
+ assertTextIncludes(models, 'export type PerAgentReasoningEffort = (typeof PER_AGENT_REASONING_EFFORTS)[number];', 'models declaration');
1165
+ assertTextMatches(models, new RegExp(`export\\s+declare\\s+const\\s+ROOT_REASONING_EFFORTS\\s*:\\s*readonly\\s*\\[\\s*${tuple(['low', 'medium', 'high', 'xhigh'])}\\s*\\]\\s*;`), 'models declaration');
1166
+ assertTextIncludes(models, 'export type RootReasoningEffort = (typeof ROOT_REASONING_EFFORTS)[number];', 'models declaration');
1167
+ assertTextMatches(models, new RegExp(`export\\s+declare\\s+const\\s+ROOT_UNSUPPORTED_REASONING_EFFORTS\\s*:\\s*readonly\\s*\\[\\s*${tuple(['max', 'ultra'])}\\s*\\]\\s*;`), 'models declaration');
1168
+ if (/isUnsupportedRootReasoningEffort\(value: string\): value is/.test(models)) {
1169
+ throw new Error('isUnsupportedRootReasoningEffort must be declared as a plain boolean, not a type predicate');
1170
+ }
1171
+ assertTextIncludes(models, 'export declare function isUnsupportedRootReasoningEffort(value: string): boolean;', 'models declaration');
1172
+ assertTextIncludes(models, 'export declare function normalizeUnsupportedRootReasoningEffort(value: string): RootUnsupportedReasoningEffort | undefined;', 'models declaration');
1173
+ const definitionMatch = /export interface AgentDefinition\s*\{([\s\S]*?)\n\}/.exec(definitions);
1174
+ if (!definitionMatch)
1175
+ throw new Error('AgentDefinition declaration is missing');
1176
+ const reasoningField = /\breasoningEffort(\?)?:\s*([^;]+);/.exec(definitionMatch[1]);
1177
+ if (!reasoningField || reasoningField[1] === '?') {
1178
+ throw new Error('AgentDefinition.reasoningEffort must remain required');
1179
+ }
1180
+ if (reasoningField[2] !== "'low' | 'medium' | 'high' | 'xhigh'") {
1181
+ throw new Error(`AgentDefinition.reasoningEffort widened unexpectedly: ${reasoningField[2] ?? 'missing'}`);
1182
+ }
1183
+ if (/\b(?:undefined|max|ultra)\b/.test(reasoningField[2])) {
1184
+ throw new Error('AgentDefinition.reasoningEffort must not allow undefined, max, or ultra');
1185
+ }
1186
+ assertTextIncludes(nativeConfig, 'reasoningEffort?: PerAgentReasoningEffort;', 'native config declaration');
1187
+ assertTextIncludes(team, 'export type TeamReasoningEffort = PerAgentReasoningEffort;', 'Team declaration');
1188
+ if (/\bultra\b/.test(team)) {
1189
+ throw new Error('Team declaration must not recognize ultra');
1190
+ }
1191
+ }
1192
+ export function assertInstalledTeamSkillContract(canonical, pluginMirror) {
1193
+ if (!canonical.equals(pluginMirror)) {
1194
+ throw new Error('canonical and plugin Team skills must be byte-identical');
1195
+ }
1196
+ const skill = canonical.toString('utf-8');
1197
+ assertTextMatches(skill, /agentReasoning[\s\S]{0,240}`low`, `medium`, `high`, `xhigh`,(?: and)? `max`/, 'Team skill');
1198
+ assertTextMatches(skill, /`max`[\s\S]{0,240}passed[\s\S]{0,80}unchanged/i, 'Team skill');
1199
+ assertTextMatches(skill, /`max`[\s\S]{0,240}capability-dependent/i, 'Team skill');
1200
+ assertTextMatches(skill, /`ultra`[\s\S]{0,180}unsupported[\s\S]{0,180}not an alias for[\s\S]{0,100}`max`/i, 'Team skill');
1201
+ assertTextMatches(skill, /invalid configured values[\s\S]{0,180}built-in role-default fallback/i, 'Team skill');
1202
+ assertTextMatches(skill, /explicit raw `-c model_reasoning_effort=\.\.\.`[\s\S]{0,240}opaque[\s\S]{0,240}wins/i, 'Team skill');
1203
+ assertTextMatches(skill, /both sources[\s\S]{0,160}explicit raw reasoning[\s\S]{0,160}inherited Team reasoning[\s\S]{0,160}environment reasoning/i, 'Team skill');
1204
+ assertTextMatches(skill, /do not downgrade or retry `max` as `xhigh`[\s\S]{0,160}built-in role defaults remain unchanged/i, 'Team skill');
1205
+ }
1206
+ function isJsonRecord(value) {
1207
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
1208
+ }
1209
+ function readInstalledPluginManifest(packageRoot, relativePath) {
1210
+ const path = join(packageRoot, relativePath);
1211
+ if (!existsSync(path))
1212
+ throw new Error(`installed plugin manifest is missing: ${relativePath}`);
1213
+ let manifest;
1214
+ try {
1215
+ manifest = JSON.parse(readFileSync(path, 'utf-8'));
1216
+ }
1217
+ catch (error) {
1218
+ throw new Error(`installed plugin manifest is not parseable JSON: ${relativePath}: ${error instanceof Error ? error.message : String(error)}`);
1219
+ }
1220
+ if (!isJsonRecord(manifest)) {
1221
+ throw new Error(`installed plugin manifest must be a JSON object: ${relativePath}`);
1222
+ }
1223
+ return manifest;
1224
+ }
1225
+ function assertInstalledPluginString(value, path, expected) {
1226
+ if (typeof value !== 'string' || value.trim() === '' || (expected !== undefined && value !== expected)) {
1227
+ throw new Error(`installed plugin manifest field is invalid: ${path}`);
1228
+ }
1229
+ }
1230
+ function assertInstalledPluginHookLauncherContract(hookSource) {
1231
+ const requiredShape = [
1232
+ [
1233
+ /['"]omx-command\.json['"]/,
1234
+ 'read its pinned omx-command.json delegate configuration',
1235
+ ],
1236
+ [
1237
+ /\bspawn\s*\(\s*command\s*,\s*\[\s*\.\.\.\s*argsPrefix\s*,\s*['"]codex-native-hook['"]\s*\]/s,
1238
+ 'spawn the configured delegate with the codex-native-hook argument',
1239
+ ],
1240
+ [
1241
+ /\.stdin\s*\.end\s*\(\s*input\s*\)/,
1242
+ 'forward the hook input to the delegate stdin',
1243
+ ],
1244
+ ];
1245
+ for (const [pattern, requirement] of requiredShape) {
1246
+ if (!pattern.test(hookSource)) {
1247
+ throw new Error(`installed plugin native hook launcher must ${requirement}`);
1248
+ }
1249
+ }
1250
+ }
1251
+ export function assertInstalledPluginSurface(packageRoot) {
1252
+ const pluginManifest = readInstalledPluginManifest(packageRoot, 'plugins/oh-my-codex/.codex-plugin/plugin.json');
1253
+ assertInstalledPluginString(pluginManifest.name, 'plugin.json.name', 'oh-my-codex');
1254
+ assertInstalledPluginString(pluginManifest.version, 'plugin.json.version');
1255
+ assertInstalledPluginString(pluginManifest.skills, 'plugin.json.skills', './skills/');
1256
+ assertInstalledPluginString(pluginManifest.mcpServers, 'plugin.json.mcpServers', './.mcp.json');
1257
+ assertInstalledPluginString(pluginManifest.apps, 'plugin.json.apps', './.app.json');
1258
+ assertInstalledPluginString(pluginManifest.hooks, 'plugin.json.hooks', './hooks/hooks.json');
1259
+ if (!isJsonRecord(pluginManifest.interface)) {
1260
+ throw new Error('installed plugin manifest field is invalid: plugin.json.interface');
1261
+ }
1262
+ for (const key of ['displayName', 'shortDescription', 'longDescription', 'developerName', 'category']) {
1263
+ assertInstalledPluginString(pluginManifest.interface[key], `plugin.json.interface.${key}`);
1264
+ }
1265
+ const mcpManifest = readInstalledPluginManifest(packageRoot, 'plugins/oh-my-codex/.mcp.json');
1266
+ if (!isJsonRecord(mcpManifest.mcpServers)) {
1267
+ throw new Error('installed plugin manifest field is invalid: .mcp.json.mcpServers');
1268
+ }
1269
+ const mcpServers = mcpManifest.mcpServers;
1270
+ for (const [serverName, server] of Object.entries(mcpServers)) {
1271
+ if (!isJsonRecord(server)
1272
+ || typeof server.command !== 'string'
1273
+ || server.command.trim() === ''
1274
+ || !Array.isArray(server.args)
1275
+ || server.args.some((arg) => typeof arg !== 'string')
1276
+ || typeof server.enabled !== 'boolean') {
1277
+ throw new Error(`installed MCP server is invalid: ${serverName}`);
1278
+ }
1279
+ }
1280
+ for (const [serverName, service] of PACKED_INSTALL_PLUGIN_MCP_TARGETS) {
1281
+ const server = mcpServers[serverName];
1282
+ if (!isJsonRecord(server)
1283
+ || server.command !== 'omx'
1284
+ || !Array.isArray(server.args)
1285
+ || server.args.length !== 2
1286
+ || server.args[0] !== 'mcp-serve'
1287
+ || server.args[1] !== service
1288
+ || server.enabled !== false) {
1289
+ throw new Error(`installed MCP server does not match the packaged contract: ${serverName}`);
1290
+ }
1291
+ }
1292
+ const appManifest = readInstalledPluginManifest(packageRoot, 'plugins/oh-my-codex/.app.json');
1293
+ if (!isJsonRecord(appManifest.apps)) {
1294
+ throw new Error('installed plugin manifest field is invalid: .app.json.apps');
1295
+ }
1296
+ const hooksManifest = readInstalledPluginManifest(packageRoot, 'plugins/oh-my-codex/hooks/hooks.json');
1297
+ if (!isJsonRecord(hooksManifest.hooks)) {
1298
+ throw new Error('installed plugin manifest field is invalid: hooks.json.hooks');
1299
+ }
1300
+ const expectedHookCommand = 'node "${PLUGIN_ROOT}/hooks/codex-native-hook.mjs"';
1301
+ for (const eventName of PACKED_INSTALL_NATIVE_HOOK_SMOKE_EVENTS) {
1302
+ const entries = hooksManifest.hooks[eventName];
1303
+ if (!Array.isArray(entries) || entries.length === 0) {
1304
+ throw new Error(`installed plugin hook event is missing: ${eventName}`);
1305
+ }
1306
+ for (const entry of entries) {
1307
+ if (!isJsonRecord(entry) || !Array.isArray(entry.hooks) || entry.hooks.length === 0) {
1308
+ throw new Error(`installed plugin hook entry is invalid: ${eventName}`);
1309
+ }
1310
+ for (const hook of entry.hooks) {
1311
+ if (!isJsonRecord(hook) || hook.type !== 'command' || hook.command !== expectedHookCommand) {
1312
+ throw new Error(`installed plugin hook command must target the native launcher: ${eventName}`);
1313
+ }
1314
+ }
1315
+ }
1316
+ }
1317
+ const hookPath = join(packageRoot, 'plugins/oh-my-codex/hooks/codex-native-hook.mjs');
1318
+ if (!existsSync(hookPath)) {
1319
+ throw new Error('installed plugin native hook launcher is missing');
1320
+ }
1321
+ const hookSource = readFileSync(hookPath, 'utf-8');
1322
+ if (!hookSource.startsWith('#!/usr/bin/env node')) {
1323
+ throw new Error('installed plugin native hook launcher must start with a Node shebang');
1324
+ }
1325
+ if (!/\bconst\s+OMX_PLUGIN_HOOK_LAUNCHER_CONTRACT_MARKER\s*=\s*['"]omx-plugin-hook-launcher:v1['"]/.test(hookSource)) {
1326
+ throw new Error('installed plugin native hook launcher is missing its stable contract marker');
1327
+ }
1328
+ assertInstalledPluginHookLauncherContract(hookSource);
1329
+ const syntaxCheck = spawnSync(process.execPath, ['--check', hookPath], { encoding: 'utf-8' });
1330
+ if (syntaxCheck.error || syntaxCheck.status !== 0) {
1331
+ const detail = String(syntaxCheck.stderr || syntaxCheck.error?.message || 'unknown syntax error').trim();
1332
+ throw new Error(`installed plugin native hook launcher fails Node syntax check: ${detail}`);
1333
+ }
1334
+ }
1335
+ function assertInstalledRequiredArtifacts(packageRoot) {
1336
+ for (const relativePath of PACKED_INSTALL_REQUIRED_ARTIFACT_PATHS) {
1337
+ if (!existsSync(join(packageRoot, relativePath))) {
1338
+ throw new Error(`installed package is missing required artifact: ${relativePath}`);
1339
+ }
1340
+ }
1341
+ }
1342
+ async function assertInstalledReasoningArtifacts(packageRoot) {
1343
+ const models = await import(pathToFileURL(join(packageRoot, 'dist/config/models.js')).href);
1344
+ const nativeConfig = await import(pathToFileURL(join(packageRoot, 'dist/agents/native-config.js')).href);
1345
+ const definitions = await import(pathToFileURL(join(packageRoot, 'dist/agents/definitions.js')).href);
1346
+ const team = await import(pathToFileURL(join(packageRoot, 'dist/team/model-contract.js')).href);
1347
+ assertInstalledReasoningRuntimeContract(models);
1348
+ assertInstalledReasoningDeclarationContract({
1349
+ models: readFileSync(join(packageRoot, 'dist/config/models.d.ts'), 'utf-8'),
1350
+ definitions: readFileSync(join(packageRoot, 'dist/agents/definitions.d.ts'), 'utf-8'),
1351
+ nativeConfig: readFileSync(join(packageRoot, 'dist/agents/native-config.d.ts'), 'utf-8'),
1352
+ team: readFileSync(join(packageRoot, 'dist/team/model-contract.d.ts'), 'utf-8'),
1353
+ });
1354
+ const codexHome = mkdtempSync(join(tmpdir(), 'omx-packed-max-native-'));
1355
+ try {
1356
+ writeFileSync(join(codexHome, '.omx-config.json'), JSON.stringify({
1357
+ agentReasoning: { architect: ' MAX ' },
1358
+ }));
1359
+ const generateAgentToml = nativeConfig.generateAgentToml;
1360
+ const agentDefinitions = definitions.AGENT_DEFINITIONS;
1361
+ const maxToml = generateAgentToml(agentDefinitions.architect, 'installed native max contract', { codexHomeOverride: codexHome });
1362
+ assertTextIncludes(maxToml, 'model_reasoning_effort = "max"', 'installed native TOML');
1363
+ if (maxToml.includes('model_reasoning_effort = "xhigh"')) {
1364
+ throw new Error('installed native TOML must not downgrade max to xhigh');
1365
+ }
1366
+ if (parseToml(maxToml).model_reasoning_effort !== 'max') {
1367
+ throw new Error('installed native TOML must parse back to exact max');
1368
+ }
1369
+ }
1370
+ finally {
1371
+ rmSync(codexHome, { recursive: true, force: true });
1372
+ }
1373
+ const resolveTeamWorkerLaunchArgs = team.resolveTeamWorkerLaunchArgs;
1374
+ const resolveTeamWorkerLaunchDiagnostics = team.resolveTeamWorkerLaunchDiagnostics;
1375
+ const maxArgs = resolveTeamWorkerLaunchArgs({ preferredReasoning: 'max' });
1376
+ if (maxArgs.join('\u0000') !== ['-c', 'model_reasoning_effort="max"'].join('\u0000')) {
1377
+ throw new Error(`Team must transport configured max exactly, received ${JSON.stringify(maxArgs)}`);
1378
+ }
1379
+ if (maxArgs.includes('model_reasoning_effort="xhigh"')) {
1380
+ throw new Error('Team must not downgrade max to xhigh');
1381
+ }
1382
+ const opaqueUltra = resolveTeamWorkerLaunchDiagnostics({
1383
+ existingRaw: '-c model_reasoning_effort="ultra"',
1384
+ preferredReasoning: 'max',
1385
+ });
1386
+ if (opaqueUltra.reasoningSource !== 'explicit' || opaqueUltra.actualReasoning !== undefined) {
1387
+ throw new Error('Team must preserve opaque explicit ultra without recognizing it');
1388
+ }
1389
+ assertInstalledTeamSkillContract(readFileSync(join(packageRoot, 'skills/team/SKILL.md')), readFileSync(join(packageRoot, 'plugins/oh-my-codex/skills/team/SKILL.md')));
1390
+ assertInstalledPluginSurface(packageRoot);
1391
+ }
1392
+ function smokeInstalledRootReasoningRejections(omxPath, cwd) {
1393
+ const codexHome = mkdtempSync(join(tmpdir(), 'omx-packed-root-reasoning-'));
1394
+ try {
1395
+ const env = buildPackedProbeEnv({ CODEX_HOME: codexHome });
1396
+ const configPath = join(codexHome, 'config.toml');
1397
+ const usageResult = spawnSync(omxPath, ['reasoning'], { cwd, encoding: 'utf-8', env });
1398
+ if (usageResult.status !== 0) {
1399
+ throw new Error(`omx reasoning usage must exit 0, received ${String(usageResult.status)}`);
1400
+ }
1401
+ assertInstalledRootReasoningHelp(String(usageResult.stdout ?? ''));
1402
+ const maxResult = spawnSync(omxPath, ['reasoning', 'max'], { cwd, encoding: 'utf-8', env });
1403
+ assertInstalledRootReasoningRejection('max', maxResult, undefined, existsSync(configPath) ? readFileSync(configPath, 'utf-8') : undefined);
1404
+ const originalConfig = 'model = "unchanged-root-model"\n';
1405
+ writeFileSync(configPath, originalConfig);
1406
+ const ultraResult = spawnSync(omxPath, ['reasoning', 'ultra'], { cwd, encoding: 'utf-8', env });
1407
+ assertInstalledRootReasoningRejection('ultra', ultraResult, originalConfig, readFileSync(configPath, 'utf-8'));
1408
+ }
1409
+ finally {
1410
+ rmSync(codexHome, { recursive: true, force: true });
1411
+ }
1412
+ }
1413
+ export function parseFakeCodexLaunches(capture) {
1414
+ const trimmed = capture.trim();
1415
+ if (!trimmed)
1416
+ return [];
1417
+ return trimmed.split('\n').map((line) => {
1418
+ const launch = JSON.parse(line);
1419
+ if (!isJsonRecord(launch)
1420
+ || !Array.isArray(launch.argv)
1421
+ || launch.argv.some((arg) => typeof arg !== 'string')
1422
+ || typeof launch.cwd !== 'string'
1423
+ || typeof launch.hasResumeSqlite !== 'boolean'
1424
+ || (launch.OMX_NOTIFY_TEMP_CONTRACT !== null && typeof launch.OMX_NOTIFY_TEMP_CONTRACT !== 'string')
1425
+ || (launch.OMX_TEAM_WORKER_LAUNCH_ARGS !== null && typeof launch.OMX_TEAM_WORKER_LAUNCH_ARGS !== 'string')) {
1426
+ throw new Error('fake Codex capture did not contain a valid launch record');
1427
+ }
1428
+ return {
1429
+ argv: launch.argv,
1430
+ cwd: launch.cwd,
1431
+ hasResumeSqlite: launch.hasResumeSqlite,
1432
+ OMX_NOTIFY_TEMP_CONTRACT: launch.OMX_NOTIFY_TEMP_CONTRACT,
1433
+ OMX_TEAM_WORKER_LAUNCH_ARGS: launch.OMX_TEAM_WORKER_LAUNCH_ARGS,
1434
+ };
1435
+ });
1436
+ }
1437
+ function readFakeCodexLaunches(capturePath) {
1438
+ if (!existsSync(capturePath))
1439
+ return [];
1440
+ return parseFakeCodexLaunches(readFileSync(capturePath, 'utf-8'));
1441
+ }
1442
+ function smokeInstalledLaunchArgumentBoundary(omxPath) {
1443
+ const smokeRoot = mkdtempSync(join(tmpdir(), 'omx-packed-launch-boundary-'));
1444
+ const modelInstructionsPath = join(smokeRoot, 'model-instructions.md');
1445
+ try {
1446
+ const fakeBinDir = join(smokeRoot, 'bin');
1447
+ const launchCwd = join(smokeRoot, 'cwd');
1448
+ const codexHome = join(smokeRoot, 'codex-home');
1449
+ const isolatedHome = join(smokeRoot, 'home');
1450
+ const capturePath = join(smokeRoot, 'fake-codex-argv.jsonl');
1451
+ mkdirSync(fakeBinDir, { recursive: true });
1452
+ mkdirSync(launchCwd, { recursive: true });
1453
+ mkdirSync(codexHome, { recursive: true });
1454
+ mkdirSync(isolatedHome, { recursive: true });
1455
+ const fakeCodexSource = [
1456
+ 'const { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } = require("node:fs");',
1457
+ 'const capturePath = process.env.OMX_PACKED_FAKE_CODEX_CAPTURE_PATH;',
1458
+ 'if (!capturePath) process.exit(2);',
1459
+ 'const priorLaunchCount = existsSync(capturePath) ? readFileSync(capturePath, "utf8").trim().split("\\n").filter(Boolean).length : 0;',
1460
+ 'appendFileSync(capturePath, `${JSON.stringify({',
1461
+ ' argv: process.argv.slice(2),',
1462
+ ' cwd: process.cwd(),',
1463
+ ' OMX_NOTIFY_TEMP_CONTRACT: process.env.OMX_NOTIFY_TEMP_CONTRACT ?? null,',
1464
+ ' hasResumeSqlite: existsSync(require("node:path").join(process.env.CODEX_HOME ?? "", "state_5.sqlite")),',
1465
+ ' OMX_TEAM_WORKER_LAUNCH_ARGS: process.env.OMX_TEAM_WORKER_LAUNCH_ARGS ?? null,',
1466
+ '})}\\n`);',
1467
+ 'if (process.env.OMX_PACKED_FAKE_CODEX_QUOTA_ON_FIRST === "1" && priorLaunchCount === 0) {',
1468
+ ' const sessionDir = require("node:path").join(process.env.CODEX_HOME ?? "", "sessions", "2026", "07", "13");',
1469
+ ' mkdirSync(sessionDir, { recursive: true });',
1470
+ ' writeFileSync(require("node:path").join(sessionDir, "rollout-packed-session-123.jsonl"), "{}\\n");',
1471
+ ' process.stderr.write("HTTP 429 quota exceeded\\n");',
1472
+ ' process.exit(1);',
1473
+ '}',
1474
+ ].join('\n');
1475
+ if (process.platform === 'win32') {
1476
+ const fakeCodexScript = join(fakeBinDir, 'codex.cjs');
1477
+ writeFileSync(fakeCodexScript, fakeCodexSource);
1478
+ writeFileSync(join(fakeBinDir, 'codex.cmd'), [
1479
+ '@echo off',
1480
+ `"${process.execPath}" "${fakeCodexScript}" %*`,
1481
+ ].join('\r\n'));
1482
+ }
1483
+ else {
1484
+ const fakeCodexPath = join(fakeBinDir, 'codex');
1485
+ writeFileSync(fakeCodexPath, `#!/usr/bin/env node\n${fakeCodexSource}\n`);
1486
+ chmodSync(fakeCodexPath, 0o755);
1487
+ }
1488
+ const pathKey = Object.keys(process.env).find((key) => key.toUpperCase() === 'PATH') ?? 'PATH';
1489
+ const env = buildPackedProbeEnv({
1490
+ [pathKey]: `${fakeBinDir}${delimiter}${process.env[pathKey] ?? ''}`,
1491
+ CODEX_HOME: codexHome,
1492
+ HOME: isolatedHome,
1493
+ USERPROFILE: isolatedHome,
1494
+ OMX_BYPASS_DEFAULT_SYSTEM_PROMPT: '1',
1495
+ OMX_MODEL_INSTRUCTIONS_FILE: modelInstructionsPath,
1496
+ OMX_LAUNCH_POLICY: 'direct',
1497
+ OMX_PACKED_FAKE_CODEX_CAPTURE_PATH: capturePath,
1498
+ });
1499
+ delete env.OMX_NOTIFY_TEMP_CONTRACT;
1500
+ delete env.OMX_TEAM_WORKER_LAUNCH_ARGS;
1501
+ writeFileSync(modelInstructionsPath, '# Packed launch boundary instructions\n');
1502
+ for (const shorthand of ['--max', '--ultra']) {
1503
+ const result = spawnSync(omxPath, [shorthand], { cwd: launchCwd, encoding: 'utf-8', env });
1504
+ if (result.status !== 1) {
1505
+ throw new Error(`omx ${shorthand} must reject before launching Codex, received ${String(result.status)}`);
1506
+ }
1507
+ assertTextIncludes(String(result.stderr ?? ''), `Unsupported OMX launch shorthand "${shorthand}".`, `omx ${shorthand} stderr`);
1508
+ if (readFakeCodexLaunches(capturePath).length !== 0) {
1509
+ throw new Error(`omx ${shorthand} must not launch Codex before the end-of-options marker`);
1510
+ }
1511
+ }
1512
+ for (const expectedArgs of [
1513
+ ['--', '--max'],
1514
+ ['--', '--ultra'],
1515
+ ['--', '-c', 'model_reasoning_effort=opaque-packed-smoke'],
1516
+ ['--', '--worktree', 'post-marker-branch'],
1517
+ ['--', '--notify-temp'],
1518
+ ['--', '--discord'],
1519
+ ['--', '--spark'],
1520
+ ['--', '--hotswap'],
1521
+ ['--', 'resume', '--project', '--codex-home', 'literal-codex-home', '--version'],
1522
+ ]) {
1523
+ writeFileSync(capturePath, '');
1524
+ const result = spawnSync(omxPath, expectedArgs, {
1525
+ cwd: launchCwd,
1526
+ encoding: 'utf-8',
1527
+ env,
1528
+ timeout: PACKED_INSTALL_OPERATIONAL_PROBE_TIMEOUT_MS,
1529
+ killSignal: 'SIGKILL',
1530
+ });
1531
+ if (result.status !== 0) {
1532
+ throw new Error(formatCommandFailure(omxPath, expectedArgs, result));
1533
+ }
1534
+ const launches = readFakeCodexLaunches(capturePath);
1535
+ const launch = launches[0];
1536
+ const expectedCodexArgs = [
1537
+ '-c',
1538
+ `model_instructions_file="${escapeTomlString(modelInstructionsPath)}"`,
1539
+ ...expectedArgs,
1540
+ ];
1541
+ if (launches.length !== 1 || !launch) {
1542
+ throw new Error(`omx must launch exactly one fake Codex process: ${JSON.stringify(launches)}`);
1543
+ }
1544
+ assert.deepStrictEqual(launch.argv, expectedCodexArgs, 'omx must inject model instructions before -- and preserve exact post-marker argument boundaries');
1545
+ if (launch.cwd !== launchCwd) {
1546
+ throw new Error(`omx must not change cwd for post-marker arguments: expected ${launchCwd}, received ${launch.cwd}`);
1547
+ }
1548
+ if (launch.OMX_NOTIFY_TEMP_CONTRACT !== null) {
1549
+ throw new Error(`omx must not activate temporary notification routing for post-marker arguments: ${JSON.stringify(launch)}`);
1550
+ }
1551
+ if (launch.OMX_TEAM_WORKER_LAUNCH_ARGS !== null) {
1552
+ throw new Error(`omx must not enable spark worker routing for post-marker arguments: ${JSON.stringify(launch)}`);
1553
+ }
1554
+ }
1555
+ const projectOmxDir = join(launchCwd, '.omx');
1556
+ const projectCodexHome = join(launchCwd, '.codex');
1557
+ mkdirSync(projectOmxDir, { recursive: true });
1558
+ mkdirSync(projectCodexHome, { recursive: true });
1559
+ writeFileSync(join(projectOmxDir, 'setup-scope.json'), JSON.stringify({ scope: 'project' }));
1560
+ writeFileSync(join(projectCodexHome, 'state_5.sqlite'), 'resume-only-sentinel');
1561
+ const imageResumeEnv = { ...env };
1562
+ delete imageResumeEnv.CODEX_HOME;
1563
+ const imageResumeCases = [
1564
+ { args: ['-i', 'resume'], resumes: false },
1565
+ { args: ['--image', 'resume'], resumes: false },
1566
+ { args: ['--image=resume'], resumes: false },
1567
+ { args: ['-iresume'], resumes: false },
1568
+ { args: ['-i=resume'], resumes: false },
1569
+ { args: ['-i', 'screenshot.png', 'resume'], resumes: false },
1570
+ { args: ['--image', 'screenshot.png', 'resume'], resumes: false },
1571
+ { args: ['--image=screenshot.png', 'resume'], resumes: true },
1572
+ { args: ['-iscreenshot.png', 'resume'], resumes: true },
1573
+ { args: ['-i=screenshot.png', 'resume'], resumes: true },
1574
+ { args: ['-i', 'one.png', '-i', 'two.png', 'resume'], resumes: false },
1575
+ { args: ['--image', 'one.png', '--image', 'two.png', 'resume'], resumes: false },
1576
+ { args: ['-i', 'one.png,two.png', 'resume'], resumes: false },
1577
+ { args: ['--image', 'one.png,two.png', 'resume'], resumes: false },
1578
+ { args: ['--image', 'one.png', '--model', 'gpt-review', 'resume'], resumes: true },
1579
+ { args: ['--image=one.png', '--model=gpt-review', 'resume'], resumes: true },
1580
+ ];
1581
+ for (const testCase of imageResumeCases) {
1582
+ writeFileSync(capturePath, '');
1583
+ const result = spawnSync(omxPath, ['--direct', ...testCase.args], {
1584
+ cwd: launchCwd,
1585
+ encoding: 'utf-8',
1586
+ env: imageResumeEnv,
1587
+ timeout: PACKED_INSTALL_OPERATIONAL_PROBE_TIMEOUT_MS,
1588
+ killSignal: 'SIGKILL',
1589
+ });
1590
+ assertBoundedProbeExit(`installed image resume arity ${JSON.stringify(testCase.args)}`, result, 0);
1591
+ const launches = readFakeCodexLaunches(capturePath);
1592
+ const launch = launches[0];
1593
+ if (launches.length !== 1 || !launch) {
1594
+ throw new Error(`installed image resume arity must invoke fake Codex exactly once: ${JSON.stringify(launches)}`);
1595
+ }
1596
+ const modelInstructionsArgs = ['-c', `model_instructions_file="${escapeTomlString(modelInstructionsPath)}"`];
1597
+ assert.deepStrictEqual(launch.argv, [...testCase.args, ...modelInstructionsArgs], 'installed image resume arity must preserve exact Codex argv');
1598
+ if (launch.hasResumeSqlite !== testCase.resumes) {
1599
+ throw new Error(`installed image resume arity mismatch for ${JSON.stringify(testCase.args)}: ${JSON.stringify(launch)}`);
1600
+ }
1601
+ }
1602
+ const packedAuthDir = join(isolatedHome, '.omx', 'auth');
1603
+ mkdirSync(packedAuthDir, { recursive: true });
1604
+ writeFileSync(join(packedAuthDir, 'first.json'), '{"access_token":"first-secret"}\n');
1605
+ writeFileSync(join(packedAuthDir, 'second.json'), '{"access_token":"second-secret"}\n');
1606
+ writeFileSync(join(packedAuthDir, 'slots.json'), JSON.stringify({
1607
+ version: 1,
1608
+ currentSlot: 'first',
1609
+ slots: [
1610
+ { slot: 'first', createdAt: 'now', updatedAt: 'now' },
1611
+ { slot: 'second', createdAt: 'now', updatedAt: 'now' },
1612
+ ],
1613
+ }, null, 2));
1614
+ writeFileSync(capturePath, '');
1615
+ const quotaSelectors = ['--last', '--all', '--include-non-interactive'];
1616
+ const quotaSuffix = ['--', ...quotaSelectors, '--model', 'opaque-model', 'opaque suffix'];
1617
+ const quotaResult = spawnSync(omxPath, [
1618
+ '--hotswap', '--direct', 'resume', ...quotaSelectors,
1619
+ '--model', 'gpt-review', '--remote', 'ws://127.0.0.1:4500', ...quotaSuffix,
1620
+ ], {
1621
+ cwd: launchCwd,
1622
+ encoding: 'utf-8',
1623
+ env: { ...env, OMX_PACKED_FAKE_CODEX_QUOTA_ON_FIRST: '1' },
1624
+ timeout: PACKED_INSTALL_OPERATIONAL_PROBE_TIMEOUT_MS,
1625
+ killSignal: 'SIGKILL',
1626
+ });
1627
+ assertBoundedProbeExit('installed quota hotswap explicit-session retry', quotaResult, 0);
1628
+ const quotaLaunches = readFakeCodexLaunches(capturePath);
1629
+ const generatedInstructions = ['-c', `model_instructions_file="${escapeTomlString(modelInstructionsPath)}"`];
1630
+ assert.deepStrictEqual(quotaLaunches.map((launch) => launch.argv), [
1631
+ [
1632
+ 'resume', ...quotaSelectors,
1633
+ '--model', 'gpt-review', '--remote', 'ws://127.0.0.1:4500',
1634
+ ...generatedInstructions, ...quotaSuffix,
1635
+ ],
1636
+ [
1637
+ 'resume', 'packed-session-123',
1638
+ '--model', 'gpt-review', '--remote', 'ws://127.0.0.1:4500',
1639
+ ...generatedInstructions, ...quotaSuffix,
1640
+ ],
1641
+ ], 'installed quota retry must remove resume selectors only before the marker');
1642
+ const twoMarkerArgs = [
1643
+ '--direct',
1644
+ '--',
1645
+ '--notify-temp',
1646
+ '--spark',
1647
+ '--worktree',
1648
+ 'post-marker-worktree',
1649
+ '--hotswap',
1650
+ '--xhigh',
1651
+ '--',
1652
+ '--madmax',
1653
+ '--notify-temp',
1654
+ '--spark',
1655
+ 'trailing argument with spaces',
1656
+ '',
1657
+ ];
1658
+ writeFileSync(capturePath, '');
1659
+ const twoMarkerResult = spawnSync(omxPath, twoMarkerArgs, {
1660
+ cwd: launchCwd,
1661
+ encoding: 'utf-8',
1662
+ env,
1663
+ timeout: PACKED_INSTALL_OPERATIONAL_PROBE_TIMEOUT_MS,
1664
+ killSignal: 'SIGKILL',
1665
+ });
1666
+ assertBoundedProbeExit('installed first-marker boundary launch', twoMarkerResult, 0);
1667
+ const twoMarkerLaunches = readFakeCodexLaunches(capturePath);
1668
+ if (twoMarkerLaunches.length !== 1 || !twoMarkerLaunches[0]) {
1669
+ throw new Error(`installed first-marker boundary launch must invoke fake Codex exactly once: ${JSON.stringify(twoMarkerLaunches)}`);
1670
+ }
1671
+ const twoMarkerLaunch = twoMarkerLaunches[0];
1672
+ assert.deepStrictEqual(twoMarkerLaunch.argv, [
1673
+ '-c',
1674
+ `model_instructions_file="${escapeTomlString(modelInstructionsPath)}"`,
1675
+ ...twoMarkerArgs.slice(1),
1676
+ ], 'the first literal -- must terminate OMX parsing and preserve every later argv element');
1677
+ if (twoMarkerLaunch.cwd !== launchCwd) {
1678
+ throw new Error(`the first literal -- must preserve launch cwd: expected ${launchCwd}, received ${twoMarkerLaunch.cwd}`);
1679
+ }
1680
+ if (twoMarkerLaunch.OMX_NOTIFY_TEMP_CONTRACT !== null) {
1681
+ throw new Error(`the first literal -- must not activate notification routing: ${JSON.stringify(twoMarkerLaunch)}`);
1682
+ }
1683
+ if (twoMarkerLaunch.OMX_TEAM_WORKER_LAUNCH_ARGS !== null) {
1684
+ throw new Error(`the first literal -- must not activate Team worker routing: ${JSON.stringify(twoMarkerLaunch)}`);
1685
+ }
1686
+ }
1687
+ finally {
1688
+ rmSync(smokeRoot, { recursive: true, force: true });
1689
+ }
1690
+ }
24
1691
  function usage() {
25
1692
  return [
26
1693
  'Usage: node scripts/smoke-packed-install.mjs',
27
1694
  '',
28
1695
  'Creates an npm tarball, installs it into an isolated prefix, and smoke tests the installed omx CLI.',
29
- 'Release smoke stays intentionally minimal: install + boot + 1-2 core commands only.',
1696
+ 'Release smoke validates installed CLI boot, native-hook dispatch, packaged artifacts, installed reasoning boundaries, and the isolated setup/rerun/uninstall lifecycle; Codex trust checks run when the pinned CLI is present.',
30
1697
  ].join('\n');
31
1698
  }
32
1699
  function formatCommandFailure(cmd, args, result) {
@@ -93,15 +1760,24 @@ function resolveGlobalNodeModules(prefixDir) {
93
1760
  return root;
94
1761
  }
95
1762
  export function validateHookStdout(eventName, stdout) {
1763
+ if (eventName === 'PostCompact') {
1764
+ if (stdout.length === 0)
1765
+ return;
1766
+ throw new Error('native hook PostCompact must emit empty stdout');
1767
+ }
96
1768
  const trimmed = stdout.trim();
97
1769
  if (!trimmed)
98
1770
  return;
1771
+ let parsed;
99
1772
  try {
100
- JSON.parse(trimmed);
1773
+ parsed = JSON.parse(trimmed);
101
1774
  }
102
1775
  catch (error) {
103
1776
  throw new Error(`native hook ${eventName} emitted invalid JSON stdout: ${error instanceof Error ? error.message : String(error)}`);
104
1777
  }
1778
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
1779
+ throw new Error(`native hook ${eventName} emitted a non-object JSON stdout payload`);
1780
+ }
105
1781
  }
106
1782
  export function buildNativeHookSmokePayload(eventName, smokeCwd) {
107
1783
  const base = {
@@ -146,7 +1822,122 @@ export function buildNativeHookSmokePayload(eventName, smokeCwd) {
146
1822
  return base;
147
1823
  }
148
1824
  }
1825
+ function parseNativeHookSmokeOutput(probe, stdout) {
1826
+ validateHookStdout(probe, stdout);
1827
+ if (!stdout.trim())
1828
+ throw new Error(`native hook ${probe} emitted no JSON stdout`);
1829
+ const parsed = JSON.parse(stdout);
1830
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
1831
+ throw new Error(`native hook ${probe} emitted a non-object JSON result`);
1832
+ }
1833
+ return parsed;
1834
+ }
1835
+ function requireNativeHookPermissionDeny(probe, output, reason) {
1836
+ const hookSpecificOutput = output.hookSpecificOutput;
1837
+ if (!hookSpecificOutput || typeof hookSpecificOutput !== 'object' || Array.isArray(hookSpecificOutput)) {
1838
+ throw new Error(`native hook ${probe} did not emit hookSpecificOutput`);
1839
+ }
1840
+ const hookOutput = hookSpecificOutput;
1841
+ if (hookOutput.permissionDecision !== 'deny') {
1842
+ throw new Error(`native hook ${probe} did not deny permission`);
1843
+ }
1844
+ if (!reason.test(String(hookOutput.permissionDecisionReason ?? ''))) {
1845
+ throw new Error(`native hook ${probe} denial did not match ${reason.source}`);
1846
+ }
1847
+ }
149
1848
  function smokeInstalledNativeHookDist(prefixDir) {
1849
+ function runPackedTransportRegressions(hookScript, smokeCwd) {
1850
+ const invoke = (cwd, environment, payload) => run(process.execPath, [realpathSync(hookScript)], {
1851
+ cwd, env: environment, input: JSON.stringify(payload),
1852
+ });
1853
+ const stopDecision = (cwd, environment, payload) => JSON.parse(String(invoke(cwd, environment, { ...payload, hook_event_name: 'Stop' }).stdout || '{}')).decision;
1854
+ const g1bCwd = join(smokeCwd, 'g1bu');
1855
+ const g1bSession = 'g1bu';
1856
+ const g1bThread = 'g1bu-thread';
1857
+ const g1bPriorTurn = 'g1bu-turn-old';
1858
+ const g1bTurn = 'g1bu-turn-new';
1859
+ const g1bStateDir = join(g1bCwd, '.omx', 'state');
1860
+ const g1bSessionDir = join(g1bStateDir, 'sessions', g1bSession);
1861
+ mkdirSync(g1bSessionDir, { recursive: true });
1862
+ for (const [path, marker] of [
1863
+ [join(g1bStateDir, 'skill-active-state.json'), 'root-skill'],
1864
+ [join(g1bSessionDir, 'skill-active-state.json'), 'session-skill'],
1865
+ ]) {
1866
+ writeFileSync(path, JSON.stringify({ active: true, skill: 'autopilot', phase: 'completing', session_id: g1bSession, thread_id: g1bThread, turn_id: g1bPriorTurn, marker, active_skills: [{ skill: 'autopilot', phase: 'completing', active: true, session_id: g1bSession, thread_id: g1bThread, turn_id: g1bPriorTurn }] }));
1867
+ }
1868
+ for (const [path, marker] of [
1869
+ [join(g1bStateDir, 'autopilot-state.json'), 'root-autopilot'],
1870
+ [join(g1bSessionDir, 'autopilot-state.json'), 'session-autopilot'],
1871
+ ]) {
1872
+ writeFileSync(path, JSON.stringify({ active: false, mode: 'autopilot', current_phase: 'complete', session_id: g1bSession, thread_id: g1bThread, turn_id: g1bPriorTurn, marker }));
1873
+ }
1874
+ const g1bEnv = { ...buildPackedRegressionEnvironment({ name: 'g1bu' }), OMX_TEAM_MODE: 'disabled' };
1875
+ const g1bPrompt = '$team $autopilot restart — café';
1876
+ const g1bPayload = { hook_event_name: 'UserPromptSubmit', cwd: g1bCwd, session_id: g1bSession, thread_id: g1bThread, turn_id: g1bTurn, prompt: g1bPrompt };
1877
+ validateHookStdout('UserPromptSubmit', String(invoke(g1bCwd, g1bEnv, g1bPayload).stdout || ''));
1878
+ const g1bState = JSON.parse(readFileSync(join(g1bSessionDir, 'skill-active-state.json'), 'utf-8'));
1879
+ const [g1bActiveSkill] = g1bState.active_skills ?? [];
1880
+ if (!g1bState.active || g1bState.skill !== 'autopilot' || g1bState.session_id !== g1bSession || g1bState.thread_id !== g1bThread || g1bState.turn_id !== g1bTurn || g1bState.active_skills?.length !== 1 || !g1bActiveSkill?.active || g1bActiveSkill.skill !== 'autopilot' || g1bActiveSkill.session_id !== g1bSession || g1bActiveSkill.thread_id !== g1bThread || g1bActiveSkill.turn_id !== g1bTurn)
1881
+ throw new Error('packed G1b-U did not persist singleton Autopilot ownership for the new turn');
1882
+ const g1bAutopilotState = JSON.parse(readFileSync(join(g1bSessionDir, 'autopilot-state.json'), 'utf-8'));
1883
+ const g1bContextSnapshot = g1bAutopilotState.state?.handoff_artifacts?.context_snapshot?.path;
1884
+ if (!g1bAutopilotState.active || g1bAutopilotState.current_phase !== 'deep-interview' || g1bAutopilotState.session_id !== g1bSession || g1bAutopilotState.thread_id !== g1bThread || g1bAutopilotState.turn_id !== g1bTurn || !g1bContextSnapshot || !readFileSync(resolve(g1bCwd, g1bContextSnapshot)).includes(Buffer.from(g1bPrompt, 'utf-8')))
1885
+ throw new Error('packed G1b-U did not restart Autopilot with its UTF-8 context snapshot');
1886
+ if (existsSync(join(g1bStateDir, 'team-state.json')) || existsSync(join(g1bSessionDir, 'team-state.json')))
1887
+ throw new Error('packed G1b-U created Team state');
1888
+ if (stopDecision(g1bCwd, g1bEnv, g1bPayload) !== 'block')
1889
+ throw new Error('packed G1b-U did not block Stop');
1890
+ const g2aCwd = join(smokeCwd, 'g2a');
1891
+ const g2aSession = 'g2a-stale-predecessor';
1892
+ const g2aStateDir = join(g2aCwd, '.omx', 'state');
1893
+ const g2aSessionDir = join(g2aStateDir, 'sessions', g2aSession);
1894
+ const g2aFiles = [join(g2aStateDir, 'skill-active-state.json'), join(g2aStateDir, 'ralplan-state.json'), join(g2aSessionDir, 'skill-active-state.json'), join(g2aSessionDir, 'ralplan-state.json'), join(g2aStateDir, 'session.json')];
1895
+ mkdirSync(g2aCwd, { recursive: true });
1896
+ if (g2aFiles.some((file) => existsSync(file)))
1897
+ throw new Error('packed G2a fixture unexpectedly contains state');
1898
+ const g2aEnv = buildPackedRegressionEnvironment({ name: 'g2a' });
1899
+ const g2aPayload = { hook_event_name: 'UserPromptSubmit', cwd: g2aCwd, session_id: g2aSession, thread_id: 'g2a-thread', turn_id: 'g2a-turn', prompt: 'use $ralplan is the consensus-planning command' };
1900
+ validateHookStdout('UserPromptSubmit', String(invoke(g2aCwd, g2aEnv, g2aPayload).stdout || ''));
1901
+ if (g2aFiles.some((file) => existsSync(file)))
1902
+ throw new Error('packed G2a stale predecessor created skill/detail state');
1903
+ if (stopDecision(g2aCwd, g2aEnv, g2aPayload) === 'block')
1904
+ throw new Error('packed G2a stale predecessor blocked Stop');
1905
+ if (g2aFiles.some((file) => existsSync(file)))
1906
+ throw new Error('packed G2a stale predecessor created skill/detail state after Stop');
1907
+ const g2bCwd = join(smokeCwd, 'g2b');
1908
+ const g2bSession = 'g2b-terminal-session';
1909
+ const g2bStateDir = join(g2bCwd, '.omx', 'state');
1910
+ const g2bSessionDir = join(g2bStateDir, 'sessions', g2bSession);
1911
+ const g2bFiles = [join(g2bStateDir, 'skill-active-state.json'), join(g2bStateDir, 'autopilot-state.json'), join(g2bSessionDir, 'skill-active-state.json'), join(g2bSessionDir, 'autopilot-state.json'), join(g2bStateDir, 'session.json')];
1912
+ mkdirSync(g2bSessionDir, { recursive: true });
1913
+ writeFileSync(g2bFiles[0], JSON.stringify({ version: 1, active: false, skill: 'autopilot', phase: 'complete', completed_at: '2026-06-01T00:00:01.001Z', session_id: 'g2b-root-session', thread_id: 'g2b-root-thread', turn_id: 'g2b-root-skill-turn', marker: 'g2b-root-skill' }));
1914
+ writeFileSync(g2bFiles[1], JSON.stringify({ mode: 'autopilot', active: false, current_phase: 'complete', completed_at: '2026-06-01T00:00:02.002Z', session_id: 'g2b-root-session', thread_id: 'g2b-root-thread', turn_id: 'g2b-root-detail-turn', marker: 'g2b-root-detail' }));
1915
+ writeFileSync(g2bFiles[2], JSON.stringify({ version: 1, active: false, skill: 'autopilot', phase: 'complete', completed_at: '2026-06-01T00:00:03.003Z', session_id: g2bSession, thread_id: 'g2b-session-thread', turn_id: 'g2b-session-skill-turn', marker: 'g2b-session-skill' }));
1916
+ writeFileSync(g2bFiles[3], JSON.stringify({ mode: 'autopilot', active: false, current_phase: 'complete', completed_at: '2026-06-01T00:00:04.004Z', session_id: g2bSession, thread_id: 'g2b-session-thread', turn_id: 'g2b-session-detail-turn', marker: 'g2b-session-detail' }));
1917
+ writeFileSync(g2bFiles[4], JSON.stringify({ session_id: g2bSession, cwd: g2bCwd, created_at: '2026-06-01T00:00:05.005Z', updated_at: '2026-06-01T00:00:06.006Z', last_turn_id: 'g2b-session-json-turn', marker: 'g2b-session-json' }));
1918
+ const g2bBefore = g2bFiles.map((file) => readFileSync(file));
1919
+ const g2bEnv = buildPackedRegressionEnvironment({ name: 'g2b' });
1920
+ const g2bPayload = { hook_event_name: 'UserPromptSubmit', cwd: g2bCwd, session_id: g2bSession, thread_id: 'g2b-prompt-thread', turn_id: 'g2b-prompt-turn', prompt: 'do not start $autopilot — café' };
1921
+ validateHookStdout('UserPromptSubmit', String(invoke(g2bCwd, g2bEnv, g2bPayload).stdout || ''));
1922
+ for (const [index, file] of g2bFiles.entries())
1923
+ if (Buffer.compare(readFileSync(file), g2bBefore[index]) !== 0)
1924
+ throw new Error(`packed G2b negated prompt mutated terminal state ${file}`);
1925
+ if (stopDecision(g2bCwd, g2bEnv, g2bPayload) === 'block')
1926
+ throw new Error('packed G2b negated prompt blocked Stop');
1927
+ for (const [index, file] of g2bFiles.entries())
1928
+ if (Buffer.compare(readFileSync(file), g2bBefore[index]) !== 0)
1929
+ throw new Error(`packed G2b negated prompt mutated terminal state after Stop ${file}`);
1930
+ const g2cCwd = join(smokeCwd, 'g2c-01445');
1931
+ mkdirSync(g2cCwd, { recursive: true });
1932
+ const g2cPayload = { ...PACKED_CODEX_01445_NO_POINTER_NO_TRACKER_FIXTURE, cwd: g2cCwd };
1933
+ const g2cResult = invoke(g2cCwd, buildPackedRegressionEnvironment({ name: 'g2c-01445' }), g2cPayload);
1934
+ const g2cStdout = String(g2cResult.stdout || '');
1935
+ const g2cExpected = '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"unsupported_documented_leader_proof: Codex 0.144.5 hooks do not expose documented root identity required for adapted Ralplan."}}\n';
1936
+ if (g2cStdout !== g2cExpected)
1937
+ throw new Error('packed Codex 0.144.5 fixture returned unexpected PreToolUse bytes');
1938
+ if (existsSync(join(g2cCwd, '.omx', 'state')))
1939
+ throw new Error('packed Codex 0.144.5 fixture created pointer or tracker state');
1940
+ }
150
1941
  const globalNodeModules = resolveGlobalNodeModules(prefixDir);
151
1942
  const packageRoot = join(globalNodeModules, 'oh-my-codex');
152
1943
  const hookScript = join(packageRoot, 'dist', 'scripts', 'codex-native-hook.js');
@@ -156,23 +1947,2507 @@ function smokeInstalledNativeHookDist(prefixDir) {
156
1947
  const payload = buildNativeHookSmokePayload(eventName, smokeCwd);
157
1948
  const result = run(process.execPath, [realpathSync(hookScript)], {
158
1949
  cwd: smokeCwd,
159
- env: {
160
- ...process.env,
1950
+ env: buildPackedProbeEnv({
161
1951
  OMX_NATIVE_HOOK_DOCTOR_SMOKE: '1',
162
1952
  OMX_ROOT: join(smokeCwd, '.omx-packed-hook-root'),
163
1953
  OMX_SESSION_ID: `packed-install-smoke-${eventName}`,
164
1954
  OMX_SOURCE_CWD: smokeCwd,
165
1955
  OMX_STARTUP_CWD: smokeCwd,
166
- },
1956
+ }),
167
1957
  input: JSON.stringify(payload),
168
1958
  });
169
1959
  validateHookStdout(eventName, result.stdout);
170
1960
  }
1961
+ const pluginHookScript = join(packageRoot, 'plugins', 'oh-my-codex', 'hooks', 'codex-native-hook.mjs');
1962
+ const pluginChildScript = join(smokeCwd, 'packed-plugin-oversized-child.mjs');
1963
+ const pluginLauncher = join(smokeCwd, process.platform === 'win32' ? 'packed-plugin-oversized.cmd' : 'packed-plugin-oversized.sh');
1964
+ writeFileSync(pluginChildScript, `import { writeFileSync } from 'node:fs';\nwriteFileSync(process.env.OMX_PLUGIN_SENTINEL, 'delegated');\nprocess.stdout.write('{}\\n');\n`);
1965
+ if (process.platform === 'win32') {
1966
+ writeFileSync(pluginLauncher, `@echo off\r\n"${process.execPath}" "${pluginChildScript}" %*\r\n`);
1967
+ }
1968
+ else {
1969
+ writeFileSync(pluginLauncher, `#!/bin/sh\nexec "${process.execPath}" "${pluginChildScript}" "$@"\n`, { mode: 0o755 });
1970
+ }
1971
+ for (const eventName of ['PreToolUse', 'PostToolUse']) {
1972
+ const launchId = eventName === 'PreToolUse' ? 'packed-plugin-oversized-pre' : 'packed-plugin-oversized-post';
1973
+ const sessionId = `${launchId}-session`;
1974
+ const sentinel = join(smokeCwd, `${launchId}.sentinel`);
1975
+ const base = JSON.stringify({ hook_event_name: eventName, session_id: sessionId, unicode: '😀é', padding: '' });
1976
+ const input = JSON.stringify({ hook_event_name: eventName, session_id: sessionId, unicode: '😀é', padding: 'x'.repeat(1_048_577 - Buffer.byteLength(base, 'utf8')) });
1977
+ if (Buffer.byteLength(input, 'utf8') !== 1_048_577 || input.length === Buffer.byteLength(input, 'utf8')) {
1978
+ throw new Error(`packed plugin ${eventName} oversized fixture did not prove UTF-8 byte sizing`);
1979
+ }
1980
+ const result = run(process.execPath, [realpathSync(pluginHookScript)], {
1981
+ cwd: smokeCwd,
1982
+ env: {
1983
+ ...process.env,
1984
+ OMX_ENTRY_PATH: join(packageRoot, 'dist', 'cli', 'omx.js'),
1985
+ OMX_CODEX_LAUNCH_ID: launchId,
1986
+ OMX_NATIVE_HOOK_COMMAND: pluginLauncher,
1987
+ OMX_PLUGIN_SENTINEL: sentinel,
1988
+ },
1989
+ input,
1990
+ });
1991
+ const systemMessage = 'OMX native hook rejected oversized stdin JSON before parsing; maxBytes=1048576.';
1992
+ const expected = eventName === 'PreToolUse'
1993
+ ? { systemMessage, hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: systemMessage } }
1994
+ : { continue: false, stopReason: 'native_hook_stdin_oversized', systemMessage };
1995
+ if (result.status !== 0 || String(result.stderr) !== '' || String(result.stdout) !== `${JSON.stringify(expected)}\n` || existsSync(sentinel)) {
1996
+ throw new Error(`packed plugin ${eventName} oversized stdin did not return the native-equivalent local response without delegation`);
1997
+ }
1998
+ }
1999
+ for (const input of [
2000
+ '{"hook_event_name":"PreToolUse",',
2001
+ `{"hook_event_name":"PreToolUse","transcript":"${'x'.repeat(1_048_577)}"}`,
2002
+ ]) {
2003
+ const result = run(process.execPath, [realpathSync(hookScript)], {
2004
+ cwd: smokeCwd,
2005
+ env: { ...process.env, OMX_ROOT: join(smokeCwd, '.omx-packed-hook-root') },
2006
+ input,
2007
+ });
2008
+ const output = JSON.parse(String(result.stdout));
2009
+ if (output.hookSpecificOutput?.permissionDecision !== 'deny' || output.hookSpecificOutput?.hookEventName !== 'PreToolUse') {
2010
+ throw new Error('packed malformed or oversized PreToolUse stdin must emit a canonical deny envelope');
2011
+ }
2012
+ }
2013
+ const hookRoot = smokeCwd;
2014
+ const stateDir = join(hookRoot, '.omx', 'state');
2015
+ const sessionId = 'packed-install-option-c';
2016
+ const leaderAgentId = 'agent-packed-install-leader';
2017
+ const teamName = 'packed-option-c';
2018
+ mkdirSync(join(stateDir, 'sessions', sessionId), { recursive: true });
2019
+ mkdirSync(join(stateDir, 'team', teamName, 'workers', 'worker-1'), { recursive: true });
2020
+ mkdirSync(join(smokeCwd, 'src', 'shared'), { recursive: true });
2021
+ mkdirSync(join(smokeCwd, 'src', 'state'), { recursive: true });
2022
+ mkdirSync(join(smokeCwd, '.omx', 'state'), { recursive: true });
2023
+ mkdirSync(join(smokeCwd, '.omx', 'state', 'tmp'), { recursive: true });
2024
+ writeFileSync(join(smokeCwd, '.omx', 'state', 'conductor-ledger.json'), '{}\n');
2025
+ writeFileSync(join(smokeCwd, '.omx', 'state', 'reference-copy'), 'metadata reference target\n');
2026
+ writeFileSync(join(smokeCwd, 'README.md'), 'read-only source\n');
2027
+ mkdirSync(join(smokeCwd, '.omx', 'state', 'inbox'), { recursive: true });
2028
+ writeFileSync(join(smokeCwd, '.omx', 'state', 'payload'), 'metadata payload\n');
2029
+ writeFileSync(join(smokeCwd, 'src', 'runtime.ts'), 'export {};\n');
2030
+ writeFileSync(join(smokeCwd, 'a'), 'finite metadata source\n');
2031
+ writeFileSync(join(smokeCwd, 'src', 'large.bin'), Buffer.alloc(16 * 1024 * 1024 + 1));
2032
+ mkdirSync(join(smokeCwd, '.omx', 'state', 'zsh-home'), { recursive: true });
2033
+ writeFileSync(join(smokeCwd, '.omx', 'state', 'zsh-home', '.zshenv'), 'touch src/zsh-owned.ts\n');
2034
+ writeFileSync(join(smokeCwd, '.omx', 'state', 'inbox', 'time'), '#!/bin/sh\ntouch src/time-wrapper-owned.ts\n');
2035
+ chmodSync(join(smokeCwd, '.omx', 'state', 'inbox', 'time'), 0o755);
2036
+ writeFileSync(join(smokeCwd, '.omx', 'state', 'inbox', 'node'), '#!/bin/sh\ntouch src/shebang-owned.ts\n');
2037
+ chmodSync(join(smokeCwd, '.omx', 'state', 'inbox', 'node'), 0o755);
2038
+ symlinkSync(join(smokeCwd, 'src', 'runtime.ts'), join(smokeCwd, '.omx', 'state', 'inbox', 'payload'));
2039
+ symlinkSync(join(smokeCwd, 'src', 'runtime.ts'), join(smokeCwd, '.omx', 'state', 'curl-glob-1.log'));
2040
+ mkdirSync(join(smokeCwd, 'src', 'subdir'), { recursive: true });
2041
+ mkdirSync(join(smokeCwd, '.omx', 'state', 'bash-home'), { recursive: true });
2042
+ writeFileSync(join(smokeCwd, '.omx', 'state', 'bash-home', '.bashrc'), 'touch src/interactive-owned.ts\n');
2043
+ symlinkSync(join(smokeCwd, 'src', 'subdir'), join(smokeCwd, '.omx', 'state', 'link'));
2044
+ symlinkSync(join(smokeCwd, 'src'), join(smokeCwd, '.omx', 'state', 'inbox', 'product-dir'));
2045
+ symlinkSync(join(smokeCwd, 'src', 'dangling-target.ts'), join(smokeCwd, '.omx', 'state', 'inbox', 'dangling'));
2046
+ writeFileSync(join(smokeCwd, '.omx', 'state', 'session.json'), JSON.stringify({ session_id: sessionId, native_session_id: leaderAgentId, cwd: smokeCwd }));
2047
+ writeFileSync(join(stateDir, 'session.json'), JSON.stringify({ session_id: sessionId, native_session_id: leaderAgentId, cwd: smokeCwd }));
2048
+ writeFileSync(join(stateDir, 'subagent-tracking.json'), JSON.stringify({
2049
+ schemaVersion: 1,
2050
+ sessions: {
2051
+ [sessionId]: {
2052
+ session_id: sessionId,
2053
+ leader_thread_id: leaderAgentId,
2054
+ threads: { [leaderAgentId]: { thread_id: leaderAgentId, kind: 'leader' } },
2055
+ },
2056
+ },
2057
+ }));
2058
+ writeFileSync(join(stateDir, 'sessions', sessionId, 'skill-active-state.json'), JSON.stringify({
2059
+ active: true,
2060
+ skill: 'ultragoal',
2061
+ phase: 'executing',
2062
+ session_id: sessionId,
2063
+ active_skills: [{ skill: 'ultragoal', phase: 'executing', active: true, session_id: sessionId }],
2064
+ }));
2065
+ writeFileSync(join(stateDir, 'sessions', sessionId, 'ultragoal-state.json'), JSON.stringify({ active: true, mode: 'ultragoal', current_phase: 'executing', session_id: sessionId }));
2066
+ const packedTeamAuthority = {
2067
+ name: teamName,
2068
+ leader_pane_id: '%packed-leader',
2069
+ leader_cwd: smokeCwd,
2070
+ team_state_root: stateDir,
2071
+ workers: [{
2072
+ name: 'worker-1',
2073
+ pane_id: '%packed-worker',
2074
+ working_dir: smokeCwd,
2075
+ worktree_path: smokeCwd,
2076
+ team_state_root: stateDir,
2077
+ }],
2078
+ };
2079
+ for (const fileName of ['config.json', 'manifest.v2.json']) {
2080
+ writeFileSync(join(stateDir, 'team', teamName, fileName), JSON.stringify(packedTeamAuthority));
2081
+ }
2082
+ writeFileSync(join(stateDir, 'team', teamName, 'workers', 'worker-1', 'identity.json'), JSON.stringify({
2083
+ name: 'worker-1',
2084
+ pane_id: '%packed-worker',
2085
+ team_state_root: stateDir,
2086
+ worktree_path: smokeCwd,
2087
+ }));
2088
+ const invokeAuthorizationProbe = (payload, env) => run(process.execPath, [realpathSync(hookScript)], { cwd: smokeCwd, env, input: JSON.stringify(payload) });
2089
+ const hookEnv = {
2090
+ ...process.env,
2091
+ OMX_NATIVE_HOOK_DOCTOR_SMOKE: '1',
2092
+ OMX_ROOT: hookRoot,
2093
+ OMX_SOURCE_CWD: smokeCwd,
2094
+ OMX_STARTUP_CWD: smokeCwd,
2095
+ };
2096
+ const officialTeamRootPayload = {
2097
+ hook_event_name: 'PreToolUse',
2098
+ cwd: smokeCwd,
2099
+ session_id: sessionId,
2100
+ tool_name: 'Edit',
2101
+ tool_use_id: 'packed-install-team-root',
2102
+ tool_input: { file_path: 'src/packed-team-worker.ts', old_string: 'a', new_string: 'b' },
2103
+ };
2104
+ const teamEnv = {
2105
+ ...hookEnv,
2106
+ OMX_TEAM_WORKER: `packed-display/worker-1`,
2107
+ OMX_TEAM_STATE_ROOT: stateDir,
2108
+ OMX_TEAM_INTERNAL_WORKER: `${teamName}/worker-1`,
2109
+ OMX_TEAM_LEADER_CWD: smokeCwd,
2110
+ TMUX_PANE: '%packed-worker',
2111
+ };
2112
+ const teamOutput = parseNativeHookSmokeOutput('PreToolUse official Team root', String(invokeAuthorizationProbe(officialTeamRootPayload, teamEnv).stdout));
2113
+ if (Object.keys(teamOutput).length !== 0) {
2114
+ throw new Error('native hook official Team root did not preserve the validated Team-worker exemption');
2115
+ }
2116
+ const leaderOutput = parseNativeHookSmokeOutput('PreToolUse leader with Team environment', String(invokeAuthorizationProbe({
2117
+ ...officialTeamRootPayload,
2118
+ session_id: leaderAgentId,
2119
+ tool_use_id: 'packed-install-team-leader',
2120
+ agent_id: leaderAgentId,
2121
+ thread_id: leaderAgentId,
2122
+ }, teamEnv).stdout));
2123
+ requireNativeHookPermissionDeny('PreToolUse leader with Team environment', leaderOutput, /Main-root Conductor mode is active/);
2124
+ const childEnv = {
2125
+ ...hookEnv,
2126
+ OMX_TEAM_WORKER: '',
2127
+ OMX_TEAM_INTERNAL_WORKER: '',
2128
+ OMX_TEAM_STATE_ROOT: '',
2129
+ };
2130
+ const runActorProbeResult = (actor, probe, toolName, toolInput, inheritedEnv = {}) => {
2131
+ const result = invokeAuthorizationProbe({
2132
+ hook_event_name: 'PreToolUse',
2133
+ cwd: smokeCwd,
2134
+ session_id: actor === 'main-root' ? leaderAgentId : sessionId,
2135
+ ...(actor === 'main-root'
2136
+ ? { agent_id: leaderAgentId, thread_id: leaderAgentId }
2137
+ : { agent_id: 'agent-packed-install-child' }),
2138
+ tool_name: toolName,
2139
+ tool_use_id: `packed-install-${actor}-${probe.replace(/\W+/g, '-')}`,
2140
+ tool_input: toolInput,
2141
+ }, { ...childEnv, ...inheritedEnv });
2142
+ const stdout = String(result.stdout);
2143
+ return { output: parseNativeHookSmokeOutput(`PreToolUse ${actor} ${probe}`, stdout), stdout };
2144
+ };
2145
+ const runActorProbe = (actor, probe, toolName, toolInput, inheritedEnv = {}) => runActorProbeResult(actor, probe, toolName, toolInput, inheritedEnv).output;
2146
+ const requireActorDeny = (actor, probe, output, expectedReason = actor === 'native-child' ? /OWNER_CONFIRMATION_REQUIRED/ : /Main-root Conductor mode is active/) => requireNativeHookPermissionDeny(`PreToolUse ${actor} ${probe}`, output, expectedReason);
2147
+ for (const actor of ['main-root', 'native-child']) {
2148
+ for (const [probe, command] of [
2149
+ ['finite cp directory leaf symlink', 'cp .omx/state/payload .omx/state/inbox'],
2150
+ ['finite curl remote-name leaf symlink', 'curl -q --output-dir .omx/state/inbox -O https://example.test/payload'],
2151
+ ['finite wget prefix leaf symlink', 'wget --no-config -P .omx/state/inbox https://example.test/payload'],
2152
+ ['finite curl remote-name scheme-less extra operand leaf symlink', 'curl -q --output-dir .omx/state/inbox -O https://example.test/payload ftp.example.test/payload'],
2153
+ ['finite wget prefix scheme-less extra operand leaf symlink', 'wget --no-config --no-hsts -P .omx/state/inbox https://example.test/payload ftp.example.test/payload'],
2154
+ ['finite curl template leaf symlink', "curl -q -o .omx/state/curl-glob-#1.log 'https://example.test/file[1-2]'"],
2155
+ ['dangling symlink touch', 'touch .omx/state/inbox/dangling'],
2156
+ ['dangling symlink Wget output', 'wget --no-config --no-hsts -P .omx/state/inbox https://example.test/dangling'],
2157
+ ['wait -p POSIX writer', 'export POSIXLY_CORRECT; sleep 0 & wait -n -p POSIXLY_CORRECT; wget --no-config --no-hsts -O src/wait-owned.ts https://example.test/file -O -'],
2158
+ ['clustered wait -p POSIX writer', 'export POSIXLY_CORRECT; sleep 0 & wait -np POSIXLY_CORRECT; wget --no-config --no-hsts -O src/wait-cluster-owned.ts https://example.test/file -O -'],
2159
+ ['fd redirect source sink', 'printf pwn >& src/native-child-owned.ts'],
2160
+ ['fd redirect protected state sink', `printf pwn >& .omx/state/sessions/${sessionId}/ultragoal-state.json`],
2161
+ ['function return cwd flow', 'f(){ cd src; return; cd ..; }; f; touch .omx/state/inbox/return-owned'],
2162
+ ['protected state ancestor removal', `rm -rf .omx/state/sessions/${sessionId}`],
2163
+ ['ordered metadata append overflow', 'truncate --size=16777216 .omx/state/inbox/oversized; printf x >> .omx/state/inbox/oversized'],
2164
+ ['rsync aliased source and log', 'rsync --log-file=.omx/state/conductor-ledger.json .omx/state/conductor-ledger.json .omx/state/inbox/rsync-alias-copy'],
2165
+ ['exec login shell startup', 'HOME=.omx/state/bash-home exec -l /bin/bash -c ":"'],
2166
+ ['env argv0 login shell startup', 'HOME=.omx/state/bash-home env --argv0=-bash /bin/bash -c ":"'],
2167
+ ['function keyword command-not-found handler', 'function command_not_found_handle { /usr/bin/touch src/path-keyword-owned.ts; }; PATH=/definitely/not-present; cat README.md'],
2168
+ ['shadowed printf redirect producer', 'printf(){ /usr/bin/head -c 16777217 /dev/zero; }; printf safe > .omx/state/inbox/oversized'],
2169
+ ['shadowed cat heredoc producer', "cat(){ /usr/bin/head -c 16777217 /dev/zero; }; cat <<'EOF' > .omx/state/inbox/oversized-heredoc\nsafe\nEOF"],
2170
+ ['glob redirect producer', 'echo .omx/state/* > .omx/state/inbox/glob-producer'],
2171
+ ['brace redirect producer', 'echo {a,b} > .omx/state/inbox/brace-producer'],
2172
+ ['tilde redirect producer', 'echo ~/metadata > .omx/state/inbox/tilde-producer'],
2173
+ ['oversized truncate', 'truncate --size=16777217 .omx/state/inbox/oversized'],
2174
+ ['attached oversized truncate', 'truncate -s16777217 .omx/state/inbox/oversized-attached'],
2175
+ ['relative truncate size', 'truncate --size=+1 .omx/state/inbox/relative'],
2176
+ ['Wget uncapped file sink', 'wget --no-config --no-hsts -O .omx/state/inbox/stream https://example.test/file'],
2177
+ ['Wget non-HTTP file sink', 'wget --no-config --no-hsts -O .omx/state/inbox/ftp ftp://example.test/file'],
2178
+ ['Wget multiple file sink URLs', 'wget --no-config --no-hsts -O .omx/state/inbox/multiple https://example.test/one https://example.test/two'],
2179
+ ['curl uncapped file sink', 'curl -q --max-time 1 -o .omx/state/inbox/stream https://example.test/file'],
2180
+ ['trusted script environment interpreter shadow', 'PATH=.omx/state/inbox:/usr/bin:/bin /usr/bin/npm --version'],
2181
+ ]) {
2182
+ requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command }));
2183
+ }
2184
+ }
2185
+ for (const fileName of [
2186
+ 'autopilot-state.json',
2187
+ 'autoresearch-state.json',
2188
+ 'deep-interview-state.json',
2189
+ 'ralplan-state.json',
2190
+ 'ralph-state.json',
2191
+ 'ultrawork-state.json',
2192
+ 'team-state.json',
2193
+ 'ultraqa-state.json',
2194
+ 'ultragoal-state.json',
2195
+ 'skill-active-state.json',
2196
+ 'release-readiness-state.json',
2197
+ 'run-state.json',
2198
+ 'session.json',
2199
+ 'subagent-tracking.json',
2200
+ 'native-stop-state.json',
2201
+ ]) {
2202
+ for (const actor of ['main-root', 'native-child']) {
2203
+ requireActorDeny(actor, `raw gate state write ${fileName}`, runActorProbe(actor, `raw gate state write ${fileName}`, 'Write', {
2204
+ file_path: `.omx/state/sessions/${sessionId}/${fileName}`,
2205
+ content: JSON.stringify({ active: false, mode: fileName.slice(0, -'-state.json'.length), current_phase: 'complete', session_id: sessionId }),
2206
+ }));
2207
+ }
2208
+ }
2209
+ for (const filePath of [
2210
+ `.omx/state/team/${teamName}/phase.json`,
2211
+ `.omx/state/team/${teamName}/manifest.v2.json`,
2212
+ `.omx/state/team/${teamName}/config.json`,
2213
+ `.omx/state/team/${teamName}/workers/worker-1/identity.json`,
2214
+ ]) {
2215
+ for (const actor of ['main-root', 'native-child']) {
2216
+ requireActorDeny(actor, `raw Team authority write ${filePath}`, runActorProbe(actor, `raw Team authority write ${filePath}`, 'Write', { file_path: filePath, content: '{}' }));
2217
+ }
2218
+ }
2219
+ for (const filePath of [
2220
+ `.omx/state/sessions/${sessionId}/ULTRAGOAL-STATE.JSON`,
2221
+ `.omx/state/sessions/${sessionId}/ultragoal-state.json. `,
2222
+ ]) {
2223
+ for (const actor of ['main-root', 'native-child']) {
2224
+ requireActorDeny(actor, `protected gate-state filesystem alias ${filePath}`, runActorProbe(actor, `protected gate-state filesystem alias ${filePath}`, 'Write', { file_path: filePath, content: '{}' }));
2225
+ }
2226
+ }
2227
+ for (const [probe, command] of [
2228
+ ['gate state redirect', `printf x > .omx/state/sessions/${sessionId}/ultragoal-state.json`],
2229
+ ['gate state editor', `sed -i 's/executing/complete/' .omx/state/sessions/${sessionId}/ultragoal-state.json`],
2230
+ ['gate state interpreter', `node -e "require('fs').writeFileSync('.omx/state/sessions/${sessionId}/ultragoal-state.json','{}')"`],
2231
+ ]) {
2232
+ for (const actor of ['main-root', 'native-child']) {
2233
+ requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command }));
2234
+ }
2235
+ }
2236
+ requireNativeHookPermissionDeny('identityless remote mutation', parseNativeHookSmokeOutput('PreToolUse identityless remote mutation', String(invokeAuthorizationProbe({
2237
+ hook_event_name: 'PreToolUse',
2238
+ cwd: smokeCwd,
2239
+ session_id: sessionId,
2240
+ tool_name: 'Bash',
2241
+ tool_use_id: 'packed-install-identityless-remote-mutation',
2242
+ tool_input: { command: 'PATH=/usr/bin:/bin gh issue create --title x --body y' },
2243
+ }, childEnv).stdout)), /OWNER_CONFIRMATION_REQUIRED/);
2244
+ for (const [probe, identity] of [
2245
+ ['conflicting owner and current thread aliases', { owner_codex_thread_id: leaderAgentId, thread_id: 'agent-packed-install-child' }],
2246
+ ['conflicting session aliases', { session_id: sessionId, sessionId: 'foreign-session', agent_id: leaderAgentId, thread_id: leaderAgentId }],
2247
+ ['reversed conflicting session aliases', { session_id: 'foreign-session', sessionId, agent_id: leaderAgentId, thread_id: leaderAgentId }],
2248
+ ['conflicting owner and agent aliases', { owner_codex_thread_id: 'foreign-owner', agent_id: leaderAgentId }],
2249
+ ]) {
2250
+ requireNativeHookPermissionDeny(probe, parseNativeHookSmokeOutput(probe, String(invokeAuthorizationProbe({
2251
+ hook_event_name: 'PreToolUse',
2252
+ cwd: smokeCwd,
2253
+ session_id: leaderAgentId,
2254
+ ...identity,
2255
+ tool_name: 'Bash',
2256
+ tool_use_id: `packed-install-${probe.replace(/\W+/g, '-')}`,
2257
+ tool_input: { command: 'PATH=/usr/bin:/bin gh issue create --title x --body y' },
2258
+ }, childEnv).stdout)), /PROVENANCE_DENIED/);
2259
+ }
2260
+ for (const [probe, identity] of [
2261
+ ['foreign active session', { session_id: 'foreign-session', agent_id: leaderAgentId, thread_id: leaderAgentId }],
2262
+ ['absent active session', { agent_id: leaderAgentId, thread_id: leaderAgentId }],
2263
+ ]) {
2264
+ requireNativeHookPermissionDeny(probe, parseNativeHookSmokeOutput(probe, String(invokeAuthorizationProbe({
2265
+ hook_event_name: 'PreToolUse',
2266
+ cwd: smokeCwd,
2267
+ ...identity,
2268
+ tool_name: 'Write',
2269
+ tool_use_id: `packed-install-${probe.replace(/\W+/g, '-')}`,
2270
+ tool_input: { file_path: 'src/session-bypass.ts', content: 'owned\n' },
2271
+ }, childEnv).stdout)), /PROVENANCE_DENIED/);
2272
+ }
2273
+ for (const [order, sessionAliases] of [
2274
+ ['canonical-first', { session_id: sessionId, sessionId: 'foreign-session' }],
2275
+ ['foreign-first', { session_id: 'foreign-session', sessionId }],
2276
+ ]) {
2277
+ for (const [transport, toolName, toolInput] of [
2278
+ ['path', 'Write', { file_path: 'src/alias-bypass.ts', content: 'owned\n' }],
2279
+ ['bash', 'Bash', { command: 'printf pwn >& src/alias-bypass.ts' }],
2280
+ ['state', 'mcp__omx_state__state_write', { mode: 'ultragoal', active: true }],
2281
+ ]) {
2282
+ requireNativeHookPermissionDeny(`session alias ${order}/${transport}`, parseNativeHookSmokeOutput(`PreToolUse session alias ${order}/${transport}`, String(invokeAuthorizationProbe({
2283
+ hook_event_name: 'PreToolUse',
2284
+ cwd: smokeCwd,
2285
+ ...sessionAliases,
2286
+ agent_id: leaderAgentId,
2287
+ thread_id: leaderAgentId,
2288
+ tool_name: toolName,
2289
+ tool_use_id: `packed-install-session-alias-${order}-${transport}`,
2290
+ tool_input: toolInput,
2291
+ }, childEnv).stdout)), /PROVENANCE_DENIED/);
2292
+ }
2293
+ }
2294
+ requireNativeHookPermissionDeny('noncanonical cwd metadata target', parseNativeHookSmokeOutput('PreToolUse noncanonical cwd metadata target', String(invokeAuthorizationProbe({
2295
+ hook_event_name: 'PreToolUse',
2296
+ cwd: join(smokeCwd, 'src'),
2297
+ session_id: sessionId,
2298
+ agent_id: leaderAgentId,
2299
+ thread_id: leaderAgentId,
2300
+ tool_name: 'Write',
2301
+ tool_use_id: 'packed-install-noncanonical-cwd-metadata-target',
2302
+ tool_input: { file_path: '.omx/state/inbox/cwd-bypass', content: 'owned\n' },
2303
+ }, childEnv).stdout)), /Main-root Conductor mode is active/);
2304
+ requireNativeHookPermissionDeny('identityless native-session remote mutation', parseNativeHookSmokeOutput('PreToolUse identityless native-session remote mutation', String(invokeAuthorizationProbe({
2305
+ hook_event_name: 'PreToolUse',
2306
+ cwd: smokeCwd,
2307
+ session_id: leaderAgentId,
2308
+ tool_name: 'Bash',
2309
+ tool_use_id: 'packed-install-identityless-native-session-remote-mutation',
2310
+ tool_input: { command: 'PATH=/usr/bin:/bin gh issue create --title x --body y' },
2311
+ }, childEnv).stdout)), /Main-root Conductor mode is active/);
2312
+ if (Object.keys(runActorProbe('main-root', 'finite cp metadata leaf control', 'Bash', {
2313
+ command: 'cp .omx/state/payload .omx/handoffs/run-1/payload',
2314
+ })).length !== 0) {
2315
+ throw new Error('packed main-root finite cp metadata leaf control should be allowed');
2316
+ }
2317
+ if (Object.keys(runActorProbe('main-root', 'bounded dd metadata copy control', 'Bash', {
2318
+ command: 'dd if=a of=.omx/state/inbox/dd-copy bs=1 count=1',
2319
+ })).length !== 0) {
2320
+ throw new Error('packed main-root bounded dd metadata copy control should be allowed');
2321
+ }
2322
+ if (Object.keys(runActorProbe('main-root', 'bounded truncate metadata control', 'Bash', {
2323
+ command: 'truncate --size=16777216 .omx/state/inbox/truncate',
2324
+ })).length !== 0) {
2325
+ throw new Error('packed main-root bounded truncate metadata control should be allowed');
2326
+ }
2327
+ if (Object.keys(runActorProbe('main-root', 'bounded quoted heredoc metadata control', 'Bash', {
2328
+ command: "cat <<'EOF' > .omx/state/conductor-heredoc.json\n{}\nEOF",
2329
+ })).length !== 0) {
2330
+ throw new Error('packed main-root bounded quoted heredoc metadata control should be allowed');
2331
+ }
2332
+ if (Object.keys(runActorProbe('main-root', 'zsh fast startup control', 'Bash', {
2333
+ command: `zsh -f -c ':'`,
2334
+ })).length !== 0) {
2335
+ throw new Error('packed main-root zsh fast startup control should be allowed');
2336
+ }
2337
+ const boxedPlanningRoot = join(smokeCwd, 'boxed-planning-root');
2338
+ const boxedPlanningStateDir = join(boxedPlanningRoot, '.omx', 'state');
2339
+ const boxedPlanningStatePath = join(boxedPlanningStateDir, 'sessions', leaderAgentId, 'ralplan-state.json');
2340
+ writeFileSync(join(smokeCwd, 'src', 'boxed-planning-runtime.ts'), JSON.stringify({
2341
+ active: true,
2342
+ mode: 'ralplan',
2343
+ current_phase: 'planning',
2344
+ session_id: leaderAgentId,
2345
+ }));
2346
+ mkdirSync(join(boxedPlanningStateDir, 'sessions', leaderAgentId), { recursive: true });
2347
+ writeFileSync(join(boxedPlanningStateDir, 'session.json'), JSON.stringify({ session_id: leaderAgentId, cwd: smokeCwd }));
2348
+ writeFileSync(join(boxedPlanningStateDir, 'sessions', leaderAgentId, 'skill-active-state.json'), JSON.stringify({
2349
+ active: true,
2350
+ skill: 'ralplan',
2351
+ phase: 'planning',
2352
+ session_id: leaderAgentId,
2353
+ active_skills: [{ skill: 'ralplan', phase: 'planning', active: true, session_id: leaderAgentId }],
2354
+ }));
2355
+ symlinkSync(join(smokeCwd, 'src', 'boxed-planning-runtime.ts'), boxedPlanningStatePath);
2356
+ const boxedPlanningHardLinkPath = join(boxedPlanningStateDir, 'sessions', leaderAgentId, 'deep-interview-state.json');
2357
+ linkSync(join(smokeCwd, 'src', 'boxed-planning-runtime.ts'), boxedPlanningHardLinkPath);
2358
+ const boxedPlanningSiblingSessionId = `${leaderAgentId}-sibling`;
2359
+ const boxedPlanningSiblingPath = join(boxedPlanningStateDir, 'sessions', boxedPlanningSiblingSessionId, 'ralplan-state.json');
2360
+ mkdirSync(dirname(boxedPlanningSiblingPath), { recursive: true });
2361
+ writeFileSync(boxedPlanningSiblingPath, JSON.stringify({
2362
+ active: true,
2363
+ mode: 'ralplan',
2364
+ current_phase: 'planning',
2365
+ session_id: boxedPlanningSiblingSessionId,
2366
+ }));
2367
+ requireNativeHookPermissionDeny('main-root boxed planning state symlink', runActorProbe('main-root', 'boxed planning state symlink', 'Write', { file_path: boxedPlanningStatePath, content: 'owned\n' }, {
2368
+ OMX_ROOT: boxedPlanningRoot,
2369
+ OMX_TEAM_STATE_ROOT: '',
2370
+ }), /(?:Ralplan is active|Main-root Conductor mode is active)/);
2371
+ requireNativeHookPermissionDeny('main-root boxed planning state hardlink', runActorProbe('main-root', 'boxed planning state hardlink', 'Write', { file_path: boxedPlanningHardLinkPath, content: 'owned\n' }, {
2372
+ OMX_ROOT: boxedPlanningRoot,
2373
+ OMX_TEAM_STATE_ROOT: '',
2374
+ }), /(?:Ralplan is active|Main-root Conductor mode is active)/);
2375
+ requireNativeHookPermissionDeny('main-root boxed planning sibling session', runActorProbe('main-root', 'boxed planning sibling session', 'Write', { file_path: boxedPlanningSiblingPath, content: 'owned\n' }, {
2376
+ OMX_ROOT: boxedPlanningRoot,
2377
+ OMX_TEAM_STATE_ROOT: '',
2378
+ }), /(?:Ralplan is active|Main-root Conductor mode is active)/);
2379
+ if (Object.keys(runActorProbe('main-root', 'hardlink metadata source control', 'Bash', {
2380
+ command: 'ln .omx/state/conductor-ledger.json .omx/handoffs/run-1/ledger-link',
2381
+ })).length !== 0) {
2382
+ throw new Error('packed main-root hardlink metadata source control should be allowed');
2383
+ }
2384
+ writeFileSync(join(stateDir, 'session.json'), JSON.stringify({ session_id: sessionId }));
2385
+ requireActorDeny('native-child', 'anchorless state write', runActorProbe('native-child', 'anchorless state write', 'mcp__omx_state__state_write', { mode: 'ultragoal', active: true }));
2386
+ writeFileSync(join(stateDir, 'session.json'), JSON.stringify({ session_id: sessionId, native_session_id: leaderAgentId, cwd: smokeCwd }));
2387
+ for (const [probe, toolName, toolInput] of [
2388
+ ['filesystem write', 'mcp__filesystem__write_file', { path: 'src/packed-mcp-write.ts', content: 'escaped' }],
2389
+ ['state write', 'mcp__omx_state__state_write', { mode: 'ultragoal', active: true }],
2390
+ ]) {
2391
+ requireActorDeny('native-child', probe, runActorProbe('native-child', probe, toolName, toolInput));
2392
+ }
2393
+ for (const [probe, toolInput] of [
2394
+ ['state write foreign routing', { mode: 'ultragoal', workingDirectory: 'src', session_id: 'foreign', active: false }],
2395
+ ['state write unknown payload key', { mode: 'ultragoal', active: true, child_marker: 'forbidden' }],
2396
+ ]) {
2397
+ for (const actor of ['main-root', 'native-child']) {
2398
+ requireActorDeny(actor, probe, runActorProbe(actor, probe, 'mcp__omx_state__state_write', toolInput));
2399
+ }
2400
+ }
2401
+ for (const actor of ['main-root', 'native-child']) {
2402
+ requireActorDeny(actor, 'state clear active workflow', runActorProbe(actor, 'state clear active workflow', 'mcp__omx_state__state_clear', { mode: 'ultragoal' }));
2403
+ }
2404
+ for (const identity of [
2405
+ { agent_id: leaderAgentId, thread_id: 'thread-packed-install-foreign' },
2406
+ { agent_id: 'agent-packed-install-foreign', thread_id: leaderAgentId },
2407
+ ]) {
2408
+ const output = parseNativeHookSmokeOutput('PreToolUse conflicting leader identity', String(invokeAuthorizationProbe({
2409
+ hook_event_name: 'PreToolUse',
2410
+ cwd: smokeCwd,
2411
+ session_id: sessionId,
2412
+ ...identity,
2413
+ tool_name: 'Write',
2414
+ tool_input: { file_path: 'src/packed-conflict.ts', content: 'escaped' },
2415
+ }, childEnv).stdout));
2416
+ requireNativeHookPermissionDeny('PreToolUse conflicting leader identity', output, /PROVENANCE_DENIED/);
2417
+ }
2418
+ const consistentLeaderOutput = parseNativeHookSmokeOutput('PreToolUse consistent leader identity', String(invokeAuthorizationProbe({
2419
+ hook_event_name: 'PreToolUse',
2420
+ cwd: smokeCwd,
2421
+ session_id: sessionId,
2422
+ agent_id: leaderAgentId,
2423
+ thread_id: leaderAgentId,
2424
+ tool_name: 'Write',
2425
+ tool_input: { file_path: 'src/packed-consistent-leader.ts', content: 'escaped' },
2426
+ }, childEnv).stdout));
2427
+ requireNativeHookPermissionDeny('PreToolUse consistent leader identity', consistentLeaderOutput, /Main-root Conductor mode is active/);
2428
+ const ownerSessionClaimOutput = parseNativeHookSmokeOutput('PreToolUse owner-session identity claim', String(invokeAuthorizationProbe({
2429
+ hook_event_name: 'PreToolUse',
2430
+ cwd: smokeCwd,
2431
+ session_id: sessionId,
2432
+ owner_codex_session_id: leaderAgentId,
2433
+ owner_omx_session_id: sessionId,
2434
+ tool_name: 'Write',
2435
+ tool_input: { file_path: 'src/packed-session-claim.ts', content: 'escaped' },
2436
+ }, childEnv).stdout));
2437
+ requireNativeHookPermissionDeny('PreToolUse owner-session identity claim', ownerSessionClaimOutput, /OWNER_CONFIRMATION_REQUIRED/);
2438
+ const wgetReviewMutationCommands = [
2439
+ ['bare wget download', 'wget https://example.test/file'],
2440
+ ['wget short output log', 'wget -o .omx/state/wget.log https://example.invalid/native-child-write'],
2441
+ ['wget long output log', 'wget --output-file=.omx/state/wget.log https://example.invalid/native-child-write'],
2442
+ ['wget short append log', 'wget -a .omx/state/wget.log https://example.invalid/native-child-write'],
2443
+ ['wget long append log', 'wget --append-output=.omx/state/wget.log https://example.invalid/native-child-write'],
2444
+ ['wget dot-slash dash file target', 'wget --no-config --no-hsts -O ./- https://example.test/file'],
2445
+ ['xargs short arg file', 'xargs -a .omx/state/urls wget'],
2446
+ ['xargs long arg file', 'xargs --arg-file .omx/state/urls wget'],
2447
+ ['xargs short delimiter', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs -d , wget`],
2448
+ ['xargs long delimiter', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs --delimiter , wget`],
2449
+ ['xargs short eof', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs -E STOP wget`],
2450
+ ['xargs long eof', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs --eof=STOP wget`],
2451
+ ['xargs short replace', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs -I X wget X`],
2452
+ ['xargs long replace', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs --replace=X wget X`],
2453
+ ['xargs bsd replace', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs -J X wget X`],
2454
+ ['xargs short max lines', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs -L 1 wget`],
2455
+ ['xargs long max lines', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs --max-lines=1 wget`],
2456
+ ['xargs short max args', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs -n 1 wget`],
2457
+ ['xargs long max args', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs --max-args 1 wget`],
2458
+ ['xargs short max procs', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs -P 1 wget`],
2459
+ ['xargs long max procs', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs --max-procs 1 wget`],
2460
+ ['xargs short max chars', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs -s 4096 wget`],
2461
+ ['xargs long max chars', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs --max-chars 4096 wget`],
2462
+ ['xargs process slot var', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs --process-slot-var SLOT wget`],
2463
+ ['xargs abbreviated process slot var', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs --process-slot-v SLOT wget`],
2464
+ ['xargs ambiguous long option', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs --max SLOT wget`],
2465
+ ['xargs unknown long option', `printf '%s\n' 'https://example.invalid/native-child-write' | xargs --future-option SLOT wget`],
2466
+ ['wget end-of-options spider operand', 'wget -- --spider https://example.test/file'],
2467
+ ['wget short option argument smuggling', 'wget -U --spider https://example.test/file'],
2468
+ ['wget long option argument smuggling', 'wget --user-agent --spider https://example.test/file'],
2469
+ ['xargs eof no-value mutator', `printf '%s\n' src/victim.ts | xargs --eof rm`],
2470
+ ['xargs replace no-value mutator', `printf '%s\n' src/victim.ts | xargs --replace rm {}`],
2471
+ ['xargs max-lines no-value wget', `printf '%s\n' 'https://example.test/file' | xargs --max-lines wget true`],
2472
+ ['xargs single quoted empty eof mutator', `printf '%s\n' src/xargs-owned.ts | xargs -E '' touch true`],
2473
+ ['xargs double quoted empty eof mutator', `printf '%s\n' src/xargs-owned.ts | xargs -E "" touch true`],
2474
+ ['xargs wget short stdout body override', `printf '%s\n' '--output-document=src/xargs-wget-owned.ts' 'https://example.test/file' | xargs wget -O -`],
2475
+ ['xargs wget long stdout body override', `printf '%s\n' '--output-document=src/xargs-wget-owned.ts' 'https://example.test/file' | xargs wget --output-document=-`],
2476
+ ['xargs wget stdout log injection', `printf '%s\n' '--output-file=src/xargs-wget.log' 'https://example.test/file' | xargs wget -O -`],
2477
+ ['dynamic wget stdout body override', `WGET_OPT=--output-document=src/dynamic-wget-owned.ts; wget -O - $WGET_OPT https://example.test/file`],
2478
+ ['dynamic wget stdout log injection', `WGET_OPT=--output-file=src/dynamic-wget.log; wget -O - $WGET_OPT https://example.test/file`],
2479
+ ['posix wget option ordering', `POSIXLY_CORRECT=1 wget -O src/posix-wget-owned.ts https://example.test/file -O -`],
2480
+ ['posix wget append assignment', `POSIXLY_CORRECT+=1 wget --no-config -O src/plus-prefix-owned.ts https://example.test/file -O -`],
2481
+ ['export posix wget append assignment', `export POSIXLY_CORRECT+=1; wget --no-config -O src/plus-export-owned.ts https://example.test/file -O -`],
2482
+ ['declare posix wget append assignment', `declare -x POSIXLY_CORRECT+=1; wget --no-config -O src/plus-declare-owned.ts https://example.test/file -O -`],
2483
+ ['function posix wget append assignment', `f(){ export POSIXLY_CORRECT+=1; }; f; wget --no-config -O src/plus-function-owned.ts https://example.test/file -O -`],
2484
+ ['wrapper posix wget append assignment', `command env POSIXLY_CORRECT+=1 wget --no-config -O src/plus-wrapper-owned.ts https://example.test/file -O -`],
2485
+ ['quoted posix wget append assignment', `export "POSIXLY_CORRECT+=1"; wget --no-config -O src/plus-quoted-owned.ts https://example.test/file -O -`],
2486
+ ['function local posix wget append assignment', `f(){ local -x POSIXLY_CORRECT+=1; wget --no-config -O src/plus-local-owned.ts https://example.test/file -O -; }; f`],
2487
+ ['unset posix then append wget', `export POSIXLY_CORRECT=1; unset POSIXLY_CORRECT; export POSIXLY_CORRECT+=1; wget --no-config -O src/unset-append-owned.ts https://example.test/file -O -`],
2488
+ ['braced wget stdout body override', `WGET_OPT=--output-document=src/braced-wget-owned.ts; wget -O - \${WGET_OPT} https://example.test/file`],
2489
+ ['command substitution wget log injection', `wget -O - $(printf '%s' '--output-file=src/substitution-wget.log') https://example.test/file`],
2490
+ ['exported posix wget ordering', `export POSIXLY_CORRECT=1; wget -O src/exported-posix-owned.ts https://example.test/file -O -`],
2491
+ ['nested posix wget ordering', `POSIXLY_CORRECT=1 sh -c 'wget -O src/nested-posix-owned.ts https://example.test/file -O -'`],
2492
+ ['quote concatenated posix wget ordering', `export POSIXLY_''CORRECT=1; wget -O src/quoted-posix-owned.ts https://example.test/file -O -`],
2493
+ ['escaped posix wget ordering', `export POSIXLY_\\CORRECT=1; wget -O src/escaped-posix-owned.ts https://example.test/file -O -`],
2494
+ ['ansi c posix wget ordering', `export $'POSIXLY_CORRECT'=1; wget -O src/ansi-posix-owned.ts https://example.test/file -O -`],
2495
+ ['dynamic name posix wget ordering', `POSIX_NAME=POSIXLY_CORRECT; export "$POSIX_NAME=1"; wget -O src/dynamic-posix-owned.ts https://example.test/file -O -`],
2496
+ ['split dynamic name posix wget ordering', `A=POSIXLY; B=_CORRECT; export "$A$B=1"; wget -O src/split-dynamic-posix-owned.ts https://example.test/file -O -`],
2497
+ ['command export posix wget ordering', `command export POSIXLY_CORRECT=1; wget -O src/command-export-posix-owned.ts https://example.test/file -O -`],
2498
+ ['command env posix wget ordering', `command env POSIXLY_CORRECT=1 wget -O src/command-env-posix-owned.ts https://example.test/file -O -`],
2499
+ ['command double dash export posix wget ordering', `command -- export POSIXLY_CORRECT=1; wget -O src/command-dash-posix-owned.ts https://example.test/file -O -`],
2500
+ ['compound dynamic export posix wget ordering', `N=POSIXLY_CORRECT; if export "$N=1"; then wget -O src/compound-posix-owned.ts https://example.test/file -O -; fi`],
2501
+ ['time env posix wget ordering', `time env POSIXLY_CORRECT=1 wget -O src/time-posix-owned.ts https://example.test/file -O -`],
2502
+ ['function export posix wget ordering', `f(){ export POSIXLY_CORRECT=1; }; f; wget -O src/function-posix-owned.ts https://example.test/file -O -`],
2503
+ ['function dynamic export posix wget ordering', `f(){ N=POSIXLY_CORRECT; export "$N=1"; }; f; wget -O src/function-dynamic-posix-owned.ts https://example.test/file -O -`],
2504
+ ['function local export posix wget ordering', `f(){ local -x POSIXLY_CORRECT=1; wget -O src/local-posix-owned.ts https://example.test/file -O -; }; f`],
2505
+ ['function local dynamic export posix wget ordering', `f(){ N=POSIXLY_CORRECT; local -x "$N=1"; wget -O src/local-dynamic-posix-owned.ts https://example.test/file -O -; }; f`],
2506
+ ['case arm function wget', `f(){ wget https://example.test/file; }; case true in true) f;; esac`],
2507
+ ['multi case arm function wget', `f(){ wget https://example.test/file; }; case x in a) true;; x) f;; esac`],
2508
+ ['direct case arm wget log', `case true in true) wget --no-config -o src/case-wget.log -O - http://127.0.0.1:1/;; esac`],
2509
+ ['direct case arm mutator', `case true in true) touch src/case-mutator.ts;; esac`],
2510
+ ['pre redefinition posix wget', `f(){ export POSIXLY_CORRECT=1; }; f; f(){ :; }; wget --no-config -O src/redefined-posix-owned.ts https://example.test/file -O -`],
2511
+ ['nested shadowed builtin posix wget', `echo(){ export POSIXLY_CORRECT=1; }; f(){ echo; }; f; wget --no-config -O src/nested-posix-owned.ts https://example.test/file -O -`],
2512
+ ['deep nested shadowed builtin posix wget', `echo(){ export POSIXLY_CORRECT=1; }; f(){ g(){ echo; }; g; }; f; wget --no-config -O src/deep-nested-posix-owned.ts https://example.test/file -O -`],
2513
+ ['case pattern alternation posix wget', `f(){ export POSIXLY_CORRECT=1; }; case true in true|false) f;; esac; wget --no-config -O src/case-posix-owned.ts https://example.test/file -O -`],
2514
+ ['env chdir wget log', `env -C src wget --no-config -o .omx/state/env-c.log -O - http://127.0.0.1:1/`],
2515
+ ['wgetrc logfile write', `printf '%s\n' 'logfile = src/wgetrc.log' | WGETRC=/dev/stdin wget -O - http://127.0.0.1:1/`],
2516
+ ['wgetrc output document write', `printf '%s\n' 'output_document = src/wgetrc-output.ts' | WGETRC=/dev/stdin wget -O - http://127.0.0.1:1/`],
2517
+ ['wgetrc directory prefix write', `printf '%s\n' 'dir_prefix = src/wgetrc-directory' | WGETRC=/dev/stdin wget -O - http://127.0.0.1:1/`],
2518
+ ['command wrapper function shadow', `touch(){ :; }; command touch src/wrapped-shadow-owned.ts`],
2519
+ ['short circuit posix unset', `export POSIXLY_CORRECT=1; false && unset POSIXLY_CORRECT; wget --no-config -O src/short-circuit-owned.ts http://127.0.0.1:1/ -O -`],
2520
+ ['conditional function rebind', `f(){ export POSIXLY_CORRECT=1; }; if false; then f(){ :; }; fi; f; wget --no-config -O src/conditional-rebind-owned.ts http://127.0.0.1:1/ -O -`],
2521
+ ['readonly posix unset', `export POSIXLY_CORRECT=1; readonly POSIXLY_CORRECT; unset POSIXLY_CORRECT; wget --no-config -O src/readonly-unset-owned.ts http://127.0.0.1:1/ -O -`],
2522
+ ['prefix posix function', `f(){ wget --no-config -O src/prefix-function-owned.ts http://127.0.0.1:1/ -O -; }; POSIXLY_CORRECT=1 f`],
2523
+ ['prefix posix append function', `f(){ wget --no-config -O src/prefix-append-function-owned.ts http://127.0.0.1:1/ -O -; }; POSIXLY_CORRECT+=1 f`],
2524
+ ['declare global posix', `f(){ declare -gx POSIXLY_CORRECT=1; }; f; wget --no-config -O src/declare-global-owned.ts http://127.0.0.1:1/ -O -`],
2525
+ ['export before local posix', `f(){ export POSIXLY_CORRECT=1; local POSIXLY_CORRECT=1; }; f; wget --no-config -O src/export-before-local-owned.ts http://127.0.0.1:1/ -O -`],
2526
+ ['process substitution current shell', `f(){ export POSIXLY_CORRECT=1; }; f <(true); wget --no-config -O src/process-substitution-owned.ts http://127.0.0.1:1/ -O -`],
2527
+ ['coproc ordinary argument', `f(){ export POSIXLY_CORRECT=1; }; f coproc; wget --no-config -O src/coproc-argument-owned.ts http://127.0.0.1:1/ -O -`],
2528
+ ['subshell function rebind', `g(){ export POSIXLY_CORRECT=1; }; f(){ g(){ :; }; }; ( f ); g; wget --no-config -O src/subshell-rebind-owned.ts http://127.0.0.1:1/ -O -`],
2529
+ ['background function rebind', `g(){ export POSIXLY_CORRECT=1; }; f(){ g(){ :; }; }; f & wait; g; wget --no-config -O src/background-rebind-owned.ts http://127.0.0.1:1/ -O -`],
2530
+ ['pipeline function rebind', `g(){ export POSIXLY_CORRECT=1; }; f(){ g(){ :; }; }; f | cat; g; wget --no-config -O src/pipeline-rebind-owned.ts http://127.0.0.1:1/ -O -`],
2531
+ ['coproc function rebind', `g(){ export POSIXLY_CORRECT=1; }; f(){ g(){ :; }; }; coproc f; wait; g; wget --no-config -O src/coproc-rebind-owned.ts http://127.0.0.1:1/ -O -`],
2532
+ ['later case pattern posix', `f(){ export POSIXLY_CORRECT=1; }; case true in false) :;; true|false) f;; esac; wget --no-config -o src/later-case.log http://127.0.0.1:1/ -o .omx/state/final.log -O -`],
2533
+ ['optional case pattern posix', `f(){ export POSIXLY_CORRECT=1; }; case true in (true) f;; esac; wget --no-config -O src/optional-case-owned.ts http://127.0.0.1:1/ -O -`],
2534
+ ['nested case pattern posix', `f(){ export POSIXLY_CORRECT=1; }; case true in false) case true in true) :;; esac;; true|false) f;; esac; wget --no-config -O src/nested-case-owned.ts http://127.0.0.1:1/ -O -`],
2535
+ ['env chdir curl', `env -C src curl -q -o .omx/state/curl-owned.log http://127.0.0.1:1/`],
2536
+ ['env chdir mutator', `env -C src mkdir -p .omx/state/cwd-owned`],
2537
+ ['env long chdir mutator', `env --chdir=src touch .omx/state/env-touch-owned`],
2538
+ ['persistent wgetrc config', `export WGETRC=/dev/stdin; printf '%s\n' 'logfile = src/persistent-wgetrc.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
2539
+ ['nested posix shell environment', `POSIXLY_CORRECT=1 sh -c 'wget --no-config -o src/nested-posix.log http://127.0.0.1:1/ -o .omx/state/final.log -O -'`],
2540
+ ['lastpipe posix', `bash -O lastpipe -c 'f(){ export POSIXLY_CORRECT=1; }; true | f; wget --no-config -o src/lastpipe.log http://127.0.0.1:1/ -o .omx/state/final.log -O -'`],
2541
+ ['subshell definition scope', `f(){ export POSIXLY_CORRECT=1; }; ( :; f(){ :; }; ); f; wget --no-config -o src/redefinition-scope.log http://127.0.0.1:1/ -o .omx/state/final.log -O -`],
2542
+ ['shadowed env function', `env(){ export POSIXLY_CORRECT=1; }; env; wget --no-config -o src/shadow-env.log http://127.0.0.1:1/ -o .omx/state/final.log -O -`],
2543
+ ['command suppressed shadow', `export POSIXLY_CORRECT=1; echo(){ unset POSIXLY_CORRECT; }; command echo safe; wget --no-config -o src/command-shadow.log http://127.0.0.1:1/ -o .omx/state/final.log -O -`],
2544
+ ['assignment shaped wget operand', `POSIXLY_CORRECT=1 wget --no-config -o src/assignment-operand.log NAME=value -o .omx/state/final.log -O -`],
2545
+ ['wrapped xargs wget', `printf '%s\n' '--output-file=src/wrapped-xargs.log' 'http://127.0.0.1:1/' | env xargs wget --no-config -O -`],
2546
+ ['wrapped xargs empty eof mutator', `printf '%s\n' src/wrapped-xargs-owned.ts | env xargs -E '' touch .omx/state/anchor`],
2547
+ ['nested chdir shell', `env --chdir=src sh -c 'touch .omx/state/env-nested-owned'`],
2548
+ ['case branch posix unset', `case true in true) export POSIXLY_CORRECT=1;; false) unset POSIXLY_CORRECT;; esac; wget --no-config -O src/case-branch-owned.ts http://127.0.0.1:1/ -O -`],
2549
+ ['subshell direct posix unset', `export POSIXLY_CORRECT=1; ( unset POSIXLY_CORRECT ); wget --no-config -o src/subshell-unset.log http://127.0.0.1:1/ -o .omx/state/final.log -O -`],
2550
+ ['env long chdir wget', `env --chdir=src wget --no-config -o .omx/state/env-long-wget.log -O - http://127.0.0.1:1/`],
2551
+ ['env long chdir curl', `env --chdir=src curl -q -o .omx/state/env-long-curl.log http://127.0.0.1:1/`],
2552
+ ['posix no config after operand', `POSIXLY_CORRECT=1 wget --spider https://example.test/file --no-config`],
2553
+ ['posix no hsts after operand', `HOME=src POSIXLY_CORRECT=1 wget --no-config --spider https://example.test/file --no-hsts`],
2554
+ ['bare bash login shell', `bash -lc "printf safe"`],
2555
+ ['subshell sequence posix wget', `( export POSIXLY_CORRECT=1; wget --no-config -O src/subshell-sequence-owned.ts http://127.0.0.1:1/ -O - )`],
2556
+ ['conditional unbound touch', `if false; then :; touch(){ :; }; fi; touch src/conditional-binding-owned.ts`],
2557
+ ['reassigned exported wgetrc', `export WGETRC=/dev/null; WGETRC=/dev/stdin; printf '%s\n' 'logfile = src/reassigned-wgetrc.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
2558
+ ['function local wgetrc restoration', `export WGETRC=/dev/stdin; f(){ local -x WGETRC=/dev/null; export POSIXLY_CORRECT=1; }; f; printf '%s\n' 'logfile = src/function-scope-wgetrc.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
2559
+ ['short circuit wgetrc', `export WGETRC=/dev/stdin; false && export WGETRC=/dev/null; printf '%s\n' 'logfile = src/short-circuit-wgetrc.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
2560
+ ['short circuit function rebind', `f(){ export POSIXLY_CORRECT=1; }; false && f(){ :; }; f; wget --no-config -O src/short-rebind-owned.ts http://127.0.0.1:1/ -O -`],
2561
+ ['case arm pipeline touch', `case x in x) : | touch src/case-pipeline-owned.ts;; esac`],
2562
+ ['case pipeline function rebind', `g(){ export POSIXLY_CORRECT=1; }; f(){ g(){ :; }; }; case true in true) f | cat;; esac; g; wget --no-config -O src/case-pipeline-rebind-owned.ts http://127.0.0.1:1/ -O -`],
2563
+ ['process substitution wgetrc state', `export WGETRC=/dev/stdin; : <(export WGETRC=/dev/null); printf '%s\n' 'logfile = src/process-sub-state.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
2564
+ ['background group wgetrc state', `export WGETRC=/dev/stdin; { export WGETRC=/dev/null; } & wait; printf '%s\n' 'logfile = src/background-group.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
2565
+ ['readonly wgetrc unset', `export WGETRC=/dev/stdin; readonly WGETRC; unset WGETRC; printf '%s\n' 'logfile = src/readonly-wgetrc.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
2566
+ ['unset function mode wgetrc', `export WGETRC=/dev/stdin; unset -f WGETRC; printf '%s\n' 'logfile = src/unset-mode-wgetrc.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
2567
+ ['quoted empty wget operand', `env -u POSIXLY_CORRECT wget --no-config -O - '' -o src/quoted-empty.log http://127.0.0.1:1/`],
2568
+ ['assignment shaped mutator argv', `env -C src touch ../.omx/state/final.log OWNED=value`],
2569
+ ['comment function binding', `true # ; touch(){ :; }\ntouch src/comment-binding-owned.ts`],
2570
+ ['skipped if wgetrc', `export WGETRC=/dev/stdin; if false; then export WGETRC=/dev/null; fi; printf '%s\n' 'logfile = src/skipped-if-wgetrc.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
2571
+ ['mutually exclusive case home', `export HOME=/dev/stdin; case true in true) export HOME=/dev/null;; false) :;; esac; wget -O - http://127.0.0.1:1/`],
2572
+ ['synchronous brace posix', `f(){ export POSIXLY_CORRECT=1; }; { f; }; WGETRC=/dev/null wget -O src/synchronous-brace-owned.ts https://example.test/file -O -`],
2573
+ ['nested isolation parent state', `( export POSIXLY_CORRECT=1; ( wget --no-config -O src/nested-region-owned.ts http://127.0.0.1:1/ -O - ) )`],
2574
+ ['function alternative state join', `f(){ export WGETRC=/dev/stdin; }; if false; then f(){ export WGETRC=/dev/null; }; fi; f; printf '%s\n' 'logfile = src/function-alt-join.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
2575
+ ['function binding alternative join', `g(){ :; }; f(){ g(){ export POSIXLY_CORRECT=1; }; }; if false; then f(){ :; }; fi; f; g; wget --no-config -O src/function-binding-join-owned.ts http://127.0.0.1:1/ -O -`],
2576
+ ['conditional wrapper unbound', `if false; then :; env(){ :; }; fi; env touch src/conditional-env-owned.ts`],
2577
+ ['shadowed false status', `false(){ :; }; false && export POSIXLY_CORRECT=1; wget --no-config -O src/shadow-false-owned.ts http://127.0.0.1:1/ -O -`],
2578
+ ['path conditional readonly', `export WGETRC=/dev/null; if false; then readonly WGETRC; fi; WGETRC=/dev/stdin; printf '%s\n' 'logfile = src/maybe-readonly-wgetrc.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
2579
+ ['readonly function mode', `export WGETRC=/dev/null; readonly -f WGETRC; WGETRC=/dev/stdin; printf '%s\n' 'logfile = src/readonly-function-mode.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
2580
+ ['local inherit wgetrc', `export WGETRC=/dev/stdin; f(){ local -I WGETRC; printf '%s\n' 'logfile = src/local-inherit-wgetrc.log' | HOME=/dev/null wget -O - http://127.0.0.1/; }; f`],
2581
+ ['case fallthrough touch', `case x in x) : ;& true) touch src/case-fallthrough-owned.ts;; esac`],
2582
+ ['case reserved word touch', `case case in case) touch src/case-reserved-word-owned.ts;; esac`],
2583
+ ['parameter length comment touch', `: \${#x}; touch src/parameter-length-comment-owned.ts`],
2584
+ ['touch mode arity', `touch .omx/state/final.log -m src/touch-option-owned.ts`],
2585
+ ['nested export posix', `export POSIXLY_CORRECT=1; sh -c 'wget --no-config -O src/nested-export-owned.ts http://127.0.0.1:1/ -O -'`],
2586
+ ['process substitution inherited posix', `export POSIXLY_CORRECT=1; cat <(wget --no-config -O src/process-inherited-owned.ts http://127.0.0.1:1/ -O -)`],
2587
+ ['bashopts lastpipe', `env BASHOPTS=lastpipe bash -c 'f(){ export POSIXLY_CORRECT=1; }; true | f; wget --no-config -O src/bashopts-lastpipe-owned.ts http://127.0.0.1:1/ -O -'`],
2588
+ ['skipped function call state', `export WGETRC=/dev/stdin; f(){ export WGETRC=/dev/null; }; if false; then f; fi; printf '%s\n' 'logfile = src/skipped-function.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
2589
+ ['nested isolation wgetrc', `export WGETRC=/dev/null; ( export WGETRC=/dev/stdin; ( printf '%s\n' 'logfile = src/nested-isolation.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/ ) )`],
2590
+ ['declare function local', `export WGETRC=/dev/stdin; f(){ declare -x WGETRC=/dev/null; }; f; printf '%s\n' 'logfile = src/declare-local.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
2591
+ ['readonly branch join', `export WGETRC=/dev/null; if true; then :; else readonly WGETRC; fi; WGETRC=/dev/stdin; printf '%s\n' 'logfile = src/readonly-join.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
2592
+ ['nested background isolation parent state', `( export POSIXLY_CORRECT=1; ( wget --no-config -O src/nested-background-owned.ts http://127.0.0.1:1/ -O - ) ) & wait`],
2593
+ ['case retest fallthrough touch', `case x in x) : ;;& x) touch src/case-retest-owned.ts;; esac`],
2594
+ ['recursive nested cwd root', `env --chdir=src sh -c "sh -c 'mkdir -p .omx/state/nested-cwd-owned'"`],
2595
+ ['process substitution lexical cwd', `cd src; cat <(mkdir -p .omx/state/process-cwd-owned)`],
2596
+ ['nameref posix wget', `f(){ declare -n ref=POSIXLY_CORRECT; export ref=1; }; f; wget --no-config -O src/nameref-posix-owned.ts https://example.test/file -O -`],
2597
+ ['global nameref posix wget', `f(){ declare -gn ref=POSIXLY_CORRECT; }; f; export ref=1; wget --no-config -O src/global-nameref-posix-owned.ts https://example.test/file -O -`],
2598
+ ['arithmetic expansion posix wget', `export POSIXLY_CORRECT; : $((POSIXLY_CORRECT=1)); wget --no-config https://example.test/file -O src/arithmetic-posix-owned.ts`],
2599
+ ['curl header sink', `curl -q --dump-header src/curl-header-owned.txt https://example.test/file`],
2600
+ ['rsync remote sink', `rsync .omx/state/conductor-ledger.json example.test:state/`],
2601
+ ['rsync remote log sink', `rsync --log-file=.omx/state/rsync.log .omx/state/conductor-ledger.json example.test:state/`],
2602
+ ['function cwd state', `f(){ cd src; }; f; mkdir -p .omx/state/function-cwd-owned`],
2603
+ ['function caller local state', `export WGETRC=/dev/null; g(){ WGETRC=/dev/stdin; }; f(){ local -x WGETRC=/dev/null; g; printf '%s\n' 'logfile = src/function-caller-local-owned.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/; }; f`],
2604
+ ['process substitution before inline unset', `export POSIXLY_CORRECT=1; unset POSIXLY_CORRECT <(wget --no-config -O src/process-inline-unset-owned.ts http://127.0.0.1:1/ -O -)`],
2605
+ ['process substitution before function', `export POSIXLY_CORRECT=1; f(){ unset POSIXLY_CORRECT; }; f <(wget --no-config -O src/process-before-function-owned.ts http://127.0.0.1:1/ -O -)`],
2606
+ ['nested process substitution touch', `cat <(cat <(touch src/nested-process-owned.ts))`],
2607
+ ['conditional job control lastpipe', `export POSIXLY_CORRECT=1; shopt -s lastpipe; if true; then set -m; fi; f(){ unset POSIXLY_CORRECT; }; true | f; wget --no-config -O src/jobcontrol-owned.ts http://127.0.0.1:1/ -O -`],
2608
+ ['unsupported shopt option', `shopt -s extglob; touch .omx/state/final.log`],
2609
+ ['dynamic shopt lastpipe', `mode=lastpipe; shopt -s "$mode"; touch .omx/state/final.log`],
2610
+ ['loop function definition', `while false; do touch(){ :; }; done; touch src/loop-definition-owned.ts`],
2611
+ ['background function definition', `{ touch(){ :; }; } & wait; touch src/background-definition-owned.ts`],
2612
+ ['pipeline function definition', `{ touch(){ :; }; } | cat; touch src/pipeline-definition-owned.ts`],
2613
+ ['coproc function definition', `coproc { touch(){ :; }; }; wait; touch src/coproc-definition-owned.ts`],
2614
+ ['coproc external touch mutation', `coproc touch src/coproc-external-owned.ts; wait`],
2615
+ ['coproc function runtime does not leak', `coproc { local_read(){ :; }; local_read; }; wait; local_read`],
2616
+ ['subshell function definition', `( touch(){ :; }; ); touch src/subshell-definition-owned.ts`],
2617
+ ['conditional readonly function', `f(){ :; }; if false; then readonly -f f; fi; f(){ export POSIXLY_CORRECT=1; }; f; wget --no-config -O src/conditional-readonly-function-owned.ts http://127.0.0.1:1/ -O -`],
2618
+ ['readonly unbound function', `readonly -f touch; touch src/readonly-unbound-owned.ts`],
2619
+ ['unexported child function', `touch(){ :; }; bash -c 'touch src/unexported-child-function-owned.ts'`],
2620
+ ['export nf removes child function', `f(){ :; }; export -f f; export -nf f; bash -c 'f; wget --no-config -O - https://example.test/file'`],
2621
+ ['exported child function uninspected body', `f(){ source src/exported-child-uninspected.sh; }; export -f f; bash -c 'f'`],
2622
+ ['env child unset', `export POSIXLY_CORRECT=1; env unset POSIXLY_CORRECT; wget --no-config -O src/env-child-unset-owned.ts http://127.0.0.1:1/ -O -`],
2623
+ ['global attribute under local', `WGETRC=/dev/stdin; f(){ local WGETRC=/dev/null; declare -gx WGETRC; }; f; printf '%s\n' 'logfile = src/global-attribute-owned.log' | HOME=/dev/null wget -O - http://127.0.0.1:1/`],
2624
+ ['nameref unset referent', `export HOME=src; export WGETRC=/dev/null; declare -n ref=WGETRC; unset ref; wget -O - http://127.0.0.1:1/`],
2625
+ ['arithmetic compound posix', `unset POSIXLY_CORRECT; export POSIXLY_CORRECT; : x$((POSIXLY_CORRECT+=1)); wget --no-config -O src/arithmetic-compound-owned.ts http://127.0.0.1:1/ -O -`],
2626
+ ['parameter nameref posix', `unset POSIXLY_CORRECT; export POSIXLY_CORRECT; declare -n ref=POSIXLY_CORRECT; : x\${ref:=1}; wget --no-config -O src/parameter-nameref-owned.ts https://example.test/file -O -`],
2627
+ ['case escaped esac', `case x in \\esac|x) touch src/case-escaped-esac-owned.ts;; esac`],
2628
+ ['reserved filename touch', `touch .omx/state/final.log case`],
2629
+ ['reserved filename copy', `cp .omx/state/conductor-ledger.json case`],
2630
+ ['chmod reference product', `chmod --reference=.omx/state/session.json src/chmod-reference-owned.ts .omx/state/session.json`],
2631
+ ['chown reference product', `chown --reference=.omx/state/session.json src/chown-reference-owned.ts .omx/state/session.json`],
2632
+ ['chgrp reference product', `chgrp --reference=.omx/state/session.json src/chgrp-reference-owned.ts .omx/state/session.json`],
2633
+ ['chmod reference separated product', `chmod --reference .omx/state/session.json src/chmod-reference-separated-owned.ts`],
2634
+ ['chown reference separated product', `chown --reference .omx/state/session.json src/chown-reference-separated-owned.ts`],
2635
+ ['chgrp reference separated product', `chgrp --reference .omx/state/session.json src/chgrp-reference-separated-owned.ts`],
2636
+ ['curl cluster header sink', `curl -q -sD src/curl-cluster-owned.txt -o - http://127.0.0.1:1/`],
2637
+ ['curl config sink', `curl -q -K src/curl-config-owned.txt http://127.0.0.1:1/`],
2638
+ ['rsync log sink', `rsync --log-file=src/rsync-owned.log .omx/state/conductor-ledger.json .omx/state/rsync-copy`],
2639
+ ['rsync partial sink', `rsync --partial-dir=src/rsync-partial .omx/state/conductor-ledger.json .omx/state/rsync-copy`],
2640
+ ['rsync write batch sink', `rsync --write-batch=src/rsync-owned.batch .omx/state/conductor-ledger.json .omx/state/rsync-copy`],
2641
+ ['failed cd rsync metadata control', `cd .omx/missing; rsync --log-file=.omx/state/failed-cd-rsync.log .omx/state/conductor-ledger.json .omx/state/failed-cd-rsync-copy`],
2642
+ ['failed pushd rsync metadata control', `pushd .omx/missing; rsync --log-file=.omx/state/failed-pushd-rsync.log .omx/state/conductor-ledger.json .omx/state/failed-pushd-rsync-copy`],
2643
+ ['cleared exported child function', `touch(){ :; }; export -f touch; env -u BASH_FUNC_touch%% bash -c 'touch src/cleared-exported-child-owned.ts'`],
2644
+ ['cdpath relative cwd', `CDPATH=src; cd team; mkdir -p ../.omx/state/cdpath-owned`],
2645
+ ['cdpath absolute cwd product', `CDPATH=src; cd /tmp; touch src/cdpath-absolute-owned.ts`],
2646
+ ['cdpath balanced stack product', `CDPATH=src; pushd ./src; popd; touch src/cdpath-balanced-stack-owned.ts`],
2647
+ ['unbalanced popd product', `popd; touch src/unbalanced-popd-owned.ts`],
2648
+ ['reference without target', `chmod --reference=.omx/state/session.json`],
2649
+ ['chown reference without target', `chown --reference=.omx/state/session.json`],
2650
+ ['chgrp reference without target', `chgrp --reference=.omx/state/session.json`],
2651
+ ['chmod reference separated without target', `chmod --reference .omx/state/session.json`],
2652
+ ['chown reference separated without target', `chown --reference .omx/state/session.json`],
2653
+ ['chgrp reference separated without target', `chgrp --reference .omx/state/session.json`],
2654
+ ['exported bashopts lastpipe', `shopt -s lastpipe; export BASHOPTS; bash -c 'f(){ export POSIXLY_CORRECT=1; }; true | f; wget --no-config -O src/exported-bashopts-owned.ts http://127.0.0.1:1/ -O -'`],
2655
+ ['depth process substitution prefix', `f4(){ export POSIXLY_CORRECT=1; cat <(true); cat <(wget --no-config -O src/depth-prefix-owned.ts http://127.0.0.1:1/ -O -); }; f3(){ f4; }; f2(){ f3; }; f1(){ f2; }; f1`],
2656
+ ['wget stdout short directory prefix', `wget -O - -P src https://example.test/file`],
2657
+ ['wget stdout long directory prefix', `wget --output-document=- --directory-prefix=src https://example.test/file`],
2658
+ ['wget stdout short log target', `wget -O - -o src/wget-stdout.log https://example.test/file`],
2659
+ ['wget stdout long log target', `wget --output-document=- --output-file=src/wget-stdout.log https://example.test/file`],
2660
+ ['wget repeated output last file', `wget -O - https://example.test/file -O src/wget-last-output.ts`],
2661
+ ['wget repeated log last file', `wget -o .omx/state/ignored.log -o src/wget-final.log -O - https://example.test/file`],
2662
+ ['wget repeated directory last file', `wget -P .omx/state -P src https://example.test/file`],
2663
+ ['wget repeated long directory last file', `wget --directory-prefix=.omx/state --directory-prefix=src https://example.test/file`],
2664
+ ['wget short execute output sink', `wget --no-config -e 'output_document=src/wget-execute-owned.ts' https://example.test/file`],
2665
+ ['wget long execute log sink', `wget --no-config --execute='logfile=src/wget-execute.log' -O - https://example.test/file`],
2666
+ ['wget execute directory prefix sink', `wget --no-config --execute='dir_prefix=src/wget-execute-directory' https://example.test/file`],
2667
+ ['wget execute unknown directive', `wget --no-config --execute='future_directive=src/wget-unknown.ts' -O - https://example.test/file`],
2668
+ ['stdbuf i retains environment', `export POSIXLY_CORRECT=1; stdbuf -i 0 sh -c 'wget --no-config -O src/stdbuf-posix-owned.ts https://example.test/file -O -'`],
2669
+ ['env i reapplies prefix environment', `export POSIXLY_CORRECT=1; env -i POSIXLY_CORRECT=1 sh -c 'wget --no-config -O src/env-i-posix-owned.ts https://example.test/file -O -'`],
2670
+ ['curl startup config unresolved', `curl https://example.test/file`],
2671
+ ['curl write out output sink', `curl -q --write-out '%output{src/curl-write-out-owned.log}' https://example.test/file`],
2672
+ ['curl short write out output sink', `curl -q -w '%output{src/curl-short-write-out-owned.log}' https://example.test/file`],
2673
+ ['curl write out append sink', `curl -q --write-out '%output{>>src/curl-write-out-append.log}' https://example.test/file`],
2674
+ ['time short output sink', `time -o src/time-output-owned.log printf safe`],
2675
+ ['time long output sink', `time --output=src/time-long-output-owned.log printf safe`],
2676
+ ['command substitution later cwd', `cd src; printf '%s' "$(mkdir -p .omx/state/command-substitution-cwd-owned)"; cd ..`],
2677
+ ['backtick substitution later cwd', "cd src; printf '%s' \`mkdir -p .omx/state/backtick-substitution-cwd-owned\`; cd .."],
2678
+ ['command substitution later posix unset', `export POSIXLY_CORRECT=1; printf '%s' "$(wget --no-config -O src/command-substitution-posix-owned.ts http://127.0.0.1:1/ -O -)"; unset POSIXLY_CORRECT`],
2679
+ ['command substitution later function unset', `export POSIXLY_CORRECT=1; printf '%s' "$(wget --no-config -O src/command-substitution-function-owned.ts http://127.0.0.1:1/ -O -)"; f(){ unset POSIXLY_CORRECT; }; f`],
2680
+ ['grouped command substitution cwd', `( cd src; printf '%s' "$(mkdir -p .omx/state/group-command-substitution-owned)" )`],
2681
+ ['grouped process substitution cwd', `( cd src; cat <(mkdir -p .omx/state/group-process-substitution-owned) )`],
2682
+ ['printf v cdpath writer', `printf -v CDPATH '%s' src; cd shared; mkdir -p .omx/state/printf-v-cdpath-owned`],
2683
+ ['read posix writer', `export POSIXLY_CORRECT; read POSIXLY_CORRECT <<< 1; wget --no-config -O src/read-posix-writer-owned.ts http://127.0.0.1:1/ -O -`],
2684
+ ['printf v wgetrc writer', `printf -v WGETRC '%s' src/wgetrc; export WGETRC; wget -O - https://example.test/file`],
2685
+ ['getopts wgetrc writer', `export WGETRC; getopts a WGETRC; wget -O - https://example.test/file`],
2686
+ ['for cdpath writer', `for CDPATH in src; do :; done; cd shared; mkdir -p .omx/state/for-cdpath-owned`],
2687
+ ['select cdpath writer', `select CDPATH in src; do break; done; cd shared; mkdir -p .omx/state/select-cdpath-owned`],
2688
+ ['curl dynamic output', `OUT=src/curl-dynamic-output.log; curl -q -o "$OUT" https://example.test/file`],
2689
+ ['curl dynamic write out', `FMT='%output{src/curl-dynamic-write-out.log}'; curl -q -w "$FMT" https://example.test/file`],
2690
+ ['curl write out at file', `curl -q --write-out @.omx/state/curl-format https://example.test/file`],
2691
+ ['curl dynamic argv injection', `CURL_OPTIONS='--upload-file=.omx/state/conductor-ledger.json'; curl -q $CURL_OPTIONS https://example.test/file`],
2692
+ ['wget dynamic argv injection', `WGET_OPTIONS='--post-data=mode=write'; wget --no-config $WGET_OPTIONS -O - https://example.test/file`],
2693
+ ['curl post data', `curl -q --data 'mode=write' https://example.test/file`],
2694
+ ['curl request post', `curl -q --request POST https://example.test/file`],
2695
+ ['curl upload file', `curl -q --upload-file .omx/state/conductor-ledger.json https://example.test/file`],
2696
+ ['wget post data', `wget --no-config --post-data='mode=write' -O - https://example.test/file`],
2697
+ ['wget method post', `wget --no-config --method=POST -O - https://example.test/file`],
2698
+ ['wget background', `wget --no-config --background -O - https://example.test/file`],
2699
+ ['rsync dynamic option', `OPTS='--log-file=src/rsync-dynamic.log'; rsync $OPTS .omx/state/conductor-ledger.json .omx/state/rsync-copy`],
2700
+ ['rsync short cluster temp dir', `rsync -aTsrc/rsync-cluster-temp .omx/state/conductor-ledger.json .omx/state/rsync-copy`],
2701
+ ['rsync short cluster temp dir next argv', `rsync -aT src/rsync-cluster-temp .omx/state/conductor-ledger.json .omx/state/rsync-copy`],
2702
+ ['rsync short cluster unknown', `rsync -aZ .omx/state/conductor-ledger.json .omx/state/rsync-copy`],
2703
+ ['time abbreviated output sink', `time --out=src/time-abbreviated-output.log printf safe`],
2704
+ ['time exact separate output sink', `time --output src/time-exact-separate-output.log printf safe`],
2705
+ ['time separate abbreviated output sink', `time --out src/time-separate-output.log printf safe`],
2706
+ ['time attached output sink', `time -osrc/time-attached-output.log printf safe`],
2707
+ ['time wrapper attached output sink', `env time -osrc/time-wrapper-attached-output.log printf safe`],
2708
+ ['time append attached output sink', `time -aosrc/time-append-attached-output.log printf safe`],
2709
+ ['time dynamic output sink', `OUT=src/time-dynamic-output.log; time -o "$OUT" printf safe`],
2710
+ ['time malformed output sink', `time --output= printf safe`],
2711
+ ['path repository shadow', `cp -p /usr/bin/touch .omx/state/cat; PATH=.omx/state; cat src/conductor-owned.ts`],
2712
+ ['path prefix repository shadow', `cp -p /usr/bin/touch .omx/state/cat; PATH=.omx/state cat src/conductor-owned.ts`],
2713
+ ['path env repository shadow', `cp -p /usr/bin/touch .omx/state/cat; env PATH=.omx/state cat src/conductor-owned.ts`],
2714
+ ['cdpath global dirty function', `CDPATH=.omx; f(){ local CDPATH=.omx; declare -g CDPATH=src; }; f; cd shared; mkdir -p .omx/state/cdpath-global-dirty-owned`],
2715
+ ['cp hardlink then reference', `cp -l .omx/state/conductor-ledger.json .omx/state/linked-ledger; chmod --reference=.omx/state/conductor-ledger.json .omx/state/linked-ledger`],
2716
+ ['curl method override header', `curl -q -H 'X-HTTP-Method-Override: POST' -o .omx/state/curl-method-override.log https://example.test/file`],
2717
+ ['curl next transfer product output', `curl -q --output-dir src -o first.log https://example.test/first --next --output-dir .omx/state -o second.log https://example.test/second`],
2718
+ ['wget warc cdx standalone sink', `wget --no-config --warc-cdx .omx/state/wget-cdx.log https://example.test/file`],
2719
+ ['wget no proxy standalone sink', `wget --no-config --no-proxy .omx/state/wget-proxy.log https://example.test/file`],
2720
+ ['wget short x standalone sink', `wget --no-config -x .omx/state/wget-x.log https://example.test/file`],
2721
+ ['rsync rsync-path helper execution', `rsync --rsync-path=.omx/state/helper .omx/state/conductor-ledger.json .omx/state/rsync-copy`],
2722
+ ['rmdir invalidates static cwd proof', `rmdir .omx/state/tmp; cd .omx/state/tmp; touch .omx/state/final.log`],
2723
+ ['posix special builtin prefix persistence', `set -o posix; POSIXLY_CORRECT=1 export POSIXLY_CORRECT; wget --no-config -O src/posix-special-owned.ts https://example.test/file -O -`],
2724
+ ['wget late no config', `wget -q --no-config -O - https://example.test/file`],
2725
+ ['curl output template traversal', `curl -q -o .omx/state/curl-#1.log https://example.test/file[..]`],
2726
+ ['native child dead reference marker', `f(){ chmod --reference=.omx/state/session.json .omx/state/dead-reference.log; }; touch src/live-after-dead-marker.log`],
2727
+ ['reference looking post end of options', `chmod 600 -- --reference=.omx/state/session.json`],
2728
+ ['curl quote request control', `curl -q --quote 'DELE state' https://example.test/file`],
2729
+ ['curl short quote request control', `curl -q -Q 'DELE state' https://example.test/file`],
2730
+ ['curl postquote request control', `curl -q --postquote 'DELE state' https://example.test/file`],
2731
+ ['curl dynamic prequote request control', `OP='NOOP'; curl -q --prequote "$OP" https://example.test/file`],
2732
+ ['curl unknown request control', `curl -q --request-target=/state https://example.test/file`],
2733
+ ['wget header method override', `wget --no-config --header='X-HTTP-Method-Override: POST' -O - https://example.test/file`],
2734
+ ['wget dynamic header method override', `HEADER='X-HTTP-Method-Override: POST'; wget --no-config --header="$HEADER" -O - https://example.test/file`],
2735
+ ['curl brace template traversal', `curl -q -o .omx/state/curl-#1.log 'https://example.test/{safe,../escape}'`],
2736
+ ['rsync archive then reference', `rsync -a --log-file=.omx/state/rsync-alias.log .omx/state/conductor-ledger.json .omx/state/rsync-alias-copy; chmod --reference=.omx/state/session.json .omx/state/rsync-alias-copy`],
2737
+ ['cp archive then reference', `cp -a .omx/state/conductor-ledger.json .omx/state/archive-ledger; chmod --reference=.omx/state/session.json .omx/state/archive-ledger`],
2738
+ ['nested rmdir invalidates cwd proof', `rmdir .omx/state/tmp; sh -c 'cd .omx/state/tmp; touch .omx/state/final.log'`],
2739
+ ['nested chmod invalidates cwd proof', `chmod --reference=.omx/state/session.json .omx/state/tmp; sh -c 'cd .omx/state/tmp; touch .omx/state/final.log'`],
2740
+ ['relative slash command', `./src/conductor-owned.ts`],
2741
+ ['unresolved arithmetic expansion', `touch .omx/state/unresolved-arithmetic.log $((UNFINISHED)`],
2742
+ ['elif mutation reachability', `if false; then :; elif true; then touch src/elif-reachable-owned.ts; fi`],
2743
+ ['dynamic loader preload wrapper', `LD_PRELOAD=.omx/state/mutator.so cat src/conductor-owned.ts`],
2744
+ ['dynamic loader audit env wrapper', `env LD_AUDIT=.omx/state/audit.so cat src/conductor-owned.ts`],
2745
+ ['curl clustered form mutation', `curl -q -sF field=value https://example.test/file`],
2746
+ ['curl clustered quote mutation', `curl -q -sQ 'DELE state' https://example.test/file`],
2747
+ ['curl proxy header method override', `curl -q --proxy-header 'X-HTTP-Method-Override: POST' https://example.test/file`],
2748
+ ['rsync product destination', `rsync README.md src/readme.md`],
2749
+ ['recursive arithmetic substitution', `arith='slot[$(touch src/arithmetic-owned.ts)0]'; : $((arith))`],
2750
+ ['arithmetic array subscript', `slot=1; : $((slot[$(touch src/arithmetic-array-owned.ts)0]))`],
2751
+ ['numeric binding arithmetic', `N=1; : $((N << 2))`],
2752
+ ['conditional numeric binding arithmetic', `arith='slot[$(touch src/arithmetic-conditional-owned.ts)0]'; if false; then arith=1; fi; : $((arith))`],
2753
+ ['printf v numeric binding arithmetic', `N=1; printf -v N '%s' 'slot[$(touch src/arithmetic-printf-owned.ts)0]'; : $((N))`],
2754
+ ['loader conditional clear', `declare -n loader=LD_PRELOAD; export loader; loader=.omx/state/mutator.so; false && unset loader; /usr/bin/cat /dev/null`],
2755
+ ['loader function clear', `declare -n loader=LD_PRELOAD; export loader; loader=.omx/state/mutator.so; clear_loader(){ unset loader; }; false && clear_loader; /usr/bin/cat /dev/null`],
2756
+ ['node persistent v8 coverage output', `export NODE_V8_COVERAGE=src; node -e "console.log('ok')"`],
2757
+ ['node function compile cache output', `configure_node(){ export NODE_COMPILE_CACHE=src; }; configure_node; node -e "console.log('ok')"`],
2758
+ ['cp parents path shaping', `cp --parents src/runtime.ts .omx/state`],
2759
+ ['cp hardlink cross boundary', `cp --link src/runtime.ts .omx/state/runtime-link.ts`],
2760
+ ['curl template unbounded capture', `curl -q -o .omx/state/curl-glob-#1.log https://example.test/file[1-999]`],
2761
+ ['gh dynamic web helper', `OPT=--web; GH_BROWSER=.omx/state/mutator PATH=/usr/bin:/bin gh issue create --title x --body y $OPT`],
2762
+ ['gh command prefix pager helper', `GH_PAGER=cat PATH=/usr/bin:/bin gh issue create --title x --body y`],
2763
+ ['loader nameref preload', `declare -n loader=LD_PRELOAD; export loader; loader=.omx/state/mutator.so; cat src/conductor-owned.ts`],
2764
+ ['loader debug output', `LD_DEBUG=libs LD_DEBUG_OUTPUT=src/ld-debug cat src/conductor-owned.ts`],
2765
+ ['loader profile output', `LD_PROFILE=cat LD_PROFILE_OUTPUT=src/ld-profile cat src/conductor-owned.ts`],
2766
+ ['node cpu profile output', `node --cpu-prof --cpu-prof-dir=src -e "console.log('ok')"`],
2767
+ ['node v8 coverage output', `NODE_V8_COVERAGE=src node -e "console.log('ok')"`],
2768
+ ['node compile cache output', `NODE_COMPILE_CACHE=src node -e "console.log('ok')"`],
2769
+ ['curl expand data request', `curl -q --expand-data 'mode=write' https://example.test/state`],
2770
+ ['curl expand output sink', `curl -q --variable OUT=src/curl-expand-owned.txt --expand-output '{{OUT}}' file:///etc/hosts`],
2771
+ ['curl unknown long option', `curl -q --future-option https://example.test/file`],
2772
+ ['gh repo create interactive', `PATH=/usr/bin:/bin gh repo create`],
2773
+ ['gh repo fork local remote', `PATH=/usr/bin:/bin gh repo fork OWNER/REPO`],
2774
+ ['curl header file source', `curl -q --header @.omx/state/headers -o .omx/state/header.log https://example.test/file`],
2775
+ ['wget warc tempdir sink', `wget --no-config --warc-tempdir=src -O - https://example.test/file`],
2776
+ ['install strip program execution', `install --strip-program=.omx/state/mutator package.json .omx/state/install-copy`],
2777
+ ['python unsafe x option', `python3 -I -X importtime -c "print('ok')"`],
2778
+ ['perl debugger option', `perl -d -e 'print'`],
2779
+ ['rg config file', `rg --config .omx/state/rg-config pattern`],
2780
+ ['git trace output', `GIT_TRACE=src/git-trace git diff`],
2781
+ ['python pycache prefix', `PYTHONPYCACHEPREFIX=src python3 -I -c "print('ok')"`],
2782
+ ['allexport node output', `set -a; NODE_V8_COVERAGE=src; node -e "console.log('ok')"`],
2783
+ ['loader command wrapper', `command env LD_PRELOAD=.omx/state/mutator.so cat src/conductor-owned.ts`],
2784
+ ['loader before env clear', `LD_PRELOAD=.omx/state/mutator.so /usr/bin/env -i /usr/bin/cat src/conductor-owned.ts`],
2785
+ ['loader in background pipeline', `LD_PRELOAD=.omx/state/mutator.so /usr/bin/cat src/conductor-owned.ts | cat &`],
2786
+ ['loader in later pipeline member', `printf x | LD_DEBUG=libs LD_DEBUG_OUTPUT=src/pipe-loader /usr/bin/cat /dev/null`],
2787
+ ['dynamic set option', `PS4='$(touch src/dynamic-set-owned.ts)'; OPTION=-x; set $OPTION; :`],
2788
+ ['prompt parameter transform', `PS4='$(touch src/prompt-transform-owned.ts)'; : \${PS4@P}`],
2789
+ ['dynamic declaration attribute', `ATTRIBUTE=i; declare -$ATTRIBUTE 'slot[$(touch src/dynamic-declare-owned.ts)0]=1'`],
2790
+ ['perl regex assertion in place', `perl -pi -e 's/(?!safe)/x/' .omx/state/conductor-ledger.json`],
2791
+ ['uniq second output operand', `uniq src/conductor-owned.ts src/uniq-owned.ts`],
2792
+ ['rg hostname helper', `rg --hostname-bin=.omx/state/inbox/helper needle src`],
2793
+ ['escaped time path shadow', `PATH=.omx/state/inbox:/usr/bin:/bin \\time printf safe`],
2794
+ ['quoted time path shadow', `PATH=.omx/state/inbox:/usr/bin:/bin 'time' printf safe`],
2795
+ ['ansi-c time path shadow', `PATH=.omx/state/inbox:/usr/bin:/bin $'time' printf safe`],
2796
+ ['concatenated time path shadow', `PATH=.omx/state/inbox:/usr/bin:/bin "ti"me printf safe`],
2797
+ ['coproc path shadow', `PATH=.omx/state/inbox:/usr/bin:/bin 'coproc' printf safe`],
2798
+ ['env chdir product mutation', `env --chdir=src touch ../.omx/state/final.log`],
2799
+ ['rsync permissions alias', `rsync -p .omx/state/conductor-ledger.json .omx/state/rsync-copy`],
2800
+ ['reference directory target', `chmod --reference=.omx/state/session.json .omx/state`],
2801
+ ['reference glob target', `chmod --reference=.omx/state/session.json .omx/state/*`],
2802
+ ['wget startup options after end', `WGETRC=/dev/stdin wget -O - http://127.0.0.1:1/ -- --no-config --no-hsts`],
2803
+ ['wget recursive metadata transfer', `wget --no-config --no-hsts --recursive --page-requisites --directory-prefix=.omx/state https://example.test/index.html`],
2804
+ ['wget SSL key log output', `SSLKEYLOGFILE=src/wget-tls.keys wget --no-config --no-hsts -O - https://example.test/file`],
2805
+ ['curl SSL key log output', `SSLKEYLOGFILE=src/curl-tls.keys curl -q -o - https://example.test/file`],
2806
+ ['curl SSL key log persistent output', `export SSLKEYLOGFILE=src/curl-tls.keys; curl -q -o - https://example.test/file`],
2807
+ ['wget SSL key log persistent output', `export SSLKEYLOGFILE=src/wget-tls.keys; wget --no-config --no-hsts -O - https://example.test/file`],
2808
+ ['curl SSL key log dynamic output', `SSLKEYLOGFILE="$TLS_KEY_LOG" curl -q -o - https://example.test/file`],
2809
+ ['rsync partial environment output', `RSYNC_PARTIAL_DIR=src/rsync-partials rsync .omx/state/conductor-ledger.json .omx/state/inbox/rsync-copy`],
2810
+ ['rsync exported runtime output', `export RSYNC_PARTIAL_DIR=src/rsync-partials; rsync .omx/state/conductor-ledger.json .omx/state/inbox/rsync-copy`],
2811
+ ['shellopts allexport posix ordering', `env SHELLOPTS=allexport bash -c 'POSIXLY_CORRECT=1; wget --no-config --no-hsts -O src/shellopts-owned.ts https://example.test/file -O -'`],
2812
+ ['bash posix PATH persistence', `bash -o posix -c 'PATH=.omx/state export PATH; cat src/conductor-owned.ts'`],
2813
+ ['bash long posix PATH persistence', `bash --posix -c 'PATH=.omx/state export PATH; cat src/conductor-owned.ts'`],
2814
+ ['bashopts allexport posix ordering', `env BASHOPTS=allexport bash -c 'POSIXLY_CORRECT=1; wget --no-config --no-hsts -O src/bashopts-owned.ts https://example.test/file -O -'`],
2815
+ ['unresolved PATH command not found', `command_not_found_handle(){ /usr/bin/touch src/path-owned.ts; }; PATH=/definitely/not-present; cat README.md`],
2816
+ ['curl command protocol local listener', `printf 'set state\n' | curl -q -m 1 telnet://127.0.0.1:9`],
2817
+ ['curl command protocol gopher', `curl -q -m 1 gopher://127.0.0.1:9/1/state`],
2818
+ ['curl command protocol smtp', `curl -q -m 1 smtp://127.0.0.1:9`],
2819
+ ['curl command protocol unknown', `curl -q -m 1 mutator://127.0.0.1/state`],
2820
+ ['curl remote header name cluster', `curl -q -OJ https://example.test/payload`],
2821
+ ['curl remote header name long', `curl -q --remote-header-name -O https://example.test/payload`],
2822
+ ['wget execute recursive directive', `wget --no-config --no-hsts -e 'recursive=on' -O - https://example.test/file`],
2823
+ ['mixed scheme wget directory prefix', `wget --no-config --no-hsts --directory-prefix=.omx/state https://example.test/file file:///etc/hosts`],
2824
+ ['mixed scheme curl remote name', `curl -q --output-dir=.omx/state -O https://example.test/file file:///etc/hosts`],
2825
+ ['unsupported bashopts localvar inherit', `CDPATH=src env BASHOPTS=localvar_inherit bash --noprofile --norc -c 'f(){ local CDPATH; cd state; touch ../.omx/state/owned; }; f'`],
2826
+ ['curl schemeless extra operand remote name', `curl -q --output-dir=.omx/state/inbox -O https://example.test/payload ftp.example.test/payload`],
2827
+ ['wget schemeless extra operand directory prefix', `wget --no-config --no-hsts --directory-prefix=.omx/state/inbox https://example.test/payload ftp.example.test/payload`],
2828
+ ['curl multiple static URLs remote name', `curl -q --output-dir=.omx/state/inbox -O https://example.test/first https://example.test/second`],
2829
+ ['wget multiple static URLs directory prefix', `wget --no-config --no-hsts --directory-prefix=.omx/state/inbox https://example.test/first https://example.test/second`],
2830
+ ['wget short input file decoy URL', `wget --no-config --no-hsts -i urls.txt -O - https://example.test/file`],
2831
+ ['wget attached input file decoy URL', `wget --no-config --no-hsts -iurls.txt -O - https://example.test/file`],
2832
+ ['wget clustered input file decoy URL', `wget --no-config --no-hsts -qiurls.txt -O - https://example.test/file`],
2833
+ ['wget long input file decoy URL', `wget --no-config --no-hsts --input-file=urls.txt -O - https://example.test/file`],
2834
+ ['wget execute tries zero', `wget --no-config --no-hsts -e 'tries=0' -O - https://example.test/file`],
2835
+ ['wget execute timeout zero', `wget --no-config --no-hsts -e 'timeout=0' -O - https://example.test/file`],
2836
+ ['wget direct tries zero', `wget --no-config --no-hsts --tries=0 -O - https://example.test/file`],
2837
+ ['wget short tries zero', `wget --no-config --no-hsts -t0 -O - https://example.test/file`],
2838
+ ['wget direct timeout zero', `wget --no-config --no-hsts --timeout=0 -O - https://example.test/file`],
2839
+ ['wget direct wait excessive', `wget --no-config --no-hsts --wait=61 -O - https://example.test/file`],
2840
+ ['wget execute waitretry infinite', `wget --no-config --no-hsts -e 'waitretry=infinite' -O - https://example.test/file`],
2841
+ ['wget short timeout zero', `wget --no-config --no-hsts -T0 -O - https://example.test/file`],
2842
+ ['wget short wait zero', `wget --no-config --no-hsts -w0 -O - https://example.test/file`],
2843
+ ['wget direct waitretry zero', `wget --no-config --no-hsts --waitretry=0 -O - https://example.test/file`],
2844
+ ['wget malformed wait', `wget --no-config --no-hsts --wait=one -O - https://example.test/file`],
2845
+ ['wget dynamic timeout', `wget --no-config --no-hsts --timeout="$WGET_TIMEOUT" -O - https://example.test/file`],
2846
+ ['wget connect timeout zero', `wget --no-config --no-hsts --connect-timeout=0 -O - https://example.test/file`],
2847
+ ['wget DNS timeout excessive', `wget --no-config --no-hsts --dns-timeout=61 -O - https://example.test/file`],
2848
+ ['wget read timeout dynamic', `wget --no-config --no-hsts --read-timeout="$WGET_TIMEOUT" -O - https://example.test/file`],
2849
+ ['wget execute connect timeout zero', `wget --no-config --no-hsts -e 'connecttimeout=0' -O - https://example.test/file`],
2850
+ ['wget late no-config WGETRC product log', `WGETRC=/dev/stdin; printf 'output = src/late-no-config-wgetrc.log\n' | wget -O - https://example.test/file --no-config --no-hsts`],
2851
+ ['curl glob cumulative cardinality', `curl -q -o .omx/state/curl-explosive.log 'https://example.test/{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p}{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p}{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p}{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p}'`],
2852
+ ['select conditional touch', `select choice in safe; do touch src/select-owned.ts; done </dev/null`],
2853
+ ['allexport POSIX Wget', `set -a; POSIXLY_CORRECT=1; wget --no-config --no-hsts -O src/allexport-posix-owned.ts https://example.test/file -O -`],
2854
+ ['allexport SSL keylog curl', `set -o allexport; SSLKEYLOGFILE=src/allexport-curl.keys; curl -q -o - https://example.test/file`],
2855
+ ['nested allexport SSL keylog curl', `bash -a -c 'SSLKEYLOGFILE=src/nested-allexport-curl.keys; curl -q -o - https://example.test/file'`],
2856
+ ['composed SSL keylog export', `NAME=SSLKEYLOGFILE; export "$NAME"; SSLKEYLOGFILE=src/composed-curl.keys; curl -q -o - https://example.test/file`],
2857
+ ['dynamic export name', `export "$UNRESOLVED_NAME"; cat src/conductor-owned.ts`],
2858
+ ['curl unquoted glob output sink', `curl -q -o .omx/state/*.log https://example.test/file`],
2859
+ ['curl unquoted brace URL', `curl -q -o .omx/state/curl-brace-#1.log https://example.test/{one,two}`],
2860
+ ['nested bash shopt localvar inherit cdpath', `CDPATH=src bash -O localvar_inherit -c 'f(){ local CDPATH; cd state; touch ../.omx/state/owned; }; f'`],
2861
+ ['nested bash shopt localvar inherit posix', `POSIXLY_CORRECT=1 bash -Olocalvar_inherit -c 'f(){ local POSIXLY_CORRECT; wget --no-config --no-hsts -O - https://example.test/file; }; f'`],
2862
+ ['nested bash shopt plus localvar inherit cdpath', `CDPATH=src bash +O localvar_inherit -c 'f(){ local CDPATH; cd state; touch ../.omx/state/owned; }; f'`],
2863
+ ['nested bash shopt plus attached localvar inherit posix', `POSIXLY_CORRECT=1 bash +Olocalvar_inherit -c 'f(){ local POSIXLY_CORRECT; wget --no-config --no-hsts -O - https://example.test/file; }; f'`],
2864
+ ['git cat-file filters', `git cat-file --filters HEAD:filtered.txt`],
2865
+ ['xtrace PS4 command substitution', `PS4='$(touch src/ps4-owned.ts)'; set -x; :`],
2866
+ ['keyword late environment assignment', `set -o keyword; wget --no-config --no-hsts -U POSIXLY_CORRECT=1 -O src/keyword-owned.ts https://example.test/file -O -`],
2867
+ ['interactive Bash startup', `HOME=.omx/state/bash-home bash -ic ':'`],
2868
+ ['clustered allexport POSIX ordering', `set -ae; POSIXLY_CORRECT=1; wget --no-config --no-hsts -O src/cluster-owned.ts https://example.test/file -O -`],
2869
+ ['nested clustered allexport POSIX ordering', `bash -ae -c 'POSIXLY_CORRECT=1; wget --no-config --no-hsts -O src/nested-cluster-owned.ts https://example.test/file -O -'`],
2870
+ ['local shell option snapshot', `set -a; f(){ local -; set +a; }; f; POSIXLY_CORRECT=1; wget --no-config --no-hsts -O src/local-option-owned.ts https://example.test/file -O -`],
2871
+ ['monitor disables lastpipe', `export POSIXLY_CORRECT=1; shopt -s lastpipe; set -o monitor; f(){ unset POSIXLY_CORRECT; }; true | f; wget --no-config --no-hsts -O src/monitor-owned.ts https://example.test/file -O -`],
2872
+ ['generic glob mutation target', `truncate --size=0 .omx/state/*.log`],
2873
+ ['generic brace mutation target', `touch .omx/state/{conductor,ledger}.log`],
2874
+ ['generic glob directory destination', `mv .omx/state/conductor-ledger.json .omx/state/inbox/*`],
2875
+ ['physical cd symlink escape', `cd -P .omx/state/link; cd ..; touch physical-owned.ts`],
2876
+ ['physical shell mode symlink escape', `set -P; cd .omx/state/link; cd ..; touch physical-mode-owned.ts`],
2877
+ ['Python metadata copy eval alias', `python3 -I - <<'PY'
2878
+ import shutil
2879
+ shutil.copyfile('a', '.omx/state/inbox/py-control')
2880
+ print=eval
2881
+ print("'Pa' + 'th'('src/python-alias-owned.ts').write_text('x')")
2882
+ PY`],
2883
+ ['literal quote metadata path', "mkdir -p \\'.omx/handoffs/run-1"],
2884
+ ['external dispatcher time wrapper', `PATH=.omx/state/inbox:/usr/bin:/bin env time printf safe`],
2885
+ ['Python interactive short cluster stdin', `python3 -Ii -c "print('safe')" <<'PY'
2886
+ __import__('os').system('touch src/python-interactive-owned.ts')
2887
+ PY`],
2888
+ ['Perl trailing substitution statement', `perl -pi -e 's/a/b/;system("touch src/perl-owned.ts") #/' .omx/state/conductor.log`],
2889
+ ['multiple Perl substitution programs', `perl -pi -e 's/a/b/' -e 's/b/c/' .omx/state/conductor.log`],
2890
+ ['multiple sed substitution programs', `sed -i -e 's/a/b/' -e 's/b/c/' .omx/state/conductor.log`],
2891
+ ['integer declaration secondary expansion', `declare -a slot; declare -i sink='slot[$(touch src/declare-integer-owned.ts)0]'`],
2892
+ ['zsh HOME startup', `HOME=.omx/state/zsh-home zsh -c ':'`],
2893
+ ['interactive Bash no-rc startup', `PS0='$(touch src/ps0-owned.ts)' bash --noprofile --norc -ic ':'`],
2894
+ ['background lastpipe pipeline', `export POSIXLY_CORRECT=1; shopt -s lastpipe; f(){ unset POSIXLY_CORRECT; }; true | f & wait; wget --no-config --no-hsts -o src/async-lastpipe.log http://127.0.0.1:1/ -o - -O -`],
2895
+ ['sed in-place backup sink', `printf x > .omx/state/runtime.ts; cd .omx/state; command sed --in-place='../../src/*' -e 's/x/y/' runtime.ts`],
2896
+ ['unbounded Python metadata copy', `python3 -I - <<'PY'
2897
+ import shutil
2898
+ shutil.copyfile('/dev/zero', '.omx/state/inbox/unbounded')
2899
+ PY`],
2900
+ ['unbounded dd metadata copy', `dd if=/dev/zero of=.omx/state/inbox/unbounded`],
2901
+ ];
2902
+ for (const [probe, command] of [
2903
+ ['node fs.rmSync', `node -e "require('fs').rmSync('src/victim.ts')"`],
2904
+ ['node create read stream write', `node -e "require('fs').createReadStream('src/victim.ts',{flags:'w'})"`],
2905
+ ['node bound create read stream append', `node -e "const {createReadStream: stream}=require('fs');stream('src/victim.ts',{flags:'a'})"`],
2906
+ ['posix uniq output after operand', `cd src; POSIXLY_CORRECT=1 uniq runtime.ts --count`],
2907
+ ['posix touch reference after operand', `POSIXLY_CORRECT=1 touch .omx/state/inbox/marker --reference src/runtime.ts`],
2908
+ ['nested sh posix special builtin', `sh -c 'POSIXLY_CORRECT=1 export POSIXLY_CORRECT; wget --no-config --no-hsts -O src/sh-posix-owned.ts https://example.test/file -O -'`],
2909
+ ['nested exec argv0 sh posix', `exec -a sh bash -c 'POSIXLY_CORRECT=1 export POSIXLY_CORRECT; wget --no-config --no-hsts -O src/exec-argv0-posix-owned.ts https://example.test/file -O -'`],
2910
+ ['nested env argv0 sh posix', `env --argv0 sh bash -c 'POSIXLY_CORRECT=1 export POSIXLY_CORRECT; wget --no-config --no-hsts -O src/env-argv0-posix-owned.ts https://example.test/file -O -'`],
2911
+ ['bare arithmetic command secondary expansion', `true='slot[$(touch src/arithmetic-command-owned.ts)0]'; ((true))`],
2912
+ ['arithmetic for secondary expansion', `for ((i=$(touch src/arithmetic-for-owned.ts); i<1; i++)); do :; done`],
2913
+ ['let secondary expansion', `let 'slot[$(touch src/let-owned.ts)0]=1'`],
2914
+ ['oversized metadata redirect', `head -c 16777217 /dev/zero > .omx/state/inbox/oversized`],
2915
+ ['unbounded metadata redirect', `cat /dev/zero > .omx/state/inbox/unbounded`],
2916
+ ['unbounded metadata redirect with unrelated heredoc', `cat /dev/zero > .omx/state/inbox/unbounded-with-heredoc; cat <<'EOF'
2917
+ bounded
2918
+ EOF`],
2919
+ ['oversized rsync metadata source', `rsync src/large.bin .omx/state/inbox/large-copy`],
2920
+ ['node fs.renameSync', `node -e "require('fs').renameSync('src/a.ts','src/b.ts')"`],
2921
+ ['node template interpolation', "node -e '" + '`${require("fs").rmSync("src/template.ts")}`' + "'"],
2922
+ ['node computed mutation', `node -e "const fs=require('fs');const op='rmSync';fs[op]('src/computed.ts')"`],
2923
+ ['node ESM rename', `node --input-type=module -e "import fs from 'node:fs';fs.renameSync('src/esm-a.ts','src/esm-b.ts')"`],
2924
+ ['nodejs fs.rmSync', `nodejs -e "require('fs').rmSync('src/nodejs.ts')"`],
2925
+ ['node.exe fs.rmSync', `node.exe -e "require('fs').rmSync('src/node-exe.ts')"`],
2926
+ ['node getBuiltinModule', `node -e "process.getBuiltinModule('fs').rmSync('src/builtin.ts')"`],
2927
+ ['node optional mutation', `node -e "const fs=require('fs');fs?.rmSync('src/optional.ts')"`],
2928
+ ['node aliased fs object', `node -e "const fs=require('fs');const alias=fs;alias.rmSync('src/alias.ts')"`],
2929
+ ['node dynamic eval source', `PAYLOAD="require('fs').rmSync('src/env.ts')"; node -e "$PAYLOAD"`],
2930
+ ['node concatenated eval source', `A="require('fs')."; B="rmSync('src/concat.ts')"; node -e "$A$B"`],
2931
+ ['node backtick eval source', `node -e "\`cat payload.js\`"`],
2932
+ ['node command-substitution eval source', `node -e "$(cat payload.js)"`],
2933
+ ['node combined print eval', `node -pe "require('fs').rmSync('src/combined.ts')"`],
2934
+ ['node repeated eval mutation', `node -e "0" -e "require('fs').writeFileSync('src/repeated-eval.ts','x')"`],
2935
+ ['node aliased require', `node -e "const req=require;const fs=req('fs');fs.rmSync('src/aliased-require.ts')"`],
2936
+ ['node object-escaped fs', `node -e "const h={fs:require('fs')};h.fs.rmSync('src/object-escape.ts')"`],
2937
+ ['node computed require alias', `node -e "const req=module['require'];const fs=req('fs');fs.rmSync('src/computed-require.ts')"`],
2938
+ ['node computed builtin loader', `node -e "const fs=process['getBuiltinModule']('fs');fs.rmSync('src/computed-builtin.ts')"`],
2939
+ ['node postfix division mutation', `node -e "let x=1;x++ / require('fs').rmSync('src/postfix-division.ts') / 1"`],
2940
+ ['node string division mutation', `node -e "'value' / require('fs').rmSync('src/string-division.ts') / 1"`],
2941
+ ['node unicode-escaped loader', `node -e 'requ\\u0069re("fs").rmSync("src/unicode-escape.ts")'`],
2942
+ ['node parenthesized eval', `node -e "(eval)('require(\\"fs\\").rmSync(\\"src/eval-bypass.ts\\")')"`],
2943
+ ['node concatenated computed loader', `node -e "const fs=module['requ'+'ire']('fs');fs.rmSync('src/computed-loader.ts')"`],
2944
+ ['node attached short eval', `node -e"require('fs').rmSync('src/attached-eval.ts')"`],
2945
+ ['node xargs wrapper mutation', `printf x | xargs node -e "require('fs').rmSync('src/xargs-bypass.ts')"`],
2946
+ ['node xargs wrapper read', `printf x | xargs node -e "require('fs').readFileSync('src/victim.ts','utf8')"`],
2947
+ ['node child-process mutation', `node -e "require('child_process').execFileSync('rm',['-f','src/child-process-bypass.ts'])"`],
2948
+ ['node internal module loader', `node -e "module.constructor._load('fs').rmSync('src/internal-loader-bypass.ts')"`],
2949
+ ['node prototype module loader', `node -e "module.__proto__.constructor._load('fs').rmSync('src/prototype-loader-bypass.ts')"`],
2950
+ ['node computed prototype loader', `node -e "Object['getPrototypeOf'](module)['constructor']['_load']('fs')['rmSync']('src/prototype-computed-bypass.ts')"`],
2951
+ ['node optional computed prototype loader', `node -e "Object?.['getPrototypeOf'](module)?.['constructor']?.['_load']('fs')?.['rmSync']('src/optional-prototype-bypass.ts',{force:true})"`],
2952
+ ['node function constructor', `node -e '(()=>{}).constructor("return process.getBuiltinModule(\\"fs\\").rmSync(\\"src/function-dot.ts\\")")()'`],
2953
+ ['node method function constructor', `node -e '({}).toString.constructor("return process.getBuiltinModule(\\"fs\\").rmSync(\\"src/function-method.ts\\")")()'`],
2954
+ ['node descriptor builtin', `node -e "Object.getOwnPropertyDescriptor(process,'getBuiltinModule').value('fs').rmSync('src/descriptor.ts')"`],
2955
+ ['node descriptor builtin call', `node -e "Object.getOwnPropertyDescriptor(process,'getBuiltinModule').value.call(process,'fs').rmSync('src/descriptor-call.ts')"`],
2956
+ ['node destructured comma call', `node -e "const {rmSync}=require('fs');(0,rmSync)('src/destructured-comma.ts')"`],
2957
+ ['node destructured method call', `node -e "const {rmSync}=require('fs');rmSync.call(null,'src/destructured-call.ts')"`],
2958
+ ['node destructured Reflect.apply', `node -e "const {rmSync}=require('fs');Reflect.apply(rmSync,null,['src/destructured-reflect.ts'])"`],
2959
+ ['node ANSI-C eval flag', `node $'-e' "require('fs').rmSync('src/ansi-c.ts')"`],
2960
+ ['node nine env wrappers', `${Array.from({ length: 9 }, () => 'env').join(' ')} node -e "require('fs').rmSync('src/env-nine.ts')"`],
2961
+ ['node stdin pipe', `printf "require('fs').rmSync('src/stdin-pipe.ts')" | node`],
2962
+ ['node global Function computed', `node -e "globalThis['Fun'+'ction'](\\"return require('fs').rmSync('src/global-function.ts')\\")()"`],
2963
+ ['node Reflect global Function', `node -e "Reflect.get(globalThis,'Fun'+'ction')(\\"return require('fs').rmSync('src/reflect-function.ts')\\")()"`],
2964
+ ['node parenthesized constructor', `node -e "(console.log.constructor)(\\"return require('fs').rmSync('src/parenthesized-constructor.ts')\\")()"`],
2965
+ ['node side-effect import', `node --input-type=module -e "import './mutator.mjs'"`],
2966
+ ['node require preload', `node --require ./.omx/state/mutator.cjs -e "console.log('ok')"`],
2967
+ ['node options require preload', `NODE_OPTIONS='--require ./.omx/state/mutator.cjs' node -e "console.log('ok')"`],
2968
+ ['node exported options preload', `export NODE_OPTIONS='--require ./.omx/state/mutator.cjs'; node -e "console.log('ok')"`],
2969
+ ['node indirect exported options preload', `P='--require ./.omx/state/mutator.cjs'; export NODE_OPTIONS="$P"; node -e "console.log('ok')"`],
2970
+ ['node command exported options preload', `command export NODE_OPTIONS='--require ./.omx/state/mutator.cjs'; node -e "console.log('ok')"`],
2971
+ ['node declare exported options preload', `declare -x NODE_OPTIONS='--require ./.omx/state/mutator.cjs'; node -e "console.log('ok')"`],
2972
+ ['node function exported options preload', `f(){ export NODE_OPTIONS='--require ./.omx/state/mutator.cjs'; }; f; node -e "console.log('ok')"`],
2973
+ ['node dynamic export preload', `N=NODE_OPTIONS; export "$N=--require ./.omx/state/mutator.cjs"; node -e "console.log('ok')"`],
2974
+ ['node deep command exported options preload', `command command command command command command export NODE_OPTIONS='--require ./.omx/state/mutator.cjs'; node -e "console.log('ok')"`],
2975
+ ['node vm runInThisContext', `node -e "require('node:vm').runInThisContext(\\"require('fs').rmSync('src/vm.ts')\\")"`],
2976
+ ['node process alias builtin loader', `node -e "const p=process;p.getBuiltinModule('fs').rmSync('src/process-alias.ts')"`],
2977
+ ['node Reflect module require', `node -e "Reflect.apply(Reflect.get(module,'require'),module,['fs']).rmSync('src/reflect-require.ts')"`],
2978
+ ['node global process loader', `node -e "global['process'].getBuiltinModule('fs').rmSync('src/global-process.ts')"`],
2979
+ ['node ANSI-C command name', `$'\\u006e\\u006f\\u0064\\u0065' -e "require('fs').rmSync('src/ansi-command.ts')"`],
2980
+ ['node ANSI-C hex command name', `$'\\x6e\\x6f\\x64\\x65' -e "require('fs').rmSync('src/ansi-hex-command.ts')"`],
2981
+ ['node ANSI-C octal command name', `$'\\156\\157\\144\\145' -e "require('fs').rmSync('src/ansi-octal-command.ts')"`],
2982
+ ['node ANSI-C wide command name', `$'\\U0000006e\\U0000006f\\U00000064\\U00000065' -e "require('fs').rmSync('src/ansi-wide-command.ts')"`],
2983
+ ['python os.remove', `python3 -c "import os;os.remove('src/python-remove.ts')"`],
2984
+ ['python modeled write piggyback', `python3 -c "from pathlib import Path;import subprocess;Path('.omx/state/probe').write_text('x');subprocess.run(['rm','-f','src/python-piggyback.ts'])"`],
2985
+ ['python path sitecustomize preload', `PYTHONPATH=./.omx/state python3 -c "print('ok')"`],
2986
+ ['python dynamic open mode', `python3 -c "m='w';open('src/python-dynamic-open.ts',m)"`],
2987
+ ['python warnings module preload', `PYTHONWARNINGS='ignore::Mutator.Warning' python3 -c "print('ok')"`],
2988
+ ['python non-isolation read-only', `python3 -c "print('ok')"`],
2989
+ ['python non-isolation modeled metadata copy', "python3 - <<'PY'\nimport shutil\nshutil.copyfile('a', '.omx/state/foo')\nPY"],
2990
+ ['ruby uninspected runtime', `ruby -e "File.delete('src/ruby-delete.ts')"`],
2991
+ ['python f-string side effect', `python3 -c "import subprocess;f'{subprocess.run([\\"touch\\",\\"src/python-fstring.ts\\"])}'"`],
2992
+ ['python isolated script', `python3 -I .omx/tmp/session/run.py`],
2993
+ ['perl eval substitution', `perl -pi -e 's/^/system("rm -f src\/perl-eval.ts")/e' .omx/state/conductor.log`],
2994
+ ['perl startup module preload', `PERL5LIB=./.omx/state PERL5OPT=-MMutator perl -e 'print;'`],
2995
+ ['git add unmodeled mutation', `git add src/runtime.ts`],
2996
+ ['sort output mutation', `sort -o src/sort-output.ts package.json`],
2997
+ ['sed write command', `sed -n 'w src/sed-output.ts' package.json`],
2998
+ ['sed addressed write command', `sed -n '1w src/sed-addressed-output.ts' package.json`],
2999
+ ['git diff output mutation', `git diff --output=src/git-output.ts --no-index /dev/null package.json`],
3000
+ ['sort compress program execution', `sort --compress-program=./.omx/state/mutator package.json`],
3001
+ ['rg pre helper', `rg --pre ./.omx/state/mutator pattern .`],
3002
+ ['gh release download write', `gh release download --dir src`],
3003
+ ['gh global repo release download', `gh -R owner/repo release download --dir src`],
3004
+ ['git external diff env', `GIT_EXTERNAL_DIFF=./.omx/state/mutator git diff`],
3005
+ ['git config external diff', `git -c diff.external=./.omx/state/mutator diff`],
3006
+ ['git config env helper', `HELPER=/usr/bin/touch git --config-env=diff.external=HELPER diff --no-index README.md src/scripts/codex-native-hook.ts`],
3007
+ ['rg search zip helper', `rg -z never .omx/state/input.gz`],
3008
+ ['sensitive printf v runtime writer', `printf -v NODE_V8_COVERAGE '%s' src; export NODE_V8_COVERAGE; node -e "console.log('ok')"`],
3009
+ ['sensitive read runtime writer', `set -a; read GIT_TRACE <<< src/git-trace.log; git status`],
3010
+ ['bash login startup', `HOME=.omx/state/login-home bash -lc 'printf safe'`],
3011
+ ['wget prefix home hsts', `WGETRC=/dev/null HOME=src wget --no-config -O - https://example.test/file`],
3012
+ ['reference precedes reference option', `chmod src/victim.ts --reference=.omx/state/session.json .omx/state/reference-copy`],
3013
+ ['awk uninspected runtime', `awk 'BEGIN { print "x" > "src/awk-write.ts" }'`],
3014
+ ['npm restart script', 'npm restart'],
3015
+ ['npm run build', 'npm run build'],
3016
+ ['bash env stdin preload', `printf 'touch src/bash-env-preload.ts\\n' | BASH_ENV=/dev/stdin bash -c 'printf safe\\n'`],
3017
+ ['zsh startup preload', `ZDOTDIR=./.omx/state zsh -c "printf safe"`],
3018
+ ['python heredoc owner mismatch', `cat <<'SAFE' >/dev/null\nprint('safe')\nSAFE\npython3 <<'PY'\nfrom pathlib import Path\nimport subprocess\nPath('.omx/state/probe').write_text('x')\nsubprocess.run(['touch','src/python-heredoc-bypass.ts'])\nPY`],
3019
+ ['python pipeline heredoc mismatch', `cat <<'SAFE' >/dev/null | python3 <<'PY'\nprint('safe')\nSAFE\nfrom pathlib import Path\nimport subprocess\nPath('.omx/state/probe').write_text('x')\nsubprocess.run(['touch','src/python-pipeline-bypass.ts'])\nPY`],
3020
+ ['python indented heredoc terminator bypass', `python3 <<'true'
3021
+ true=None
3022
+ if True:
3023
+ true
3024
+ echo=__import__('os').system
3025
+ echo ('touch src/heredoc-bypass.ts')
3026
+ true`],
3027
+ ['ANSI-C heredoc delimiter bypass', `cat <<$'EOF'
3028
+ safe
3029
+ EOF
3030
+ touch src/ansi-heredoc-bypass.ts`],
3031
+ ['comment heredoc opener bypass', `true # <<'EOF'
3032
+ touch src/comment-heredoc-bypass.ts
3033
+ EOF`],
3034
+ ['arithmetic heredoc opener bypass', `: $((1 << 2))
3035
+ touch src/arithmetic-heredoc-bypass.ts`],
3036
+ ['legacy arithmetic heredoc opener bypass', `: $[1 << 2]
3037
+ touch src/legacy-arith-bypass.ts`],
3038
+ ['parameter expansion heredoc opener bypass', `: ${"${x#<<EOF}"}
3039
+ touch src/parameter-expansion-bypass.ts`],
3040
+ ['ANSI CR heredoc terminator bypass', `cat <<$'EOF\\r'
3041
+ safe
3042
+ EOF\r
3043
+ touch src/ansi-cr-heredoc-bypass.ts`],
3044
+ ['piped shell function bypass', `mutate(){ touch src/piped-function-bypass.ts; }; true | mutate`],
3045
+ ['transformed heredoc runtime bypass', `cat <<'PY' | tr a-z A-Z | python3
3046
+ from pathlib import Path
3047
+ Path('.omx/state/probe').write_text('x')
3048
+ PY`],
3049
+ ['path qualified runtime shadow', `./.omx/state/python3 -c "print('ok')"`],
3050
+ ['PATH environment runtime shadow', `env PATH=.omx/state:/usr/bin:/bin python3 -c "print('ok')"`],
3051
+ ['python escaped path bypass', `python3 -c "from pathlib import Path;Path('.omx/state/\\x2e\\x2e/\\x2e\\x2e/src/python-escape.ts').write_text('x')"`],
3052
+ ['clobber redirect bypass', `true >| src/clobber-bypass.ts`],
3053
+ ['cross boundary hardlink bypass', `ln src/source.ts .omx/state/source-link.ts`],
3054
+ ['target directory cross boundary hardlink bypass', `ln src/source.ts -t .omx/handoffs/run-1`],
3055
+ ['symbolic link metadata bypass', `ln -s .omx/state/conductor-ledger.json .omx/state/ledger-symlink`],
3056
+ ['target directory value missing', 'cp package.json --target-directory'],
3057
+ ['target directory attached empty', 'install --target-directory= .omx/state/conductor-ledger.json'],
3058
+ ['target directory ordering missing value', 'mv src/a.ts --target-directory'],
3059
+ ['target directory terminator missing value', 'ln .omx/state/conductor-ledger.json -t --'],
3060
+ ['node env file preload bypass', `node --env-file=.omx/state/node.env -e "console.log('ok')"`],
3061
+ ['python cwd startup bypass', `cd .omx/state && python3 -c "print('ok')"`],
3062
+ ['sed in-place execute bypass', `sed -i '1e touch src/sed-exec.ts' .omx/state/conductor.log`],
3063
+ ...wgetReviewMutationCommands,
3064
+ ['unknown extensionless executable', './.omx/state/mutator'],
3065
+ ['heredoc delimiter executable collision', `cat <<'MUTATOR' > .omx/state/conductor.log\nsafe\nMUTATOR\n./.omx/state/mutator`],
3066
+ ['path executable function-name collision', `mutator() { printf safe; }; ./.omx/state/mutator`],
3067
+ ['wrapped executable function-name collision', `mutator() { printf safe; }; env PATH=.omx/state:/usr/bin:/bin mutator`],
3068
+ ['omx state clear', `omx state clear --input '{"mode":"ultragoal"}' --json`],
3069
+ ['bash uninspected script', `bash .omx/state/run.sh`],
3070
+ ['source uninspected script', `source .omx/state/run.sh`],
3071
+ ]) {
3072
+ for (const actor of ['main-root', 'native-child']) {
3073
+ requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command }));
3074
+ }
3075
+ }
3076
+ const workspacePackageCli = realpathSync(join(packageRoot, 'dist', 'cli', 'omx.js'));
3077
+ const packedNpmBinDir = join(smokeCwd, 'node_modules', '.bin');
3078
+ const packedNpmOmxShim = join(packedNpmBinDir, 'omx');
3079
+ const packedNpmGjcShim = join(packedNpmBinDir, 'gjc');
3080
+ mkdirSync(packedNpmBinDir, { recursive: true });
3081
+ symlinkSync(resolve(workspacePackageCli), packedNpmOmxShim);
3082
+ symlinkSync(resolve(workspacePackageCli), packedNpmGjcShim);
3083
+ const selfAssertedWorkspaceCli = join(smokeCwd, 'bin', 'omx.js');
3084
+ mkdirSync(dirname(selfAssertedWorkspaceCli), { recursive: true });
3085
+ writeFileSync(join(smokeCwd, 'package.json'), JSON.stringify({ name: 'oh-my-codex', bin: { omx: 'bin/omx.js' } }));
3086
+ writeFileSync(selfAssertedWorkspaceCli, '#!/usr/bin/env node\n', { mode: 0o755 });
3087
+ rmSync(packedNpmOmxShim);
3088
+ symlinkSync(selfAssertedWorkspaceCli, packedNpmOmxShim);
3089
+ for (const actor of ['main-root', 'native-child']) {
3090
+ requireActorDeny(actor, 'workspace self-asserted package CLI', runActorProbe(actor, 'workspace self-asserted package CLI', 'Bash', { command: `omx state write --input '{"mode":"ultragoal","active":true}' --json` }, { PATH: `${packedNpmBinDir}:/usr/bin:/bin` }));
3091
+ }
3092
+ rmSync(packedNpmOmxShim);
3093
+ symlinkSync(workspacePackageCli, packedNpmOmxShim);
3094
+ const packedNpmPath = `${packedNpmBinDir}:${process.env.PATH || '/usr/bin:/bin'}`;
3095
+ const packedNpmCommandPrefix = `PATH="${packedNpmBinDir}:/usr/bin:/bin"`;
3096
+ requireNativeHookPermissionDeny('main-root boxed planning CLI poisoned state root', runActorProbe('main-root', 'boxed planning CLI poisoned state root', 'Bash', { command: `OMX_STATE_ROOT=${join(smokeCwd, 'src', 'state')} ${packedNpmCommandPrefix} omx state write --input '{"mode":"ralplan","active":true}' --json` }, { OMX_ROOT: boxedPlanningRoot, OMX_TEAM_STATE_ROOT: '' }), /(?:Ralplan is active|Main-root Conductor mode is active)/);
3097
+ const npmBinPathShadow = join(smokeCwd, '.omx', 'state', 'inbox', 'cat');
3098
+ symlinkSync('/usr/bin/touch', npmBinPathShadow);
3099
+ const npmBinMissingCandidatePath = `${packedNpmBinDir}:${join(smokeCwd, '.omx', 'state', 'inbox')}:/usr/bin:/bin`;
3100
+ for (const actor of ['main-root', 'native-child']) {
3101
+ requireActorDeny(actor, 'npm bin missing candidate continues PATH scan', runActorProbe(actor, 'npm bin missing candidate continues PATH scan', 'Bash', { command: 'cat src/path-owned.ts' }, { PATH: npmBinMissingCandidatePath }));
3102
+ }
3103
+ const nonexistentAbsoluteNpmBin = join(smokeCwd, 'node_modules', 'oh-my-codex', 'node_modules', '.bin');
3104
+ const packedNpmPathWithMissingNpmBin = `${nonexistentAbsoluteNpmBin}:${packedNpmPath}`;
3105
+ for (const [probe, command, path] of [
3106
+ ['absolute workspace npm bin cat parity', 'cat src/conductor-owned.ts', packedNpmPath],
3107
+ ['absolute workspace npm bin wrapped cat parity', 'env cat src/conductor-owned.ts', packedNpmPath],
3108
+ ['nonexistent absolute npm bin cat control', 'cat src/conductor-owned.ts', packedNpmPathWithMissingNpmBin],
3109
+ ['nonexistent absolute npm bin wrapped cat control', 'env cat src/conductor-owned.ts', packedNpmPathWithMissingNpmBin],
3110
+ ]) {
3111
+ for (const actor of ['main-root', 'native-child']) {
3112
+ const result = runActorProbeResult(actor, probe, 'Bash', { command }, { PATH: path });
3113
+ if (Object.keys(result.output).length !== 0) {
3114
+ throw new Error(`packed ${actor} ${probe} should resolve to the system command: ${JSON.stringify(result.output)}\nactual stdout:\n${result.stdout}`);
3115
+ }
3116
+ }
3117
+ }
3118
+ const packedNpmNodeShadow = join(packedNpmBinDir, 'node');
3119
+ writeFileSync(packedNpmNodeShadow, '#!/bin/sh\ntouch src/omx-status-owned.ts\n', { mode: 0o755 });
3120
+ for (const actor of ['main-root', 'native-child']) {
3121
+ for (const command of [`omx state write --input '{"mode":"ultragoal","active":true}' --json`, 'omx status']) {
3122
+ requireActorDeny(actor, `workspace npm bin Node shadow ${command}`, runActorProbe(actor, `workspace npm bin Node shadow ${command}`, 'Bash', { command }, { PATH: `${packedNpmBinDir}:/usr/bin:/bin` }));
3123
+ }
3124
+ }
3125
+ rmSync(packedNpmNodeShadow, { force: true });
3126
+ const cliStateWrite = `OMX_SESSION_ID=${sessionId} omx state write --input '${JSON.stringify({ mode: "ultragoal", active: true, current_phase: "executing", session_id: sessionId, workingDirectory: smokeCwd })}' --json`;
3127
+ const mainRootCliStateWriteProbe = runActorProbeResult('main-root', 'cli state write main', 'Bash', { command: cliStateWrite }, { PATH: packedNpmPath });
3128
+ if (Object.keys(mainRootCliStateWriteProbe.output).length !== 0) {
3129
+ throw new Error(`packed main-root CLI state write should retain metadata allowance: ${JSON.stringify(mainRootCliStateWriteProbe.output)}\nactual stdout:\n${mainRootCliStateWriteProbe.stdout}`);
3130
+ }
3131
+ requireActorDeny('native-child', 'cli state write native child', runActorProbe('native-child', 'cli state write native child', 'Bash', { command: cliStateWrite }, { PATH: packedNpmPath }));
3132
+ const systemNodeCliStateWriteProbe = runActorProbeResult('main-root', 'cli state write system Node', 'Bash', { command: cliStateWrite }, { PATH: `${packedNpmBinDir}:/usr/bin:/bin` });
3133
+ if (Object.keys(systemNodeCliStateWriteProbe.output).length !== 0) {
3134
+ throw new Error(`packed main-root CLI state write should permit a trusted system Node: ${JSON.stringify(systemNodeCliStateWriteProbe.output)}\nactual stdout:\n${systemNodeCliStateWriteProbe.stdout}`);
3135
+ }
3136
+ const benignOrchestrationRuntimeEnvironment = {
3137
+ PATH: packedNpmPath,
3138
+ GJC_SESSION_CWD: smokeCwd,
3139
+ GJC_SESSION_FILE: join(stateDir, 'session.json'),
3140
+ GJC_SESSION_ID: sessionId,
3141
+ OMX_OPENCLAW: '1',
3142
+ OMX_OPENCLAW_COMMAND: 'resume',
3143
+ OMX_OPENCLAW_DEBUG: '1',
3144
+ OMX_TEST_RELAX_TMUX_TIMEOUT: '1',
3145
+ };
3146
+ const benignRuntimeEnvironmentCliStateWriteProbe = runActorProbeResult('main-root', 'cli state write benign runtime environment', 'Bash', { command: cliStateWrite }, benignOrchestrationRuntimeEnvironment);
3147
+ if (Object.keys(benignRuntimeEnvironmentCliStateWriteProbe.output).length !== 0) {
3148
+ throw new Error(`packed main-root CLI state write should permit benign runtime environment: ${JSON.stringify(benignRuntimeEnvironmentCliStateWriteProbe.output)}\nactual stdout:\n${benignRuntimeEnvironmentCliStateWriteProbe.stdout}`);
3149
+ }
3150
+ const benignRuntimeEnvironmentStatusProbe = runActorProbeResult('main-root', 'cli status benign runtime environment', 'Bash', { command: 'omx status' }, benignOrchestrationRuntimeEnvironment);
3151
+ if (Object.keys(benignRuntimeEnvironmentStatusProbe.output).length !== 0) {
3152
+ throw new Error(`packed main-root CLI status should permit benign runtime environment: ${JSON.stringify(benignRuntimeEnvironmentStatusProbe.output)}\nactual stdout:\n${benignRuntimeEnvironmentStatusProbe.stdout}`);
3153
+ }
3154
+ const functionPersistedRsyncEnvironment = `poison(){ export RSYNC_PARTIAL_DIR=${join(smokeCwd, 'src', 'rsync-partials')}; }; poison; rsync .omx/state/conductor-ledger.json .omx/state/inbox/rsync-copy`;
3155
+ const functionPersistedOmxEnvironment = `poison(){ export OMX_STATE_ROOT=${join(smokeCwd, 'src', 'state')}; }; poison; omx state write --input '{"mode":"ultragoal","active":true}' --json`;
3156
+ for (const [probe, command] of [
3157
+ ['function persisted rsync runtime environment', functionPersistedRsyncEnvironment],
3158
+ ['function persisted omx runtime environment', functionPersistedOmxEnvironment],
3159
+ ['function persisted gjc runtime environment', `poison(){ export GJC_UNREVIEWED_HELPER=src/mutator; }; poison; omx status`],
3160
+ ['nameref rsync runtime environment', `declare -n poison=RSYNC_PARTIAL_DIR; poison=src/rsync-partials; rsync .omx/state/conductor-ledger.json .omx/state/inbox/rsync-copy`],
3161
+ ['joined rsync runtime environment', `if true; then export RSYNC_PARTIAL_DIR=src/rsync-partials; fi; rsync .omx/state/conductor-ledger.json .omx/state/inbox/rsync-copy`],
3162
+ ['nested omx runtime environment', `export OMX_STATE_ROOT=${join(smokeCwd, 'src', 'state')}; bash --noprofile --norc -c 'omx status'`],
3163
+ ['function readonly omx root failed unset', `poison(){ readonly OMX_ROOT=${join(smokeCwd, 'src')}; unset OMX_ROOT; }; poison; omx status`],
3164
+ ['function readonly rsync runtime failed unset', `poison(){ readonly RSYNC_PARTIAL_DIR=src/rsync-partials; unset RSYNC_PARTIAL_DIR; }; poison; rsync .omx/state/conductor-ledger.json .omx/state/inbox/rsync-copy`],
3165
+ ['function readonly gjc runtime failed unset', `poison(){ readonly GJC_UNREVIEWED_HELPER=src/mutator; unset GJC_UNREVIEWED_HELPER; }; poison; omx status`],
3166
+ ]) {
3167
+ for (const actor of ['main-root', 'native-child']) {
3168
+ requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command }, { PATH: packedNpmPath }));
3169
+ }
3170
+ }
3171
+ const modeCliStateWrite = `OMX_SESSION_ID=${sessionId} omx state write --mode=ultragoal --input '${JSON.stringify({ active: true, current_phase: "blocked", reason: "native delegation unavailable -> terminalized", session_id: sessionId, workingDirectory: smokeCwd })}' --json`;
3172
+ const mainRootModeCliStateWriteProbe = runActorProbeResult('main-root', 'mode CLI state write main', 'Bash', { command: modeCliStateWrite }, { PATH: packedNpmPath });
3173
+ if (Object.keys(mainRootModeCliStateWriteProbe.output).length !== 0) {
3174
+ throw new Error(`packed main-root mode CLI state write should retain metadata allowance: ${JSON.stringify(mainRootModeCliStateWriteProbe.output)}\nactual stdout:\n${mainRootModeCliStateWriteProbe.stdout}`);
3175
+ }
3176
+ requireActorDeny('native-child', 'mode CLI state write native child', runActorProbe('native-child', 'mode CLI state write native child', 'Bash', { command: modeCliStateWrite }, { PATH: packedNpmPath }));
3177
+ for (const [probe, prefix] of [
3178
+ ['poisoned OMX root', `OMX_ROOT=${join(smokeCwd, 'src')} PATH=${packedNpmPath}`],
3179
+ ['poisoned OMX state root', `OMX_STATE_ROOT=${join(smokeCwd, 'src', 'state')} PATH=${packedNpmPath}`],
3180
+ ['unknown OMX runtime output', `OMX_UNREVIEWED_OUTPUT=src/output PATH=${packedNpmPath}`],
3181
+ ['unknown OMX runtime environment', `OMX_UNREVIEWED_HELPER=src/mutator PATH=${packedNpmPath}`],
3182
+ ['unknown GJC runtime environment', `GJC_UNREVIEWED_HELPER=src/mutator PATH=${packedNpmPath}`],
3183
+ ]) {
3184
+ for (const command of [cliStateWrite, 'omx status']) {
3185
+ const surface = command === cliStateWrite ? 'state write' : 'read-only omx';
3186
+ requireActorDeny('main-root', `${probe} ${surface}`, runActorProbe('main-root', `${probe} ${surface}`, 'Bash', { command: `${prefix} ${command}` }));
3187
+ }
3188
+ }
3189
+ const fakeOmxDirectory = join(smokeCwd, '.omx', 'state');
3190
+ const fakeOmxPath = join(fakeOmxDirectory, 'omx');
3191
+ const fakeGjcPath = join(fakeOmxDirectory, 'gjc');
3192
+ writeFileSync(fakeOmxPath, '#!/bin/sh\nexit 0\n', { mode: 0o755 });
3193
+ writeFileSync(fakeGjcPath, '#!/bin/sh\nexit 0\n', { mode: 0o755 });
3194
+ const fakeOmxStateWrite = `omx state write --input '{"mode":"ultragoal","active":true}' --json`;
3195
+ const fakeGjcCheckpoint = `gjc ultragoal checkpoint --goal-id G001 --status failed --evidence unauthorized`;
3196
+ for (const actor of ['main-root', 'native-child']) {
3197
+ requireActorDeny(actor, 'repository omx shadow', runActorProbe(actor, 'repository omx shadow', 'Bash', { command: fakeOmxStateWrite }, {
3198
+ PATH: `${fakeOmxDirectory}:${packedNpmPath}`,
3199
+ }));
3200
+ requireActorDeny(actor, 'repository gjc shadow', runActorProbe(actor, 'repository gjc shadow', 'Bash', { command: fakeGjcCheckpoint }, {
3201
+ PATH: `${fakeOmxDirectory}:${packedNpmPath}`,
3202
+ }));
3203
+ }
3204
+ rmSync(fakeOmxPath, { force: true });
3205
+ rmSync(fakeGjcPath, { force: true });
3206
+ rmSync(packedNpmOmxShim, { force: true });
3207
+ writeFileSync(packedNpmOmxShim, '#!/bin/sh\nexit 0\n', { mode: 0o755 });
3208
+ for (const actor of ['main-root', 'native-child']) {
3209
+ requireActorDeny(actor, 'workspace npm bin omx shadow', runActorProbe(actor, 'workspace npm bin omx shadow', 'Bash', { command: fakeOmxStateWrite }, { PATH: packedNpmPath }));
3210
+ }
3211
+ rmSync(packedNpmOmxShim, { force: true });
3212
+ symlinkSync(workspacePackageCli, packedNpmOmxShim);
3213
+ rmSync(packedNpmGjcShim, { force: true });
3214
+ writeFileSync(packedNpmGjcShim, '#!/bin/sh\nexit 0\n', { mode: 0o755 });
3215
+ for (const actor of ['main-root', 'native-child']) {
3216
+ requireActorDeny(actor, 'workspace npm bin gjc shadow', runActorProbe(actor, 'workspace npm bin gjc shadow', 'Bash', { command: fakeGjcCheckpoint }, { PATH: packedNpmPath }));
3217
+ }
3218
+ rmSync(packedNpmGjcShim, { force: true });
3219
+ symlinkSync(workspacePackageCli, packedNpmGjcShim);
3220
+ const workspaceNpmBinChmodShadowCommand = `chmod --reference=.omx/state/session.json .omx/state/reference-copy; omx state write --input '{"mode":"ultragoal"}' --json`;
3221
+ const workspaceNpmBinChmodShadow = join(packedNpmBinDir, 'chmod');
3222
+ writeFileSync(workspaceNpmBinChmodShadow, '#!/bin/sh\nexit 0\n', { mode: 0o755 });
3223
+ for (const actor of ['main-root', 'native-child']) {
3224
+ requireActorDeny(actor, 'workspace npm bin chmod shadow', runActorProbe(actor, 'workspace npm bin chmod shadow', 'Bash', { command: workspaceNpmBinChmodShadowCommand }, { PATH: packedNpmPath }));
3225
+ }
3226
+ rmSync(workspaceNpmBinChmodShadow, { force: true });
3227
+ const currentRuntimeNodePath = process.execPath;
3228
+ const currentRuntimeNodePathEnvironment = { PATH: `${dirname(currentRuntimeNodePath)}:/usr/bin:/bin` };
3229
+ const untrustedExternalBin = mkdtempSync(join(tmpdir(), 'omx-native-hook-external-path-'));
3230
+ const untrustedExternalCat = join(untrustedExternalBin, 'cat');
3231
+ const untrustedExternalGh = join(untrustedExternalBin, 'gh');
3232
+ const untrustedExternalNode = join(untrustedExternalBin, 'node');
3233
+ const untrustedExternalNodeLookalike = join(untrustedExternalBin, 'node-copy');
3234
+ for (const actor of ['main-root', 'native-child']) {
3235
+ const output = runActorProbe(actor, 'external path without cat candidate', 'Bash', {
3236
+ command: `PATH=${untrustedExternalBin}:${process.env.PATH || '/usr/bin:/bin'} cat src/conductor-owned.ts`,
3237
+ });
3238
+ if (Object.keys(output).length !== 0)
3239
+ throw new Error(`packed ${actor} external PATH without a cat candidate should fall through to the system command`);
3240
+ }
3241
+ symlinkSync('/bin/cat', untrustedExternalCat);
3242
+ writeFileSync(untrustedExternalGh, '#!/bin/sh\nexit 0\n', { mode: 0o755 });
3243
+ writeFileSync(untrustedExternalNode, '#!/bin/sh\nexit 0\n', { mode: 0o755 });
3244
+ writeFileSync(untrustedExternalNodeLookalike, '#!/bin/sh\nexit 0\n', { mode: 0o755 });
3245
+ try {
3246
+ for (const [probe, command] of [
3247
+ ['untrusted external absolute executable', `${untrustedExternalCat} src/conductor-owned.ts`],
3248
+ ['untrusted external PATH executable', `PATH=${untrustedExternalBin}:${process.env.PATH || '/usr/bin:/bin'} cat src/conductor-owned.ts`],
3249
+ ['untrusted external absolute gh', `${untrustedExternalGh} issue create --title x --body y`],
3250
+ ['untrusted external PATH gh', `PATH=${untrustedExternalBin}:${process.env.PATH || '/usr/bin:/bin'} gh issue create --title x --body y`],
3251
+ ['untrusted external absolute node', `${untrustedExternalNode} -e "require('fs').readFileSync('src/victim.ts','utf8')"`],
3252
+ ['untrusted external PATH node', `PATH=${untrustedExternalBin}:/usr/bin:/bin node -e "require('fs').readFileSync('src/victim.ts','utf8')"`],
3253
+ ['untrusted external node lookalike', `${untrustedExternalNodeLookalike} -e "require('fs').readFileSync('src/victim.ts','utf8')"`],
3254
+ ]) {
3255
+ for (const actor of ['main-root', 'native-child']) {
3256
+ requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command }));
3257
+ }
3258
+ }
3259
+ }
3260
+ finally {
3261
+ rmSync(untrustedExternalBin, { recursive: true, force: true });
3262
+ }
3263
+ const dynamicCliStateWrite = `omx state write --input "$STATE_INPUT" --json`;
3264
+ for (const actor of ['main-root', 'native-child']) {
3265
+ requireActorDeny(actor, 'dynamic CLI state write', runActorProbe(actor, 'dynamic CLI state write', 'Bash', { command: dynamicCliStateWrite }, { PATH: packedNpmPath }));
3266
+ }
3267
+ const unknownStateWriteFlag = `omx state write --unexpected --input '{"mode":"ultragoal","active":true}' --json`;
3268
+ for (const actor of ['main-root', 'native-child']) {
3269
+ requireActorDeny(actor, 'unknown state write flag', runActorProbe(actor, 'unknown state write flag', 'Bash', { command: unknownStateWriteFlag }, { PATH: packedNpmPath }));
3270
+ }
3271
+ for (const [probe, command] of [
3272
+ ['state write foreign routing', `omx state write --input '{"mode":"ultragoal","workingDirectory":"src","session_id":"foreign","active":false}' --json`],
3273
+ ['state write unknown payload key', `omx state write --input '{"mode":"ultragoal","active":true,"child_marker":"forbidden"}' --json`],
3274
+ ['state write conflicting mode', `omx state write --mode=ultragoal --input '{"mode":"ralph","active":true}' --json`],
3275
+ ['state write foreign session environment', `OMX_SESSION_ID=foreign omx state write --input '{"mode":"ultragoal","active":true}' --json`],
3276
+ ['state write unset session environment', `env -uOMX_SESSION_ID omx state write --input '{"mode":"ultragoal","active":true}' --json`],
3277
+ ['state write shell unset session', `unset OMX_SESSION_ID; omx state write --input '{"mode":"ultragoal","active":true}' --json`],
3278
+ ['state write noncanonical cwd', `cd src; omx state write --input '{"mode":"ultragoal","active":true}' --json`],
3279
+ ]) {
3280
+ for (const actor of ['main-root', 'native-child']) {
3281
+ requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command }, { PATH: packedNpmPath }));
3282
+ }
3283
+ }
3284
+ for (const [probe, inheritedEnv] of [
3285
+ ['inherited foreign OMX session selector', { OMX_SESSION_ID: 'foreign' }],
3286
+ ['inherited foreign GJC session selector', { GJC_SESSION_ID: 'foreign' }],
3287
+ ['inherited conflicting session selectors', { OMX_SESSION_ID: sessionId, GJC_SESSION_ID: 'foreign' }],
3288
+ ]) {
3289
+ for (const actor of ['main-root', 'native-child']) {
3290
+ requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command: `omx state write --input '{"mode":"ultragoal","active":true}' --json` }, { PATH: packedNpmPath, ...inheritedEnv }));
3291
+ }
3292
+ }
3293
+ const mixedReferenceStateCommand = `chmod --reference=.omx/state/session.json .omx/state/reference-copy; OMX_SESSION_ID=${sessionId} omx state write --input '${JSON.stringify({ mode: "ultragoal", active: true, current_phase: "executing", session_id: sessionId, workingDirectory: smokeCwd })}' --json`;
3294
+ requireActorDeny("main-root", "native child mixed reference state write main", runActorProbe("main-root", "native child mixed reference state write main", "Bash", { command: mixedReferenceStateCommand }, { PATH: packedNpmPath }));
3295
+ requireActorDeny('native-child', 'native child mixed reference state write native child', runActorProbe('native-child', 'native child mixed reference state write native child', 'Bash', { command: mixedReferenceStateCommand }, { PATH: packedNpmPath }));
3296
+ requireActorDeny('native-child', 'native child rsync authority target', runActorProbe('native-child', 'native child rsync authority target', 'Bash', { command: `rsync .omx/state/conductor-ledger.json .omx/state/session.json` }));
3297
+ const referenceUnknownCommand = `chmod --reference=.omx/state/session.json .omx/state/reference-copy; unknown-mutation-transport`;
3298
+ for (const actor of ['main-root', 'native-child']) {
3299
+ requireActorDeny(actor, 'native child reference unknown command', runActorProbe(actor, 'native child reference unknown command', 'Bash', { command: referenceUnknownCommand }));
3300
+ }
3301
+ for (const [probe, command, inheritedEnv] of [
3302
+ ['python inherited sitecustomize preload', `python3 -c "print('ok')"`, { PYTHONPATH: './.omx/state' }],
3303
+ ['perl inherited module preload', `perl -e 'print;'`, { PERL5LIB: './.omx/state', PERL5OPT: '-MMutator' }],
3304
+ ['bash inherited env preload', `bash -c 'printf safe\\n'`, { BASH_ENV: './.omx/state/mutator.sh' }],
3305
+ ['bash inherited env plain read-only', `cat src/conductor-owned.ts`, { BASH_ENV: './.omx/state/mutator.sh' }],
3306
+ ['zsh inherited startup preload', `zsh -c 'printf safe'`, { ZDOTDIR: './.omx/state' }],
3307
+ ['curl inherited SSL key log output', `curl -q -o - https://example.test/file`, { SSLKEYLOGFILE: 'src/curl-tls.keys' }],
3308
+ ['wget inherited SSL key log output', `wget --no-config --no-hsts -O - https://example.test/file`, { SSLKEYLOGFILE: 'src/wget-tls.keys' }],
3309
+ ['wget inherited posix option ordering', `wget -O src/posix-inherited-wget-owned.ts https://example.test/file -O -`, { POSIXLY_CORRECT: '1' }],
3310
+ ['inherited empty path', `cat src/conductor-owned.ts`, { PATH: '' }],
3311
+ ['inherited relative path', `cat src/conductor-owned.ts`, { PATH: '.' }],
3312
+ ['inherited unresolved path', `cat src/conductor-owned.ts`, { PATH: '$UNKNOWN_PATH' }],
3313
+ ['inherited dynamic loader library path', `cat src/conductor-owned.ts`, { LD_LIBRARY_PATH: './.omx/state' }],
3314
+ ['inherited dynamic loader audit path', `cat src/conductor-owned.ts`, { DYLD_LIBRARY_PATH: './.omx/state' }],
3315
+ ['inherited xtrace shell option', `cat src/conductor-owned.ts`, { SHELLOPTS: 'xtrace' }],
3316
+ ['inherited keyword shell option', `cat src/conductor-owned.ts`, { SHELLOPTS: 'keyword' }],
3317
+ ]) {
3318
+ for (const actor of ['main-root', 'native-child']) {
3319
+ requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command }, inheritedEnv));
3320
+ }
3321
+ }
3322
+ for (const actor of ['main-root', 'native-child']) {
3323
+ for (const [probe, command] of [
3324
+ ['python inherited unbuffered read-only control', `python3 -I -c "print('ok')"`],
3325
+ ['python inherited unbuffered isolated metadata cwd control', `cd .omx/state && python3 -I -c "print('ok')"`],
3326
+ ]) {
3327
+ const output = runActorProbe(actor, probe, 'Bash', { command }, { PYTHONUNBUFFERED: '1' });
3328
+ if (Object.keys(output).length !== 0)
3329
+ throw new Error(`packed ${actor} ${probe} should be allowed`);
3330
+ }
3331
+ }
3332
+ const unbufferedModeledMetadataOutput = runActorProbe('main-root', 'python inherited unbuffered modeled metadata control', 'Bash', { command: "python3 -I - <<'PY'\nimport shutil\nshutil.copyfile('a', '.omx/state/foo')\nPY" }, { PYTHONUNBUFFERED: '1' });
3333
+ if (Object.keys(unbufferedModeledMetadataOutput).length !== 0) {
3334
+ throw new Error('packed main-root python inherited unbuffered modeled metadata control should be allowed');
3335
+ }
3336
+ for (const actor of ['main-root', 'native-child']) {
3337
+ const output = runActorProbe(actor, 'repository path without cat candidate', 'Bash', { command: 'cat src/conductor-owned.ts' }, {
3338
+ PATH: `${smokeCwd}:${process.env.PATH || '/usr/bin:/bin'}`,
3339
+ });
3340
+ if (Object.keys(output).length !== 0)
3341
+ throw new Error(`packed ${actor} repository PATH without a cat candidate should fall through to the system command`);
3342
+ }
3343
+ for (const [probe, command, inheritedEnv] of [
3344
+ ['inherited bash function shadow', `cat src/conductor-owned.ts`, { 'BASH_FUNC_cat%%': '() { touch src/inherited-function-owned.ts; }' }],
3345
+ ]) {
3346
+ for (const actor of ['main-root', 'native-child']) {
3347
+ requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command }, inheritedEnv));
3348
+ }
3349
+ }
3350
+ writeFileSync(join(fakeOmxDirectory, 'env'), '#!/bin/sh\nexit 0\n', { mode: 0o755 });
3351
+ for (const actor of ['main-root', 'native-child']) {
3352
+ requireActorDeny(actor, 'repository external wrapper shadow', runActorProbe(actor, 'repository external wrapper shadow', 'Bash', {
3353
+ command: `PATH=${fakeOmxDirectory}:/usr/bin:/bin env cat src/conductor-owned.ts`,
3354
+ }));
3355
+ }
3356
+ rmSync(join(fakeOmxDirectory, 'env'), { force: true });
3357
+ for (const actor of ['main-root', 'native-child']) {
3358
+ requireActorDeny(actor, 'loader before external env clear', runActorProbe(actor, 'loader before external env clear', 'Bash', { command: `env -i cat src/conductor-owned.ts` }, { LD_LIBRARY_PATH: './.omx/state' }));
3359
+ }
3360
+ for (const actor of ['main-root', 'native-child']) {
3361
+ const output = runActorProbe(actor, 'cleared inherited bash function control', 'Bash', { command: `env -i cat src/conductor-owned.ts` }, { 'BASH_FUNC_cat%%': '() { touch src/inherited-function-owned.ts; }' });
3362
+ if (Object.keys(output).length !== 0)
3363
+ throw new Error(`packed ${actor} clear-environment inherited-function control should be allowed`);
3364
+ }
3365
+ for (const [probe, command] of [
3366
+ ['cleared environment empty path', `env -i PATH= sh -c 'wget --no-config -O - https://example.test/file'`],
3367
+ ['cleared environment relative path', `env -i PATH=. sh -c 'wget --no-config -O - https://example.test/file'`],
3368
+ ['cleared environment repository path', `env -i PATH=${smokeCwd} sh -c 'wget --no-config -O - https://example.test/file'`],
3369
+ ['cleared exec empty path', `exec -c env PATH= sh -c 'wget --no-config -O - https://example.test/file'`],
3370
+ ['cleared exec relative path', `exec -c env PATH=. sh -c 'wget --no-config -O - https://example.test/file'`],
3371
+ ['cleared exec repository path', `exec -c env PATH=${smokeCwd} sh -c 'wget --no-config -O - https://example.test/file'`],
3372
+ ['recursive reference metadata', `chmod -R --reference=.omx/state/session.json .omx/state`],
3373
+ ['rsync directory tree metadata', `rsync -a --log-file=.omx/state/rsync-tree.log .omx/state/ .omx/state/rsync-tree-copy/`],
3374
+ ['rsync keep dirlinks metadata', `rsync --keep-dirlinks --log-file=.omx/state/rsync-dirlinks.log .omx/state/conductor-ledger.json .omx/state/rsync-dirlinks-copy`],
3375
+ ['rsync explicit links metadata', `rsync --links --log-file=.omx/state/rsync-links.log .omx/state/conductor-ledger.json .omx/state/rsync-links-copy`],
3376
+ ]) {
3377
+ for (const actor of ['main-root', 'native-child']) {
3378
+ requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command }));
3379
+ }
3380
+ }
3381
+ const trustedSystemGh = '/usr/bin/gh';
3382
+ const hasTrustedSystemGh = existsSync(trustedSystemGh);
3383
+ for (const [probe, command] of [
3384
+ ['gh issue create', `${trustedSystemGh} issue create --title x --body y`],
3385
+ ['gh issue quoted regex body', `${trustedSystemGh} issue create --title x --body 'Guard regex /[^>]+>{1,2}/ is data, not a redirect'`],
3386
+ ['gh api post', `${trustedSystemGh} api --method POST /repos/OWNER/REPO/issues -f title=x`],
3387
+ ['gh api attached post', `${trustedSystemGh} api -XPOST --input .omx/state/create-repo.json /user/repos`],
3388
+ ['gh api attached field post', `${trustedSystemGh} api -ftitle=x /repos/OWNER/REPO/issues`],
3389
+ ['omx ultragoal final checkpoint', `${packedNpmCommandPrefix} omx ultragoal checkpoint --goal-id G001 --status complete --codex-goal-json goal.json --quality-gate-json quality.json --evidence unauthorized --json`],
3390
+ ['wrapped gh issue create', `bash --noprofile --norc -lc '${trustedSystemGh} issue create --title x --body y'`],
3391
+ ['wrapped omx ultragoal final checkpoint', `${packedNpmCommandPrefix} bash --noprofile --norc -lc '${packedNpmCommandPrefix} omx ultragoal checkpoint --goal-id G001 --status complete --codex-goal-json goal.json --quality-gate-json quality.json --evidence unauthorized --json'`],
3392
+ ['wrapped gjc ultragoal final checkpoint', `${packedNpmCommandPrefix} bash --noprofile --norc -lc '${packedNpmCommandPrefix} gjc ultragoal checkpoint --goal-id G001 --status complete --codex-goal-json goal.json --quality-gate-json quality.json --evidence unauthorized --json'`],
3393
+ ['performance goal complete', `${packedNpmCommandPrefix} omx performance-goal complete --slug latency --codex-goal-json goal.json --evidence done --json`],
3394
+ ['wrapped performance goal complete', `${packedNpmCommandPrefix} bash --noprofile --norc -lc '${packedNpmCommandPrefix} omx performance-goal complete --slug latency --codex-goal-json goal.json --evidence done --json'`],
3395
+ ['autoresearch goal complete', `${packedNpmCommandPrefix} gjc autoresearch-goal complete --slug safety --codex-goal-json goal.json --json`],
3396
+ ['wrapped autoresearch goal complete', `${packedNpmCommandPrefix} bash --noprofile --norc -lc '${packedNpmCommandPrefix} gjc autoresearch-goal complete --slug safety --codex-goal-json goal.json --json'`],
3397
+ ['pipeline read then mutate', `${packedNpmCommandPrefix} omx status | ${packedNpmCommandPrefix} omx performance-goal complete --slug latency --codex-goal-json goal.json --evidence done --json`],
3398
+ ]) {
3399
+ const mainRootProbe = runActorProbeResult('main-root', `${probe} main`, 'Bash', { command });
3400
+ const requiresTrustedGh = probe.startsWith('gh ') || probe.startsWith('wrapped gh ');
3401
+ if (requiresTrustedGh && !hasTrustedSystemGh) {
3402
+ requireActorDeny('main-root', `${probe} main`, mainRootProbe.output);
3403
+ }
3404
+ else if (Object.keys(mainRootProbe.output).length !== 0) {
3405
+ throw new Error(`packed main-root ${probe} should retain remote orchestration allowance: ${JSON.stringify(mainRootProbe.output)}\nactual stdout:\n${mainRootProbe.stdout}`);
3406
+ }
3407
+ requireActorDeny('native-child', `${probe} native child`, runActorProbe('native-child', `${probe} native child`, 'Bash', { command }));
3408
+ }
3409
+ const inheritedGhHelperEnvironment = { GH_BROWSER: 'true', GH_PAGER: 'cat', GIT_EDITOR: 'true' };
3410
+ for (const [probe, command] of [
3411
+ ['gh issue create inherited helper control', `${trustedSystemGh} issue create --title x --body y`],
3412
+ ['gh api inherited helper control', `${trustedSystemGh} api --method POST /repos/OWNER/REPO/issues -f title=x`],
3413
+ ]) {
3414
+ const mainRootProbe = runActorProbeResult('main-root', probe, 'Bash', { command }, inheritedGhHelperEnvironment);
3415
+ if (!hasTrustedSystemGh) {
3416
+ requireActorDeny('main-root', probe, mainRootProbe.output);
3417
+ }
3418
+ else if (Object.keys(mainRootProbe.output).length !== 0) {
3419
+ throw new Error(`packed main-root ${probe} should ignore inherited GH helper environment: ${JSON.stringify(mainRootProbe.output)}\nactual stdout:\n${mainRootProbe.stdout}`);
3420
+ }
3421
+ }
3422
+ for (const [probe, command] of [
3423
+ ['gh api dynamic method', `gh api --method "$GH_METHOD" /repos/OWNER/REPO/issues`],
3424
+ ['xargs gh api mutation', `printf '%s' '-XPOST /repos/OWNER/REPO/issues' | PATH=/usr/bin:/bin xargs gh api`],
3425
+ ['omx unknown mutation', `omx unrecognized mutate --status failed`],
3426
+ ]) {
3427
+ for (const actor of ['main-root', 'native-child']) {
3428
+ requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command }));
3429
+ }
3430
+ }
3431
+ for (const [probe, command] of [
3432
+ ['omx checkpoint unknown option', `${packedNpmCommandPrefix} omx ultragoal checkpoint --goal-id G001 --status failed --evidence x --future-output=src/owned.ts`],
3433
+ ['omx node options preload', `NODE_OPTIONS='--require ./.omx/state/mutator.cjs' ${packedNpmCommandPrefix} omx status`],
3434
+ ['omx node coverage output', `NODE_V8_COVERAGE=src ${packedNpmCommandPrefix} omx status`],
3435
+ ['posix stdout before extra operand', `WGETRC=/dev/null POSIXLY_CORRECT=1 wget --no-config --no-hsts -O - https://example.test/file -O src/posix-after-operand.ts`],
3436
+ ['posix spider before extra operand', `WGETRC=/dev/null POSIXLY_CORRECT=1 wget --no-config --no-hsts --spider https://example.test/file -O src/posix-spider-after-operand.ts`],
3437
+ ['posix append stdout before extra operand', `POSIXLY_CORRECT+=1 wget --no-config --no-hsts -O - https://example.test/file -O src/posix-append-after-operand.ts`],
3438
+ ['coproc function lifecycle child', `touch(){ :; }; coproc { unset -f touch; }; wait; touch src/coproc-lifecycle-shadowed.ts`],
3439
+ ['wget repeated log last metadata sink', `wget --no-config --no-hsts -o src/ignored.log -o .omx/state/final.log -O - https://example.test/file`],
3440
+ ['wget repeated directory last metadata sink', `wget --no-config --no-hsts -P src -P .omx/state https://example.test/file`],
3441
+ ['wget repeated long directory last metadata sink', `wget --no-config --no-hsts --directory-prefix=src --directory-prefix=.omx/state https://example.test/file`],
3442
+ ['wget exact output document metadata sink', `wget --no-config --no-hsts --output-document=.omx/state/download.ts https://example.test/file`],
3443
+ ['wget execute metadata log sink', `wget --no-config --no-hsts --execute='logfile=.omx/state/wget-execute.log' -O - https://example.test/file`],
3444
+ ['curl literal hash output metadata sink', `curl -q -o .omx/state/curl-literal#hash.log https://example.test/file`],
3445
+ ['curl write out metadata sink', `curl -q --write-out '%output{.omx/state/curl-write-out.log}' https://example.test/file`],
3446
+ ['curl inline write out metadata sink', `curl -q --write-out='%output{.omx/state/curl-inline-write-out.log}' https://example.test/file`],
3447
+ ]) {
3448
+ for (const actor of ['main-root', 'native-child']) {
3449
+ requireActorDeny(actor, probe, runActorProbe(actor, probe, 'Bash', { command }));
3450
+ }
3451
+ }
3452
+ for (const [probe, command] of [
3453
+ ['current runtime node absolute read', `${currentRuntimeNodePath} -e "require('fs').readFileSync('src/victim.ts','utf8')"`],
3454
+ ['node fs readFileSync', `node -e "require('fs').readFileSync('src/victim.ts','utf8')"`],
3455
+ ['node write mutation text', `node -e 'console.log("require(\\"fs\\").writeFileSync(\\"src/victim.ts\\", \\"x\\")")'`],
3456
+ ['node ESM fs readFileSync', `node --input-type=module -e "import fs from 'node:fs';fs.readFileSync('src/victim.ts','utf8')"`],
3457
+ ['node fs openSync read-only', `node -e "require('fs').openSync('src/victim.ts','r')"`],
3458
+ ['node regex mutation text', `node -e 'console.log(/require\\("fs"\\)\\.rmSync\\("src\\/victim.ts"\\)/.test("x"))'`],
3459
+ ['node static unrelated computed member', `node -e "console.log(module['filename'])"`],
3460
+ ['node attached short read', `node -e"require('fs').readFileSync('src/victim.ts','utf8')"`],
3461
+ ['node read-only path module', `node -e "console.log(require('path').join('src','victim.ts'))"`],
3462
+ ['node array index', `node -e "const a=[1];console.log(a[0])"`],
3463
+ ['node dynamic object read', `node -e "const o={x:1};const k='x';console.log(o[k])"`],
3464
+ ['node Object.getPrototypeOf', `node -e "console.log(Object.getPrototypeOf({}))"`],
3465
+ ['node Object computed getPrototypeOf', `node -e "Object['getPrototypeOf']({x:1})"`],
3466
+ ['node object computed constructor', `node -e "const o={constructor:7};console.log(o['constructor'])"`],
3467
+ ['node Reflect.get', `node -e "console.log(Reflect.get({x:1},'x'))"`],
3468
+ ['python isolated read-only', `python3 -I -c "print('ok')"`],
3469
+ ['wget spider no-body', 'wget --no-config --no-hsts --spider https://example.test/file'],
3470
+ ['wget stdout short output document', 'wget --no-config --no-hsts -O - https://example.test/file'],
3471
+ ['wget stdout long output document', 'wget --no-config --no-hsts --output-document=- https://example.test/file'],
3472
+ ['wget stdout attached short output document', 'wget --no-config --no-hsts -O- https://example.test/file'],
3473
+ ['wget repeated output last stdout', 'wget --no-config --no-hsts -O src/wget-first-output.ts https://example.test/file -O -'],
3474
+ ['posix wget literal data', `printf '%s\n' 'POSIXLY_CORRECT wget -O src/not-executed.ts -O -'`],
3475
+ ['posix url data before stdout wget', `printf '%s\n' 'https://example.test/POSIXLY_CORRECT'; wget --no-config --no-hsts -O - https://example.test/file`],
3476
+ ['function after wget no posix taint', `f(){ export POSIXLY_CORRECT=1; }; wget --no-config --no-hsts -O src/function-after.ts https://example.test/file -O -; f`],
3477
+ ['subshell function no posix taint', `f(){ export POSIXLY_CORRECT=1; }; ( f ); wget --no-config --no-hsts -O src/function-subshell.ts https://example.test/file -O -`],
3478
+ ['returned local posix control', `f(){ local -x POSIXLY_CORRECT=1; }; f; wget --no-config --no-hsts -O src/local-scope-control.ts https://example.test/file -O -`],
3479
+ ['background posix control', `f(){ export POSIXLY_CORRECT=1; }; f & wait; wget --no-config --no-hsts -O src/background-control.ts https://example.test/file -O -`],
3480
+ ['pipeline posix control', `f(){ export POSIXLY_CORRECT=1; }; f | cat; wget --no-config --no-hsts -O src/pipeline-control.ts https://example.test/file -O -`],
3481
+ ['coproc posix control', `f(){ export POSIXLY_CORRECT=1; }; coproc f; wait; WGETRC=/dev/null wget --no-config --no-hsts -O - https://example.test/file`],
3482
+ ['nested subshell posix control', `f(){ export POSIXLY_CORRECT=1; }; if ( f ); then :; fi; wget --no-config --no-hsts -O src/nested-subshell-control.ts https://example.test/file -O -`],
3483
+ ['wrapper only posix control', `env POSIXLY_CORRECT=1 true; wget --no-config --no-hsts -O src/wrapper-control.ts https://example.test/file -O -`],
3484
+ ['wgetrc null control', `WGETRC=/dev/null wget --no-config --no-hsts -O - https://example.test/file`],
3485
+ ['home null control', `HOME=/dev/null wget --no-config --no-hsts -O - https://example.test/file`],
3486
+ ['case arm stdout control', `case true in true) wget --no-config --no-hsts -O - https://example.test/file;; esac`],
3487
+ ['xargs empty eof read-only control', `printf '%s\n' safe | xargs -E '' printf`],
3488
+ ['env chdir stdout control', `env -C src wget --no-config --no-hsts -O - https://example.test/file`],
3489
+ ['nonexported declare posix control', `declare POSIXLY_CORRECT=1; WGETRC=/dev/null wget --no-config --no-hsts -O src/declare-control.ts https://example.test/file -O -`],
3490
+ ['persistent wgetrc null control', `export WGETRC=/dev/null; wget --no-config --no-hsts -O - https://example.test/file`],
3491
+ ['restored wgetrc control', `export WGETRC=/dev/stdin; unset WGETRC; HOME=/dev/null wget --no-config --no-hsts -O - https://example.test/file`],
3492
+ ['dynamic case subject stdout control', `mode=true; case "$mode" in true) wget --no-config --no-hsts -O - https://example.test/file;; esac`],
3493
+ ['case arm pipeline read only', `case x in x) : | true;; esac`],
3494
+ ['later optional case pattern stdout', `case x in a) :;; (x) wget --no-config --no-hsts -O - https://example.test/file;; esac`],
3495
+ ['successful posix unset stdout', `export POSIXLY_CORRECT=1; unset POSIXLY_CORRECT; WGETRC=/dev/null wget --no-config --no-hsts -O src/unset-posix-first.ts https://example.test/file -O -`],
3496
+ ['nested posix stdout', `POSIXLY_CORRECT=1 sh -c 'wget --no-config --no-hsts -O - https://example.test/file'`],
3497
+ ['nested chdir stdout', `env --chdir=src sh -c 'wget --no-config --no-hsts -O - https://example.test/file'`],
3498
+ ['nested lastpipe stdout', `bash -O lastpipe -c 'wget --no-config --no-hsts -O - https://example.test/file'`],
3499
+ ['nested lastpipe pipeline stdout', `bash -O lastpipe -c 'printf safe | cat; wget --no-config --no-hsts -O - https://example.test/file'`],
3500
+ ['process substitution static read only', `cat <(printf safe)`],
3501
+ ['process substitution wget stdout', `cat <(wget --no-config --no-hsts -O - https://example.test/file)`],
3502
+ ['background brace posix control', `f(){ export POSIXLY_CORRECT=1; }; { f; } & wait; WGETRC=/dev/null wget --no-config --no-hsts -O - https://example.test/file`],
3503
+ ['background function definition child control', `{ touch(){ :; }; touch src/background-child-shadowed.ts; } & wait`],
3504
+ ['pipeline function definition child control', `{ touch(){ :; }; touch src/pipeline-child-shadowed.ts; } | cat`],
3505
+ ['coproc function definition child control', `coproc { touch(){ :; }; touch src/coproc-child-shadowed.ts; }; wait`],
3506
+ ['coproc inherited function runtime child control', `inherited_read(){ :; }; coproc { inherited_read; }; wait`],
3507
+ ['subshell function definition child control', `( touch(){ :; }; touch src/subshell-child-shadowed.ts )`],
3508
+ ['background function lifecycle child control', `touch(){ :; }; { unset -f touch; } & wait; touch src/background-lifecycle-shadowed.ts`],
3509
+ ['pipeline function lifecycle child control', `touch(){ :; }; { unset -f touch; } | cat; touch src/pipeline-lifecycle-shadowed.ts`],
3510
+ ['subshell function lifecycle child control', `touch(){ :; }; ( unset -f touch; ); touch src/subshell-lifecycle-shadowed.ts`],
3511
+ ['nested isolation stdout control', `( export POSIXLY_CORRECT=1; ( wget --no-config --no-hsts -O - https://example.test/file ) )`],
3512
+ ['function alternative null control', `f(){ export WGETRC=/dev/null; }; if false; then f(){ export WGETRC=/dev/null; }; fi; f; wget --no-config --no-hsts -O - https://example.test/file`],
3513
+ ['readonly function mode control', `export WGETRC=/dev/null; readonly -f WGETRC; HOME=/dev/null wget --no-config --no-hsts -O - https://example.test/file`],
3514
+ ['case fallthrough read only', `case x in x) : ;& true) printf safe;; esac`],
3515
+ ['export n wgetrc control', `export WGETRC=/dev/stdin; export -n WGETRC; HOME=/dev/null wget --no-config --no-hsts -O - https://example.test/file`],
3516
+ ['nested export posix stdout', `export POSIXLY_CORRECT=1; sh -c 'wget --no-config --no-hsts -O - https://example.test/file'`],
3517
+ ['bashopts lastpipe stdout', `env BASHOPTS=lastpipe bash -c 'true | printf safe; wget --no-config --no-hsts -O - https://example.test/file'`],
3518
+ ['wget quiet cluster stdout', `wget --no-config --no-hsts -qO- https://example.test/file`],
3519
+ ['case retest fallthrough read only', `case x in x) : ;;& x) printf safe;; esac`],
3520
+ ['declare plus x wgetrc control', `export WGETRC=/dev/stdin; declare +x WGETRC; HOME=/dev/null wget --no-config --no-hsts -O - https://example.test/file`],
3521
+ ['declare function local stdout', `f(){ declare -x WGETRC=/dev/null; wget --no-config --no-hsts -O - https://example.test/file; }; f`],
3522
+ ['parameter length expansion stdout control', `: \${#x}; wget --no-config --no-hsts -O - https://example.test/file`],
3523
+ ['parameter expansion stdout control', `: \${x:-safe}; wget --no-config --no-hsts -O - https://example.test/file`],
3524
+ ['nested clear environment stdout', `export POSIXLY_CORRECT=1; env -i sh -c 'wget --no-config --no-hsts -O - https://example.test/file'`],
3525
+ ['parameter assignment expansion stdout', `export POSIXLY_CORRECT; : \${POSIXLY_CORRECT:=1}; wget --no-config --no-hsts -O - https://example.test/file`],
3526
+ ['nested exec clear environment stdout', `export POSIXLY_CORRECT=1; exec -c sh -c 'wget --no-config --no-hsts -O - https://example.test/file'`],
3527
+ ['curl header stdout control', `curl -q --dump-header - https://example.test/file`],
3528
+ ['unset wgetrc then append null control', `export WGETRC=/dev/stdin; unset WGETRC; WGETRC+=/dev/null wget --no-config --no-hsts -O - https://example.test/file`],
3529
+ ['process substitution nested read only', `cat <(cat <(printf safe))`],
3530
+ ['sed read-only control', `sed -n '1,20p' src/runtime.ts`],
3531
+ ['exported child function read only', `f(){ :; }; export -f f; bash -c 'f; wget --no-config --no-hsts -O - https://example.test/file'`],
3532
+ ['cleared child function read only', `f(){ :; }; export -f f; env -i bash -c 'wget --no-config --no-hsts -O - https://example.test/file'`],
3533
+ ['wgetrc unset control', `export WGETRC=/dev/null; unset WGETRC; HOME=/dev/null wget --no-config --no-hsts -O - https://example.test/file`],
3534
+ ['cdpath explicit relative control', `CDPATH=src; cd ./src; wget --no-config --no-hsts -O - https://example.test/file`],
3535
+ ['cdpath pushd popd control', `CDPATH=src; pushd ./src; popd; wget --no-config --no-hsts -O - https://example.test/file`],
3536
+ ['python isolated metadata cwd control', `cd .omx/state && python3 -I -c "print('ok')"`],
3537
+ ['curl cluster stdout control', `curl -q -sD - -o - https://example.test/file`],
3538
+ ['wget version control', `wget --no-config --no-hsts -V`],
3539
+ ['wget value option stdout control', `wget --no-config --no-hsts -t 1 -O - https://example.test/file`],
3540
+ ['wget help control', `wget --no-config --no-hsts --help`],
3541
+ ['cdpath absolute control', `CDPATH=src; cd /tmp; wget --no-config --no-hsts -O - https://example.test/file`],
3542
+ ['function lastpipe option control', `export POSIXLY_CORRECT=1; f(){ shopt -s lastpipe; }; f; g(){ unset POSIXLY_CORRECT; }; true | g; wget --no-config --no-hsts -O src/function-lastpipe-first.ts https://example.test/file -O -`],
3543
+ ['wget execute static nonsink stdout', `wget --no-config --no-hsts -e 'timeout=1' -O - https://example.test/file`],
3544
+ ['wget bounded connect DNS read timeout control', `wget --no-config --no-hsts --connect-timeout=1 --dns-timeout=1 --read-timeout=1 -O - https://example.test/file`],
3545
+ ['nested bash shopt lastpipe control', `bash -O lastpipe -c 'true | printf safe'`],
3546
+ ['curl disabled startup stdout', `curl -q --write-out '%output{-}' https://example.test/file`],
3547
+ ['curl short write out stdout', `curl -q -w '%output{-}' https://example.test/file`],
3548
+ ['curl disable startup stdout', `curl --disable --write-out '%output{-}' https://example.test/file`],
3549
+ ['curl inline write out stdout', `curl -q --write-out='%output{-}' https://example.test/file`],
3550
+ ['static benign export control', `export GJC_SESSION_ID; cat src/conductor-owned.ts`],
3551
+ ['curl SSL key log unset control', `SSLKEYLOGFILE=src/curl-tls.keys env -u SSLKEYLOGFILE curl -q -o - https://example.test/file`],
3552
+ ['wget SSL key log unset control', `SSLKEYLOGFILE=src/wget-tls.keys env -u SSLKEYLOGFILE wget --no-config --no-hsts -O - https://example.test/file`],
3553
+ ['exported readonly child function control', `f(){ :; }; declare -frx f; bash -c 'f; wget --no-config --no-hsts -O - https://example.test/file'`],
3554
+ ['exported redefined child function control', `f(){ :; }; export -f f; f(){ :; }; bash -c 'f; wget --no-config --no-hsts -O - https://example.test/file'`],
3555
+ ['curl static head stdout', `curl -q --request HEAD https://example.test/file`],
3556
+ ['wget no proxy standalone stdout control', `wget --no-config --no-hsts --no-proxy -O - https://example.test/file`],
3557
+ ['wget quiet standalone stdout control', `wget --no-config --no-hsts -q -O - https://example.test/file`],
3558
+ ['wget short quiet standalone stdout control', `wget --no-config --no-hsts -qO- https://example.test/file`],
3559
+ ['wget static head stdout', `wget --no-config --no-hsts --method=HEAD -O - https://example.test/file`],
3560
+ ['node output clear environment control', `NODE_V8_COVERAGE=src env -i node -e "console.log('ok')"`],
3561
+ ['node persistent output clear environment control', `export NODE_V8_COVERAGE=src; env -i node -e "console.log('ok')"`],
3562
+ ['path inherited system control', `cat src/conductor-owned.ts`],
3563
+ ['absolute system path control', `/usr/bin/cat src/conductor-owned.ts`],
3564
+ ['loader clear environment control', `env -i /usr/bin/cat src/conductor-owned.ts`],
3565
+ ['loader unrelated declaration control', `export WGETRC=/dev/stdin; declare +x WGETRC; HOME=/dev/null wget --no-config --no-hsts -O - https://example.test/file`],
3566
+ ['sort numeric read-only', `sort -n README.md`],
3567
+ ['isolated bash login read-only', `bash --noprofile --norc -lc "printf safe"`],
3568
+ ]) {
3569
+ for (const actor of ['main-root', 'native-child']) {
3570
+ const output = runActorProbe(actor, probe, 'Bash', { command });
3571
+ if (Object.keys(output).length !== 0) {
3572
+ throw new Error(`native hook blocked semantic Node read-only operation: ${actor} ${probe}: ${JSON.stringify(output)}`);
3573
+ }
3574
+ }
3575
+ }
3576
+ for (const [probe, command] of [
3577
+ ['chmod reference separated metadata mutation', `chmod --reference .omx/state/reference-copy .omx/state/reference-copy`],
3578
+ ['chown reference metadata mutation', `chown --reference=.omx/state/reference-copy .omx/state/reference-copy`],
3579
+ ['chgrp reference metadata mutation', `chgrp --reference=.omx/state/reference-copy .omx/state/reference-copy`],
3580
+ ['rsync metadata mutation', `rsync .omx/state/conductor-ledger.json .omx/state/inbox/rsync-copy`],
3581
+ ['rsync product source metadata mutation', `rsync README.md .omx/state/inbox/readme.md`],
3582
+ ['cdpath rsync metadata mutation', `CDPATH=.omx; cd state; rsync --log-file=rsync-cdpath.log conductor-ledger.json inbox/rsync-cdpath-copy`],
3583
+ ['pushd cdpath rsync metadata mutation', `CDPATH=.omx; pushd state; rsync --log-file=rsync-pushd.log conductor-ledger.json inbox/rsync-pushd-copy`],
3584
+ ['cdpath prefix cd metadata mutation', `CDPATH=src cd state; rsync --log-file=../../.omx/state/rsync-prefix-cdpath.log ../../.omx/state/conductor-ledger.json ../../.omx/state/inbox/rsync-prefix-cdpath-copy`],
3585
+ ['cdpath local function rsync mutation', `CDPATH=.omx; f(){ local CDPATH=src; cd shared; cd ../..; }; f; cd state; rsync --log-file=rsync-local-cdpath.log conductor-ledger.json inbox/rsync-local-cdpath-copy`],
3586
+ ['cdpath nested local function rsync mutation', `CDPATH=.omx; f(){ local CDPATH=src; g(){ local CDPATH=.omx; cd state; cd ../..; }; g; cd shared; cd ../..; }; f; cd state; rsync --log-file=rsync-nested-local-cdpath.log conductor-ledger.json inbox/rsync-nested-local-cdpath-copy`],
3587
+ ]) {
3588
+ const mainRootOutput = runActorProbe('main-root', probe, 'Bash', { command });
3589
+ if (Object.keys(mainRootOutput).length !== 0) {
3590
+ throw new Error(`packed main-root metadata mutation should remain allowed: ${probe}: ${JSON.stringify(mainRootOutput)}`);
3591
+ }
3592
+ requireActorDeny('native-child', probe, runActorProbe('native-child', probe, 'Bash', { command }));
3593
+ }
3594
+ for (const actor of ['main-root', 'native-child']) {
3595
+ const output = runActorProbe(actor, 'current runtime node bare read', 'Bash', { command: `node -e "require('fs').readFileSync('src/victim.ts','utf8')"` }, currentRuntimeNodePathEnvironment);
3596
+ if (Object.keys(output).length !== 0) {
3597
+ throw new Error(`packed ${actor} current runtime bare node should retain read-only allowance`);
3598
+ }
3599
+ }
3600
+ for (const actor of ['main-root', 'native-child']) {
3601
+ requireActorDeny(actor, 'current runtime node loader', runActorProbe(actor, 'current runtime node loader', 'Bash', { command: `LD_PRELOAD=.omx/state/mutator.so ${currentRuntimeNodePath} -e "require('fs').readFileSync('src/victim.ts','utf8')"` }));
3602
+ }
3603
+ for (const [probe, command] of [
3604
+ ['nested chdir metadata', `env --chdir=src sh -c 'touch ../.omx/state/final.log'`],
3605
+ ['touch mode metadata', `touch .omx/state/final.log -m`],
3606
+ ['time output metadata', `time --output=.omx/state/time-output.log printf safe`],
3607
+ ]) {
3608
+ if (Object.keys(runActorProbe('main-root', `${probe} main`, 'Bash', { command })).length !== 0) {
3609
+ throw new Error(`packed main-root ${probe} should retain metadata allowance`);
3610
+ }
3611
+ requireActorDeny('native-child', `${probe} native child`, runActorProbe('native-child', `${probe} native child`, 'Bash', { command }));
3612
+ }
3613
+ for (const actor of ['main-root', 'native-child']) {
3614
+ requireActorDeny(actor, 'curl static glob output template', runActorProbe(actor, 'curl static glob output template', 'Bash', {
3615
+ command: `curl -q -o .omx/state/curl-glob-#1.log 'https://example.test/file[1-2]'`,
3616
+ }));
3617
+ }
3618
+ const curlBraceTemplateCommand = `curl -q -o .omx/state/curl-brace-#2.log 'https://example.test/{one,two}[1-2]'`;
3619
+ for (const actor of ['main-root', 'native-child']) {
3620
+ requireActorDeny(actor, 'curl brace glob output template', runActorProbe(actor, 'curl brace glob output template', 'Bash', { command: curlBraceTemplateCommand }));
3621
+ }
3622
+ for (const actor of ['main-root', 'native-child']) {
3623
+ requireActorDeny(actor, 'unknown transport', runActorProbe(actor, 'unknown transport', 'mcp__example__future_mutation', {
3624
+ target: 'src/packed-unknown.ts',
3625
+ }));
3626
+ }
3627
+ for (const [probe, toolName] of [
3628
+ ['wiki ingest', 'mcp__omx_wiki__wiki_ingest'],
3629
+ ['project memory write', 'mcp__omx_memory__project_memory_write'],
3630
+ ]) {
3631
+ for (const actor of ['main-root', 'native-child']) {
3632
+ requireActorDeny(actor, probe, runActorProbe(actor, probe, toolName, { content: 'mutation' }));
3633
+ }
3634
+ }
3635
+ for (const [probe, toolName, toolInput] of [
3636
+ ['trace summary', 'mcp__omx_trace__trace_summary', { workingDirectory: smokeCwd }],
3637
+ ['LSP diagnostics', 'mcp__omx_code_intel__lsp_diagnostics', { file: 'src/runtime.ts' }],
3638
+ ['wiki query', 'mcp__omx_wiki__wiki_query', { query: 'native hook', workingDirectory: smokeCwd }],
3639
+ ['project memory read', 'mcp__omx_memory__project_memory_read', { workingDirectory: smokeCwd }],
3640
+ ['notepad stats', 'mcp__omx_memory__notepad_stats', { workingDirectory: smokeCwd }],
3641
+ ]) {
3642
+ for (const actor of ['main-root', 'native-child']) {
3643
+ const output = runActorProbe(actor, probe, toolName, toolInput);
3644
+ if (Object.keys(output).length !== 0) {
3645
+ throw new Error(`native hook blocked audited read-only MCP operation: ${actor} ${probe}`);
3646
+ }
3647
+ }
3648
+ }
3649
+ writeFileSync(join(stateDir, 'sessions', sessionId, 'skill-active-state.json'), JSON.stringify({
3650
+ active: true,
3651
+ skill: 'ralplan',
3652
+ phase: 'planning',
3653
+ session_id: sessionId,
3654
+ active_skills: [{ skill: 'ralplan', phase: 'planning', active: true, session_id: sessionId }],
3655
+ }));
3656
+ writeFileSync(join(stateDir, 'sessions', sessionId, 'ralplan-state.json'), JSON.stringify({
3657
+ active: true,
3658
+ mode: 'ralplan',
3659
+ current_phase: 'planning',
3660
+ session_id: sessionId,
3661
+ }));
3662
+ mkdirSync(join(stateDir, 'sessions', leaderAgentId), { recursive: true });
3663
+ writeFileSync(join(stateDir, 'sessions', leaderAgentId, 'skill-active-state.json'), JSON.stringify({
3664
+ active: true,
3665
+ skill: 'ralplan',
3666
+ phase: 'planning',
3667
+ session_id: leaderAgentId,
3668
+ active_skills: [{ skill: 'ralplan', phase: 'planning', active: true, session_id: leaderAgentId }],
3669
+ }));
3670
+ writeFileSync(join(stateDir, 'sessions', leaderAgentId, 'ralplan-state.json'), JSON.stringify({
3671
+ active: true,
3672
+ mode: 'ralplan',
3673
+ current_phase: 'planning',
3674
+ session_id: leaderAgentId,
3675
+ }));
3676
+ requireActorDeny('native-child', 'direct planning artifact write', runActorProbe('native-child', 'direct planning artifact write', 'Write', { file_path: '.omx/plans/native-child.md', content: '# unauthorized\n' }));
3677
+ requireActorDeny('native-child', 'sessionless planning state write', runActorProbe('native-child', 'sessionless planning state write', 'mcp__omx_state__state_write', { mode: 'ralplan', active: true, current_phase: 'planning' }), /PROVENANCE_DENIED/);
3678
+ requireActorDeny('native-child', 'foreign planning state write', runActorProbe('native-child', 'foreign planning state write', 'mcp__omx_state__state_write', { mode: 'ralplan', active: false, current_phase: 'complete', session_id: 'foreign-session' }), /PROVENANCE_DENIED/);
3679
+ symlinkSync(join(smokeCwd, 'src'), join(smokeCwd, '.omx', 'drafts'));
3680
+ for (const actor of ['main-root', 'native-child']) {
3681
+ const output = runActorProbe(actor, 'linked planning artifact redirect', 'Bash', {
3682
+ command: 'printf owned > .omx/drafts/linked-plan.md',
3683
+ });
3684
+ if (output.hookSpecificOutput?.permissionDecision !== 'deny') {
3685
+ throw new Error(`packed ${actor} linked planning artifact redirect should be denied`);
3686
+ }
3687
+ }
3688
+ writeFileSync(join(stateDir, 'sessions', sessionId, 'skill-active-state.json'), JSON.stringify({
3689
+ active: true,
3690
+ skill: 'ralph',
3691
+ phase: 'starting',
3692
+ session_id: sessionId,
3693
+ active_skills: [{ skill: 'ralph', phase: 'starting', active: true, session_id: sessionId }],
3694
+ }));
3695
+ writeFileSync(join(stateDir, 'sessions', sessionId, 'ralph-state.json'), JSON.stringify({
3696
+ active: true,
3697
+ mode: 'ralph',
3698
+ current_phase: 'starting',
3699
+ session_id: sessionId,
3700
+ }));
3701
+ requireActorDeny('native-child', 'Ralph starting write', runActorProbe('native-child', 'Ralph starting write', 'Write', {
3702
+ file_path: 'src/ralph-starting-bypass.ts',
3703
+ content: 'owned\n',
3704
+ }));
3705
+ const childReadOutput = runActorProbe('native-child', 'read-only', 'Read', { file_path: 'src/packed-read-only.ts' });
3706
+ if (Object.keys(childReadOutput).length !== 0) {
3707
+ throw new Error('native hook blocked a positively classified native-child read-only operation');
3708
+ }
3709
+ // #3194: the installed Codex 0.144.5 documented surface fails closed before
3710
+ // authority state or an adapted role intent can be created.
3711
+ const roleIntentCwd = join(smokeCwd, 'issue-3194-role-intent');
3712
+ const roleIntentHome = join(roleIntentCwd, 'home');
3713
+ const roleIntentCodexHome = join(roleIntentCwd, 'codex-home');
3714
+ const roleIntentNativeSessionId = 'synthetic-3194-session';
3715
+ mkdirSync(roleIntentHome, { recursive: true });
3716
+ mkdirSync(roleIntentCodexHome, { recursive: true });
3717
+ const roleIntentEnvironment = {
3718
+ ...buildPackedRegressionEnvironment({ name: 'issue-3194-role-intent' }),
3719
+ HOME: roleIntentHome,
3720
+ CODEX_HOME: roleIntentCodexHome,
3721
+ };
3722
+ const sessionStartResult = run(process.execPath, [realpathSync(hookScript)], {
3723
+ cwd: roleIntentCwd,
3724
+ env: roleIntentEnvironment,
3725
+ input: JSON.stringify({ hook_event_name: 'SessionStart', cwd: roleIntentCwd, session_id: roleIntentNativeSessionId }),
3726
+ });
3727
+ validateHookStdout('SessionStart', String(sessionStartResult.stdout || ''));
3728
+ const trackingPath = join(roleIntentCwd, '.omx', 'state', 'subagent-tracking.json');
3729
+ if (existsSync(trackingPath))
3730
+ throw new Error('installed #3194 SessionStart unexpectedly created tracker authority');
3731
+ const preToolUseResult = run(process.execPath, [realpathSync(hookScript)], {
3732
+ cwd: roleIntentCwd,
3733
+ env: roleIntentEnvironment,
3734
+ input: JSON.stringify({
3735
+ hook_event_name: 'PreToolUse',
3736
+ cwd: roleIntentCwd,
3737
+ session_id: roleIntentNativeSessionId,
3738
+ turn_id: 'synthetic-3194-turn',
3739
+ tool_name: 'Bash',
3740
+ tool_use_id: 'synthetic-3194-tool',
3741
+ tool_input: { command: 'omx ralplan role-intent write --role architect --parent-thread "$CODEX_THREAD_ID" --json' },
3742
+ }),
3743
+ });
3744
+ const preToolUseExpected = '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"unsupported_documented_leader_proof: Codex 0.144.5 hooks do not expose documented root identity required for adapted Ralplan."}}\n';
3745
+ if (String(preToolUseResult.stdout || '') !== preToolUseExpected) {
3746
+ throw new Error('installed #3194 PreToolUse did not emit the exact unsupported denial');
3747
+ }
3748
+ if (existsSync(trackingPath))
3749
+ throw new Error('installed #3194 PreToolUse unexpectedly created tracker authority');
3750
+ const roleIntentCliResult = spawnSync(process.execPath, [realpathSync(join(packageRoot, 'dist', 'cli', 'omx.js')), 'ralplan', 'role-intent', 'write', '--role', 'architect', '--parent-thread', roleIntentNativeSessionId, '--json'], {
3751
+ cwd: roleIntentCwd,
3752
+ env: roleIntentEnvironment,
3753
+ encoding: 'utf-8',
3754
+ stdio: 'pipe',
3755
+ });
3756
+ if (roleIntentCliResult.status === 0)
3757
+ throw new Error('installed #3194 role-intent CLI unexpectedly authorized an Architect receipt');
3758
+ if (String(roleIntentCliResult.stdout || '') !== '{"ok":false,"reason":"unsupported_documented_leader_proof"}\n') {
3759
+ throw new Error('installed #3194 role-intent CLI returned an unexpected denial');
3760
+ }
3761
+ if (existsSync(trackingPath))
3762
+ throw new Error('installed #3194 role-intent CLI unexpectedly created tracker authority');
3763
+ for (const [caseIndex, testCase] of PACKED_INSTALL_NATIVE_HOOK_REGRESSION_PROMPTS.entries()) {
3764
+ const caseCwd = join(smokeCwd, testCase.name);
3765
+ const sessionId = `packed-regression-${caseIndex}`;
3766
+ mkdirSync(caseCwd, { recursive: true });
3767
+ const environment = buildPackedRegressionEnvironment(testCase);
3768
+ const promptPayload = {
3769
+ hook_event_name: 'UserPromptSubmit',
3770
+ cwd: caseCwd,
3771
+ source: 'codex-app',
3772
+ session_id: sessionId,
3773
+ thread_id: `thread-${caseIndex}`,
3774
+ turn_id: `turn-${caseIndex}`,
3775
+ prompt: testCase.prompt,
3776
+ };
3777
+ const promptResult = run(process.execPath, [realpathSync(hookScript)], {
3778
+ cwd: caseCwd,
3779
+ env: environment,
3780
+ input: JSON.stringify(promptPayload),
3781
+ });
3782
+ validateHookStdout('UserPromptSubmit', promptResult.stdout);
3783
+ const skillStatePath = join(caseCwd, '.omx', 'state', 'sessions', sessionId, 'skill-active-state.json');
3784
+ if (testCase.expectedSkill === null) {
3785
+ if (existsSync(skillStatePath))
3786
+ throw new Error(`packed regression ${testCase.name} created workflow state`);
3787
+ }
3788
+ else {
3789
+ if (!existsSync(skillStatePath))
3790
+ throw new Error(`packed regression ${testCase.name} did not create workflow state`);
3791
+ const skillState = JSON.parse(readFileSync(skillStatePath, 'utf-8'));
3792
+ if (!skillState.active || skillState.skill !== testCase.expectedSkill) {
3793
+ throw new Error(`packed regression ${testCase.name} persisted unexpected workflow state`);
3794
+ }
3795
+ if ('expectedDeferredSkills' in testCase && JSON.stringify(skillState.deferred_skills ?? []) !== JSON.stringify(testCase.expectedDeferredSkills)) {
3796
+ throw new Error(`packed regression ${testCase.name} persisted unexpected deferred workflows`);
3797
+ }
3798
+ if ('expectedActiveSkills' in testCase && JSON.stringify(skillState.active_skills?.map((entry) => entry.skill) ?? []) !== JSON.stringify(testCase.expectedActiveSkills)) {
3799
+ throw new Error(`packed regression ${testCase.name} persisted unexpected active workflows`);
3800
+ }
3801
+ }
3802
+ const stopResult = run(process.execPath, [realpathSync(hookScript)], {
3803
+ cwd: caseCwd,
3804
+ env: environment,
3805
+ input: JSON.stringify({ ...promptPayload, hook_event_name: 'Stop', turn_id: `stop-${caseIndex}` }),
3806
+ });
3807
+ const stopOutput = JSON.parse(String(stopResult.stdout || '{}'));
3808
+ if (testCase.expectedStopBlock && stopOutput.decision !== 'block') {
3809
+ throw new Error(`packed regression ${testCase.name} did not block Stop`);
3810
+ }
3811
+ if (!testCase.expectedStopBlock && stopOutput.decision === 'block') {
3812
+ throw new Error(`packed regression ${testCase.name} blocked Stop: ${JSON.stringify(stopOutput)}`);
3813
+ }
3814
+ }
3815
+ runPackedTransportRegressions(hookScript, smokeCwd);
171
3816
  }
172
3817
  finally {
173
3818
  rmSync(smokeCwd, { recursive: true, force: true });
174
3819
  }
175
3820
  }
3821
+ function sameJson(left, right) {
3822
+ return JSON.stringify(left) === JSON.stringify(right);
3823
+ }
3824
+ function isolatedSmokeEnv(home, codexHome) {
3825
+ const env = { ...process.env, HOME: home, CODEX_HOME: codexHome };
3826
+ for (const key of [
3827
+ 'OMX_SESSION_ID',
3828
+ 'OMX_RUN_ID',
3829
+ 'OMX_ROOT',
3830
+ 'OMX_STATE_ROOT',
3831
+ 'OMX_ACTIVE_SESSION_PID',
3832
+ 'CODEX_SESSION_ID',
3833
+ 'TMUX',
3834
+ 'TMUX_PANE',
3835
+ ]) {
3836
+ delete env[key];
3837
+ }
3838
+ return env;
3839
+ }
3840
+ function trustedProjectConfig(projectDir) {
3841
+ const escapedProjectDir = projectDir.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
3842
+ return `[projects."${escapedProjectDir}"]\ntrust_level = "trusted"\n`;
3843
+ }
3844
+ function rawManagedHookCount(hooksContent) {
3845
+ const parsed = JSON.parse(hooksContent);
3846
+ if (!isRecord(parsed.hooks))
3847
+ throw new Error('hooks.json is missing hooks object');
3848
+ return Object.values(parsed.hooks).flatMap((entries) => Array.isArray(entries) ? entries : [])
3849
+ .flatMap((entry) => isRecord(entry) && Array.isArray(entry.hooks) ? entry.hooks : [])
3850
+ .filter((hook) => isRecord(hook)
3851
+ && typeof hook.command === 'string'
3852
+ && isManagedCodexHookCommand(hook.command)).length;
3853
+ }
3854
+ function assertNoEmptyManagedEventGroups(hooksContent) {
3855
+ const parsed = JSON.parse(hooksContent);
3856
+ if (!isRecord(parsed.hooks))
3857
+ throw new Error('hooks.json is missing hooks object');
3858
+ for (const event of MANAGED_CODEX_HOOK_EVENTS) {
3859
+ const entries = parsed.hooks[event];
3860
+ if (!Array.isArray(entries))
3861
+ continue;
3862
+ entries.forEach((entry, groupIndex) => {
3863
+ if (isRecord(entry) && (!Array.isArray(entry.hooks) || entry.hooks.length === 0)) {
3864
+ throw new Error(`packed uninstall left an empty ${event} group at coordinate ${groupIndex}`);
3865
+ }
3866
+ });
3867
+ }
3868
+ }
3869
+ function assertNoSetupOwnedTrustKeys(configToml, setupTrust) {
3870
+ const parsed = TOML.parse(configToml);
3871
+ const state = parsed.hooks?.state;
3872
+ if (!isRecord(state))
3873
+ return;
3874
+ const retained = Object.keys(setupTrust).filter((key) => Object.hasOwn(state, key));
3875
+ if (retained.length > 0) {
3876
+ throw new Error(`packed uninstall retained setup-owned OMX hook trust keys: ${retained.join(', ')}`);
3877
+ }
3878
+ }
3879
+ function assertNativeHooksEnabledForForeignGroups(configToml) {
3880
+ const parsed = TOML.parse(configToml);
3881
+ const features = parsed.features;
3882
+ if (!isRecord(features) || (features.hooks !== true && features.codex_hooks !== true)) {
3883
+ throw new Error('packed uninstall disabled native hooks despite preserved foreign hook groups');
3884
+ }
3885
+ }
3886
+ function assertNoUninstallTransactionArtifacts(codexDir) {
3887
+ const artifacts = readdirSync(codexDir).filter((entry) => entry.includes('.omx-uninstall-'));
3888
+ if (artifacts.length > 0) {
3889
+ throw new Error(`unsafe packed uninstall left replacement, staged, or tombstone paths: ${artifacts.join(', ')}`);
3890
+ }
3891
+ }
3892
+ function preToolUseGroupIndices(hooksContent, marker) {
3893
+ const parsed = JSON.parse(hooksContent);
3894
+ if (!isRecord(parsed.hooks))
3895
+ throw new Error('hooks.json is missing hooks object');
3896
+ const entries = parsed.hooks.PreToolUse;
3897
+ if (!Array.isArray(entries))
3898
+ throw new Error('hooks.PreToolUse is missing');
3899
+ const foreignIndices = [];
3900
+ const managedIndices = [];
3901
+ entries.forEach((entry, index) => {
3902
+ if (!isRecord(entry) || !Array.isArray(entry.hooks))
3903
+ return;
3904
+ if (entry.hooks.some((hook) => isRecord(hook)
3905
+ && typeof hook.command === 'string'
3906
+ && hook.command.includes(marker))) {
3907
+ foreignIndices.push(index);
3908
+ }
3909
+ if (entry.hooks.some((hook) => isRecord(hook)
3910
+ && typeof hook.command === 'string'
3911
+ && isManagedCodexHookCommand(hook.command))) {
3912
+ managedIndices.push(index);
3913
+ }
3914
+ });
3915
+ return { foreignIndices, managedIndices };
3916
+ }
3917
+ function assertManagedHooksAppendAfterForeign(hooksContent, marker) {
3918
+ const { foreignIndices, managedIndices } = preToolUseGroupIndices(hooksContent, marker);
3919
+ if (foreignIndices.length !== 2 ||
3920
+ managedIndices.length !== 1 ||
3921
+ managedIndices[0] <= Math.max(...foreignIndices)) {
3922
+ throw new Error('packed first install did not append the OMX PreToolUse group after both foreign groups');
3923
+ }
3924
+ }
3925
+ function assertManagedHooksRemainBeforeForeign(hooksContent, marker) {
3926
+ const { foreignIndices, managedIndices } = preToolUseGroupIndices(hooksContent, marker);
3927
+ if (foreignIndices.length !== 2 ||
3928
+ managedIndices.length !== 1 ||
3929
+ managedIndices[0] >= Math.min(...foreignIndices)) {
3930
+ throw new Error('packed setup rerun reordered the managed-first PreToolUse topology');
3931
+ }
3932
+ }
3933
+ function assertTrustedManagedHooks(hooks) {
3934
+ for (const [event, hook] of Object.entries(managedCodexHooksByEvent(hooks))) {
3935
+ if (hook.trustStatus !== 'trusted') {
3936
+ throw new Error(`Codex did not trust OMX ${event}; received ${hook.trustStatus}`);
3937
+ }
3938
+ }
3939
+ }
3940
+ function assertManagedHooksNotAlreadyTrusted(hooks) {
3941
+ for (const [event, hook] of Object.entries(managedCodexHooksByEvent(hooks))) {
3942
+ if (hook.trustStatus === 'trusted') {
3943
+ throw new Error(`setup-generated project trust unexpectedly pre-approved OMX ${event}`);
3944
+ }
3945
+ }
3946
+ }
3947
+ function preseededForeignHookMetadata(hooks, marker) {
3948
+ return hookMetadataSnapshot(hooks.filter((hook) => hook.command.includes(marker) && !hook.command.includes('-inserted.js')));
3949
+ }
3950
+ function assertPreseededForeignHookEvidence(expected, actualHooks, codexUserConfig, marker, phase) {
3951
+ const actual = preseededForeignHookMetadata(actualHooks, marker);
3952
+ if (actual.length !== 2) {
3953
+ throw new Error(`Codex ${phase} did not report exactly two pre-seeded foreign hooks; received ${actual.length}`);
3954
+ }
3955
+ if (!sameJson(actual, expected)) {
3956
+ const changes = expected.flatMap((expectedHook, index) => Object.entries(expectedHook).flatMap(([field, expectedValue]) => {
3957
+ const actualValue = actual[index]?.[field];
3958
+ return actualValue === expectedValue
3959
+ ? []
3960
+ : [`${expectedHook.event}.${field}: ${JSON.stringify(expectedValue)} -> ${JSON.stringify(actualValue)}`];
3961
+ }));
3962
+ throw new Error(`Codex ${phase} changed pre-seeded foreign hook key, hash, coordinate, or display order: ${changes.join('; ')}`);
3963
+ }
3964
+ if (actual.some((hook) => hook.trustStatus !== 'trusted')) {
3965
+ throw new Error(`Codex ${phase} did not retain trusted status for every pre-seeded foreign hook`);
3966
+ }
3967
+ const trust = generatedHookTrustState(codexUserConfig);
3968
+ for (const hook of actual) {
3969
+ if (trust[hook.key] !== hook.currentHash) {
3970
+ throw new Error(`Codex ${phase} did not retain the foreign trust entry for ${hook.key}`);
3971
+ }
3972
+ }
3973
+ }
3974
+ async function observeInstalledCodexHooks(projectDir, hooksPath, env) {
3975
+ const server = await CodexAppServer.start({ cwd: projectDir, env });
3976
+ try {
3977
+ await initializeCodexAppServer(server, 'omx-packed-install-smoke');
3978
+ return await listCodexHooks(server, projectDir, hooksPath);
3979
+ }
3980
+ finally {
3981
+ await server.close();
3982
+ }
3983
+ }
3984
+ /**
3985
+ * Exercise setup and removal against the installed package. The deterministic
3986
+ * filesystem lifecycle always runs; the installed-Codex trust leg is skipped
3987
+ * only when the `codex` executable is absent.
3988
+ */
3989
+ export async function smokePackedHookTrustLifecycle(omxPath) {
3990
+ const lifecycleRoot = mkdtempSync(join(tmpdir(), 'omx-packed-hook-trust-'));
3991
+ const projectDir = resolve(lifecycleRoot, 'project');
3992
+ const home = join(lifecycleRoot, 'home');
3993
+ const codexHome = join(lifecycleRoot, 'codex-home');
3994
+ const hooksPath = join(projectDir, '.codex', 'hooks.json');
3995
+ const configPath = join(projectDir, '.codex', 'config.toml');
3996
+ const marker = 'omx-packed-foreign';
3997
+ const agentsPath = join(projectDir, 'AGENTS.md');
3998
+ const foreignAgentsContent = '# User project instructions\n\nPreserve this packed lifecycle guidance.\n';
3999
+ const env = isolatedSmokeEnv(home, codexHome);
4000
+ try {
4001
+ mkdirSync(projectDir, { recursive: true });
4002
+ mkdirSync(home, { recursive: true });
4003
+ mkdirSync(codexHome, { recursive: true });
4004
+ mkdirSync(join(projectDir, '.codex'), { recursive: true });
4005
+ writeFileSync(join(codexHome, 'config.toml'), trustedProjectConfig(projectDir), 'utf-8');
4006
+ writeFileSync(agentsPath, foreignAgentsContent, 'utf-8');
4007
+ // Pre-seed foreign groups so uninstall may safely remove OMX's appended suffix.
4008
+ writeFileSync(hooksPath, appendDisplayOrderStableForeignHookGroups('{"hooks":{}}', marker, { appendGroups: true }), 'utf-8');
4009
+ const preSetupForeignSnapshot = foreignHookGroupSnapshot(readFileSync(hooksPath, 'utf-8'), marker);
4010
+ if (preSetupForeignSnapshot.length !== 2) {
4011
+ throw new Error(`expected two pre-seeded foreign hook coordinates, received ${preSetupForeignSnapshot.length}`);
4012
+ }
4013
+ let codexVersion;
4014
+ try {
4015
+ codexVersion = probeCodexVersion(projectDir, env);
4016
+ }
4017
+ catch (error) {
4018
+ if (!(error instanceof CodexExecutableNotFoundError))
4019
+ throw error;
4020
+ codexVersion = null;
4021
+ }
4022
+ const codexUserConfigPath = join(codexHome, 'config.toml');
4023
+ let approvedPreSetupForeignMetadata = null;
4024
+ if (codexVersion !== null) {
4025
+ const server = await CodexAppServer.start({ cwd: projectDir, env });
4026
+ try {
4027
+ await initializeCodexAppServer(server, 'omx-packed-install-smoke');
4028
+ const preSetupCodexHooks = await listCodexHooks(server, projectDir, hooksPath);
4029
+ const preSetupForeignHooks = preSetupCodexHooks.hooks.filter((hook) => hook.command.includes(marker) && !hook.command.includes('-inserted.js'));
4030
+ if (preSetupForeignHooks.length !== 2) {
4031
+ throw new Error(`Codex hooks/list did not report exactly two pre-seeded foreign hooks; received ${preSetupForeignHooks.length}`);
4032
+ }
4033
+ if (preSetupForeignHooks.some((hook) => hook.trustStatus === 'trusted')) {
4034
+ throw new Error('isolated Codex approval state unexpectedly pre-approved a foreign hook before setup');
4035
+ }
4036
+ const approval = await server.request(createCodexBatchWriteEnvelope(Object.fromEntries(preSetupForeignHooks.map((hook) => [hook.key, hook.currentHash]))), CODEX_APP_SERVER_TIMEOUTS.requestMs);
4037
+ assertCodexBatchWriteResult(approval, codexUserConfigPath);
4038
+ }
4039
+ finally {
4040
+ await server.close();
4041
+ }
4042
+ const approved = await observeInstalledCodexHooks(projectDir, hooksPath, env);
4043
+ approvedPreSetupForeignMetadata = preseededForeignHookMetadata(approved.hooks, marker);
4044
+ assertPreseededForeignHookEvidence(approvedPreSetupForeignMetadata, approved.hooks, readFileSync(codexUserConfigPath, 'utf-8'), marker, 'pre-setup approval');
4045
+ }
4046
+ const setupResult = run(omxPath, ['setup', '--scope', 'project', '--merge-agents', '--legacy'], {
4047
+ cwd: projectDir,
4048
+ env,
4049
+ });
4050
+ if (!existsSync(agentsPath) || !readFileSync(agentsPath, 'utf-8').includes(foreignAgentsContent.trim())) {
4051
+ throw new Error(`packed setup --merge-agents did not preserve pre-existing user AGENTS.md guidance; stdout=${JSON.stringify(String(setupResult.stdout || ''))} stderr=${JSON.stringify(String(setupResult.stderr || ''))}`);
4052
+ }
4053
+ const initialHooksContent = readFileSync(hooksPath, 'utf-8');
4054
+ assertForeignHookGroupsPreserved(preSetupForeignSnapshot, initialHooksContent, marker);
4055
+ if (rawManagedHookCount(initialHooksContent) !== MANAGED_CODEX_HOOK_EVENTS.length) {
4056
+ throw new Error('packed setup did not install exactly seven managed native hooks');
4057
+ }
4058
+ assertManagedHooksAppendAfterForeign(initialHooksContent, marker);
4059
+ const generatedTrust = generatedHookTrustState(readFileSync(configPath, 'utf-8'));
4060
+ let postForeignCodexHooks = null;
4061
+ if (codexVersion !== null) {
4062
+ const server = await CodexAppServer.start({ cwd: projectDir, env });
4063
+ try {
4064
+ await initializeCodexAppServer(server, 'omx-packed-install-smoke');
4065
+ const initialCodexHooks = await listCodexHooks(server, projectDir, hooksPath);
4066
+ assertGeneratedTrustMatchesCodex(generatedTrust, initialCodexHooks.hooks);
4067
+ assertManagedHooksNotAlreadyTrusted(initialCodexHooks.hooks);
4068
+ await approveManagedHooksInCodex(server, initialCodexHooks.hooks, codexUserConfigPath);
4069
+ }
4070
+ finally {
4071
+ await server.close();
4072
+ }
4073
+ const afterSetup = await observeInstalledCodexHooks(projectDir, hooksPath, env);
4074
+ assertTrustedManagedHooks(afterSetup.hooks);
4075
+ assertPreseededForeignHookEvidence(approvedPreSetupForeignMetadata, afterSetup.hooks, readFileSync(codexUserConfigPath, 'utf-8'), marker, 'setup');
4076
+ }
4077
+ writeFileSync(hooksPath, appendDisplayOrderStableForeignHookGroups(readFileSync(hooksPath, 'utf-8'), marker, { appendGroups: false }), 'utf-8');
4078
+ const postForeignHooksContent = readFileSync(hooksPath, 'utf-8');
4079
+ const postForeignConfigContent = readFileSync(configPath, 'utf-8');
4080
+ const foreignSnapshot = foreignHookGroupSnapshot(postForeignHooksContent, marker);
4081
+ if (foreignSnapshot.length !== 4) {
4082
+ throw new Error(`expected four foreign hook coordinates after insertion, received ${foreignSnapshot.length}`);
4083
+ }
4084
+ if (codexVersion !== null) {
4085
+ const inspected = await observeInstalledCodexHooks(projectDir, hooksPath, env);
4086
+ assertTrustedManagedHooks(inspected.hooks);
4087
+ postForeignCodexHooks = inspected.hooks;
4088
+ assertPreseededForeignHookEvidence(approvedPreSetupForeignMetadata, inspected.hooks, readFileSync(codexUserConfigPath, 'utf-8'), marker, 'foreign insertion before setup rerun');
4089
+ }
4090
+ run(omxPath, ['setup', '--scope', 'project', '--merge-agents', '--legacy'], {
4091
+ cwd: projectDir,
4092
+ env,
4093
+ });
4094
+ const afterRerunHooksContent = readFileSync(hooksPath, 'utf-8');
4095
+ if (afterRerunHooksContent !== postForeignHooksContent) {
4096
+ throw new Error('packed setup rerun changed hooks.json after foreign insertion');
4097
+ }
4098
+ if (readFileSync(configPath, 'utf-8') !== postForeignConfigContent) {
4099
+ throw new Error('packed setup rerun changed config.toml after foreign insertion');
4100
+ }
4101
+ assertForeignHookGroupsPreserved(foreignSnapshot, afterRerunHooksContent, marker);
4102
+ if (postForeignCodexHooks !== null) {
4103
+ const afterRerun = await observeInstalledCodexHooks(projectDir, hooksPath, env);
4104
+ assertTrustedManagedHooks(afterRerun.hooks);
4105
+ assertPreseededForeignHookEvidence(approvedPreSetupForeignMetadata, afterRerun.hooks, readFileSync(codexUserConfigPath, 'utf-8'), marker, 'setup rerun');
4106
+ if (!sameJson(hookMetadataSnapshot(afterRerun.hooks), hookMetadataSnapshot(postForeignCodexHooks))) {
4107
+ throw new Error('packed setup rerun changed Codex hook metadata or display order');
4108
+ }
4109
+ }
4110
+ run(omxPath, ['setup', '--scope', 'project', '--merge-agents', '--legacy'], {
4111
+ cwd: projectDir,
4112
+ env,
4113
+ });
4114
+ const afterNoopHooksContent = readFileSync(hooksPath, 'utf-8');
4115
+ if (afterNoopHooksContent !== postForeignHooksContent) {
4116
+ throw new Error('third packed setup was not a hooks.json byte no-op');
4117
+ }
4118
+ if (readFileSync(configPath, 'utf-8') !== postForeignConfigContent) {
4119
+ throw new Error('third packed setup was not a config.toml byte no-op');
4120
+ }
4121
+ assertForeignHookGroupsPreserved(foreignSnapshot, afterNoopHooksContent, marker);
4122
+ if (postForeignCodexHooks !== null) {
4123
+ const afterNoop = await observeInstalledCodexHooks(projectDir, hooksPath, env);
4124
+ assertTrustedManagedHooks(afterNoop.hooks);
4125
+ assertPreseededForeignHookEvidence(approvedPreSetupForeignMetadata, afterNoop.hooks, readFileSync(codexUserConfigPath, 'utf-8'), marker, 'third setup');
4126
+ if (!sameJson(hookMetadataSnapshot(afterNoop.hooks), hookMetadataSnapshot(postForeignCodexHooks))) {
4127
+ throw new Error('third packed setup changed Codex hook metadata or display order');
4128
+ }
4129
+ }
4130
+ run(omxPath, ['doctor', '--verbose'], { cwd: projectDir, env });
4131
+ if (!existsSync(agentsPath) || !readFileSync(agentsPath, 'utf-8').includes(foreignAgentsContent.trim())) {
4132
+ throw new Error('packed doctor lifecycle did not preserve pre-existing user AGENTS.md guidance');
4133
+ }
4134
+ run(omxPath, ['uninstall'], { cwd: projectDir, env });
4135
+ const afterUninstallHooksContent = readFileSync(hooksPath, 'utf-8');
4136
+ if (rawManagedHookCount(afterUninstallHooksContent) !== 0) {
4137
+ throw new Error('packed uninstall retained an OMX-managed native hook');
4138
+ }
4139
+ assertNoEmptyManagedEventGroups(afterUninstallHooksContent);
4140
+ assertForeignHookGroupsPreserved(foreignSnapshot, afterUninstallHooksContent, marker);
4141
+ if (!existsSync(agentsPath) || !readFileSync(agentsPath, 'utf-8').includes(foreignAgentsContent.trim())) {
4142
+ throw new Error('packed uninstall removed pre-existing user AGENTS.md guidance');
4143
+ }
4144
+ const afterUninstallConfig = readFileSync(configPath, 'utf-8');
4145
+ assertNoSetupOwnedTrustKeys(afterUninstallConfig, generatedTrust);
4146
+ assertNativeHooksEnabledForForeignGroups(afterUninstallConfig);
4147
+ if (postForeignCodexHooks !== null) {
4148
+ const afterUninstall = await observeInstalledCodexHooks(projectDir, hooksPath, env);
4149
+ const foreignIdentity = (hooks) => hookMetadataSnapshot(hooks.filter((hook) => hook.command.includes(marker)));
4150
+ const foreignBefore = foreignIdentity(postForeignCodexHooks);
4151
+ const foreignAfter = foreignIdentity(afterUninstall.hooks);
4152
+ if (!sameJson(foreignAfter, foreignBefore)) {
4153
+ throw new Error('packed uninstall changed foreign Codex hook metadata or display order');
4154
+ }
4155
+ }
4156
+ const managedFirstProjectDir = resolve(lifecycleRoot, 'managed-first-project');
4157
+ const managedFirstHooksPath = join(managedFirstProjectDir, '.codex', 'hooks.json');
4158
+ const managedFirstConfigPath = join(managedFirstProjectDir, '.codex', 'config.toml');
4159
+ const managedFirstMarker = 'omx-packed-managed-first-foreign';
4160
+ mkdirSync(join(managedFirstProjectDir, '.codex'), { recursive: true });
4161
+ run(omxPath, ['setup', '--scope', 'project', '--merge-agents', '--legacy'], {
4162
+ cwd: managedFirstProjectDir,
4163
+ env,
4164
+ });
4165
+ writeFileSync(managedFirstHooksPath, appendDisplayOrderStableForeignHookGroups(readFileSync(managedFirstHooksPath, 'utf-8'), managedFirstMarker, { appendGroups: true }), 'utf-8');
4166
+ const managedFirstBeforeRerunHooks = readFileSync(managedFirstHooksPath, 'utf-8');
4167
+ const managedFirstBeforeRerunConfig = readFileSync(managedFirstConfigPath, 'utf-8');
4168
+ const managedFirstForeignSnapshot = foreignHookGroupSnapshot(managedFirstBeforeRerunHooks, managedFirstMarker);
4169
+ if (managedFirstForeignSnapshot.length !== 2) {
4170
+ throw new Error('packed managed-first fixture did not append both foreign hook groups');
4171
+ }
4172
+ assertManagedHooksRemainBeforeForeign(managedFirstBeforeRerunHooks, managedFirstMarker);
4173
+ run(omxPath, ['setup', '--scope', 'project', '--merge-agents', '--legacy'], {
4174
+ cwd: managedFirstProjectDir,
4175
+ env,
4176
+ });
4177
+ if (readFileSync(managedFirstHooksPath, 'utf-8') !== managedFirstBeforeRerunHooks) {
4178
+ throw new Error('packed setup rerun reordered managed-first foreign hook groups');
4179
+ }
4180
+ if (readFileSync(managedFirstConfigPath, 'utf-8') !== managedFirstBeforeRerunConfig) {
4181
+ throw new Error('packed setup rerun changed managed-first config.toml');
4182
+ }
4183
+ assertForeignHookGroupsPreserved(managedFirstForeignSnapshot, readFileSync(managedFirstHooksPath, 'utf-8'), managedFirstMarker);
4184
+ const managedFirstBeforeUninstallHooksBytes = readFileSync(managedFirstHooksPath);
4185
+ const managedFirstBeforeUninstallConfigBytes = readFileSync(managedFirstConfigPath);
4186
+ const expectedUnsafeManagedRemovalDiagnostic = 'Removing OMX hooks would shift a foreign coordinate or discard opaque metadata.';
4187
+ const unsafeRemoval = planManagedCodexHooksRemoval(managedFirstBeforeRerunHooks, managedFirstHooksPath);
4188
+ if (unsafeRemoval.ok ||
4189
+ unsafeRemoval.error.code !== 'unsafe_managed_removal' ||
4190
+ unsafeRemoval.error.message !== expectedUnsafeManagedRemovalDiagnostic) {
4191
+ throw new Error('packed managed-first fixture did not return the exact unsafe_managed_removal diagnostic');
4192
+ }
4193
+ const failedUninstall = spawnSync(omxPath, ['uninstall'], {
4194
+ cwd: managedFirstProjectDir,
4195
+ env,
4196
+ encoding: 'utf-8',
4197
+ stdio: 'pipe',
4198
+ });
4199
+ if (failedUninstall.error)
4200
+ throw failedUninstall.error;
4201
+ if (failedUninstall.status !== 1) {
4202
+ throw new Error(`packed unsafe uninstall exited ${String(failedUninstall.status)}, expected 1\nstdout:\n${failedUninstall.stdout || ''}\nstderr:\n${failedUninstall.stderr || ''}`);
4203
+ }
4204
+ if (failedUninstall.stderr !== `Error: ${expectedUnsafeManagedRemovalDiagnostic}\n`) {
4205
+ throw new Error(`packed unsafe uninstall returned an unexpected diagnostic: ${failedUninstall.stderr || ''}`);
4206
+ }
4207
+ if (!readFileSync(managedFirstHooksPath).equals(managedFirstBeforeUninstallHooksBytes)) {
4208
+ throw new Error('unsafe packed uninstall changed raw hooks.json bytes');
4209
+ }
4210
+ if (!readFileSync(managedFirstConfigPath).equals(managedFirstBeforeUninstallConfigBytes)) {
4211
+ throw new Error('unsafe packed uninstall changed raw config.toml bytes');
4212
+ }
4213
+ assertNoUninstallTransactionArtifacts(join(managedFirstProjectDir, '.codex'));
4214
+ return { codexVersion };
4215
+ }
4216
+ finally {
4217
+ rmSync(lifecycleRoot, { recursive: true, force: true });
4218
+ }
4219
+ }
4220
+ function assertBoundedProbeExit(label, result, expectedStatus) {
4221
+ if (result.error?.code === 'ETIMEDOUT') {
4222
+ throw new Error(`${label} exceeded ${PACKED_INSTALL_OPERATIONAL_PROBE_TIMEOUT_MS}ms`);
4223
+ }
4224
+ if (result.status !== expectedStatus) {
4225
+ const detail = String(result.stderr ?? result.error?.message ?? '').trim();
4226
+ throw new Error(`${label} must exit ${expectedStatus}, received ${String(result.status)}${detail ? `: ${detail}` : ''}`);
4227
+ }
4228
+ }
4229
+ function readPackedPluginHookDelegateCalls(capturePath) {
4230
+ const capture = existsSync(capturePath) ? readFileSync(capturePath, 'utf-8').trim() : '';
4231
+ if (!capture)
4232
+ return [];
4233
+ return capture.split('\n').map((line) => {
4234
+ const record = JSON.parse(line);
4235
+ if (!isJsonRecord(record)
4236
+ || !Array.isArray(record.argv)
4237
+ || record.argv.some((arg) => typeof arg !== 'string')
4238
+ || typeof record.stdin !== 'string'
4239
+ || typeof record.cwd !== 'string') {
4240
+ throw new Error('pinned fake plugin hook delegate did not record a valid invocation');
4241
+ }
4242
+ return { argv: record.argv, stdin: record.stdin, cwd: record.cwd };
4243
+ });
4244
+ }
4245
+ function smokeInstalledPluginHookLauncher(packageRoot, omxPath) {
4246
+ const smokeRoot = mkdtempSync(join(tmpdir(), 'omx-packed-plugin-hook-smoke-'));
4247
+ const hookDir = join(packageRoot, 'plugins', 'oh-my-codex', 'hooks');
4248
+ const hookLauncherPath = join(hookDir, 'codex-native-hook.mjs');
4249
+ const pinnedLauncherPath = join(hookDir, 'omx-command.json');
4250
+ const originalPinnedLauncher = existsSync(pinnedLauncherPath) ? readFileSync(pinnedLauncherPath) : undefined;
4251
+ const delegatePath = join(smokeRoot, 'pinned-fake-delegate.cjs');
4252
+ const capturePath = join(smokeRoot, 'pinned-fake-delegate.jsonl');
4253
+ const hookCwd = join(smokeRoot, 'cwd');
4254
+ const home = join(smokeRoot, 'home');
4255
+ const codexHome = join(smokeRoot, 'codex-home');
4256
+ const sessionId = 'packed-plugin-hook-session';
4257
+ try {
4258
+ mkdirSync(hookCwd, { recursive: true });
4259
+ mkdirSync(home, { recursive: true });
4260
+ mkdirSync(codexHome, { recursive: true });
4261
+ writeFileSync(delegatePath, [
4262
+ 'const { appendFileSync } = require("node:fs");',
4263
+ '(async () => {',
4264
+ 'const chunks = [];',
4265
+ 'for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));',
4266
+ 'const stdin = Buffer.concat(chunks).toString("utf8");',
4267
+ 'const payload = JSON.parse(stdin);',
4268
+ 'appendFileSync(process.env.OMX_PACKED_PLUGIN_HOOK_CAPTURE_PATH, `${JSON.stringify({ argv: process.argv.slice(2), stdin, cwd: process.cwd() })}\\n`);',
4269
+ 'if (payload.delegate_mode === "non-stop-failure") {',
4270
+ ' process.stdout.write("delegate-failure\\n");',
4271
+ ' process.exitCode = 23;',
4272
+ '} else if (payload.delegate_mode === "stop-failure") {',
4273
+ ' process.exitCode = 23;',
4274
+ '} else if (payload.hook_event_name === "Stop") {',
4275
+ ' process.stdout.write("{\\\"decision\\\":\\\"block\\\",\\\"reason\\\":\\\"pinned stop delegate\\\"}\\n");',
4276
+ '} else {',
4277
+ ' process.stdout.write("{\\\"hookSpecificOutput\\\":{\\\"additionalContext\\\":\\\"pinned non-stop delegate\\\"}}\\n");',
4278
+ '}',
4279
+ '})().catch((error) => { console.error(error); process.exitCode = 1; });',
4280
+ '',
4281
+ ].join('\n'));
4282
+ writeFileSync(pinnedLauncherPath, JSON.stringify({
4283
+ command: process.execPath,
4284
+ argsPrefix: [delegatePath],
4285
+ }));
4286
+ const env = buildPackedProbeEnv({
4287
+ HOME: home,
4288
+ USERPROFILE: home,
4289
+ CODEX_HOME: codexHome,
4290
+ OMX_AUTO_UPDATE: '0',
4291
+ OMX_NOTIFY_FALLBACK: '0',
4292
+ OMX_HOOK_DERIVED_SIGNALS: '0',
4293
+ OMX_ROOT: join(smokeRoot, '.omx-root'),
4294
+ OMX_STATE_ROOT: '',
4295
+ OMX_TEAM_STATE_ROOT: '',
4296
+ OMX_TEAM_WORKER: '',
4297
+ OMX_TEAM_WORKER_LAUNCH_ARGS: '',
4298
+ OMX_NOTIFY_TEMP_CONTRACT: '',
4299
+ OMX_ENTRY_PATH: omxPath,
4300
+ OMX_CODEX_LAUNCH_ID: 'packed-plugin-hook-launch',
4301
+ OMX_PACKED_PLUGIN_HOOK_CAPTURE_PATH: capturePath,
4302
+ });
4303
+ const runProbe = (name, payload, expectedStatus, expectedStdout) => {
4304
+ writeFileSync(capturePath, '');
4305
+ const stdin = JSON.stringify(payload);
4306
+ const result = spawnSync(process.execPath, [hookLauncherPath], {
4307
+ cwd: hookCwd,
4308
+ encoding: 'utf-8',
4309
+ env,
4310
+ input: stdin,
4311
+ timeout: PACKED_INSTALL_OPERATIONAL_PROBE_TIMEOUT_MS,
4312
+ killSignal: 'SIGKILL',
4313
+ });
4314
+ assertBoundedProbeExit(`installed plugin hook ${name}`, result, expectedStatus);
4315
+ if (String(result.stdout ?? '') !== expectedStdout) {
4316
+ throw new Error(`installed plugin hook ${name} stdout changed: expected ${JSON.stringify(expectedStdout)}, received ${JSON.stringify(String(result.stdout ?? ''))}`);
4317
+ }
4318
+ assert.deepStrictEqual(readPackedPluginHookDelegateCalls(capturePath), [{
4319
+ argv: ['codex-native-hook'],
4320
+ stdin,
4321
+ cwd: hookCwd,
4322
+ }], `installed plugin hook ${name} must forward exact delegate argv and stdin`);
4323
+ };
4324
+ runProbe('non-Stop', {
4325
+ hook_event_name: 'UserPromptSubmit',
4326
+ session_id: sessionId,
4327
+ cwd: hookCwd,
4328
+ prompt: 'packed plugin non-Stop probe',
4329
+ }, 0, '{"hookSpecificOutput":{"additionalContext":"pinned non-stop delegate"}}\n');
4330
+ runProbe('Stop', {
4331
+ hook_event_name: 'Stop',
4332
+ session_id: sessionId,
4333
+ cwd: hookCwd,
4334
+ }, 0, '{"decision":"block","reason":"pinned stop delegate"}\n');
4335
+ runProbe('non-Stop delegate failure', {
4336
+ hook_event_name: 'UserPromptSubmit',
4337
+ session_id: sessionId,
4338
+ cwd: hookCwd,
4339
+ delegate_mode: 'non-stop-failure',
4340
+ }, 23, 'delegate-failure\n');
4341
+ writeFileSync(capturePath, '');
4342
+ const failedStopInput = JSON.stringify({
4343
+ hook_event_name: 'Stop',
4344
+ session_id: sessionId,
4345
+ cwd: hookCwd,
4346
+ delegate_mode: 'stop-failure',
4347
+ });
4348
+ const failedStop = spawnSync(process.execPath, [hookLauncherPath], {
4349
+ cwd: hookCwd,
4350
+ encoding: 'utf-8',
4351
+ env,
4352
+ input: failedStopInput,
4353
+ timeout: PACKED_INSTALL_OPERATIONAL_PROBE_TIMEOUT_MS,
4354
+ killSignal: 'SIGKILL',
4355
+ });
4356
+ assertBoundedProbeExit('installed plugin hook Stop delegate failure', failedStop, 0);
4357
+ const failedStopOutput = JSON.parse(String(failedStop.stdout ?? ''));
4358
+ if (!isJsonRecord(failedStopOutput)) {
4359
+ throw new Error('installed plugin hook Stop delegate failure must emit JSON fallback output');
4360
+ }
4361
+ assert.deepStrictEqual({
4362
+ decision: failedStopOutput.decision,
4363
+ stopReason: failedStopOutput.stopReason,
4364
+ }, {
4365
+ decision: 'block',
4366
+ stopReason: 'plugin_stop_hook_launcher_exit',
4367
+ });
4368
+ assertTextMatches(String(failedStopOutput.systemMessage ?? ''), /codex-native-hook exited with code 23/, 'installed plugin hook Stop delegate failure');
4369
+ assert.deepStrictEqual(readPackedPluginHookDelegateCalls(capturePath), [{
4370
+ argv: ['codex-native-hook'],
4371
+ stdin: failedStopInput,
4372
+ cwd: hookCwd,
4373
+ }], 'installed plugin hook Stop failure must still delegate exact argv and stdin');
4374
+ }
4375
+ finally {
4376
+ if (originalPinnedLauncher === undefined) {
4377
+ rmSync(pinnedLauncherPath, { force: true });
4378
+ }
4379
+ else {
4380
+ writeFileSync(pinnedLauncherPath, originalPinnedLauncher);
4381
+ }
4382
+ rmSync(smokeRoot, { recursive: true, force: true });
4383
+ }
4384
+ }
4385
+ function smokeInstalledMcpTargets(omxPath) {
4386
+ const smokeRoot = mkdtempSync(join(tmpdir(), 'omx-packed-mcp-smoke-'));
4387
+ const home = join(smokeRoot, 'home');
4388
+ const codexHome = join(smokeRoot, 'codex-home');
4389
+ const mcpInitialize = `${JSON.stringify({
4390
+ jsonrpc: '2.0',
4391
+ id: 1,
4392
+ method: 'initialize',
4393
+ params: {
4394
+ protocolVersion: '2024-11-05',
4395
+ capabilities: {},
4396
+ clientInfo: { name: 'packed-install-smoke', version: '1' },
4397
+ },
4398
+ })}\n`;
4399
+ try {
4400
+ mkdirSync(home, { recursive: true });
4401
+ mkdirSync(codexHome, { recursive: true });
4402
+ const env = buildPackedProbeEnv({
4403
+ HOME: home,
4404
+ USERPROFILE: home,
4405
+ CODEX_HOME: codexHome,
4406
+ OMX_AUTO_UPDATE: '0',
4407
+ OMX_NOTIFY_FALLBACK: '0',
4408
+ OMX_HOOK_DERIVED_SIGNALS: '0',
4409
+ OMX_ROOT: join(smokeRoot, '.omx-root'),
4410
+ OMX_STATE_ROOT: '',
4411
+ OMX_TEAM_STATE_ROOT: '',
4412
+ OMX_TEAM_WORKER: '',
4413
+ OMX_TEAM_WORKER_LAUNCH_ARGS: '',
4414
+ OMX_NOTIFY_TEMP_CONTRACT: '',
4415
+ });
4416
+ for (const [serverName, target, expectedServerName] of PACKED_INSTALL_PLUGIN_MCP_TARGETS) {
4417
+ const result = spawnSync(omxPath, ['mcp-serve', target], {
4418
+ cwd: smokeRoot,
4419
+ encoding: 'utf-8',
4420
+ env,
4421
+ input: mcpInitialize,
4422
+ timeout: PACKED_INSTALL_OPERATIONAL_PROBE_TIMEOUT_MS,
4423
+ killSignal: 'SIGKILL',
4424
+ });
4425
+ assertBoundedProbeExit(`installed MCP target ${serverName}`, result, 0);
4426
+ const responseText = String(result.stdout ?? '').trim();
4427
+ if (!responseText) {
4428
+ throw new Error(`installed MCP target ${serverName} did not respond before EOF`);
4429
+ }
4430
+ const responses = responseText.split('\n').map((line) => {
4431
+ try {
4432
+ return JSON.parse(line);
4433
+ }
4434
+ catch (error) {
4435
+ throw new Error(`installed MCP target ${serverName} emitted non-JSON stdout: ${error instanceof Error ? error.message : String(error)}`);
4436
+ }
4437
+ });
4438
+ const initializeResponse = responses.find((response) => isJsonRecord(response) && response.id === 1);
4439
+ if (!isJsonRecord(initializeResponse)
4440
+ || !isJsonRecord(initializeResponse.result)
4441
+ || !isJsonRecord(initializeResponse.result.serverInfo)
4442
+ || initializeResponse.result.serverInfo.name !== expectedServerName) {
4443
+ throw new Error(`installed MCP target ${serverName} must initialize ${expectedServerName}, received ${JSON.stringify(initializeResponse)}`);
4444
+ }
4445
+ }
4446
+ }
4447
+ finally {
4448
+ rmSync(smokeRoot, { recursive: true, force: true });
4449
+ }
4450
+ }
176
4451
  export function parseNpmPackJsonOutput(stdout) {
177
4452
  const start = stdout.lastIndexOf('\n[');
178
4453
  const jsonText = (start >= 0 ? stdout.slice(start + 1) : stdout).trim();
@@ -187,6 +4462,19 @@ async function main() {
187
4462
  const tempRoot = mkdtempSync(join(tmpdir(), 'omx-packed-install-'));
188
4463
  const prefixDir = join(tempRoot, 'prefix');
189
4464
  mkdirSync(prefixDir, { recursive: true });
4465
+ const installHome = join(tempRoot, 'home');
4466
+ const installCodexHome = join(tempRoot, 'codex-home');
4467
+ const installNpmCache = join(tempRoot, 'npm-cache');
4468
+ mkdirSync(installHome, { recursive: true });
4469
+ mkdirSync(installCodexHome, { recursive: true });
4470
+ mkdirSync(installNpmCache, { recursive: true });
4471
+ const installEnv = buildPackedProbeEnv({
4472
+ HOME: installHome,
4473
+ USERPROFILE: installHome,
4474
+ CODEX_HOME: installCodexHome,
4475
+ npm_config_cache: installNpmCache,
4476
+ NPM_CONFIG_CACHE: installNpmCache,
4477
+ });
190
4478
  let tarballPath;
191
4479
  try {
192
4480
  ensureRepoDependencies(repoRoot, {
@@ -194,16 +4482,32 @@ async function main() {
194
4482
  });
195
4483
  const pack = run('npm', ['pack', '--json'], { cwd: repoRoot });
196
4484
  const packOutput = parseNpmPackJsonOutput(pack.stdout);
197
- const tarballName = packOutput[0]?.filename;
4485
+ const packedPackage = packOutput[0];
4486
+ const tarballName = packedPackage?.filename;
198
4487
  if (!tarballName)
199
4488
  throw new Error('npm pack did not return a tarball filename');
4489
+ if (!packedPackage.files)
4490
+ throw new Error('npm pack did not return file metadata');
4491
+ assertPackedInstallFileMetadata(packedPackage.files);
200
4492
  tarballPath = join(repoRoot, tarballName);
201
- run('npm', ['install', '-g', tarballPath, '--prefix', prefixDir], { cwd: repoRoot });
4493
+ run('npm', ['install', '-g', tarballPath, '--prefix', prefixDir], { cwd: repoRoot, env: installEnv });
4494
+ const globalNodeModules = resolveGlobalNodeModules(prefixDir);
4495
+ const packageRoot = join(globalNodeModules, 'oh-my-codex');
4496
+ assertInstalledRequiredArtifacts(packageRoot);
4497
+ await assertInstalledReasoningArtifacts(packageRoot);
202
4498
  const omxPath = join(prefixDir, process.platform === 'win32' ? '' : 'bin', npmBinName('omx'));
4499
+ smokeInstalledRootReasoningRejections(omxPath, repoRoot);
4500
+ smokeInstalledLaunchArgumentBoundary(omxPath);
4501
+ smokeInstalledPluginHookLauncher(packageRoot, omxPath);
4502
+ smokeInstalledMcpTargets(omxPath);
203
4503
  for (const argv of PACKED_INSTALL_SMOKE_CORE_COMMANDS) {
204
- run(omxPath, argv, { cwd: repoRoot });
4504
+ run(omxPath, argv, { cwd: repoRoot, env: installEnv });
205
4505
  }
206
4506
  smokeInstalledNativeHookDist(prefixDir);
4507
+ const lifecycle = await smokePackedHookTrustLifecycle(omxPath);
4508
+ console.log(lifecycle.codexVersion !== null
4509
+ ? `packed install smoke: installed Codex 0.142.5 lifecycle passed (${lifecycle.codexVersion})`
4510
+ : 'packed install smoke: Codex executable absent; installed-Codex trust leg skipped after deterministic lifecycle');
207
4511
  console.log('packed install smoke: PASS');
208
4512
  }
209
4513
  finally {