oh-my-codex 0.20.1 → 0.20.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (654) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +10 -1
  4. package/dist/adapt/__tests__/hermes.test.js.map +1 -1
  5. package/dist/adapt/contracts.d.ts.map +1 -1
  6. package/dist/agents/native-config.js.map +1 -1
  7. package/dist/auth/__tests__/hotswap.test.d.ts +2 -0
  8. package/dist/auth/__tests__/hotswap.test.d.ts.map +1 -0
  9. package/dist/auth/__tests__/hotswap.test.js +183 -0
  10. package/dist/auth/__tests__/hotswap.test.js.map +1 -0
  11. package/dist/auth/hotswap.d.ts.map +1 -1
  12. package/dist/auth/hotswap.js +17 -4
  13. package/dist/auth/hotswap.js.map +1 -1
  14. package/dist/auth/paths.js.map +1 -1
  15. package/dist/auth/storage.js.map +1 -1
  16. package/dist/autopilot/__tests__/ralplan-gate.test.js +4 -4
  17. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
  18. package/dist/autopilot/completion-gate.js.map +1 -1
  19. package/dist/autopilot/fsm.d.ts +1 -1
  20. package/dist/autopilot/fsm.d.ts.map +1 -1
  21. package/dist/autoresearch/goal.js.map +1 -1
  22. package/dist/autoresearch/runtime.js.map +1 -1
  23. package/dist/capabilities/lockfile.js.map +1 -1
  24. package/dist/catalog/reader.js.map +1 -1
  25. package/dist/catalog/skill-mirror.js.map +1 -1
  26. package/dist/cli/__tests__/adapt-help.test.js.map +1 -1
  27. package/dist/cli/__tests__/agents-init.test.js.map +1 -1
  28. package/dist/cli/__tests__/agents.test.js.map +1 -1
  29. package/dist/cli/__tests__/api.test.js.map +1 -1
  30. package/dist/cli/__tests__/ask.test.js.map +1 -1
  31. package/dist/cli/__tests__/auth.test.js.map +1 -1
  32. package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
  33. package/dist/cli/__tests__/autoresearch.test.js.map +1 -1
  34. package/dist/cli/__tests__/capabilities.test.js.map +1 -1
  35. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  36. package/dist/cli/__tests__/doctor-context-window-warning.test.js.map +1 -1
  37. package/dist/cli/__tests__/doctor-invalid-config.test.js +47 -4
  38. package/dist/cli/__tests__/doctor-invalid-config.test.js.map +1 -1
  39. package/dist/cli/__tests__/doctor-team.test.js.map +1 -1
  40. package/dist/cli/__tests__/doctor-warning-copy.test.js +650 -15
  41. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  42. package/dist/cli/__tests__/exec.test.js.map +1 -1
  43. package/dist/cli/__tests__/imagegen-continuation.test.js.map +1 -1
  44. package/dist/cli/__tests__/index.test.js +216 -11
  45. package/dist/cli/__tests__/index.test.js.map +1 -1
  46. package/dist/cli/__tests__/launch-fallback.test.js +49 -3
  47. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  48. package/dist/cli/__tests__/mcp-parity.test.js +84 -12
  49. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  50. package/dist/cli/__tests__/native-hook-claim-journal.test.d.ts +2 -0
  51. package/dist/cli/__tests__/native-hook-claim-journal.test.d.ts.map +1 -0
  52. package/dist/cli/__tests__/native-hook-claim-journal.test.js +128 -0
  53. package/dist/cli/__tests__/native-hook-claim-journal.test.js.map +1 -0
  54. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  55. package/dist/cli/__tests__/question.test.js.map +1 -1
  56. package/dist/cli/__tests__/ralph-deslop-contract.test.js.map +1 -1
  57. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +6 -0
  58. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  59. package/dist/cli/__tests__/ralph-prd-deep-interview.test.js.map +1 -1
  60. package/dist/cli/__tests__/ralph-prd-smoke.test.js.map +1 -1
  61. package/dist/cli/__tests__/ralph.test.js +8 -3
  62. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  63. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.d.ts +2 -0
  64. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.d.ts.map +1 -0
  65. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js +187 -0
  66. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js.map +1 -0
  67. package/dist/cli/__tests__/ralplan.test.d.ts +2 -0
  68. package/dist/cli/__tests__/ralplan.test.d.ts.map +1 -0
  69. package/dist/cli/__tests__/ralplan.test.js +129 -0
  70. package/dist/cli/__tests__/ralplan.test.js.map +1 -0
  71. package/dist/cli/__tests__/resume.test.js +6 -1
  72. package/dist/cli/__tests__/resume.test.js.map +1 -1
  73. package/dist/cli/__tests__/session-scoped-runtime.test.js +25 -0
  74. package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
  75. package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
  76. package/dist/cli/__tests__/session-search.test.js.map +1 -1
  77. package/dist/cli/__tests__/setup-agents-overwrite.test.js +79 -0
  78. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  79. package/dist/cli/__tests__/setup-gh-star.test.js.map +1 -1
  80. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +77 -3
  81. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +1 -1
  82. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.d.ts +2 -0
  83. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.d.ts.map +1 -0
  84. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.js +285 -0
  85. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.js.map +1 -0
  86. package/dist/cli/__tests__/setup-install-mode.test.js +2261 -14
  87. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  88. package/dist/cli/__tests__/setup-preferences.test.d.ts +2 -0
  89. package/dist/cli/__tests__/setup-preferences.test.d.ts.map +1 -0
  90. package/dist/cli/__tests__/setup-preferences.test.js +79 -0
  91. package/dist/cli/__tests__/setup-preferences.test.js.map +1 -0
  92. package/dist/cli/__tests__/setup-refresh.test.js +33 -2
  93. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  94. package/dist/cli/__tests__/setup-scope.test.js +46 -3
  95. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  96. package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
  97. package/dist/cli/__tests__/state.test.js +37 -0
  98. package/dist/cli/__tests__/state.test.js.map +1 -1
  99. package/dist/cli/__tests__/team.test.js.map +1 -1
  100. package/dist/cli/__tests__/ultragoal.test.js +61 -1
  101. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  102. package/dist/cli/__tests__/uninstall.test.js +1771 -24
  103. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  104. package/dist/cli/__tests__/update.test.js +58 -0
  105. package/dist/cli/__tests__/update.test.js.map +1 -1
  106. package/dist/cli/__tests__/url.test.js.map +1 -1
  107. package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
  108. package/dist/cli/adapt.js.map +1 -1
  109. package/dist/cli/agents-init.js.map +1 -1
  110. package/dist/cli/agents.js.map +1 -1
  111. package/dist/cli/api.js.map +1 -1
  112. package/dist/cli/ask.d.ts +1 -1
  113. package/dist/cli/ask.d.ts.map +1 -1
  114. package/dist/cli/ask.js.map +1 -1
  115. package/dist/cli/auth.js.map +1 -1
  116. package/dist/cli/autoresearch-guided.js.map +1 -1
  117. package/dist/cli/autoresearch-intake.js.map +1 -1
  118. package/dist/cli/capabilities.js.map +1 -1
  119. package/dist/cli/cleanup.js.map +1 -1
  120. package/dist/cli/codex-feature-probe.js.map +1 -1
  121. package/dist/cli/codex-home.js.map +1 -1
  122. package/dist/cli/doctor.d.ts +18 -0
  123. package/dist/cli/doctor.d.ts.map +1 -1
  124. package/dist/cli/doctor.js +594 -130
  125. package/dist/cli/doctor.js.map +1 -1
  126. package/dist/cli/explore.js.map +1 -1
  127. package/dist/cli/index.d.ts +11 -3
  128. package/dist/cli/index.d.ts.map +1 -1
  129. package/dist/cli/index.js +108 -20
  130. package/dist/cli/index.js.map +1 -1
  131. package/dist/cli/mcp-serve.js.map +1 -1
  132. package/dist/cli/mission.js.map +1 -1
  133. package/dist/cli/native-assets.js.map +1 -1
  134. package/dist/cli/native-hook-claim-journal.d.ts +18 -0
  135. package/dist/cli/native-hook-claim-journal.d.ts.map +1 -0
  136. package/dist/cli/native-hook-claim-journal.js +239 -0
  137. package/dist/cli/native-hook-claim-journal.js.map +1 -0
  138. package/dist/cli/omx.js.map +1 -1
  139. package/dist/cli/plugin-marketplace.js.map +1 -1
  140. package/dist/cli/project-runtime-codex-homes.js.map +1 -1
  141. package/dist/cli/question.js.map +1 -1
  142. package/dist/cli/ralph.d.ts.map +1 -1
  143. package/dist/cli/ralph.js +3 -2
  144. package/dist/cli/ralph.js.map +1 -1
  145. package/dist/cli/ralplan.d.ts +14 -0
  146. package/dist/cli/ralplan.d.ts.map +1 -0
  147. package/dist/cli/ralplan.js +203 -0
  148. package/dist/cli/ralplan.js.map +1 -0
  149. package/dist/cli/setup-preferences.d.ts +11 -0
  150. package/dist/cli/setup-preferences.d.ts.map +1 -1
  151. package/dist/cli/setup-preferences.js +32 -0
  152. package/dist/cli/setup-preferences.js.map +1 -1
  153. package/dist/cli/setup.d.ts +53 -0
  154. package/dist/cli/setup.d.ts.map +1 -1
  155. package/dist/cli/setup.js +1401 -398
  156. package/dist/cli/setup.js.map +1 -1
  157. package/dist/cli/sparkshell.js.map +1 -1
  158. package/dist/cli/star-prompt.js.map +1 -1
  159. package/dist/cli/state.d.ts.map +1 -1
  160. package/dist/cli/state.js +2 -1
  161. package/dist/cli/state.js.map +1 -1
  162. package/dist/cli/team.js.map +1 -1
  163. package/dist/cli/ultragoal.d.ts +5 -1
  164. package/dist/cli/ultragoal.d.ts.map +1 -1
  165. package/dist/cli/ultragoal.js +34 -2
  166. package/dist/cli/ultragoal.js.map +1 -1
  167. package/dist/cli/uninstall.d.ts +8 -0
  168. package/dist/cli/uninstall.d.ts.map +1 -1
  169. package/dist/cli/uninstall.js +944 -95
  170. package/dist/cli/uninstall.js.map +1 -1
  171. package/dist/cli/update.d.ts.map +1 -1
  172. package/dist/cli/update.js +8 -1
  173. package/dist/cli/update.js.map +1 -1
  174. package/dist/cli/url.js.map +1 -1
  175. package/dist/compat/__tests__/doctor-contract.test.js +5 -1
  176. package/dist/compat/__tests__/doctor-contract.test.js.map +1 -1
  177. package/dist/compat/__tests__/rust-runtime-compat.test.js.map +1 -1
  178. package/dist/config/__tests__/codex-hooks.test.js +1287 -11
  179. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  180. package/dist/config/__tests__/generator-idempotent.test.js +850 -32
  181. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  182. package/dist/config/__tests__/generator-notify.test.js +6 -1
  183. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  184. package/dist/config/codex-feature-flags.d.ts.map +1 -1
  185. package/dist/config/codex-feature-flags.js.map +1 -1
  186. package/dist/config/codex-hooks.d.ts +124 -1
  187. package/dist/config/codex-hooks.d.ts.map +1 -1
  188. package/dist/config/codex-hooks.js +2142 -265
  189. package/dist/config/codex-hooks.js.map +1 -1
  190. package/dist/config/commit-lore-guard.js.map +1 -1
  191. package/dist/config/generator.d.ts +21 -16
  192. package/dist/config/generator.d.ts.map +1 -1
  193. package/dist/config/generator.js +991 -137
  194. package/dist/config/generator.js.map +1 -1
  195. package/dist/config/mcp-registry.js.map +1 -1
  196. package/dist/config/models.d.ts +2 -2
  197. package/dist/config/models.d.ts.map +1 -1
  198. package/dist/config/models.js.map +1 -1
  199. package/dist/config/omx-first-party-mcp.js.map +1 -1
  200. package/dist/config/team-mode.d.ts.map +1 -1
  201. package/dist/document-refresh/__tests__/enforcer.test.js.map +1 -1
  202. package/dist/document-refresh/enforcer.js.map +1 -1
  203. package/dist/exec/followup.js.map +1 -1
  204. package/dist/goal-workflows/artifacts.js.map +1 -1
  205. package/dist/hooks/__tests__/agents-overlay.test.js +7 -4
  206. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  207. package/dist/hooks/__tests__/analyze-routing-contract.test.js.map +1 -1
  208. package/dist/hooks/__tests__/analyze-skill-contract.test.js.map +1 -1
  209. package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
  210. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +9 -3
  211. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  212. package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
  213. package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
  214. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  215. package/dist/hooks/__tests__/design-skill.test.js.map +1 -1
  216. package/dist/hooks/__tests__/keyword-detector.test.js +1372 -2
  217. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  218. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +14 -0
  219. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  220. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  221. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +669 -1
  222. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  223. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +2 -0
  224. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
  225. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +142 -1
  226. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  227. package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
  228. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js +48 -2
  229. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js.map +1 -1
  230. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +61 -44
  231. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
  232. package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
  233. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js +3 -2
  234. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js.map +1 -1
  235. package/dist/hooks/__tests__/notify-hook-session-scope.test.js +243 -4
  236. package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
  237. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  238. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +8 -1
  239. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  240. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  241. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  242. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
  243. package/dist/hooks/__tests__/notify-hook-visual-verdict.test.js.map +1 -1
  244. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  245. package/dist/hooks/__tests__/openclaw-setup-contract.test.js.map +1 -1
  246. package/dist/hooks/__tests__/pre-context-gate-skills.test.js.map +1 -1
  247. package/dist/hooks/__tests__/prometheus-strict-contract.test.js.map +1 -1
  248. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js.map +1 -1
  249. package/dist/hooks/__tests__/prompt-guidance-test-helpers.js.map +1 -1
  250. package/dist/hooks/__tests__/prompt-session-provenance.test.d.ts +2 -0
  251. package/dist/hooks/__tests__/prompt-session-provenance.test.d.ts.map +1 -0
  252. package/dist/hooks/__tests__/prompt-session-provenance.test.js +82 -0
  253. package/dist/hooks/__tests__/prompt-session-provenance.test.js.map +1 -0
  254. package/dist/hooks/__tests__/session.test.js +532 -11
  255. package/dist/hooks/__tests__/session.test.js.map +1 -1
  256. package/dist/hooks/__tests__/skill-catalog-hygiene.test.js.map +1 -1
  257. package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.js.map +1 -1
  258. package/dist/hooks/__tests__/visual-ralph-skill.test.js.map +1 -1
  259. package/dist/hooks/__tests__/visual-verdict-loop.test.js.map +1 -1
  260. package/dist/hooks/agents-overlay.js +5 -5
  261. package/dist/hooks/agents-overlay.js.map +1 -1
  262. package/dist/hooks/code-simplifier/index.js.map +1 -1
  263. package/dist/hooks/explore-routing.js.map +1 -1
  264. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  265. package/dist/hooks/extensibility/events.js.map +1 -1
  266. package/dist/hooks/extensibility/loader.js.map +1 -1
  267. package/dist/hooks/extensibility/plugin-runner-stdin.js.map +1 -1
  268. package/dist/hooks/extensibility/sdk/logging.js.map +1 -1
  269. package/dist/hooks/keyword-detector.d.ts +33 -7
  270. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  271. package/dist/hooks/keyword-detector.js +2228 -119
  272. package/dist/hooks/keyword-detector.js.map +1 -1
  273. package/dist/hooks/keyword-registry.d.ts +12 -0
  274. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  275. package/dist/hooks/keyword-registry.js +41 -0
  276. package/dist/hooks/keyword-registry.js.map +1 -1
  277. package/dist/hooks/prompt-session-provenance.d.ts +87 -0
  278. package/dist/hooks/prompt-session-provenance.d.ts.map +1 -0
  279. package/dist/hooks/prompt-session-provenance.js +180 -0
  280. package/dist/hooks/prompt-session-provenance.js.map +1 -0
  281. package/dist/hooks/session.d.ts +139 -34
  282. package/dist/hooks/session.d.ts.map +1 -1
  283. package/dist/hooks/session.js +1239 -317
  284. package/dist/hooks/session.js.map +1 -1
  285. package/dist/hooks/task-size-detector.js.map +1 -1
  286. package/dist/hud/__tests__/state.test.js.map +1 -1
  287. package/dist/hud/authority.js.map +1 -1
  288. package/dist/hud/constants.js.map +1 -1
  289. package/dist/hud/index.js.map +1 -1
  290. package/dist/hud/reconcile.js.map +1 -1
  291. package/dist/hud/render.js.map +1 -1
  292. package/dist/hud/state.js.map +1 -1
  293. package/dist/hud/tmux.js.map +1 -1
  294. package/dist/leader/__tests__/contract.test.js +103 -1
  295. package/dist/leader/__tests__/contract.test.js.map +1 -1
  296. package/dist/leader/contract.d.ts +26 -5
  297. package/dist/leader/contract.d.ts.map +1 -1
  298. package/dist/leader/contract.js +109 -3
  299. package/dist/leader/contract.js.map +1 -1
  300. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  301. package/dist/mcp/__tests__/server-lifecycle.test.js +15 -2
  302. package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
  303. package/dist/mcp/__tests__/state-paths.test.js +98 -1
  304. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  305. package/dist/mcp/bootstrap.js.map +1 -1
  306. package/dist/mcp/code-intel-server.d.ts +58 -58
  307. package/dist/mcp/code-intel-server.d.ts.map +1 -1
  308. package/dist/mcp/code-intel-server.js.map +1 -1
  309. package/dist/mcp/hermes-bridge.js.map +1 -1
  310. package/dist/mcp/hermes-server.d.ts +70 -70
  311. package/dist/mcp/hermes-server.d.ts.map +1 -1
  312. package/dist/mcp/lifecycle-telemetry.js.map +1 -1
  313. package/dist/mcp/memory-server.d.ts +34 -34
  314. package/dist/mcp/memory-server.d.ts.map +1 -1
  315. package/dist/mcp/memory-server.js.map +1 -1
  316. package/dist/mcp/state-paths.d.ts +14 -0
  317. package/dist/mcp/state-paths.d.ts.map +1 -1
  318. package/dist/mcp/state-paths.js +96 -20
  319. package/dist/mcp/state-paths.js.map +1 -1
  320. package/dist/mcp/state-server.d.ts +47 -47
  321. package/dist/mcp/state-server.d.ts.map +1 -1
  322. package/dist/mcp/trace-server.d.ts +4 -4
  323. package/dist/mcp/trace-server.d.ts.map +1 -1
  324. package/dist/mcp/trace-server.js.map +1 -1
  325. package/dist/mcp/wiki-server.d.ts +23 -23
  326. package/dist/mcp/wiki-server.d.ts.map +1 -1
  327. package/dist/mcp/wiki-server.js.map +1 -1
  328. package/dist/modes/__tests__/base-session-scope.test.js +53 -1
  329. package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
  330. package/dist/modes/base.d.ts.map +1 -1
  331. package/dist/modes/base.js +5 -5
  332. package/dist/modes/base.js.map +1 -1
  333. package/dist/notifications/__tests__/lifecycle-dedupe.test.js.map +1 -1
  334. package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
  335. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js +177 -85
  336. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js.map +1 -1
  337. package/dist/notifications/__tests__/session-registry.test.js.map +1 -1
  338. package/dist/notifications/__tests__/template-engine.test.js.map +1 -1
  339. package/dist/notifications/__tests__/verbosity.test.js.map +1 -1
  340. package/dist/notifications/http-client.js.map +1 -1
  341. package/dist/notifications/index.d.ts +4 -1
  342. package/dist/notifications/index.d.ts.map +1 -1
  343. package/dist/notifications/index.js +3 -9
  344. package/dist/notifications/index.js.map +1 -1
  345. package/dist/notifications/lifecycle-dedupe.js.map +1 -1
  346. package/dist/notifications/reply-listener.d.ts.map +1 -1
  347. package/dist/notifications/reply-listener.js.map +1 -1
  348. package/dist/notifications/session-registry.js.map +1 -1
  349. package/dist/notifications/session-status.js.map +1 -1
  350. package/dist/notifications/temp-contract.js.map +1 -1
  351. package/dist/notifications/tmux-detector.js.map +1 -1
  352. package/dist/notifications/tmux.js.map +1 -1
  353. package/dist/openclaw/config.js.map +1 -1
  354. package/dist/openclaw/dispatcher.js.map +1 -1
  355. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  356. package/dist/pipeline/__tests__/stages.test.js +8 -5
  357. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  358. package/dist/pipeline/stages/code-review.js.map +1 -1
  359. package/dist/pipeline/stages/ralph-verify.js.map +1 -1
  360. package/dist/pipeline/stages/ralplan.js.map +1 -1
  361. package/dist/pipeline/stages/team-exec.js.map +1 -1
  362. package/dist/pipeline/stages/ultragoal.js.map +1 -1
  363. package/dist/pipeline/stages/ultraqa.js.map +1 -1
  364. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js.map +1 -1
  365. package/dist/planning/artifact-names.js.map +1 -1
  366. package/dist/planning/artifacts.js.map +1 -1
  367. package/dist/question/__tests__/state.test.js.map +1 -1
  368. package/dist/question/__tests__/ui.test.js.map +1 -1
  369. package/dist/question/autopilot-wait.js.map +1 -1
  370. package/dist/question/client.d.ts.map +1 -1
  371. package/dist/question/client.js.map +1 -1
  372. package/dist/question/deep-interview.js.map +1 -1
  373. package/dist/question/events.js.map +1 -1
  374. package/dist/question/renderer.js.map +1 -1
  375. package/dist/question/state.d.ts.map +1 -1
  376. package/dist/question/state.js.map +1 -1
  377. package/dist/question/ui.js.map +1 -1
  378. package/dist/ralph/contract.d.ts +1 -1
  379. package/dist/ralph/contract.d.ts.map +1 -1
  380. package/dist/ralplan/__tests__/consensus-gate.test.js +403 -8
  381. package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
  382. package/dist/ralplan/__tests__/runtime.test.js +112 -14
  383. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  384. package/dist/ralplan/consensus-gate.d.ts +3 -3
  385. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  386. package/dist/ralplan/consensus-gate.js +155 -60
  387. package/dist/ralplan/consensus-gate.js.map +1 -1
  388. package/dist/ralplan/runtime.d.ts +2 -2
  389. package/dist/ralplan/runtime.d.ts.map +1 -1
  390. package/dist/ralplan/runtime.js +1 -2
  391. package/dist/ralplan/runtime.js.map +1 -1
  392. package/dist/runtime/bridge.d.ts.map +1 -1
  393. package/dist/runtime/bridge.js.map +1 -1
  394. package/dist/runtime/process-tree.js.map +1 -1
  395. package/dist/runtime/run-loop.js.map +1 -1
  396. package/dist/runtime/run-outcome.d.ts +3 -3
  397. package/dist/runtime/run-outcome.d.ts.map +1 -1
  398. package/dist/runtime/run-outcome.js.map +1 -1
  399. package/dist/runtime/run-state.js.map +1 -1
  400. package/dist/scripts/__tests__/codex-native-hook.test.js +4121 -592
  401. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  402. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.d.ts +2 -0
  403. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.d.ts.map +1 -0
  404. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.js +634 -0
  405. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.js.map +1 -0
  406. package/dist/scripts/__tests__/generate-release-body.test.js.map +1 -1
  407. package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -1
  408. package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -1
  409. package/dist/scripts/__tests__/notify-fallback-delivery.test.d.ts +2 -0
  410. package/dist/scripts/__tests__/notify-fallback-delivery.test.d.ts.map +1 -0
  411. package/dist/scripts/__tests__/notify-fallback-delivery.test.js +174 -0
  412. package/dist/scripts/__tests__/notify-fallback-delivery.test.js.map +1 -0
  413. package/dist/scripts/__tests__/notify-state-io.test.js +57 -1
  414. package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
  415. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.d.ts +2 -0
  416. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.d.ts.map +1 -0
  417. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js +205 -0
  418. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js.map +1 -0
  419. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.d.ts +2 -0
  420. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.d.ts.map +1 -0
  421. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js +175 -0
  422. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js.map +1 -0
  423. package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
  424. package/dist/scripts/__tests__/smoke-packed-install.test.js +848 -4
  425. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  426. package/dist/scripts/__tests__/test-reply-listener-live.test.js.map +1 -1
  427. package/dist/scripts/build-api.js.map +1 -1
  428. package/dist/scripts/build-explore-harness.js.map +1 -1
  429. package/dist/scripts/build-sparkshell.js.map +1 -1
  430. package/dist/scripts/cleanup-explore-harness.js.map +1 -1
  431. package/dist/scripts/codex-execution-surface.js.map +1 -1
  432. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  433. package/dist/scripts/codex-native-hook.js +689 -156
  434. package/dist/scripts/codex-native-hook.js.map +1 -1
  435. package/dist/scripts/generate-native-release-manifest.js.map +1 -1
  436. package/dist/scripts/generate-release-body.js.map +1 -1
  437. package/dist/scripts/hook-payload-guard.d.ts.map +1 -1
  438. package/dist/scripts/hook-payload-guard.js.map +1 -1
  439. package/dist/scripts/notify-fallback-delivery.d.ts +48 -0
  440. package/dist/scripts/notify-fallback-delivery.d.ts.map +1 -0
  441. package/dist/scripts/notify-fallback-delivery.js +419 -0
  442. package/dist/scripts/notify-fallback-delivery.js.map +1 -0
  443. package/dist/scripts/notify-fallback-watcher.js +136 -30
  444. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  445. package/dist/scripts/notify-hook/auto-nudge.d.ts +18 -9
  446. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  447. package/dist/scripts/notify-hook/auto-nudge.js +39 -13
  448. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  449. package/dist/scripts/notify-hook/managed-tmux.d.ts +23 -0
  450. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  451. package/dist/scripts/notify-hook/managed-tmux.js +122 -12
  452. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  453. package/dist/scripts/notify-hook/orchestration-intent.d.ts +7 -7
  454. package/dist/scripts/notify-hook/orchestration-intent.d.ts.map +1 -1
  455. package/dist/scripts/notify-hook/ralph-session-resume.d.ts +5 -2
  456. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
  457. package/dist/scripts/notify-hook/ralph-session-resume.js +33 -37
  458. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  459. package/dist/scripts/notify-hook/state-io.d.ts +17 -0
  460. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  461. package/dist/scripts/notify-hook/state-io.js +73 -11
  462. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  463. package/dist/scripts/notify-hook/team-dispatch.d.ts +1 -1
  464. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  465. package/dist/scripts/notify-hook/team-leader-nudge.d.ts +2 -2
  466. package/dist/scripts/notify-hook/team-tmux-guard.d.ts +4 -4
  467. package/dist/scripts/notify-hook/team-worker-posttooluse.js.map +1 -1
  468. package/dist/scripts/notify-hook/team-worker-stop.d.ts +1 -1
  469. package/dist/scripts/notify-hook/team-worker.d.ts +2 -2
  470. package/dist/scripts/notify-hook/tmux-injection.d.ts +8 -1
  471. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  472. package/dist/scripts/notify-hook/tmux-injection.js +25 -16
  473. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  474. package/dist/scripts/notify-hook/visual-verdict.d.ts +1 -1
  475. package/dist/scripts/notify-hook/visual-verdict.d.ts.map +1 -1
  476. package/dist/scripts/notify-hook/visual-verdict.js +15 -13
  477. package/dist/scripts/notify-hook/visual-verdict.js.map +1 -1
  478. package/dist/scripts/notify-hook.d.ts +24 -1
  479. package/dist/scripts/notify-hook.d.ts.map +1 -1
  480. package/dist/scripts/notify-hook.js +266 -90
  481. package/dist/scripts/notify-hook.js.map +1 -1
  482. package/dist/scripts/postinstall.js.map +1 -1
  483. package/dist/scripts/prompt-inventory.js.map +1 -1
  484. package/dist/scripts/smoke-packed-install.d.ts +879 -2
  485. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  486. package/dist/scripts/smoke-packed-install.js +1528 -7
  487. package/dist/scripts/smoke-packed-install.js.map +1 -1
  488. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  489. package/dist/scripts/test-reply-listener-live.js.map +1 -1
  490. package/dist/scripts/test-sparkshell.js.map +1 -1
  491. package/dist/scripts/verify-native-agents.js.map +1 -1
  492. package/dist/session-history/friction.js.map +1 -1
  493. package/dist/session-history/search.js.map +1 -1
  494. package/dist/sidecar/__tests__/boundary.test.js.map +1 -1
  495. package/dist/sidecar/collector.js.map +1 -1
  496. package/dist/sidecar/index.js.map +1 -1
  497. package/dist/sidecar/render.js.map +1 -1
  498. package/dist/sidecar/tmux.js.map +1 -1
  499. package/dist/state/__tests__/operations.test.js +64 -15
  500. package/dist/state/__tests__/operations.test.js.map +1 -1
  501. package/dist/state/__tests__/skill-active.test.js +19 -1
  502. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  503. package/dist/state/__tests__/workflow-transition.test.js +61 -0
  504. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  505. package/dist/state/mode-state-context.js.map +1 -1
  506. package/dist/state/operations.d.ts +1 -1
  507. package/dist/state/operations.d.ts.map +1 -1
  508. package/dist/state/operations.js +49 -55
  509. package/dist/state/operations.js.map +1 -1
  510. package/dist/state/paths.d.ts +1 -1
  511. package/dist/state/paths.d.ts.map +1 -1
  512. package/dist/state/paths.js +1 -1
  513. package/dist/state/paths.js.map +1 -1
  514. package/dist/state/skill-active.d.ts +13 -1
  515. package/dist/state/skill-active.d.ts.map +1 -1
  516. package/dist/state/skill-active.js +43 -4
  517. package/dist/state/skill-active.js.map +1 -1
  518. package/dist/state/terminal-normalization.js.map +1 -1
  519. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  520. package/dist/state/workflow-transition-reconcile.js +20 -4
  521. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  522. package/dist/state/workflow-transition.d.ts +1 -1
  523. package/dist/state/workflow-transition.d.ts.map +1 -1
  524. package/dist/state/workflow-transition.js.map +1 -1
  525. package/dist/subagents/__tests__/adapted-role-binding.test.d.ts +2 -0
  526. package/dist/subagents/__tests__/adapted-role-binding.test.d.ts.map +1 -0
  527. package/dist/subagents/__tests__/adapted-role-binding.test.js +988 -0
  528. package/dist/subagents/__tests__/adapted-role-binding.test.js.map +1 -0
  529. package/dist/subagents/__tests__/leader-bootstrap-3181.test.d.ts +2 -0
  530. package/dist/subagents/__tests__/leader-bootstrap-3181.test.d.ts.map +1 -0
  531. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js +273 -0
  532. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js.map +1 -0
  533. package/dist/subagents/__tests__/tracker.test.js +1550 -10
  534. package/dist/subagents/__tests__/tracker.test.js.map +1 -1
  535. package/dist/subagents/adapted-role-binding.d.ts +17 -0
  536. package/dist/subagents/adapted-role-binding.d.ts.map +1 -0
  537. package/dist/subagents/adapted-role-binding.js +75 -0
  538. package/dist/subagents/adapted-role-binding.js.map +1 -0
  539. package/dist/subagents/role-routing-marker.d.ts +10 -0
  540. package/dist/subagents/role-routing-marker.d.ts.map +1 -0
  541. package/dist/subagents/role-routing-marker.js +136 -0
  542. package/dist/subagents/role-routing-marker.js.map +1 -0
  543. package/dist/subagents/tracker.d.ts +172 -0
  544. package/dist/subagents/tracker.d.ts.map +1 -1
  545. package/dist/subagents/tracker.js +1077 -61
  546. package/dist/subagents/tracker.js.map +1 -1
  547. package/dist/team/__tests__/approved-execution.test.js.map +1 -1
  548. package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
  549. package/dist/team/__tests__/model-contract.test.js +231 -1
  550. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  551. package/dist/team/__tests__/role-router.test.js.map +1 -1
  552. package/dist/team/__tests__/runtime.test.js +401 -1
  553. package/dist/team/__tests__/runtime.test.js.map +1 -1
  554. package/dist/team/__tests__/scaling.test.js +181 -0
  555. package/dist/team/__tests__/scaling.test.js.map +1 -1
  556. package/dist/team/__tests__/state-root.test.js.map +1 -1
  557. package/dist/team/__tests__/tmux-session.test.js +434 -4
  558. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  559. package/dist/team/__tests__/tmux-test-fixture.js.map +1 -1
  560. package/dist/team/api-interop.d.ts +2 -2
  561. package/dist/team/api-interop.d.ts.map +1 -1
  562. package/dist/team/api-interop.js.map +1 -1
  563. package/dist/team/commit-hygiene.js.map +1 -1
  564. package/dist/team/contracts.d.ts +5 -5
  565. package/dist/team/contracts.d.ts.map +1 -1
  566. package/dist/team/delivery-log.js.map +1 -1
  567. package/dist/team/followup-planner.js.map +1 -1
  568. package/dist/team/goal-workflow.js.map +1 -1
  569. package/dist/team/idle-nudge.d.ts.map +1 -1
  570. package/dist/team/model-contract.d.ts +20 -1
  571. package/dist/team/model-contract.d.ts.map +1 -1
  572. package/dist/team/model-contract.js +267 -30
  573. package/dist/team/model-contract.js.map +1 -1
  574. package/dist/team/orchestrator.js.map +1 -1
  575. package/dist/team/pane-status.js.map +1 -1
  576. package/dist/team/phase-controller.js.map +1 -1
  577. package/dist/team/reminder-intents.d.ts +1 -1
  578. package/dist/team/reminder-intents.d.ts.map +1 -1
  579. package/dist/team/reminder-intents.js.map +1 -1
  580. package/dist/team/runtime-cli.js.map +1 -1
  581. package/dist/team/runtime.d.ts.map +1 -1
  582. package/dist/team/runtime.js +83 -52
  583. package/dist/team/runtime.js.map +1 -1
  584. package/dist/team/scaling.d.ts +9 -7
  585. package/dist/team/scaling.d.ts.map +1 -1
  586. package/dist/team/scaling.js +69 -28
  587. package/dist/team/scaling.js.map +1 -1
  588. package/dist/team/state/dispatch-lock.js.map +1 -1
  589. package/dist/team/state/dispatch.js.map +1 -1
  590. package/dist/team/state/events.js.map +1 -1
  591. package/dist/team/state/tasks.js.map +1 -1
  592. package/dist/team/state-root.js.map +1 -1
  593. package/dist/team/state.js.map +1 -1
  594. package/dist/team/team-identity.d.ts.map +1 -1
  595. package/dist/team/team-identity.js.map +1 -1
  596. package/dist/team/tmux-session.d.ts +2 -0
  597. package/dist/team/tmux-session.d.ts.map +1 -1
  598. package/dist/team/tmux-session.js +90 -42
  599. package/dist/team/tmux-session.js.map +1 -1
  600. package/dist/team/worker-bootstrap.js.map +1 -1
  601. package/dist/team/worktree.js.map +1 -1
  602. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  603. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
  604. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  605. package/dist/ultragoal/artifacts.js +4 -1
  606. package/dist/ultragoal/artifacts.js.map +1 -1
  607. package/dist/url-reader/index.js.map +1 -1
  608. package/dist/utils/agents-model-table.js.map +1 -1
  609. package/dist/utils/package.js.map +1 -1
  610. package/dist/utils/paths.js.map +1 -1
  611. package/dist/utils/platform-command.js.map +1 -1
  612. package/dist/utils/repo-deps.js.map +1 -1
  613. package/dist/utils/version.js.map +1 -1
  614. package/dist/verification/__tests__/ci-rust-gates.test.js +1 -1
  615. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
  616. package/dist/verification/verifier.js.map +1 -1
  617. package/dist/visual/constants.d.ts +1 -1
  618. package/dist/visual/constants.d.ts.map +1 -1
  619. package/dist/vscode/__tests__/index.test.js.map +1 -1
  620. package/dist/vscode/index.js.map +1 -1
  621. package/dist/wiki/__tests__/cjk-tokenize.test.js.map +1 -1
  622. package/dist/wiki/__tests__/lint.test.js.map +1 -1
  623. package/dist/wiki/__tests__/query.test.js.map +1 -1
  624. package/dist/wiki/__tests__/storage.test.js.map +1 -1
  625. package/dist/wiki/__tests__/test-helpers.d.ts.map +1 -1
  626. package/dist/wiki/lint.js.map +1 -1
  627. package/dist/wiki/query.js.map +1 -1
  628. package/dist/wiki/storage.js.map +1 -1
  629. package/package.json +2 -2
  630. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  631. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +10 -5
  632. package/plugins/oh-my-codex/skills/ralph/SKILL.md +4 -2
  633. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +5 -2
  634. package/skills/omx-setup/SKILL.md +10 -5
  635. package/skills/ralph/SKILL.md +4 -2
  636. package/skills/ralplan/SKILL.md +5 -2
  637. package/src/scripts/__tests__/codex-native-hook.test.ts +17522 -10630
  638. package/src/scripts/__tests__/fixtures/issue-3138-installed-smoke.ts +734 -0
  639. package/src/scripts/__tests__/notify-fallback-delivery.test.ts +177 -0
  640. package/src/scripts/__tests__/notify-state-io.test.ts +73 -0
  641. package/src/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.ts +215 -0
  642. package/src/scripts/__tests__/role-intent-durable-recovery-3181.test.ts +193 -0
  643. package/src/scripts/__tests__/smoke-packed-install.test.ts +991 -4
  644. package/src/scripts/codex-native-hook.ts +779 -159
  645. package/src/scripts/notify-fallback-delivery.ts +416 -0
  646. package/src/scripts/notify-fallback-watcher.ts +132 -40
  647. package/src/scripts/notify-hook/auto-nudge.ts +43 -12
  648. package/src/scripts/notify-hook/managed-tmux.ts +149 -12
  649. package/src/scripts/notify-hook/ralph-session-resume.ts +37 -36
  650. package/src/scripts/notify-hook/state-io.ts +100 -13
  651. package/src/scripts/notify-hook/tmux-injection.ts +38 -21
  652. package/src/scripts/notify-hook/visual-verdict.ts +22 -12
  653. package/src/scripts/notify-hook.ts +335 -90
  654. package/src/scripts/smoke-packed-install.ts +1829 -6
@@ -1,11 +1,24 @@
1
1
  // @ts-nocheck
2
2
  import assert from 'node:assert/strict';
3
- import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises';
3
+ import { access, chmod, mkdtemp, mkdir, rm, symlink, writeFile } from 'node:fs/promises';
4
+ import { EventEmitter } from 'node:events';
4
5
  import { tmpdir } from 'node:os';
5
- import { join } from 'node:path';
6
+ import { delimiter, join } from 'node:path';
7
+ import { PassThrough } from 'node:stream';
6
8
  import { test } from 'node:test';
7
- import { ensureRepoDependencies, hasUsableNodeModules, buildNativeHookSmokePayload, PACKED_INSTALL_NATIVE_HOOK_SMOKE_EVENTS, PACKED_INSTALL_SMOKE_CORE_COMMANDS, parseNpmPackJsonOutput, resolveGitCommonDir, resolveReusableNodeModulesSource, validateHookStdout, } from '../smoke-packed-install.js';
8
- test('packed install smoke stays limited to boot + core commands', () => {
9
+ import { ensureRepoDependencies, hasUsableNodeModules, buildPackedRegressionEnvironment, CODEX_APP_SERVER_TIMEOUTS, CodexAppServer, CodexExecutableNotFoundError, MANAGED_CODEX_HOOK_EVENTS, PACKED_INSTALL_NATIVE_HOOK_SMOKE_EVENTS, PACKED_INSTALL_NATIVE_HOOK_REGRESSION_PROMPTS, PACKED_INSTALL_SMOKE_CORE_COMMANDS, appendForeignHookGroups, appendDisplayOrderStableForeignHookGroups, buildNativeHookSmokePayload, createCodexBatchWriteEnvelope, createCodexHooksListEnvelope, createCodexInitializeEnvelope, foreignHookGroupSnapshot, generatedHookTrustState, parseNpmPackJsonOutput, parseCodexHooksListResult, probeCodexVersion, resolveGitCommonDir, resolveReusableNodeModulesSource, validateHookStdout, assertCodexBatchWriteResult, assertGeneratedTrustMatchesCodex, managedCodexHooksByEvent, } from '../smoke-packed-install.js';
10
+ function createFakeCodexAppServer(onRequest) {
11
+ const child = Object.assign(new EventEmitter(), {
12
+ stdin: new PassThrough(),
13
+ stdout: new PassThrough(),
14
+ stderr: new PassThrough(),
15
+ kill: () => true,
16
+ });
17
+ child.stdin.on('data', (chunk) => onRequest(JSON.parse(chunk.toString('utf-8')), child));
18
+ child.stdin.on('finish', () => child.emit('close', 0, null));
19
+ return new CodexAppServer(child);
20
+ }
21
+ test('packed install smoke retains narrow boot commands and adds the isolated lifecycle separately', () => {
9
22
  assert.deepEqual(PACKED_INSTALL_SMOKE_CORE_COMMANDS, [
10
23
  ['--help'],
11
24
  ['version'],
@@ -15,6 +28,640 @@ test('packed install smoke stays limited to boot + core commands', () => {
15
28
  assert.equal(PACKED_INSTALL_SMOKE_CORE_COMMANDS.some((argv) => argv.includes('api')), true);
16
29
  assert.equal(PACKED_INSTALL_SMOKE_CORE_COMMANDS.some((argv) => argv.includes('sparkshell')), true);
17
30
  });
31
+ test('packed lifecycle keeps the pinned newline-delimited Codex app-server envelopes literal', () => {
32
+ assert.deepEqual(createCodexInitializeEnvelope('omx-hook-trust-regression'), {
33
+ id: 1,
34
+ method: 'initialize',
35
+ params: {
36
+ clientInfo: { name: 'omx-hook-trust-regression', version: '1.0.0' },
37
+ capabilities: null,
38
+ },
39
+ });
40
+ assert.deepEqual(createCodexHooksListEnvelope('/tmp/project'), {
41
+ id: 2,
42
+ method: 'hooks/list',
43
+ params: { cwds: ['/tmp/project'] },
44
+ });
45
+ assert.deepEqual(createCodexBatchWriteEnvelope({ first: 'sha256:first', second: 'sha256:second' }), {
46
+ id: 3,
47
+ method: 'config/batchWrite',
48
+ params: {
49
+ edits: [{
50
+ keyPath: 'hooks.state',
51
+ value: {
52
+ first: { trusted_hash: 'sha256:first' },
53
+ second: { trusted_hash: 'sha256:second' },
54
+ },
55
+ mergeStrategy: 'upsert',
56
+ }],
57
+ filePath: null,
58
+ expectedVersion: null,
59
+ reloadUserConfig: true,
60
+ },
61
+ });
62
+ assert.deepEqual(CODEX_APP_SERVER_TIMEOUTS, {
63
+ versionProbeMs: 2_000,
64
+ initializeMs: 15_000,
65
+ requestMs: 10_000,
66
+ shutdownMs: 5_000,
67
+ });
68
+ });
69
+ test('Codex app-server accepts method-bearing notifications but rejects invalid idless envelopes', async () => {
70
+ const accepted = createFakeCodexAppServer((request, child) => {
71
+ child.stdout.write(`${JSON.stringify({ method: 'thread/started', params: { thread: 'fake' } })}\n`);
72
+ child.stdout.write(`${JSON.stringify({ id: request.id, result: { ok: true } })}\n`);
73
+ });
74
+ assert.deepEqual(await accepted.request({ id: 1, method: 'hooks/list' }, 100), { ok: true });
75
+ await accepted.close();
76
+ for (const envelope of [
77
+ { result: { invalid: true } },
78
+ { error: { code: -1, message: 'invalid' } },
79
+ { unknown: true },
80
+ { method: ' ' },
81
+ { method: 'thread/started', result: { invalid: true } },
82
+ ]) {
83
+ const server = createFakeCodexAppServer((_request, child) => {
84
+ child.stdout.write(`${JSON.stringify(envelope)}\n`);
85
+ });
86
+ await assert.rejects(server.request({ id: 1, method: 'hooks/list' }, 100), /invalid idless JSON-RPC envelope/);
87
+ await assert.rejects(server.close(), /invalid idless JSON-RPC envelope/);
88
+ }
89
+ });
90
+ test('Codex app-server rejects id-bearing envelopes that mix response and request fields', async () => {
91
+ for (const envelope of [
92
+ { id: 1, method: 'thread/started', result: { invalid: true } },
93
+ { id: 1, params: { thread: 'fake' }, result: { invalid: true } },
94
+ { id: 1, method: 'thread/started', params: {}, error: { code: -1, message: 'invalid' } },
95
+ { id: 1, result: { invalid: true }, error: { code: -1, message: 'invalid' } },
96
+ { id: 1 },
97
+ ]) {
98
+ const server = createFakeCodexAppServer((_request, child) => {
99
+ child.stdout.write(`${JSON.stringify(envelope)}\n`);
100
+ });
101
+ await assert.rejects(server.request({ id: 1, method: 'hooks/list' }, 100), /codex app-server response/);
102
+ await assert.rejects(server.close(), /codex app-server response/);
103
+ }
104
+ });
105
+ test('Codex app-server validates JSON-RPC error response shapes', async () => {
106
+ const valid = createFakeCodexAppServer((_request, child) => {
107
+ child.stdout.write(`${JSON.stringify({
108
+ id: 1,
109
+ error: { code: -32600, message: 'invalid request', data: { source: 'fake' } },
110
+ })}\n`);
111
+ });
112
+ await assert.rejects(valid.request({ id: 1, method: 'hooks/list' }, 100), /returned JSON-RPC error/);
113
+ await valid.close();
114
+ for (const error of [
115
+ null,
116
+ {},
117
+ { code: '-32600', message: 'invalid request' },
118
+ { code: -32600, message: null },
119
+ ]) {
120
+ const server = createFakeCodexAppServer((_request, child) => {
121
+ child.stdout.write(`${JSON.stringify({ id: 1, error })}\n`);
122
+ });
123
+ await assert.rejects(server.request({ id: 1, method: 'hooks/list' }, 100), /malformed JSON-RPC error/);
124
+ await assert.rejects(server.close(), /malformed JSON-RPC error/);
125
+ }
126
+ });
127
+ test('Codex app-server incrementally decodes split UTF-8 responses and rejects invalid byte sequences', async () => {
128
+ const split = createFakeCodexAppServer((_request, child) => {
129
+ child.stdout.write(Buffer.from('{"id":1,"result":"', 'utf-8'));
130
+ child.stdout.write(Buffer.from([0xe2, 0x82]));
131
+ child.stdout.write(Buffer.from([0xac]));
132
+ child.stdout.write(Buffer.from('"}\n', 'utf-8'));
133
+ });
134
+ assert.equal(await split.request({ id: 1, method: 'hooks/list' }, 100), '€');
135
+ await split.close();
136
+ for (const writeInvalidResponse of [
137
+ (child) => {
138
+ child.stdout.write(Buffer.from('{"id":1,"result":"', 'utf-8'));
139
+ child.stdout.write(Buffer.from([0xff]));
140
+ child.stdout.write(Buffer.from('"}\n', 'utf-8'));
141
+ },
142
+ (child) => {
143
+ child.stdout.write(Buffer.from('{"id":1,"result":"', 'utf-8'));
144
+ child.stdout.write(Buffer.from([0xc3]));
145
+ },
146
+ ]) {
147
+ const server = createFakeCodexAppServer((_request, child) => writeInvalidResponse(child));
148
+ const request = server.request({ id: 1, method: 'hooks/list' }, 100);
149
+ await assert.rejects(Promise.all([request, server.close()]), /invalid UTF-8 stdout/);
150
+ }
151
+ });
152
+ test('Codex app-server rejects non-empty unterminated stdout when the server closes', async () => {
153
+ const server = createFakeCodexAppServer((_request, child) => {
154
+ child.stdout.write('{"id":1');
155
+ });
156
+ const request = server.request({ id: 1, method: 'hooks/list' }, 100);
157
+ await assert.rejects(Promise.all([request, server.close()]), /unterminated JSON-RPC stdout/);
158
+ });
159
+ test('packed lifecycle parses the pinned hooks/list eventName schema', () => {
160
+ const project = '/tmp/project';
161
+ const hooksPath = '/tmp/project/.codex/hooks.json';
162
+ const parsed = parseCodexHooksListResult({
163
+ data: [{
164
+ cwd: project,
165
+ hooks: [{
166
+ eventName: 'preToolUse',
167
+ command: 'node hook.js',
168
+ sourcePath: hooksPath,
169
+ key: `${hooksPath}:pre_tool_use:0:0`,
170
+ currentHash: 'sha256:current',
171
+ displayOrder: 0,
172
+ trustStatus: 'trusted',
173
+ }],
174
+ warnings: [],
175
+ errors: [],
176
+ }],
177
+ }, project, hooksPath);
178
+ assert.equal(parsed.hooks[0]?.event, 'PreToolUse');
179
+ assert.equal(parsed.hooks[0]?.trustStatus, 'trusted');
180
+ });
181
+ test('packed lifecycle normalizes omitted enabled handlers and rejects disabled or non-integer hook metadata', () => {
182
+ const project = '/tmp/project';
183
+ const hooksPath = '/tmp/project/.codex/hooks.json';
184
+ const response = {
185
+ data: [{
186
+ cwd: project,
187
+ hooks: [{
188
+ eventName: 'preToolUse',
189
+ command: 'node hook.js',
190
+ sourcePath: hooksPath,
191
+ key: `${hooksPath}:pre_tool_use:0:0`,
192
+ currentHash: 'sha256:current',
193
+ displayOrder: 0,
194
+ trustStatus: 'trusted',
195
+ }],
196
+ warnings: [],
197
+ errors: [],
198
+ }],
199
+ };
200
+ assert.doesNotThrow(() => parseCodexHooksListResult(response, project, hooksPath));
201
+ for (const update of [
202
+ { enabled: false },
203
+ { enabled: 'true' },
204
+ { enabled: true, displayOrder: 0.5 },
205
+ { enabled: true, displayOrder: -1 },
206
+ ]) {
207
+ const invalid = structuredClone(response);
208
+ Object.assign(invalid.data[0].hooks[0], update);
209
+ assert.throws(() => parseCodexHooksListResult(invalid, project, hooksPath), /enabled command handler|invalid hooks\[0\]\.displayOrder/);
210
+ }
211
+ });
212
+ test('packed lifecycle requires exactly seven generated OMX trust keys and an isolated approval target', () => {
213
+ const hooks = MANAGED_CODEX_HOOK_EVENTS.map((event, index) => ({
214
+ event,
215
+ command: 'node /tmp/omx/dist/scripts/codex-native-hook.js',
216
+ sourcePath: '/tmp/project/.codex/hooks.json',
217
+ key: `/tmp/project/.codex/hooks.json:${event}:${index}:0`,
218
+ currentHash: `sha256:${event}`,
219
+ displayOrder: index,
220
+ trustStatus: 'untrusted',
221
+ }));
222
+ const trust = Object.fromEntries(hooks.map((hook) => [hook.key, hook.currentHash]));
223
+ assert.doesNotThrow(() => assertGeneratedTrustMatchesCodex(trust, hooks));
224
+ assert.throws(() => assertGeneratedTrustMatchesCodex({
225
+ ...trust,
226
+ '/tmp/project/.codex/hooks.json:foreign:0:0': 'sha256:foreign',
227
+ }, hooks), /exactly the 7 current OMX hooks with no stale keys/);
228
+ assert.throws(() => assertGeneratedTrustMatchesCodex(Object.fromEntries(Object.entries(trust).slice(0, -1)), hooks), /exactly the 7 current OMX hooks with no stale keys/);
229
+ assert.doesNotThrow(() => assertCodexBatchWriteResult({
230
+ filePath: '/tmp/isolated-codex-home/config.toml',
231
+ status: 'ok',
232
+ version: null,
233
+ }, '/tmp/isolated-codex-home/config.toml'));
234
+ assert.throws(() => assertCodexBatchWriteResult({
235
+ filePath: '/home/user/.codex/config.toml',
236
+ status: 'ok',
237
+ version: null,
238
+ }, '/tmp/isolated-codex-home/config.toml'), /expected isolated user config/);
239
+ });
240
+ test('packed lifecycle fails closed on malformed project hooks.state entries and stale raw keys', () => {
241
+ const hooks = MANAGED_CODEX_HOOK_EVENTS.map((event, index) => ({
242
+ event,
243
+ command: 'node /tmp/omx/dist/scripts/codex-native-hook.js',
244
+ sourcePath: '/tmp/project/.codex/hooks.json',
245
+ key: `/tmp/project/.codex/hooks.json:${event}:${index}:0`,
246
+ currentHash: `sha256:${event}`,
247
+ displayOrder: index,
248
+ trustStatus: 'untrusted',
249
+ }));
250
+ const validConfig = [
251
+ '[hooks.state]',
252
+ ...hooks.map((hook) => `${JSON.stringify(hook.key)} = { trusted_hash = ${JSON.stringify(hook.currentHash)} }`),
253
+ ].join('\n');
254
+ const generatedTrust = generatedHookTrustState(validConfig);
255
+ assert.doesNotThrow(() => assertGeneratedTrustMatchesCodex(generatedTrust, hooks));
256
+ for (const malformedEntry of [
257
+ 'first = "sha256:first"',
258
+ 'first = {}',
259
+ 'first = { trusted_hash = "" }',
260
+ 'first = { trusted_hash = 1 }',
261
+ 'first = { trusted_hash = "sha256:first", extra = "unexpected" }',
262
+ ]) {
263
+ assert.throws(() => generatedHookTrustState(`[hooks.state]\n${malformedEntry}`), /invalid hooks\.state entry first/);
264
+ }
265
+ const staleRawTrust = generatedHookTrustState(`${validConfig}\nforeign = { trusted_hash = "sha256:foreign" }`);
266
+ assert.throws(() => assertGeneratedTrustMatchesCodex(staleRawTrust, hooks), /exactly the 7 current OMX hooks with no stale keys/);
267
+ const protoRawTrust = generatedHookTrustState(`${validConfig}\n__proto__ = { trusted_hash = "sha256:proto" }`);
268
+ assert.equal(Object.hasOwn(protoRawTrust, '__proto__'), true);
269
+ assert.throws(() => assertGeneratedTrustMatchesCodex(protoRawTrust, hooks), /exactly the 7 current OMX hooks with no stale keys/);
270
+ });
271
+ test('packed lifecycle treats only the production command grammar as managed ownership', () => {
272
+ const hooks = MANAGED_CODEX_HOOK_EVENTS.map((event, index) => ({
273
+ event,
274
+ command: 'node /tmp/omx/dist/scripts/codex-native-hook.js',
275
+ sourcePath: '/tmp/project/.codex/hooks.json',
276
+ key: `/tmp/project/.codex/hooks.json:${event}:${index}:0`,
277
+ currentHash: `sha256:${event}`,
278
+ displayOrder: index,
279
+ trustStatus: 'untrusted',
280
+ }));
281
+ hooks[0].command = 'node /tmp/foreign-codex-native-hook.js';
282
+ assert.throws(() => managedCodexHooksByEvent(hooks), /Expected exactly one OMX SessionStart hook from Codex, received 0/);
283
+ });
284
+ test('packed lifecycle resolves Windows npm shims through safe command specs for version probes and app-server', async () => {
285
+ const root = await mkdtemp(join(tmpdir(), 'omx-codex-windows-shim-'));
286
+ const fakeChild = () => {
287
+ const child = Object.assign(new EventEmitter(), {
288
+ stdin: new PassThrough(),
289
+ stdout: new PassThrough(),
290
+ stderr: new PassThrough(),
291
+ kill: () => {
292
+ queueMicrotask(() => child.emit('close', 0, null));
293
+ return true;
294
+ },
295
+ });
296
+ child.stdin.once('finish', () => queueMicrotask(() => child.emit('close', 0, null)));
297
+ queueMicrotask(() => child.emit('spawn'));
298
+ return child;
299
+ };
300
+ try {
301
+ for (const extension of ['.cmd', '.ps1']) {
302
+ const bin = join(root, extension.slice(1));
303
+ const codexPath = join(bin, `codex${extension}`);
304
+ const powershellPath = join(bin, 'powershell.exe');
305
+ await mkdir(bin, { recursive: true });
306
+ await Promise.all([writeFile(codexPath, ''), writeFile(powershellPath, '')]);
307
+ const versionSpawns = [];
308
+ const appSpawns = [];
309
+ const seam = {
310
+ platform: 'win32',
311
+ spawnSyncImpl: ((command, args, options) => {
312
+ versionSpawns.push({ command, args, options });
313
+ return { status: 0, stdout: 'codex-cli 0.142.5\n', stderr: '', error: undefined };
314
+ }),
315
+ spawnImpl: ((command, args, options) => {
316
+ appSpawns.push({ command, args, options });
317
+ return fakeChild();
318
+ }),
319
+ };
320
+ const env = { PATH: bin, PATHEXT: extension.toUpperCase() };
321
+ assert.equal(probeCodexVersion(root, env, seam), 'codex-cli 0.142.5');
322
+ const server = await CodexAppServer.start({ cwd: root, env, commandSeam: seam });
323
+ await server.close();
324
+ assert.equal(versionSpawns.length, 2);
325
+ assert.deepEqual(versionSpawns.map(({ command, args, options }) => ({ command, args, options })), extension === '.cmd'
326
+ ? [
327
+ {
328
+ command: 'cmd.exe',
329
+ args: ['/d', '/s', '/c', `""${codexPath}" "--version""`],
330
+ options: {
331
+ cwd: root,
332
+ env,
333
+ encoding: 'utf-8',
334
+ timeout: CODEX_APP_SERVER_TIMEOUTS.versionProbeMs,
335
+ killSignal: 'SIGKILL',
336
+ windowsHide: true,
337
+ windowsVerbatimArguments: true,
338
+ },
339
+ },
340
+ {
341
+ command: 'cmd.exe',
342
+ args: ['/d', '/s', '/c', `""${codexPath}" "--version""`],
343
+ options: {
344
+ cwd: root,
345
+ env,
346
+ encoding: 'utf-8',
347
+ timeout: CODEX_APP_SERVER_TIMEOUTS.versionProbeMs,
348
+ killSignal: 'SIGKILL',
349
+ windowsHide: true,
350
+ windowsVerbatimArguments: true,
351
+ },
352
+ },
353
+ ]
354
+ : [
355
+ {
356
+ command: powershellPath,
357
+ args: ['-NoLogo', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', codexPath, '--version'],
358
+ options: {
359
+ cwd: root,
360
+ env,
361
+ encoding: 'utf-8',
362
+ timeout: CODEX_APP_SERVER_TIMEOUTS.versionProbeMs,
363
+ killSignal: 'SIGKILL',
364
+ windowsHide: true,
365
+ },
366
+ },
367
+ {
368
+ command: powershellPath,
369
+ args: ['-NoLogo', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', codexPath, '--version'],
370
+ options: {
371
+ cwd: root,
372
+ env,
373
+ encoding: 'utf-8',
374
+ timeout: CODEX_APP_SERVER_TIMEOUTS.versionProbeMs,
375
+ killSignal: 'SIGKILL',
376
+ windowsHide: true,
377
+ },
378
+ },
379
+ ]);
380
+ assert.deepEqual(appSpawns, extension === '.cmd'
381
+ ? [{
382
+ command: 'cmd.exe',
383
+ args: ['/d', '/s', '/c', `""${codexPath}" "app-server" "--stdio""`],
384
+ options: { cwd: root, env, stdio: 'pipe', windowsHide: true, windowsVerbatimArguments: true },
385
+ }]
386
+ : [{
387
+ command: powershellPath,
388
+ args: ['-NoLogo', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', codexPath, 'app-server', '--stdio'],
389
+ options: { cwd: root, env, stdio: 'pipe', windowsHide: true },
390
+ }]);
391
+ }
392
+ }
393
+ finally {
394
+ await rm(root, { recursive: true, force: true });
395
+ }
396
+ });
397
+ test('packed lifecycle bypasses an unrelated codex binary shadowing the pinned CLI', async () => {
398
+ if (process.platform === 'win32')
399
+ return;
400
+ const root = await mkdtemp(join(tmpdir(), 'omx-codex-path-'));
401
+ const shadowDir = join(root, 'shadow');
402
+ const pinnedDir = join(root, 'pinned');
403
+ try {
404
+ await Promise.all([
405
+ mkdir(shadowDir, { recursive: true }),
406
+ mkdir(pinnedDir, { recursive: true }),
407
+ ]);
408
+ const shadow = join(shadowDir, 'codex');
409
+ const pinned = join(pinnedDir, 'codex');
410
+ await Promise.all([
411
+ writeFile(shadow, '#!/bin/sh\necho "codex 0.2.3"\n'),
412
+ writeFile(pinned, '#!/bin/sh\necho "codex-cli 0.142.5"\n'),
413
+ ]);
414
+ await Promise.all([chmod(shadow, 0o755), chmod(pinned, 0o755)]);
415
+ assert.equal(probeCodexVersion(root, { PATH: `${shadowDir}${delimiter}${pinnedDir}` }), 'codex-cli 0.142.5');
416
+ }
417
+ finally {
418
+ await rm(root, { recursive: true, force: true });
419
+ }
420
+ });
421
+ test('packed lifecycle deduplicates repeated PATH entries before enforcing the candidate budget', async () => {
422
+ if (process.platform === 'win32')
423
+ return;
424
+ const root = await mkdtemp(join(tmpdir(), 'omx-codex-path-dedup-'));
425
+ const shadowDir = join(root, 'shadow');
426
+ const pinnedDir = join(root, 'pinned');
427
+ try {
428
+ await Promise.all([
429
+ mkdir(shadowDir, { recursive: true }),
430
+ mkdir(pinnedDir, { recursive: true }),
431
+ ]);
432
+ await Promise.all([
433
+ writeFile(join(shadowDir, 'codex'), '#!/bin/sh\necho "codex 0.2.3"\n'),
434
+ writeFile(join(pinnedDir, 'codex'), '#!/bin/sh\necho "codex-cli 0.142.5"\n'),
435
+ ]);
436
+ await Promise.all([
437
+ chmod(join(shadowDir, 'codex'), 0o755),
438
+ chmod(join(pinnedDir, 'codex'), 0o755),
439
+ ]);
440
+ assert.equal(probeCodexVersion(root, {
441
+ PATH: [...Array.from({ length: 40 }, () => shadowDir), pinnedDir].join(delimiter),
442
+ }), 'codex-cli 0.142.5');
443
+ }
444
+ finally {
445
+ await rm(root, { recursive: true, force: true });
446
+ }
447
+ });
448
+ test('packed lifecycle accepts only the exact stable pinned Codex version output', async () => {
449
+ if (process.platform === 'win32')
450
+ return;
451
+ const root = await mkdtemp(join(tmpdir(), 'omx-codex-version-'));
452
+ const candidateDir = join(root, 'candidate');
453
+ const executable = join(candidateDir, 'codex');
454
+ const candidates = [
455
+ { output: 'codex-cli 0.142.5', stderr: '', accepted: true },
456
+ { output: 'codex-cli 0.142.5', stderr: 'warning: harmless test diagnostic', accepted: true },
457
+ { output: 'codex-cli 0.142.5\nextra output', stderr: '', accepted: false },
458
+ { output: 'codex-cli 0.142.5-beta', stderr: '', accepted: false },
459
+ { output: 'codex-cli 0.142.5+meta', stderr: '', accepted: false },
460
+ { output: 'codex-cli 0.142.5.1', stderr: '', accepted: false },
461
+ { output: 'codex-cli v0.142.5', stderr: '', accepted: false },
462
+ { output: 'codex-cli 0-142-5', stderr: '', accepted: false },
463
+ { output: 'codex-cli 0.142.5', stderr: 'unexpected version probe error', accepted: false },
464
+ ];
465
+ try {
466
+ await mkdir(candidateDir, { recursive: true });
467
+ for (const candidate of candidates) {
468
+ await writeFile(executable, `#!/bin/sh\nprintf '%s\\n' '${candidate.output}'\n${candidate.stderr ? `printf '%s\\n' '${candidate.stderr}' >&2\n` : ''}`);
469
+ await chmod(executable, 0o755);
470
+ if (candidate.accepted) {
471
+ assert.equal(probeCodexVersion(root, { PATH: candidateDir }), 'codex-cli 0.142.5');
472
+ }
473
+ else {
474
+ assert.throws(() => probeCodexVersion(root, { PATH: candidateDir }), /Unsupported installed Codex version for the 0\.142\.5 boundary/);
475
+ }
476
+ }
477
+ }
478
+ finally {
479
+ await rm(root, { recursive: true, force: true });
480
+ }
481
+ });
482
+ test('packed lifecycle does not classify an installed Codex with a broken shebang as absent', async () => {
483
+ if (process.platform === 'win32')
484
+ return;
485
+ const root = await mkdtemp(join(tmpdir(), 'omx-codex-broken-shebang-'));
486
+ const candidateDir = join(root, 'candidate');
487
+ const executable = join(candidateDir, 'codex');
488
+ try {
489
+ await mkdir(candidateDir, { recursive: true });
490
+ await writeFile(executable, '#!/definitely-not-an-installed-interpreter\n');
491
+ await chmod(executable, 0o755);
492
+ assert.throws(() => probeCodexVersion(root, { PATH: candidateDir }), /launch returned ENOENT after an existing candidate was observed/);
493
+ }
494
+ finally {
495
+ await rm(root, { recursive: true, force: true });
496
+ }
497
+ });
498
+ test('packed lifecycle does not classify a dangling Codex candidate as absent', async () => {
499
+ if (process.platform === 'win32')
500
+ return;
501
+ const root = await mkdtemp(join(tmpdir(), 'omx-codex-dangling-'));
502
+ const candidateDir = join(root, 'candidate');
503
+ try {
504
+ await mkdir(candidateDir, { recursive: true });
505
+ await symlink(join(root, 'missing-codex'), join(candidateDir, 'codex'));
506
+ assert.throws(() => probeCodexVersion(root, { PATH: candidateDir }), /candidate exists but its target is unavailable/);
507
+ }
508
+ finally {
509
+ await rm(root, { recursive: true, force: true });
510
+ }
511
+ });
512
+ test('packed lifecycle does not classify a dangling PATH entry as absent', async () => {
513
+ if (process.platform === 'win32')
514
+ return;
515
+ const root = await mkdtemp(join(tmpdir(), 'omx-codex-dangling-path-'));
516
+ const danglingPathEntry = join(root, 'dangling-path-entry');
517
+ try {
518
+ await symlink(join(root, 'missing-path-entry'), danglingPathEntry);
519
+ assert.throws(() => probeCodexVersion(root, { PATH: danglingPathEntry }), /PATH entry exists but its target is unavailable/);
520
+ }
521
+ finally {
522
+ await rm(root, { recursive: true, force: true });
523
+ }
524
+ });
525
+ test('packed lifecycle bounds all slow Codex candidates with a global deadline', async () => {
526
+ if (process.platform === 'win32')
527
+ return;
528
+ const root = await mkdtemp(join(tmpdir(), 'omx-codex-global-deadline-'));
529
+ const slowDirs = ['slow-one', 'slow-two', 'slow-three'].map((name) => join(root, name));
530
+ try {
531
+ await Promise.all(slowDirs.map((dir) => mkdir(dir, { recursive: true })));
532
+ await Promise.all(slowDirs.map(async (dir) => {
533
+ const executable = join(dir, 'codex');
534
+ await writeFile(executable, '#!/bin/sh\nexec /bin/sleep 30\n');
535
+ await chmod(executable, 0o755);
536
+ }));
537
+ const startedAt = Date.now();
538
+ assert.throws(() => probeCodexVersion(root, { PATH: slowDirs.join(delimiter) }), /global deadline/);
539
+ assert.ok(Date.now() - startedAt < 6_500, 'the global version-resolution deadline must force termination');
540
+ }
541
+ finally {
542
+ await rm(root, { recursive: true, force: true });
543
+ }
544
+ });
545
+ test('packed lifecycle fails before probing the 33rd unique PATH candidate', async () => {
546
+ if (process.platform === 'win32')
547
+ return;
548
+ const root = await mkdtemp(join(tmpdir(), 'omx-codex-candidate-budget-'));
549
+ const candidateDirs = Array.from({ length: 33 }, (_value, index) => join(root, `candidate-${index}`));
550
+ const thirtyThirdProbe = join(root, '33rd-candidate-was-probed');
551
+ try {
552
+ await Promise.all(candidateDirs.map((dir) => mkdir(dir, { recursive: true })));
553
+ await Promise.all(candidateDirs.map(async (dir, index) => {
554
+ const executable = join(dir, 'codex');
555
+ await writeFile(executable, index === candidateDirs.length - 1
556
+ ? `#!/bin/sh\n: > ${JSON.stringify(thirtyThirdProbe)}\nprintf '%s\\n' 'codex-cli 0.142.5'\n`
557
+ : '#!/bin/sh\nexit 1\n');
558
+ await chmod(executable, 0o755);
559
+ }));
560
+ assert.throws(() => probeCodexVersion(root, { PATH: candidateDirs.join(delimiter) }), /32-candidate PATH budget/);
561
+ await assert.rejects(access(thirtyThirdProbe));
562
+ }
563
+ finally {
564
+ await rm(root, { recursive: true, force: true });
565
+ }
566
+ });
567
+ test('packed lifecycle continues after a timed-out version probe candidate', async () => {
568
+ if (process.platform === 'win32')
569
+ return;
570
+ const root = await mkdtemp(join(tmpdir(), 'omx-codex-timeout-'));
571
+ const slowDir = join(root, 'slow');
572
+ const pinnedDir = join(root, 'pinned');
573
+ try {
574
+ await Promise.all([mkdir(slowDir, { recursive: true }), mkdir(pinnedDir, { recursive: true })]);
575
+ await Promise.all([
576
+ writeFile(join(slowDir, 'codex'), '#!/bin/sh\nexec /bin/sleep 30\n'),
577
+ writeFile(join(pinnedDir, 'codex'), '#!/bin/sh\nprintf \'%s\\n\' \'codex-cli 0.142.5\'\n'),
578
+ ]);
579
+ await Promise.all([chmod(join(slowDir, 'codex'), 0o755), chmod(join(pinnedDir, 'codex'), 0o755)]);
580
+ const startedAt = Date.now();
581
+ assert.equal(probeCodexVersion(root, { PATH: `${slowDir}${delimiter}${pinnedDir}` }), 'codex-cli 0.142.5');
582
+ assert.ok(Date.now() - startedAt < 5_000, 'a timed-out candidate must not block later PATH candidates');
583
+ }
584
+ finally {
585
+ await rm(root, { recursive: true, force: true });
586
+ }
587
+ });
588
+ test('packed lifecycle fails instead of skipping when Codex disappears after start version validation', async () => {
589
+ if (process.platform === 'win32')
590
+ return;
591
+ const root = await mkdtemp(join(tmpdir(), 'omx-codex-disappears-after-version-'));
592
+ const candidateDir = join(root, 'candidate');
593
+ const executable = join(candidateDir, 'codex');
594
+ try {
595
+ await mkdir(candidateDir, { recursive: true });
596
+ await writeFile(executable, [
597
+ '#!/bin/sh',
598
+ 'state="${0}.version-count"',
599
+ 'if [ "$1" = "--version" ]; then',
600
+ ' if [ -f "$state" ]; then /bin/rm -f "$0"; else : > "$state"; fi',
601
+ ' printf \'%s\\n\' \'codex-cli 0.142.5\'',
602
+ ' exit 0',
603
+ 'fi',
604
+ 'exit 1',
605
+ '',
606
+ ].join('\n'));
607
+ await chmod(executable, 0o755);
608
+ assert.equal(probeCodexVersion(root, { PATH: candidateDir }), 'codex-cli 0.142.5');
609
+ await assert.rejects(CodexAppServer.start({ cwd: root, env: { PATH: candidateDir } }), (error) => {
610
+ assert.equal(error instanceof CodexExecutableNotFoundError, false);
611
+ assert.equal(error.code, 'ENOENT');
612
+ return true;
613
+ });
614
+ }
615
+ finally {
616
+ await rm(root, { recursive: true, force: true });
617
+ }
618
+ });
619
+ test('packed lifecycle preserves the true-absence Codex skip signal', async () => {
620
+ if (process.platform === 'win32')
621
+ return;
622
+ const root = await mkdtemp(join(tmpdir(), 'omx-codex-absent-'));
623
+ try {
624
+ assert.throws(() => probeCodexVersion(root, { PATH: root }), CodexExecutableNotFoundError);
625
+ }
626
+ finally {
627
+ await rm(root, { recursive: true, force: true });
628
+ }
629
+ });
630
+ test('packed lifecycle pre-seeds and preserves nested foreign group and handler coordinates', () => {
631
+ const marker = 'packed-foreign-fixture';
632
+ const seeded = appendForeignHookGroups('{"hooks":{}}', marker, { appendGroups: true });
633
+ const inserted = appendForeignHookGroups(seeded, marker, { appendGroups: false });
634
+ const snapshot = foreignHookGroupSnapshot(inserted, marker);
635
+ assert.equal(snapshot.length, 4);
636
+ assert.deepEqual(snapshot.map((entry) => ({
637
+ event: entry.event,
638
+ groupIndex: entry.groupIndex,
639
+ handlerIndex: entry.handlerIndex,
640
+ })), [
641
+ { event: 'PreToolUse', groupIndex: 0, handlerIndex: 0 },
642
+ { event: 'PreToolUse', groupIndex: 0, handlerIndex: 1 },
643
+ { event: 'PostToolUse', groupIndex: 0, handlerIndex: 0 },
644
+ { event: 'PostToolUse', groupIndex: 0, handlerIndex: 1 },
645
+ ]);
646
+ assert.match(inserted, /foreign_group_metadata/);
647
+ assert.match(inserted, /foreign_handler_metadata/);
648
+ });
649
+ test('display-order-stable foreign fixture preserves pre-approved group and handler coordinates', () => {
650
+ const marker = 'packed-display-order-fixture';
651
+ const seeded = appendDisplayOrderStableForeignHookGroups('{"hooks":{}}', marker, { appendGroups: true });
652
+ const inserted = appendDisplayOrderStableForeignHookGroups(seeded, marker, { appendGroups: false });
653
+ const snapshot = foreignHookGroupSnapshot(inserted, marker);
654
+ assert.deepEqual(snapshot.map((entry) => ({
655
+ event: entry.event,
656
+ groupIndex: entry.groupIndex,
657
+ handlerIndex: entry.handlerIndex,
658
+ })), [
659
+ { event: 'PreToolUse', groupIndex: 0, handlerIndex: 0 },
660
+ { event: 'PreToolUse', groupIndex: 1, handlerIndex: 0 },
661
+ { event: 'PreToolUse', groupIndex: 1, handlerIndex: 1 },
662
+ { event: 'PreToolUse', groupIndex: 1, handlerIndex: 2 },
663
+ ]);
664
+ });
18
665
  test('packed install smoke covers every installed native hook event with minimal payloads', () => {
19
666
  assert.deepEqual(PACKED_INSTALL_NATIVE_HOOK_SMOKE_EVENTS, [
20
667
  'SessionStart',
@@ -32,11 +679,208 @@ test('packed install smoke covers every installed native hook event with minimal
32
679
  assert.equal(payload.cwd, '/tmp/omx-packed-hook-smoke');
33
680
  }
34
681
  });
682
+ test('packed install smoke covers directive activation and terminal false-activation regressions', () => {
683
+ assert.deepEqual(PACKED_INSTALL_NATIVE_HOOK_REGRESSION_PROMPTS, [
684
+ { name: 'directive-use-ralplan', prompt: 'use $ralplan plan this', expectedSkill: 'ralplan', expectedStopBlock: true },
685
+ { name: 'directive-please-use-ralplan', prompt: 'please use $ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
686
+ { name: 'directive-run-ralplan', prompt: 'run $ralplan plan this', expectedSkill: 'ralplan', expectedStopBlock: true },
687
+ { name: 'directive-list-use-ralplan', prompt: '- use $ralplan plan this', expectedSkill: 'ralplan', expectedStopBlock: true },
688
+ { name: 'directive-documentation-then-command', prompt: 'use $ralplan is the consensus-planning command\n$autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
689
+ { name: 'directive-documentation-then-implicit-command', prompt: 'use $ralplan is the consensus-planning command\nUse autopilot mode.', expectedSkill: 'autopilot', expectedStopBlock: true },
690
+ { name: 'directive-documentation-trailing-prose-then-command', prompt: 'use $ralplan is the workflow command for planning\n$autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
691
+ { name: 'directive-documentation-implicit-prose', prompt: 'use $ralplan is the workflow command for autopilot mode', expectedSkill: null, expectedStopBlock: false },
692
+ { name: 'directive-documentation-alias-prose', prompt: 'use $ralplan is the consensus-planning command\nAutopilot mode is its alias.', expectedSkill: null, expectedStopBlock: false },
693
+ { name: 'directive-coordinated-documentation-then-command', prompt: '- use $ralplan and $autopilot are workflow commands\n$ralph execute it', expectedSkill: 'ralph', expectedStopBlock: true },
694
+ { name: 'directive-documentation-semicolon-directive', prompt: 'use $ralplan is the consensus-planning command; use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
695
+ { 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 },
696
+ { name: 'directive-documentation-embedded-token-then-command', prompt: 'use $ralplan is the workflow command for $team\n$autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
697
+ { name: 'doc-task-noun', prompt: 'use $ralplan is the workflow command; use $autopilot update the documentation', expectedSkill: 'autopilot', expectedStopBlock: true },
698
+ { name: 'doc-implicit-followup', prompt: 'use $ralplan is the consensus-planning command; use autopilot mode.', expectedSkill: 'autopilot', expectedStopBlock: true },
699
+ { name: 'doc-transition-followup', prompt: 'use $ralplan is the consensus-planning command; then use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
700
+ { name: 'doc-explicit-alias', prompt: 'use $ralplan is the consensus-planning command; $team is its alias', expectedSkill: null, expectedStopBlock: false },
701
+ { 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 },
702
+ { name: 'doc-fullwidth-separator', prompt: 'use $ralplan,$autopilot are workflow commands', expectedSkill: null, expectedStopBlock: false },
703
+ { name: 'doc-compact-slash', prompt: 'use $ralplan/$autopilot are workflow commands\n$ralph execute it', expectedSkill: 'ralph', expectedStopBlock: true },
704
+ { name: 'reference-prompts-followup', prompt: '[docs]: /target "title\nUse /prompts:architect"\n$ralph execute it', expectedSkill: 'ralph', expectedStopBlock: true },
705
+ { name: 'doc-period-implicit', prompt: 'use $ralplan is the consensus-planning command. Use autopilot mode.', expectedSkill: 'autopilot', expectedStopBlock: true },
706
+ { name: 'doc-bare-implicit', prompt: 'use $ralplan is the consensus-planning command; autopilot mode.', expectedSkill: 'autopilot', expectedStopBlock: true },
707
+ { name: 'doc-fullwidth-semicolon', prompt: 'use $ralplan is the consensus-planning command; use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
708
+ { name: 'doc-but-followup', prompt: 'use $ralplan is the workflow command; but use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
709
+ { name: 'doc-fullwidth-oxford', prompt: 'use $ralplan, $autopilot, and $team are workflow commands', expectedSkill: null, expectedStopBlock: false },
710
+ { name: 'reference-zero-title', prompt: '[docs]: ./target\n(autopilot mode)', expectedSkill: null, expectedStopBlock: false },
711
+ { name: 'doc-also-alias-explicit', prompt: 'use $ralplan is the consensus-planning command; $team is also its alias', expectedSkill: null, expectedStopBlock: false },
712
+ { name: 'doc-also-alias-implicit', prompt: 'use $ralplan is the consensus-planning command\nAutopilot mode is also its alias.', expectedSkill: null, expectedStopBlock: false },
713
+ { name: 'doc-embedded-mention', prompt: 'use $ralplan is the workflow command; $team appears in the documentation.', expectedSkill: null, expectedStopBlock: false },
714
+ { name: 'chained-negation', prompt: '$ralplan; $autopilot is prohibited', expectedSkill: 'ralplan', expectedStopBlock: true, expectedDeferredSkills: [] },
715
+ { name: 'long-negation', prompt: `$ralplan; $autopilot${' '.repeat(193)}is prohibited`, expectedSkill: 'ralplan', expectedStopBlock: true, expectedDeferredSkills: [] },
716
+ { name: 'doc-arabic-comma', prompt: 'use $ralplan، $autopilot are workflow commands', expectedSkill: null, expectedStopBlock: false },
717
+ { name: 'arabic-negation', prompt: '$ralplan، $autopilot are prohibited', expectedSkill: null, expectedStopBlock: false },
718
+ { name: 'implicit-arabic-negation', prompt: 'Autopilot mode، deep interview are prohibited.', expectedSkill: null, expectedStopBlock: false },
719
+ { name: 'fullwidth-frame-reset', prompt: 'For instance: manual mode is slower。 Use autopilot mode.', expectedSkill: 'autopilot', expectedStopBlock: true },
720
+ { name: 'doc-abbreviation', prompt: 'use $ralplan is the workflow command, e.g. use $autopilot in examples.', expectedSkill: null, expectedStopBlock: false },
721
+ { name: 'implicit-doc-mention', prompt: 'use $ralplan is the workflow command; autopilot mode appears in the documentation.', expectedSkill: null, expectedStopBlock: false },
722
+ { name: 'implicit-doc-chain', prompt: 'use $ralplan is the workflow command; autopilot mode is its alias; $ralph execute it', expectedSkill: 'ralph', expectedStopBlock: true },
723
+ { name: 'long-command-gap', prompt: `use $ralplan is the workflow command; use${' '.repeat(161)}$autopilot build it`, expectedSkill: 'autopilot', expectedStopBlock: true },
724
+ { name: 'ideographic-negation', prompt: '$ralplan、 $autopilot are prohibited', expectedSkill: null, expectedStopBlock: false },
725
+ { name: 'implicit-ideo-negation', prompt: 'Autopilot mode、 deep interview are prohibited.', expectedSkill: null, expectedStopBlock: false },
726
+ { name: 'doc-exclamation', prompt: 'use $ralplan is the consensus-planning command! run $autopilot', expectedSkill: 'autopilot', expectedStopBlock: true },
727
+ { name: 'doc-fullwidth-question', prompt: 'use $ralplan is the consensus-planning command? run $autopilot', expectedSkill: 'autopilot', expectedStopBlock: true },
728
+ { name: 'implicit-doc-predecessor', prompt: 'Autopilot mode is workflow documentation.\n$ralph execute it', expectedSkill: 'ralph', expectedStopBlock: true },
729
+ { name: 'confusable-use-verb', prompt: 'uſe $ralplan plan it', expectedSkill: null, expectedStopBlock: false },
730
+ { name: 'confusable-please', prompt: 'pleaſe use $ralplan plan it', expectedSkill: null, expectedStopBlock: false },
731
+ { name: 'confusable-prompts-token-then-command', prompt: '/promptſ:architect; use autopilot mode.', expectedSkill: 'autopilot', expectedStopBlock: true },
732
+ { name: 'reserved-em-dash-boundary', prompt: '/prompts:architect— use autopilot mode', expectedSkill: null, expectedStopBlock: false },
733
+ { name: 'reserved-fullwidth-comma-boundary', prompt: '/prompts:architect, use autopilot mode', expectedSkill: null, expectedStopBlock: false },
734
+ { name: 'confusable-implicit-verb', prompt: 'Do not use deep interview but uſe autopilot mode.', expectedSkill: null, expectedStopBlock: false },
735
+ { name: 'frame-fullwidth-colon', prompt: 'For instance: use autopilot mode.', expectedSkill: null, expectedStopBlock: false },
736
+ { name: 'frame-fullwidth-comma', prompt: 'For instance, use autopilot mode.', expectedSkill: null, expectedStopBlock: false },
737
+ { name: 'frame-arabic-comma', prompt: 'For instance، use autopilot mode.', expectedSkill: null, expectedStopBlock: false },
738
+ { name: 'frame-ideo-comma', prompt: 'For instance、 use autopilot mode.', expectedSkill: null, expectedStopBlock: false },
739
+ { name: 'doc-explicit-documented', prompt: 'use $ralplan is the workflow command; $autopilot is documented in the guide.', expectedSkill: null, expectedStopBlock: false },
740
+ { name: 'doc-explicit-described', prompt: '$autopilot is described in the manual.', expectedSkill: null, expectedStopBlock: false },
741
+ { name: 'doc-comma-followup', prompt: 'use $ralplan is the workflow command, but use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
742
+ { name: 'doc-fw-comma-followup', prompt: 'use $ralplan is the workflow command, but use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
743
+ { name: 'doc-arabic-followup', prompt: 'use $ralplan is the workflow command، but use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
744
+ { name: 'doc-ideo-followup', prompt: 'use $ralplan is the workflow command、 but use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
745
+ { name: 'neg-arabic-followup', prompt: 'Do not run $ralplan، use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
746
+ { name: 'neg-ideo-followup', prompt: 'Do not run $ralplan、 use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
747
+ { name: 'implicit-doc-prefix-next', prompt: 'The docs mention autopilot mode.\n$ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
748
+ { name: 'implicit-doc-prefix-line', prompt: 'The docs mention autopilot mode; use $ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
749
+ { name: 'implicit-doc-subject-line', prompt: 'Autopilot mode is workflow documentation; use $ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
750
+ { name: 'compact-explicit-negation', prompt: '$ralplan,$autopilot are prohibited', expectedSkill: null, expectedStopBlock: false },
751
+ { name: 'compact-implicit-negation', prompt: 'Autopilot mode،deep interview are prohibited.', expectedSkill: null, expectedStopBlock: false },
752
+ { name: 'doc-clause-local-prefix', prompt: '$ralplan; $autopilot is documented in the guide.', expectedSkill: 'ralplan', expectedStopBlock: true },
753
+ { 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 },
754
+ { name: 'doc-chain-workflow', prompt: 'use $ralplan is the workflow command; autopilot mode is workflow documentation; use $ralph execute it', expectedSkill: 'ralph', expectedStopBlock: true },
755
+ { name: 'ref-inline-explicit', prompt: '[docs]: $ralplan\n$autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
756
+ { name: 'ref-inline-prompts', prompt: '[docs]: /prompts:architect\n$autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
757
+ { name: 'list-fullwidth-explicit-doc', prompt: '- $ralplan: consensus-planning workflow', expectedSkill: null, expectedStopBlock: false },
758
+ { name: 'list-fullwidth-implicit-doc', prompt: '- autopilot mode: autonomous workflow command', expectedSkill: null, expectedStopBlock: false },
759
+ { name: 'possessive-straight', prompt: "$ralplan's workflow is documented", expectedSkill: null, expectedStopBlock: false },
760
+ { name: 'possessive-curly', prompt: '$ralplan’s workflow is documented', expectedSkill: null, expectedStopBlock: false },
761
+ { name: 'possessive-fullwidth', prompt: '$ralplan's workflow is documented', expectedSkill: null, expectedStopBlock: false },
762
+ { name: 'possessive-prompts', prompt: "/prompts:architect's syntax is documented", expectedSkill: null, expectedStopBlock: false },
763
+ { name: 'malformed-prefix-kata', prompt: '$・autopilot mode', expectedSkill: null, expectedStopBlock: false },
764
+ { name: 'malformed-prefix-half', prompt: '$・autopilot mode', expectedSkill: null, expectedStopBlock: false },
765
+ { name: 'malformed-prefix-arabic', prompt: '$٪autopilot mode', expectedSkill: null, expectedStopBlock: false },
766
+ { name: 'malformed-prefix-division', prompt: '$∕autopilot mode', expectedSkill: null, expectedStopBlock: false },
767
+ { name: 'doc-but-directive', prompt: 'use $autopilot is documented but use $ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
768
+ { name: 'list-directive-fw-colon', prompt: '- use $ralplan: consensus-planning workflow', expectedSkill: null, expectedStopBlock: false },
769
+ { name: 'doc-arabic-question', prompt: 'use $ralplan is the workflow command؟ run $autopilot', expectedSkill: 'autopilot', expectedStopBlock: true },
770
+ { name: 'arabic-semicolon-negation', prompt: '$ralplan؛ $autopilot is prohibited', expectedSkill: 'ralplan', expectedStopBlock: true, expectedDeferredSkills: [] },
771
+ { name: 'confusable-postposed-transition', prompt: '$ralplan is prohibited but uſe autopilot mode.', expectedSkill: null, expectedStopBlock: false },
772
+ { name: 'mixed-negation', prompt: 'Autopilot mode and $ralplan are prohibited.', expectedSkill: null, expectedStopBlock: false },
773
+ { name: 'both-mixed-negation', prompt: 'Both autopilot mode and $ralplan are prohibited.', expectedSkill: null, expectedStopBlock: false },
774
+ { name: 'mixed-documentation', prompt: 'use $ralplan and autopilot mode are workflow commands', expectedSkill: null, expectedStopBlock: false },
775
+ { name: 'prose-doc-no-reopen', prompt: '$ralplan is prohibited because docs use $autopilot.', expectedSkill: null, expectedStopBlock: false },
776
+ { name: 'neg-fw-dot-reopen', prompt: 'Do not run $ralplan. use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
777
+ { name: 'neg-greek-q-reopen', prompt: 'Do not run $ralplan; use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
778
+ { name: 'unicode-attached-contrast', prompt: 'Do not use deep interview яbut use autopilot mode.', expectedSkill: null, expectedStopBlock: false },
779
+ { name: 'prefix-list-followup', prompt: 'Do not run $ralplan, $autopilot; use $team execute it', expectedSkill: 'team', expectedStopBlock: false, insideTmux: true },
780
+ { name: 'mixed-postposed-chain', prompt: '$ralplan, autopilot mode, $team are prohibited.', expectedSkill: null, expectedStopBlock: false },
781
+ { name: 'implicit-first-doc-chain', prompt: 'Autopilot mode and $ralplan are workflow commands; use $team execute it', expectedSkill: 'team', expectedStopBlock: false, insideTmux: true },
782
+ { name: 'both-mixed-doc-followup', prompt: 'Both autopilot mode and $ralplan are workflow commands; use $team execute it', expectedSkill: 'team', expectedStopBlock: false, insideTmux: true },
783
+ { name: 'doc-semicolon-preserves-earlier', prompt: 'Use autopilot mode; use $ralplan is the workflow command.', expectedSkill: 'autopilot', expectedStopBlock: true },
784
+ { name: 'doc-independent-comma', prompt: 'Use autopilot mode, and $ralplan is documented in the guide.', expectedSkill: 'autopilot', expectedStopBlock: true },
785
+ { name: 'reference-unclosed-quote-destination', prompt: '[docs]: "target\n$autopilot build it', expectedSkill: null, expectedStopBlock: false },
786
+ { name: 'reference-unclosed-inline-destination', prompt: '[docs]: `target\n$autopilot build it', expectedSkill: null, expectedStopBlock: false },
787
+ { name: 'mixed-prefix-negation-implicit', prompt: 'Do not run $ralplan and use autopilot mode.', expectedSkill: null, expectedStopBlock: false },
788
+ { name: 'repeated-postposed-followup', prompt: '$team is prohibited and is forbidden; use $ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
789
+ { name: 'doc-preserves-earlier', prompt: 'Use autopilot mode; "note"; use $ralplan is the workflow command.', expectedSkill: 'autopilot', expectedStopBlock: true },
790
+ { name: 'doc-colon-followup', prompt: 'use $ralplan is the workflow command: use $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
791
+ { name: 'table-followup', prompt: 'Mode | Meaning\n--- | ---\nmanual | documentation\n$ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
792
+ { name: 'neg-advance-reopen', prompt: 'Do not run $ralplan but advance to $ultragoal', expectedSkill: 'ultragoal', expectedStopBlock: false },
793
+ { name: 'neg-jump-reopen', prompt: 'Do not run $ralplan but jump straight to $ultragoal', expectedSkill: 'ultragoal', expectedStopBlock: false },
794
+ { name: 'reference-plain-title', prompt: '[docs]: /target "title\nplain text"\n$ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
795
+ { name: 'reference-plain-destination', prompt: '[docs]: ./target\n$ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
796
+ { name: 'directive-use-the', prompt: 'Do not run $ralplan; use the $autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true },
797
+ { name: 'directive-continue-after-quote', prompt: '"quoted"\ncontinue with $ralplan', expectedSkill: 'ralplan', expectedStopBlock: true },
798
+ { name: 'doc-advance-followup', prompt: 'use $ralplan is the workflow command; advance to $ultragoal', expectedSkill: 'ultragoal', expectedStopBlock: false },
799
+ { name: 'directive-run-code-review', prompt: 'run $code-review', expectedSkill: 'code-review', expectedStopBlock: false },
800
+ { name: 'directive-documentation', prompt: 'use $ralplan is the consensus-planning command', expectedSkill: null, expectedStopBlock: false },
801
+ { name: 'nested-bounded-child-unbounded-parent', prompt: '"`x`\n$ralplan plan it', expectedSkill: null, expectedStopBlock: false },
802
+ { name: 'first-contiguous-block-terminal', prompt: '$ralplan plan it\n"x"\n$autopilot build it', expectedSkill: 'ralplan', expectedStopBlock: true, expectedDeferredSkills: [] },
803
+ { name: 'leading-reserved-dominance', prompt: '/prompts:architect\n"x"\n$ralplan plan it', expectedSkill: null, expectedStopBlock: false },
804
+ { name: 'list-fence-root-opener', prompt: '- ```\n sample\n```\n$ralplan plan it', expectedSkill: null, expectedStopBlock: false },
805
+ { name: 'list-fence-relative-closer', prompt: '- ```\n sample\n ```\n$ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
806
+ { name: 'reference-multiline-title-explicit', prompt: '[docs]: /target "title\nuse /prompts:architect\n$ralplan plan it"', expectedSkill: null, expectedStopBlock: false },
807
+ { name: 'reference-multiline-title-implicit', prompt: '[docs]: /target "title\nuse autopilot mode"', expectedSkill: null, expectedStopBlock: false },
808
+ { name: 'reference-next-line-title', prompt: '[docs]: ./target\n (autopilot mode)', expectedSkill: null, expectedStopBlock: false },
809
+ { name: 'reference-next-line-destination-title', prompt: '[docs]:\n ./target\n (autopilot mode)', expectedSkill: null, expectedStopBlock: false },
810
+ { name: 'kelvin-case-fold-suffix', prompt: '$ultraworK execute', expectedSkill: null, expectedStopBlock: false },
811
+ { name: 'katakana-middle-dot-suffix', prompt: '$ralplan・suffix plan it', expectedSkill: null, expectedStopBlock: false },
812
+ { name: 'halfwidth-middle-dot-suffix', prompt: '$ralplan・suffix plan it', expectedSkill: null, expectedStopBlock: false },
813
+ { name: 'arabic-percent-suffix', prompt: '$ralplan٪docs', expectedSkill: null, expectedStopBlock: false },
814
+ { name: 'division-slash-suffix', prompt: '$ralplan∕config', expectedSkill: null, expectedStopBlock: false },
815
+ { name: 'unclosed-prompts-quote', prompt: '"Use /prompts:architect\n$ralplan plan it', expectedSkill: null, expectedStopBlock: false },
816
+ { name: 'malformed-prompts-suffix', prompt: '/prompts:architect한글\n$ralplan plan it', expectedSkill: null, expectedStopBlock: false },
817
+ { name: 'stale-predecessor-prose-directive', prompt: '> quoted context\nProse\n$ralplan implement this', expectedSkill: null, expectedStopBlock: false },
818
+ { name: 'stale-predecessor-reserved-directive', prompt: '> quoted context\n/prompts:architect\n$ralplan plan this', expectedSkill: null, expectedStopBlock: false },
819
+ { name: 'stale-predecessor-first-block-terminal', prompt: '> quoted context\n$ralplan plan it\nLater discussion.\n$autopilot build it', expectedSkill: 'ralplan', expectedStopBlock: true, expectedDeferredSkills: [] },
820
+ { name: 'stale-predecessor-unclosed-after-negation', prompt: 'Do not run $ralplan.\n"unclosed context\n$autopilot build it', expectedSkill: null, expectedStopBlock: false },
821
+ { name: 'stale-predecessor-reference-unclosed', prompt: '[$ralplan]: ./docs\n"unclosed context\n$autopilot build it', expectedSkill: null, expectedStopBlock: false },
822
+ { name: 'stale-directive-clause-prefix', prompt: '> quoted context\nProse\nUse $ralplan plan this', expectedSkill: null, expectedStopBlock: false },
823
+ { name: 'nested-reserved-predecessor-successor', prompt: '- Use /prompts:architect.\n$ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
824
+ { name: 'nested-fence-predecessor-successor', prompt: '- - ```\n quoted context\n ```\n$ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
825
+ { name: 'reference-multiline-destination-implicit', prompt: '[docs]:\nautopilot', expectedSkill: null, expectedStopBlock: false },
826
+ { name: 'middle-dot-suffix', prompt: '$ralplan·suffix plan it', expectedSkill: null, expectedStopBlock: false },
827
+ { name: 'percent-suffix', prompt: '$ralplan%docs', expectedSkill: null, expectedStopBlock: false },
828
+ { name: 'fullwidth-percent-suffix', prompt: '$ralplan%docs', expectedSkill: null, expectedStopBlock: false },
829
+ { name: 'g1a-ordered-multi-skill', prompt: '$ralplan, $autopilot; $team', expectedSkill: 'ralplan', expectedStopBlock: true, expectedDeferredSkills: ['autopilot', 'team'], expectedActiveSkills: ['ralplan'], insideTmux: true },
830
+ { name: 'g1c-duplicate-alias', prompt: '$autopilot $oh-my-codex:autopilot build it', expectedSkill: 'autopilot', expectedStopBlock: true, expectedDeferredSkills: [], expectedActiveSkills: ['autopilot'] },
831
+ { name: 'b3-longer-valid-fence', prompt: '```text\n$autopilot build it\n````\n$ralplan plan it', expectedSkill: 'ralplan', expectedStopBlock: true },
832
+ { name: 'b4-shorter-invalid-fence', prompt: '````text\n$autopilot build it\n```\n$ralplan plan it', expectedSkill: null, expectedStopBlock: false },
833
+ { name: 'b5-different-marker-invalid-fence', prompt: '```text\n$autopilot build it\n~~~\n$ralplan plan it', expectedSkill: null, expectedStopBlock: false },
834
+ ]);
835
+ });
836
+ test('packed regression environment clears inherited Team routing state', () => {
837
+ const environment = buildPackedRegressionEnvironment({ name: 'poisoned-team-case', insideTmux: true }, {
838
+ OMX_ROOT: '/tmp/poison-root',
839
+ OMX_STATE_ROOT: '/tmp/poison-state',
840
+ OMX_TEAM_STATE_ROOT: '/tmp/poison-team-state',
841
+ OMX_SESSION_ID: 'poison-session',
842
+ CODEX_SESSION_ID: 'poison-codex-session',
843
+ SESSION_ID: 'poison-generic-session',
844
+ OMX_TEAM_WORKER: 'poison/worker-1',
845
+ OMX_TEAM_INTERNAL_WORKER: 'poison/worker-2',
846
+ OMX_TEAM_LEADER_CWD: '/tmp/poison-leader',
847
+ OMX_TEAM_MODE: 'disabled',
848
+ OMX_QUESTION_RETURN_PANE: '%1',
849
+ OMX_LEADER_PANE_ID: '%2',
850
+ OMX_TMUX_HUD_OWNER: '1',
851
+ TMUX: '/tmp/poison-tmux',
852
+ TMUX_PANE: '%9',
853
+ });
854
+ assert.equal(environment.OMX_ROOT, '');
855
+ assert.equal(environment.OMX_STATE_ROOT, '');
856
+ assert.equal(environment.OMX_TEAM_STATE_ROOT, '');
857
+ assert.equal(environment.OMX_TEAM_WORKER, '');
858
+ assert.equal(environment.OMX_TEAM_INTERNAL_WORKER, '');
859
+ assert.equal(environment.OMX_TEAM_LEADER_CWD, '');
860
+ assert.equal(environment.OMX_TEAM_MODE, 'enabled');
861
+ assert.equal(environment.TMUX, '/tmp/tmux-pr3140-regression');
862
+ assert.equal(environment.TMUX_PANE, '%3140');
863
+ });
35
864
  test('packed install native hook stdout validation allows empty or JSON output only', () => {
36
865
  assert.doesNotThrow(() => validateHookStdout('PostCompact', ''));
37
866
  assert.doesNotThrow(() => validateHookStdout('Stop', '{}\n'));
38
867
  assert.throws(() => validateHookStdout('UserPromptSubmit', '{not json'), /native hook UserPromptSubmit emitted invalid JSON stdout/);
39
868
  });
869
+ test('packed lifecycle and native hook smoke share the seven managed event names', () => {
870
+ assert.deepEqual(MANAGED_CODEX_HOOK_EVENTS, PACKED_INSTALL_NATIVE_HOOK_SMOKE_EVENTS);
871
+ });
872
+ test('packed install native hook stdout validation enforces event output contracts', () => {
873
+ assert.doesNotThrow(() => validateHookStdout('PostCompact', ''));
874
+ assert.doesNotThrow(() => validateHookStdout('Stop', '{}\n'));
875
+ assert.throws(() => validateHookStdout('PostCompact', '{}\n'), /PostCompact must emit empty stdout/);
876
+ for (const value of ['\n', ' ', ' \r\n']) {
877
+ assert.throws(() => validateHookStdout('PostCompact', value), /PostCompact must emit empty stdout/);
878
+ }
879
+ for (const value of ['null', '[]', '"text"', '1']) {
880
+ assert.throws(() => validateHookStdout('Stop', value), /non-object JSON stdout payload/);
881
+ }
882
+ assert.throws(() => validateHookStdout('UserPromptSubmit', '{not json'), /native hook UserPromptSubmit emitted invalid JSON stdout/);
883
+ });
40
884
  test('parseNpmPackJsonOutput ignores prepack logs before npm pack JSON', () => {
41
885
  const parsed = parseNpmPackJsonOutput([
42
886
  '[sync-plugin-mirror] synced 29 canonical skill directories and plugin metadata',