oh-my-codex 0.21.0 → 0.21.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 (346) hide show
  1. package/Cargo.toml +1 -1
  2. package/README.md +4 -2
  3. package/dist/cli/__tests__/detached-launch-diagnostics-3578.test.d.ts +2 -0
  4. package/dist/cli/__tests__/detached-launch-diagnostics-3578.test.d.ts.map +1 -0
  5. package/dist/cli/__tests__/detached-launch-diagnostics-3578.test.js +592 -0
  6. package/dist/cli/__tests__/detached-launch-diagnostics-3578.test.js.map +1 -0
  7. package/dist/cli/__tests__/detached-leader-teardown.test.js +270 -33
  8. package/dist/cli/__tests__/detached-leader-teardown.test.js.map +1 -1
  9. package/dist/cli/__tests__/doctor-warning-copy.test.js +133 -5
  10. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  11. package/dist/cli/__tests__/index.test.js +543 -2
  12. package/dist/cli/__tests__/index.test.js.map +1 -1
  13. package/dist/cli/__tests__/issue-windows-cancel-identity.test.d.ts +2 -0
  14. package/dist/cli/__tests__/issue-windows-cancel-identity.test.d.ts.map +1 -0
  15. package/dist/cli/__tests__/issue-windows-cancel-identity.test.js +17 -0
  16. package/dist/cli/__tests__/issue-windows-cancel-identity.test.js.map +1 -0
  17. package/dist/cli/__tests__/launch-fallback.test.js +15 -3
  18. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  19. package/dist/cli/__tests__/package-bin-contract.test.js +20 -0
  20. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  21. package/dist/cli/__tests__/plugin-cache-stale-lock.test.d.ts +2 -0
  22. package/dist/cli/__tests__/plugin-cache-stale-lock.test.d.ts.map +1 -0
  23. package/dist/cli/__tests__/plugin-cache-stale-lock.test.js +76 -0
  24. package/dist/cli/__tests__/plugin-cache-stale-lock.test.js.map +1 -0
  25. package/dist/cli/__tests__/plugin-cache-symlink-provenance-3552.test.d.ts +2 -0
  26. package/dist/cli/__tests__/plugin-cache-symlink-provenance-3552.test.d.ts.map +1 -0
  27. package/dist/cli/__tests__/plugin-cache-symlink-provenance-3552.test.js +1662 -0
  28. package/dist/cli/__tests__/plugin-cache-symlink-provenance-3552.test.js.map +1 -0
  29. package/dist/cli/__tests__/plugin-launcher-provenance-3558.test.d.ts +2 -0
  30. package/dist/cli/__tests__/plugin-launcher-provenance-3558.test.d.ts.map +1 -0
  31. package/dist/cli/__tests__/plugin-launcher-provenance-3558.test.js +522 -0
  32. package/dist/cli/__tests__/plugin-launcher-provenance-3558.test.js.map +1 -0
  33. package/dist/cli/__tests__/ralplan-advisory.test.d.ts +2 -0
  34. package/dist/cli/__tests__/ralplan-advisory.test.d.ts.map +1 -0
  35. package/dist/cli/__tests__/ralplan-advisory.test.js +301 -0
  36. package/dist/cli/__tests__/ralplan-advisory.test.js.map +1 -0
  37. package/dist/cli/__tests__/ralplan.test.js +39 -0
  38. package/dist/cli/__tests__/ralplan.test.js.map +1 -1
  39. package/dist/cli/__tests__/resume.test.js +81 -6
  40. package/dist/cli/__tests__/resume.test.js.map +1 -1
  41. package/dist/cli/__tests__/session-scoped-runtime.test.js +423 -4
  42. package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
  43. package/dist/cli/__tests__/setup-install-mode.test.js +60 -3
  44. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  45. package/dist/cli/__tests__/setup-refresh.test.js +9 -0
  46. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  47. package/dist/cli/__tests__/sparkshell-cli.test.js +4 -0
  48. package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
  49. package/dist/cli/__tests__/ultragoal.test.js +71 -0
  50. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  51. package/dist/cli/__tests__/windows-popup-loop-contract.test.js +30 -4
  52. package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
  53. package/dist/cli/doctor.d.ts.map +1 -1
  54. package/dist/cli/doctor.js +57 -8
  55. package/dist/cli/doctor.js.map +1 -1
  56. package/dist/cli/index.d.ts +113 -7
  57. package/dist/cli/index.d.ts.map +1 -1
  58. package/dist/cli/index.js +1850 -185
  59. package/dist/cli/index.js.map +1 -1
  60. package/dist/cli/native-assets.d.ts +2 -1
  61. package/dist/cli/native-assets.d.ts.map +1 -1
  62. package/dist/cli/native-assets.js +7 -7
  63. package/dist/cli/native-assets.js.map +1 -1
  64. package/dist/cli/plugin-marketplace.d.ts +89 -4
  65. package/dist/cli/plugin-marketplace.d.ts.map +1 -1
  66. package/dist/cli/plugin-marketplace.js +2135 -118
  67. package/dist/cli/plugin-marketplace.js.map +1 -1
  68. package/dist/cli/ralplan.d.ts +2 -1
  69. package/dist/cli/ralplan.d.ts.map +1 -1
  70. package/dist/cli/ralplan.js +149 -9
  71. package/dist/cli/ralplan.js.map +1 -1
  72. package/dist/cli/setup.d.ts.map +1 -1
  73. package/dist/cli/setup.js +7 -1
  74. package/dist/cli/setup.js.map +1 -1
  75. package/dist/cli/sparkshell.d.ts.map +1 -1
  76. package/dist/cli/sparkshell.js +9 -10
  77. package/dist/cli/sparkshell.js.map +1 -1
  78. package/dist/cli/update-worker.js +9 -0
  79. package/dist/cli/update-worker.js.map +1 -1
  80. package/dist/cli/update.d.ts +1 -0
  81. package/dist/cli/update.d.ts.map +1 -1
  82. package/dist/cli/update.js +9 -0
  83. package/dist/cli/update.js.map +1 -1
  84. package/dist/config/__tests__/generator-idempotent.test.js +118 -0
  85. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  86. package/dist/config/generator.d.ts.map +1 -1
  87. package/dist/config/generator.js +74 -4
  88. package/dist/config/generator.js.map +1 -1
  89. package/dist/hooks/__tests__/keyword-detector.test.js +74 -0
  90. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  91. package/dist/hooks/__tests__/ralplan-advisory-keyword.test.d.ts +2 -0
  92. package/dist/hooks/__tests__/ralplan-advisory-keyword.test.d.ts.map +1 -0
  93. package/dist/hooks/__tests__/ralplan-advisory-keyword.test.js +250 -0
  94. package/dist/hooks/__tests__/ralplan-advisory-keyword.test.js.map +1 -0
  95. package/dist/hooks/__tests__/session.test.js +10 -1
  96. package/dist/hooks/__tests__/session.test.js.map +1 -1
  97. package/dist/hooks/keyword-detector.d.ts +8 -1
  98. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  99. package/dist/hooks/keyword-detector.js +130 -9
  100. package/dist/hooks/keyword-detector.js.map +1 -1
  101. package/dist/hooks/session.d.ts +5 -0
  102. package/dist/hooks/session.d.ts.map +1 -1
  103. package/dist/hooks/session.js +17 -3
  104. package/dist/hooks/session.js.map +1 -1
  105. package/dist/hud/__tests__/index.test.js +16 -1
  106. package/dist/hud/__tests__/index.test.js.map +1 -1
  107. package/dist/hud/__tests__/reconcile.test.js +134 -7
  108. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  109. package/dist/hud/__tests__/render.test.js +57 -0
  110. package/dist/hud/__tests__/render.test.js.map +1 -1
  111. package/dist/hud/__tests__/session-attached.test.d.ts +2 -0
  112. package/dist/hud/__tests__/session-attached.test.d.ts.map +1 -0
  113. package/dist/hud/__tests__/session-attached.test.js +82 -0
  114. package/dist/hud/__tests__/session-attached.test.js.map +1 -0
  115. package/dist/hud/__tests__/state.test.js +94 -2
  116. package/dist/hud/__tests__/state.test.js.map +1 -1
  117. package/dist/hud/__tests__/tmux-split-realtmux.test.js +169 -2
  118. package/dist/hud/__tests__/tmux-split-realtmux.test.js.map +1 -1
  119. package/dist/hud/__tests__/tmux.test.js +104 -22
  120. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  121. package/dist/hud/__tests__/types.test.js +9 -0
  122. package/dist/hud/__tests__/types.test.js.map +1 -1
  123. package/dist/hud/__tests__/watch-detached.test.d.ts +2 -0
  124. package/dist/hud/__tests__/watch-detached.test.d.ts.map +1 -0
  125. package/dist/hud/__tests__/watch-detached.test.js +383 -0
  126. package/dist/hud/__tests__/watch-detached.test.js.map +1 -0
  127. package/dist/hud/__tests__/watch-realtmux.test.d.ts +2 -0
  128. package/dist/hud/__tests__/watch-realtmux.test.d.ts.map +1 -0
  129. package/dist/hud/__tests__/watch-realtmux.test.js +322 -0
  130. package/dist/hud/__tests__/watch-realtmux.test.js.map +1 -0
  131. package/dist/hud/index.d.ts +3 -0
  132. package/dist/hud/index.d.ts.map +1 -1
  133. package/dist/hud/index.js +98 -20
  134. package/dist/hud/index.js.map +1 -1
  135. package/dist/hud/reconcile.d.ts.map +1 -1
  136. package/dist/hud/reconcile.js +68 -24
  137. package/dist/hud/reconcile.js.map +1 -1
  138. package/dist/hud/render.d.ts.map +1 -1
  139. package/dist/hud/render.js +20 -0
  140. package/dist/hud/render.js.map +1 -1
  141. package/dist/hud/session-attached.d.ts +15 -0
  142. package/dist/hud/session-attached.d.ts.map +1 -0
  143. package/dist/hud/session-attached.js +47 -0
  144. package/dist/hud/session-attached.js.map +1 -0
  145. package/dist/hud/state.d.ts +9 -1
  146. package/dist/hud/state.d.ts.map +1 -1
  147. package/dist/hud/state.js +140 -3
  148. package/dist/hud/state.js.map +1 -1
  149. package/dist/hud/tmux.d.ts +4 -0
  150. package/dist/hud/tmux.d.ts.map +1 -1
  151. package/dist/hud/tmux.js +86 -23
  152. package/dist/hud/tmux.js.map +1 -1
  153. package/dist/hud/types.d.ts +20 -0
  154. package/dist/hud/types.d.ts.map +1 -1
  155. package/dist/hud/types.js +3 -0
  156. package/dist/hud/types.js.map +1 -1
  157. package/dist/modes/base.d.ts +15 -2
  158. package/dist/modes/base.d.ts.map +1 -1
  159. package/dist/modes/base.js +105 -16
  160. package/dist/modes/base.js.map +1 -1
  161. package/dist/question/autopilot-wait.js +2 -2
  162. package/dist/question/autopilot-wait.js.map +1 -1
  163. package/dist/question/deep-interview.js +2 -2
  164. package/dist/question/deep-interview.js.map +1 -1
  165. package/dist/ralplan/__tests__/advisory-activation.test.d.ts +2 -0
  166. package/dist/ralplan/__tests__/advisory-activation.test.d.ts.map +1 -0
  167. package/dist/ralplan/__tests__/advisory-activation.test.js +700 -0
  168. package/dist/ralplan/__tests__/advisory-activation.test.js.map +1 -0
  169. package/dist/ralplan/__tests__/advisory-detached-transition.test.d.ts +2 -0
  170. package/dist/ralplan/__tests__/advisory-detached-transition.test.d.ts.map +1 -0
  171. package/dist/ralplan/__tests__/advisory-detached-transition.test.js +88 -0
  172. package/dist/ralplan/__tests__/advisory-detached-transition.test.js.map +1 -0
  173. package/dist/ralplan/__tests__/advisory.test.d.ts +2 -0
  174. package/dist/ralplan/__tests__/advisory.test.d.ts.map +1 -0
  175. package/dist/ralplan/__tests__/advisory.test.js +1087 -0
  176. package/dist/ralplan/__tests__/advisory.test.js.map +1 -0
  177. package/dist/ralplan/__tests__/runtime.test.js +416 -7
  178. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  179. package/dist/ralplan/advisory-activation-verifier.d.ts +19 -0
  180. package/dist/ralplan/advisory-activation-verifier.d.ts.map +1 -0
  181. package/dist/ralplan/advisory-activation-verifier.js +77 -0
  182. package/dist/ralplan/advisory-activation-verifier.js.map +1 -0
  183. package/dist/ralplan/advisory-activation.d.ts +34 -0
  184. package/dist/ralplan/advisory-activation.d.ts.map +1 -0
  185. package/dist/ralplan/advisory-activation.js +205 -0
  186. package/dist/ralplan/advisory-activation.js.map +1 -0
  187. package/dist/ralplan/advisory-contract.d.ts +103 -0
  188. package/dist/ralplan/advisory-contract.d.ts.map +1 -0
  189. package/dist/ralplan/advisory-contract.js +83 -0
  190. package/dist/ralplan/advisory-contract.js.map +1 -0
  191. package/dist/ralplan/advisory-detached-transition.d.ts +11 -0
  192. package/dist/ralplan/advisory-detached-transition.d.ts.map +1 -0
  193. package/dist/ralplan/advisory-detached-transition.js +21 -0
  194. package/dist/ralplan/advisory-detached-transition.js.map +1 -0
  195. package/dist/ralplan/advisory-evidence.d.ts +91 -0
  196. package/dist/ralplan/advisory-evidence.d.ts.map +1 -0
  197. package/dist/ralplan/advisory-evidence.js +393 -0
  198. package/dist/ralplan/advisory-evidence.js.map +1 -0
  199. package/dist/ralplan/advisory-lifecycle-validation.d.ts +11 -0
  200. package/dist/ralplan/advisory-lifecycle-validation.d.ts.map +1 -0
  201. package/dist/ralplan/advisory-lifecycle-validation.js +72 -0
  202. package/dist/ralplan/advisory-lifecycle-validation.js.map +1 -0
  203. package/dist/ralplan/advisory-postconditions.d.ts +3 -0
  204. package/dist/ralplan/advisory-postconditions.d.ts.map +1 -0
  205. package/dist/ralplan/advisory-postconditions.js +33 -0
  206. package/dist/ralplan/advisory-postconditions.js.map +1 -0
  207. package/dist/ralplan/advisory-recovery-journal.d.ts +56 -0
  208. package/dist/ralplan/advisory-recovery-journal.d.ts.map +1 -0
  209. package/dist/ralplan/advisory-recovery-journal.js +136 -0
  210. package/dist/ralplan/advisory-recovery-journal.js.map +1 -0
  211. package/dist/ralplan/advisory-storage.d.ts +24 -0
  212. package/dist/ralplan/advisory-storage.d.ts.map +1 -0
  213. package/dist/ralplan/advisory-storage.js +106 -0
  214. package/dist/ralplan/advisory-storage.js.map +1 -0
  215. package/dist/ralplan/advisory.d.ts +99 -0
  216. package/dist/ralplan/advisory.d.ts.map +1 -0
  217. package/dist/ralplan/advisory.js +914 -0
  218. package/dist/ralplan/advisory.js.map +1 -0
  219. package/dist/ralplan/documented-leader-preflight.d.ts +8 -0
  220. package/dist/ralplan/documented-leader-preflight.d.ts.map +1 -1
  221. package/dist/ralplan/documented-leader-preflight.js +60 -2
  222. package/dist/ralplan/documented-leader-preflight.js.map +1 -1
  223. package/dist/ralplan/runtime-advisory-lifecycle.d.ts +74 -0
  224. package/dist/ralplan/runtime-advisory-lifecycle.d.ts.map +1 -0
  225. package/dist/ralplan/runtime-advisory-lifecycle.js +352 -0
  226. package/dist/ralplan/runtime-advisory-lifecycle.js.map +1 -0
  227. package/dist/ralplan/runtime-contract.d.ts +126 -0
  228. package/dist/ralplan/runtime-contract.d.ts.map +1 -0
  229. package/dist/ralplan/runtime-contract.js +2 -0
  230. package/dist/ralplan/runtime-contract.js.map +1 -0
  231. package/dist/ralplan/runtime.d.ts +6 -109
  232. package/dist/ralplan/runtime.d.ts.map +1 -1
  233. package/dist/ralplan/runtime.js +174 -56
  234. package/dist/ralplan/runtime.js.map +1 -1
  235. package/dist/scripts/__tests__/postinstall.test.js +26 -0
  236. package/dist/scripts/__tests__/postinstall.test.js.map +1 -1
  237. package/dist/scripts/__tests__/ralplan-advisory-hook.test.d.ts +2 -0
  238. package/dist/scripts/__tests__/ralplan-advisory-hook.test.d.ts.map +1 -0
  239. package/dist/scripts/__tests__/ralplan-advisory-hook.test.js +299 -0
  240. package/dist/scripts/__tests__/ralplan-advisory-hook.test.js.map +1 -0
  241. package/dist/scripts/__tests__/smoke-packed-install.test.js +102 -8
  242. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  243. package/dist/scripts/codex-native-hook.d.ts +20 -0
  244. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  245. package/dist/scripts/codex-native-hook.js +226 -15
  246. package/dist/scripts/codex-native-hook.js.map +1 -1
  247. package/dist/scripts/postinstall.d.ts +7 -0
  248. package/dist/scripts/postinstall.d.ts.map +1 -1
  249. package/dist/scripts/postinstall.js +40 -0
  250. package/dist/scripts/postinstall.js.map +1 -1
  251. package/dist/scripts/smoke-packed-install.d.ts +21 -5
  252. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  253. package/dist/scripts/smoke-packed-install.js +157 -57
  254. package/dist/scripts/smoke-packed-install.js.map +1 -1
  255. package/dist/state/__tests__/mode-binding-lease.test.d.ts +2 -0
  256. package/dist/state/__tests__/mode-binding-lease.test.d.ts.map +1 -0
  257. package/dist/state/__tests__/mode-binding-lease.test.js +498 -0
  258. package/dist/state/__tests__/mode-binding-lease.test.js.map +1 -0
  259. package/dist/state/__tests__/operations.test.js +1 -0
  260. package/dist/state/__tests__/operations.test.js.map +1 -1
  261. package/dist/state/__tests__/pinned-atomic-file.test.d.ts +2 -0
  262. package/dist/state/__tests__/pinned-atomic-file.test.d.ts.map +1 -0
  263. package/dist/state/__tests__/pinned-atomic-file.test.js +438 -0
  264. package/dist/state/__tests__/pinned-atomic-file.test.js.map +1 -0
  265. package/dist/state/__tests__/skill-active-exact-session-transaction.test.d.ts +2 -0
  266. package/dist/state/__tests__/skill-active-exact-session-transaction.test.d.ts.map +1 -0
  267. package/dist/state/__tests__/skill-active-exact-session-transaction.test.js +179 -0
  268. package/dist/state/__tests__/skill-active-exact-session-transaction.test.js.map +1 -0
  269. package/dist/state/__tests__/skill-active.test.js +29 -1
  270. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  271. package/dist/state/mode-binding-lease-helper.d.ts +2 -0
  272. package/dist/state/mode-binding-lease-helper.d.ts.map +1 -0
  273. package/dist/state/mode-binding-lease-helper.js +623 -0
  274. package/dist/state/mode-binding-lease-helper.js.map +1 -0
  275. package/dist/state/mode-binding-lease.d.ts +25 -0
  276. package/dist/state/mode-binding-lease.d.ts.map +1 -0
  277. package/dist/state/mode-binding-lease.js +380 -0
  278. package/dist/state/mode-binding-lease.js.map +1 -0
  279. package/dist/state/operations.d.ts +29 -1
  280. package/dist/state/operations.d.ts.map +1 -1
  281. package/dist/state/operations.js +203 -122
  282. package/dist/state/operations.js.map +1 -1
  283. package/dist/state/pinned-atomic-file-darwin-client.d.ts +34 -0
  284. package/dist/state/pinned-atomic-file-darwin-client.d.ts.map +1 -0
  285. package/dist/state/pinned-atomic-file-darwin-client.js +174 -0
  286. package/dist/state/pinned-atomic-file-darwin-client.js.map +1 -0
  287. package/dist/state/pinned-atomic-file-darwin-helper.d.ts +2 -0
  288. package/dist/state/pinned-atomic-file-darwin-helper.d.ts.map +1 -0
  289. package/dist/state/pinned-atomic-file-darwin-helper.js +275 -0
  290. package/dist/state/pinned-atomic-file-darwin-helper.js.map +1 -0
  291. package/dist/state/pinned-atomic-file-darwin-janitor.d.ts +2 -0
  292. package/dist/state/pinned-atomic-file-darwin-janitor.d.ts.map +1 -0
  293. package/dist/state/pinned-atomic-file-darwin-janitor.js +272 -0
  294. package/dist/state/pinned-atomic-file-darwin-janitor.js.map +1 -0
  295. package/dist/state/pinned-atomic-file.d.ts +29 -0
  296. package/dist/state/pinned-atomic-file.d.ts.map +1 -0
  297. package/dist/state/pinned-atomic-file.js +536 -0
  298. package/dist/state/pinned-atomic-file.js.map +1 -0
  299. package/dist/state/process-identity.d.ts +30 -0
  300. package/dist/state/process-identity.d.ts.map +1 -0
  301. package/dist/state/process-identity.js +99 -0
  302. package/dist/state/process-identity.js.map +1 -0
  303. package/dist/state/skill-active.d.ts +18 -1
  304. package/dist/state/skill-active.d.ts.map +1 -1
  305. package/dist/state/skill-active.js +82 -5
  306. package/dist/state/skill-active.js.map +1 -1
  307. package/dist/state/workflow-transition-reconcile.js +1 -1
  308. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  309. package/dist/team/__tests__/tmux-session.test.js +109 -0
  310. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  311. package/dist/team/tmux-session.d.ts.map +1 -1
  312. package/dist/team/tmux-session.js +10 -1
  313. package/dist/team/tmux-session.js.map +1 -1
  314. package/dist/utils/__tests__/file-durability.test.js +20 -0
  315. package/dist/utils/__tests__/file-durability.test.js.map +1 -1
  316. package/dist/utils/file-durability.d.ts +1 -1
  317. package/dist/utils/file-durability.d.ts.map +1 -1
  318. package/dist/utils/file-durability.js.map +1 -1
  319. package/dist/verification/__tests__/ci-rust-gates.test.js +12 -4
  320. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  321. package/dist/verification/__tests__/ci-trusted-publish.test.d.ts +2 -0
  322. package/dist/verification/__tests__/ci-trusted-publish.test.d.ts.map +1 -0
  323. package/dist/verification/__tests__/ci-trusted-publish.test.js +205 -0
  324. package/dist/verification/__tests__/ci-trusted-publish.test.js.map +1 -0
  325. package/dist/verification/__tests__/explore-harness-release-workflow.test.js +10 -5
  326. package/dist/verification/__tests__/explore-harness-release-workflow.test.js.map +1 -1
  327. package/dist/verification/__tests__/publish-npm-manual-retired.test.d.ts +2 -0
  328. package/dist/verification/__tests__/publish-npm-manual-retired.test.d.ts.map +1 -0
  329. package/dist/verification/__tests__/publish-npm-manual-retired.test.js +22 -0
  330. package/dist/verification/__tests__/publish-npm-manual-retired.test.js.map +1 -0
  331. package/package.json +1 -1
  332. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  333. package/plugins/oh-my-codex/skills/doctor/SKILL.md +1 -1
  334. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +1 -1
  335. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +10 -1
  336. package/plugins/oh-my-codex/skills/team/SKILL.md +2 -0
  337. package/skills/doctor/SKILL.md +1 -1
  338. package/skills/omx-setup/SKILL.md +1 -1
  339. package/skills/ralplan/SKILL.md +10 -1
  340. package/skills/team/SKILL.md +2 -0
  341. package/src/scripts/__tests__/postinstall.test.ts +28 -0
  342. package/src/scripts/__tests__/ralplan-advisory-hook.test.ts +320 -0
  343. package/src/scripts/__tests__/smoke-packed-install.test.ts +120 -7
  344. package/src/scripts/codex-native-hook.ts +220 -10
  345. package/src/scripts/postinstall.ts +46 -0
  346. package/src/scripts/smoke-packed-install.ts +175 -70
@@ -1,13 +1,13 @@
1
1
  import { afterEach, describe, it, mock } from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { existsSync, mkdirSync, readFileSync, utimesSync } from "node:fs";
3
+ import { chmodSync, chownSync, existsSync, mkdirSync, readFileSync, statSync, utimesSync } from "node:fs";
4
4
  import { spawnSync } from "node:child_process";
5
5
  import { chmod, lstat, mkdir, mkdtemp, readFile, readdir as fsReaddir, rm, stat, symlink, writeFile } from "node:fs/promises";
6
6
  import { delimiter, dirname, join } from "node:path";
7
7
  import { tmpdir } from "node:os";
8
8
  import { fileURLToPath } from "node:url";
9
9
  import TOML from "@iarna/toml";
10
- import { HELP, normalizeCodexLaunchArgs, buildTmuxShellCommand, buildTmuxPaneCommand, shouldSourceTmuxPaneShellRc, buildWindowsPromptCommand, buildWindowsDetachedChildCommand, buildTmuxSessionName, resolveCliInvocation, parseResumeCodexHomeSelection, isResumeCodexLaunch, CODEX_GLOBAL_OPTIONS_WITH_SPLIT_VALUE, isCodexVersionRequest, resolveUpdateChannelArg, commandOwnsLocalHelp, resolveCodexLaunchPolicy, resolveEffectiveLeaderLaunchPolicyOverride, resolveEnvLaunchPolicyOverride, resolveLeaderLaunchPolicyOverride, classifyCodexExecFailure, resolveSignalExitCode, parseTmuxPaneSnapshot, findHudWatchPaneIds, buildHudPaneCleanupTargets, readTopLevelTomlString, upsertTopLevelTomlString, collectInheritableTeamWorkerArgs, resolveTeamWorkerLaunchArgsEnv, injectModelInstructionsBypassArgs, resolveWorkerSparkModel, resolveSetupInstallModeArg, resolveSetupMcpModeArg, resolveSetupScopeArg, resolveSetupTeamModeArg, resolveSetupAgentsMergePolicyArg, resolveLaunchConfigRepairOptions, readPersistedSetupPreferences, readPersistedSetupScope, resolveCodexConfigPathForLaunch, resolveCodexHomeForLaunch, resolveProjectLocalCodexHomeForLaunch, shouldAutoIsolateMadmaxLaunch, createMadmaxIsolatedRoot, buildMadmaxDetachedLaunchContextKey, withMadmaxDetachedContextLock, executeDetachedLaunchStateMachine, resolveOmxRootForLaunch, resolveDisposableWorktreeOmxRootForLaunch, prepareCodexHomeForLaunch, captureMadmaxWorktreeRuntimeContext, persistProjectLaunchRuntimeAuthState, persistProjectLaunchRuntimeProjectTrustState, cleanupRuntimeCodexHome, runtimeCodexHomePath, buildDetachedSessionBootstrapSteps, buildDetachedTmuxSessionName, buildDetachedSessionFinalizeSteps, shouldAttachDetachedTmuxSession, buildDetachedSessionRollbackSteps, detectDetachedSessionWindowIndex, resolveNotifyTempContract, buildNotifyTempStartupMessages, buildNotifyFallbackWatcherEnv, shouldEnableNotifyFallbackWatcher, reapStaleNotifyFallbackWatcher, cleanupLaunchOrphanedMcpProcesses, reapPostLaunchOrphanedMcpProcesses, cleanupPostLaunchModeStateFiles, resolveBackgroundHelperLaunchMode, shouldDetachBackgroundHelper, resolveNotifyFallbackWatcherScript, resolveHookDerivedWatcherScript, resolveNotifyHookScript, acquireTmuxExtendedKeysLease, resolveNativeSessionName, releaseTmuxExtendedKeysLease, withTmuxExtendedKeys, serializeDetachedSessionParentEnv, buildInsideTmuxHudHookEnv, registerInsideTmuxHudResizeHook, buildDetachedHudHookEnv, registerDetachedHudLayoutReconcileHook, ensureOmxRuntimeCommandShim, omxRuntimeCommandShimPath, prependOmxRuntimeCommandShimToEnv, CODEX_SQLITE_HOME_ENV, DETACHED_TMUX_HISTORY_LIMIT, isExistingTmuxWindowTooCrampedForLaunchHud, guardDetachedHudDeferredMutation, DETACHED_LAUNCH_CONTROL_PLANE_KEYS, buildDetachedLaunchControlPlane, } from "../index.js";
10
+ import { HELP, normalizeCodexLaunchArgs, buildTmuxShellCommand, buildTmuxPaneCommand, shouldSourceTmuxPaneShellRc, buildWindowsPromptCommand, buildWindowsDetachedChildCommand, buildTmuxSessionName, resolveCliInvocation, parseResumeCodexHomeSelection, isResumeCodexLaunch, CODEX_GLOBAL_OPTIONS_WITH_SPLIT_VALUE, isCodexVersionRequest, resolveUpdateChannelArg, commandOwnsLocalHelp, resolveCodexLaunchPolicy, resolveEffectiveLeaderLaunchPolicyOverride, resolveEnvLaunchPolicyOverride, resolveLeaderLaunchPolicyOverride, classifyCodexExecFailure, resolveSignalExitCode, parseTmuxPaneSnapshot, findHudWatchPaneIds, buildHudPaneCleanupTargets, readTopLevelTomlString, upsertTopLevelTomlString, collectInheritableTeamWorkerArgs, resolveTeamWorkerLaunchArgsEnv, injectModelInstructionsBypassArgs, resolveWorkerSparkModel, resolveSetupInstallModeArg, resolveSetupMcpModeArg, resolveSetupScopeArg, resolveSetupTeamModeArg, resolveSetupAgentsMergePolicyArg, resolveLaunchConfigRepairOptions, readPersistedSetupPreferences, readPersistedSetupScope, resolveCodexConfigPathForLaunch, resolveCodexHomeForLaunch, resolveProjectLocalCodexHomeForLaunch, shouldAutoIsolateMadmaxLaunch, createMadmaxIsolatedRoot, buildMadmaxDetachedLaunchContextKey, withMadmaxDetachedContextLock, executeDetachedLaunchStateMachine, isExactDetachedFinalization, resolveOmxRootForLaunch, resolveDisposableWorktreeOmxRootForLaunch, prepareCodexHomeForLaunch, prepareRuntimeCodexHomeForProjectLaunch, historyDestinationMode, acquireHistoryPersistenceLock, releaseHistoryPersistenceLock, captureMadmaxWorktreeRuntimeContext, persistProjectLaunchRuntimeAuthState, persistProjectLaunchRuntimeProjectTrustState, cleanupRuntimeCodexHome, runtimeCodexHomePath, buildDetachedSessionBootstrapSteps, buildDetachedTmuxSessionName, buildDetachedSessionFinalizeSteps, shouldAttachDetachedTmuxSession, buildDetachedSessionRollbackSteps, detectDetachedSessionWindowIndex, resolveNotifyTempContract, buildNotifyTempStartupMessages, buildNotifyFallbackWatcherEnv, shouldEnableNotifyFallbackWatcher, reapStaleNotifyFallbackWatcher, cleanupLaunchOrphanedMcpProcesses, reapPostLaunchOrphanedMcpProcesses, cleanupPostLaunchModeStateFiles, resolveBackgroundHelperLaunchMode, shouldDetachBackgroundHelper, resolveNotifyFallbackWatcherScript, resolveHookDerivedWatcherScript, resolveNotifyHookScript, acquireTmuxExtendedKeysLease, resolveNativeSessionName, releaseTmuxExtendedKeysLease, withTmuxExtendedKeys, serializeDetachedSessionParentEnv, buildInsideTmuxHudHookEnv, registerInsideTmuxHudResizeHook, buildDetachedHudHookEnv, registerDetachedHudLayoutReconcileHook, ensureOmxRuntimeCommandShim, omxRuntimeCommandShimPath, prependOmxRuntimeCommandShimToEnv, CODEX_SQLITE_HOME_ENV, DETACHED_TMUX_HISTORY_LIMIT, isExistingTmuxWindowTooCrampedForLaunchHud, guardDetachedHudDeferredMutation, DETACHED_LAUNCH_CONTROL_PLANE_KEYS, buildDetachedLaunchControlPlane, } from "../index.js";
11
11
  import { buildResumeArgsWithPreservedFlags, stripHotswapArg } from "../../auth/hotswap.js";
12
12
  import { writeSkillActiveStateCopiesForStateDir } from '../../state/skill-active.js';
13
13
  import { mergeConfig, repairConfigIfNeeded } from "../../config/generator.js";
@@ -310,6 +310,87 @@ describe("detached launch state machine", () => {
310
310
  assert.equal(events.includes("D10"), false);
311
311
  });
312
312
  }
313
+ for (const failure of ["D3", "D4", "D5", "D6", "D7", "D9"]) {
314
+ it(`passes authenticated finalized failure authority to rollback when ${failure} fails`, async () => {
315
+ const events = [];
316
+ const deps = createDependencies(events, failure);
317
+ let finalization;
318
+ deps.rollback = async (_ownedRecord, _report, observedFinalization) => {
319
+ events.push("rollback");
320
+ finalization = observedFinalization;
321
+ };
322
+ await assert.rejects(executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: true, report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, deps));
323
+ assert.deepEqual(finalization, {
324
+ nonce: "nonce",
325
+ sessionId: "session",
326
+ sessionName: "session-name",
327
+ leaderPid: 123,
328
+ kind: "failed",
329
+ });
330
+ assert.equal(events.includes("rollback"), true);
331
+ });
332
+ it(`preserves the leader and does not roll back when ${failure} finalization is malformed`, async () => {
333
+ const events = [];
334
+ const deps = createDependencies(events, failure);
335
+ deps.abortAndAwaitFinalization = async () => ({
336
+ acknowledged: true,
337
+ nonce: "nonce",
338
+ sessionName: "session-name",
339
+ leaderPid: 123,
340
+ kind: "failed",
341
+ });
342
+ await assert.rejects(executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: true, report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, deps));
343
+ assert.equal(events.includes("rollback"), false);
344
+ });
345
+ }
346
+ it("routes an exact D9 terminal finalization to HUD-only cleanup instead of session rollback", async () => {
347
+ const events = [];
348
+ const deps = createDependencies(events, "D9");
349
+ deps.abortAndAwaitFinalization = async () => ({
350
+ acknowledged: true,
351
+ nonce: "nonce",
352
+ sessionId: "session",
353
+ sessionName: "session-name",
354
+ leaderPid: 123,
355
+ kind: "terminal",
356
+ });
357
+ let finalization;
358
+ deps.rollback = async (_ownedRecord, _report, observedFinalization) => {
359
+ events.push("hud-only-cleanup");
360
+ finalization = observedFinalization;
361
+ };
362
+ await assert.rejects(executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: true, report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, deps));
363
+ assert.equal(isExactDetachedFinalization(finalization, {
364
+ nonce: "nonce",
365
+ sessionId: "session",
366
+ sessionName: "session-name",
367
+ leaderPid: 123,
368
+ }), true);
369
+ assert.equal(finalization?.kind, "terminal");
370
+ assert.deepEqual(events.slice(-2), ["D9", "hud-only-cleanup"]);
371
+ });
372
+ for (const kind of ["malformed", "", "FAILED", "terminal ", "terminalized"]) {
373
+ it(`never grants HUD-only rollback authority when a matching report kind is "${kind}"`, async () => {
374
+ const events = [];
375
+ const deps = createDependencies(events, "D9");
376
+ deps.abortAndAwaitFinalization = async () => ({
377
+ acknowledged: true,
378
+ nonce: "nonce",
379
+ sessionId: "session",
380
+ sessionName: "session-name",
381
+ leaderPid: 123,
382
+ kind,
383
+ });
384
+ let observedFinalization;
385
+ deps.rollback = async (_ownedRecord, _report, finalization) => {
386
+ events.push("rollback");
387
+ observedFinalization = finalization;
388
+ };
389
+ await assert.rejects(() => executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: true, report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, deps));
390
+ assert.equal(observedFinalization, undefined);
391
+ assert.equal(events.includes("rollback"), false, `kind ${JSON.stringify(kind)} must never reach rollback`);
392
+ });
393
+ }
313
394
  it("finalizes and closes exactly once before D2 rollback without transport effects", async () => {
314
395
  const events = [];
315
396
  const deps = createDependencies(events);
@@ -2085,6 +2166,343 @@ describe("project launch scope helpers", () => {
2085
2166
  await rm(wd, { recursive: true, force: true });
2086
2167
  }
2087
2168
  });
2169
+ it("materializes resolved history links and skips broken links", async () => {
2170
+ const wd = await mkdtemp(join(tmpdir(), "omx-runtime-history-symlinks-"));
2171
+ try {
2172
+ const sourceCodexHome = join(wd, "source-codex-home");
2173
+ const targetSessions = join(wd, "project-sessions");
2174
+ const nestedSessions = join(targetSessions, "nested");
2175
+ const targetHistory = join(wd, "project-history.jsonl");
2176
+ const outsideHistory = join(wd, "outside-history.jsonl");
2177
+ await mkdir(sourceCodexHome, { recursive: true });
2178
+ await mkdir(nestedSessions, { recursive: true });
2179
+ await writeFile(join(nestedSessions, "rollout-linked.jsonl"), "linked-session\n");
2180
+ await writeFile(outsideHistory, "must-not-follow\n");
2181
+ await symlink(outsideHistory, join(nestedSessions, "nested-escape.jsonl"));
2182
+ await writeFile(targetHistory, '{"session_id":"linked-session"}\n');
2183
+ const sessionsMtime = new Date("2024-06-07T08:09:10Z");
2184
+ chmodSync(targetSessions, 0o700);
2185
+ chmodSync(nestedSessions, 0o700);
2186
+ utimesSync(targetSessions, sessionsMtime, sessionsMtime);
2187
+ await symlink(targetSessions, join(sourceCodexHome, "sessions"), "dir");
2188
+ await symlink(targetHistory, join(sourceCodexHome, "history.jsonl"));
2189
+ await symlink(join(wd, "missing-session-index.jsonl"), join(sourceCodexHome, "session_index.jsonl"));
2190
+ const createSymlink = async () => {
2191
+ throw new Error("history symlink creation must not be used");
2192
+ };
2193
+ const runtimeCodexHome = await prepareRuntimeCodexHomeForProjectLaunch(wd, "session-history-symlinks", sourceCodexHome, { includeHistoryArtifacts: true, createSymlink });
2194
+ assert.equal((await stat(join(runtimeCodexHome, "sessions"))).isDirectory(), true);
2195
+ assert.equal((await lstat(join(runtimeCodexHome, "sessions"))).isSymbolicLink(), false);
2196
+ assert.equal((await stat(join(runtimeCodexHome, "sessions"))).mtime.toISOString(), sessionsMtime.toISOString());
2197
+ if (process.platform !== "win32") {
2198
+ assert.equal((await stat(join(runtimeCodexHome, "sessions"))).mode & 0o777, 0o700);
2199
+ assert.equal((await stat(join(runtimeCodexHome, "sessions", "nested"))).mode & 0o777, 0o700);
2200
+ }
2201
+ assert.equal(await readFile(join(runtimeCodexHome, "sessions", "nested", "rollout-linked.jsonl"), "utf-8"), "linked-session\n");
2202
+ assert.equal(existsSync(join(runtimeCodexHome, "sessions", "nested", "nested-escape.jsonl")), false);
2203
+ assert.equal((await lstat(join(runtimeCodexHome, "history.jsonl"))).isSymbolicLink(), false);
2204
+ assert.equal(await readFile(join(runtimeCodexHome, "history.jsonl"), "utf-8"), '{"session_id":"linked-session"}\n');
2205
+ assert.equal(existsSync(join(runtimeCodexHome, "session_index.jsonl")), false);
2206
+ }
2207
+ finally {
2208
+ await rm(wd, { recursive: true, force: true });
2209
+ }
2210
+ });
2211
+ it("skips same-inode history content mutations during file publication", async () => {
2212
+ const wd = await mkdtemp(join(tmpdir(), "omx-runtime-history-content-race-"));
2213
+ try {
2214
+ const sourceCodexHome = join(wd, "source-codex-home");
2215
+ const sourceHistory = join(wd, "source-history.jsonl");
2216
+ await mkdir(sourceCodexHome, { recursive: true });
2217
+ await writeFile(sourceHistory, "original\n");
2218
+ await symlink(sourceHistory, join(sourceCodexHome, "history.jsonl"));
2219
+ const originalStat = statSync(sourceHistory);
2220
+ const runtimeCodexHome = await prepareRuntimeCodexHomeForProjectLaunch(wd, "session-history-content-race", sourceCodexHome, {
2221
+ includeHistoryArtifacts: true,
2222
+ afterHistorySourceOpen: async (entryName, source) => {
2223
+ if (entryName !== "history.jsonl")
2224
+ return;
2225
+ await writeFile(source, "mutated!\n");
2226
+ utimesSync(source, originalStat.atime, originalStat.mtime);
2227
+ },
2228
+ });
2229
+ assert.equal(existsSync(join(runtimeCodexHome, "history.jsonl")), false);
2230
+ }
2231
+ finally {
2232
+ await rm(wd, { recursive: true, force: true });
2233
+ }
2234
+ });
2235
+ it("merges a symlinked primary history tree with an independent source", async () => {
2236
+ const wd = await mkdtemp(join(tmpdir(), "omx-runtime-history-symlink-merge-"));
2237
+ try {
2238
+ const sourceCodexHome = join(wd, "source-codex-home");
2239
+ const primarySessions = join(wd, "primary-sessions");
2240
+ const extraCodexHome = join(wd, "extra-codex-home");
2241
+ await mkdir(sourceCodexHome, { recursive: true });
2242
+ await mkdir(primarySessions, { recursive: true });
2243
+ await mkdir(join(extraCodexHome, "sessions"), { recursive: true });
2244
+ await writeFile(join(primarySessions, "rollout-primary.jsonl"), "primary-session\n");
2245
+ await writeFile(join(extraCodexHome, "sessions", "rollout-extra.jsonl"), "extra-session\n");
2246
+ chmodSync(primarySessions, 0o700);
2247
+ chmodSync(join(extraCodexHome, "sessions"), 0o755);
2248
+ await symlink(primarySessions, join(sourceCodexHome, "sessions"), "dir");
2249
+ const runtimeCodexHome = await prepareRuntimeCodexHomeForProjectLaunch(wd, "session-history-symlink-merge", sourceCodexHome, { includeHistoryArtifacts: true, extraHistoryCodexHomes: [extraCodexHome] });
2250
+ assert.equal((await lstat(join(runtimeCodexHome, "sessions"))).isSymbolicLink(), false);
2251
+ if (process.platform !== "win32") {
2252
+ assert.equal((await stat(join(runtimeCodexHome, "sessions"))).mode & 0o777, 0o700);
2253
+ }
2254
+ assert.equal(await readFile(join(runtimeCodexHome, "sessions", "rollout-primary.jsonl"), "utf-8"), "primary-session\n");
2255
+ assert.equal(await readFile(join(runtimeCodexHome, "sessions", "rollout-extra.jsonl"), "utf-8"), "extra-session\n");
2256
+ }
2257
+ finally {
2258
+ await rm(wd, { recursive: true, force: true });
2259
+ }
2260
+ });
2261
+ it("populates read-only history directories before restoring modes", async () => {
2262
+ const wd = await mkdtemp(join(tmpdir(), "omx-runtime-history-read-only-"));
2263
+ try {
2264
+ const sourceCodexHome = join(wd, "source-codex-home");
2265
+ const sourceSessions = join(wd, "source-sessions");
2266
+ const nestedSessions = join(sourceSessions, "nested");
2267
+ const sourceMtime = new Date("2024-08-09T10:11:12Z");
2268
+ const nestedMtime = new Date("2024-08-09T10:11:13Z");
2269
+ await mkdir(nestedSessions, { recursive: true });
2270
+ await writeFile(join(nestedSessions, "rollout.jsonl"), "read-only-session\n");
2271
+ utimesSync(sourceSessions, sourceMtime, sourceMtime);
2272
+ utimesSync(nestedSessions, nestedMtime, nestedMtime);
2273
+ chmodSync(sourceSessions, 0o555);
2274
+ chmodSync(nestedSessions, 0o500);
2275
+ await mkdir(sourceCodexHome, { recursive: true });
2276
+ await symlink(sourceSessions, join(sourceCodexHome, "sessions"), "dir");
2277
+ const readOnlyRuntimeCodexHome = await prepareRuntimeCodexHomeForProjectLaunch(wd, "session-history-read-only", sourceCodexHome, { includeHistoryArtifacts: true });
2278
+ assert.equal(await readFile(join(readOnlyRuntimeCodexHome, "sessions", "nested", "rollout.jsonl"), "utf-8"), "read-only-session\n");
2279
+ if (process.platform !== "win32") {
2280
+ const copiedSessions = await stat(join(readOnlyRuntimeCodexHome, "sessions"));
2281
+ const copiedNested = await stat(join(readOnlyRuntimeCodexHome, "sessions", "nested"));
2282
+ assert.equal(copiedSessions.mode & 0o777, 0o555);
2283
+ assert.equal(copiedNested.mode & 0o777, 0o500);
2284
+ assert.equal(copiedSessions.mtime.toISOString(), sourceMtime.toISOString());
2285
+ assert.equal(copiedNested.mtime.toISOString(), nestedMtime.toISOString());
2286
+ }
2287
+ }
2288
+ finally {
2289
+ if (existsSync(join(wd, "source-sessions")))
2290
+ chmodSync(join(wd, "source-sessions"), 0o700);
2291
+ if (existsSync(join(wd, "source-sessions", "nested")))
2292
+ chmodSync(join(wd, "source-sessions", "nested"), 0o700);
2293
+ if (existsSync(join(wd, ".omx", "runtime", "codex-home"))) {
2294
+ chmodSync(join(wd, ".omx", "runtime", "codex-home"), 0o700);
2295
+ }
2296
+ if (existsSync(join(wd, ".omx", "runtime", "codex-home", "session-history-read-only", "sessions"))) {
2297
+ chmodSync(join(wd, ".omx", "runtime", "codex-home", "session-history-read-only", "sessions"), 0o700);
2298
+ }
2299
+ if (existsSync(join(wd, ".omx", "runtime", "codex-home", "session-history-read-only", "sessions", "nested"))) {
2300
+ chmodSync(join(wd, ".omx", "runtime", "codex-home", "session-history-read-only", "sessions", "nested"), 0o700);
2301
+ }
2302
+ await rm(wd, { recursive: true, force: true });
2303
+ }
2304
+ });
2305
+ it("anchors cleanup locking in a writable resolved history target", async () => {
2306
+ const wd = await mkdtemp(join(tmpdir(), "omx-runtime-history-writable-anchor-"));
2307
+ try {
2308
+ const projectCodexHome = join(wd, ".codex");
2309
+ const resolvedSessions = join(projectCodexHome, "sessions-target");
2310
+ const runtimeCodexHome = join(wd, "runtime-codex-home");
2311
+ await mkdir(resolvedSessions, { recursive: true });
2312
+ await mkdir(join(runtimeCodexHome, "sessions"), { recursive: true });
2313
+ await writeFile(join(runtimeCodexHome, "sessions", "rollout.jsonl"), "read-only-root-safe\n");
2314
+ await symlink(resolvedSessions, join(projectCodexHome, "sessions"), "dir");
2315
+ chmodSync(projectCodexHome, 0o555);
2316
+ await cleanupRuntimeCodexHome(runtimeCodexHome, projectCodexHome);
2317
+ assert.equal(await readFile(join(resolvedSessions, "rollout.jsonl"), "utf-8"), "read-only-root-safe\n");
2318
+ }
2319
+ finally {
2320
+ if (existsSync(join(wd, ".codex")))
2321
+ chmodSync(join(wd, ".codex"), 0o700);
2322
+ await rm(wd, { recursive: true, force: true });
2323
+ }
2324
+ });
2325
+ it("skips an extra history home that disappears during preparation", async () => {
2326
+ const wd = await mkdtemp(join(tmpdir(), "omx-runtime-history-disappearing-extra-"));
2327
+ try {
2328
+ const sourceCodexHome = join(wd, "source-codex-home");
2329
+ const sourceSessions = join(wd, "source-sessions");
2330
+ const extraCodexHome = join(wd, "extra-codex-home");
2331
+ await mkdir(sourceCodexHome, { recursive: true });
2332
+ await mkdir(sourceSessions, { recursive: true });
2333
+ await mkdir(join(extraCodexHome, "sessions"), { recursive: true });
2334
+ await writeFile(join(sourceSessions, "rollout-primary.jsonl"), "primary-session\n");
2335
+ await writeFile(join(extraCodexHome, "sessions", "rollout-extra.jsonl"), "extra-session\n");
2336
+ await symlink(sourceSessions, join(sourceCodexHome, "sessions"), "dir");
2337
+ const runtimeCodexHome = await prepareRuntimeCodexHomeForProjectLaunch(wd, "session-history-disappearing-extra", sourceCodexHome, {
2338
+ includeHistoryArtifacts: true,
2339
+ extraHistoryCodexHomes: [extraCodexHome],
2340
+ afterHistoryEntryValidation: async (entryName) => {
2341
+ if (entryName === "sessions")
2342
+ await rm(join(extraCodexHome, "sessions"), { recursive: true, force: true });
2343
+ },
2344
+ });
2345
+ assert.equal(await readFile(join(runtimeCodexHome, "sessions", "rollout-primary.jsonl"), "utf-8"), "primary-session\n");
2346
+ assert.equal(existsSync(join(runtimeCodexHome, "sessions", "rollout-extra.jsonl")), false);
2347
+ }
2348
+ finally {
2349
+ await rm(wd, { recursive: true, force: true });
2350
+ }
2351
+ });
2352
+ it("keeps group-only history readable and traversable by the destination owner", async () => {
2353
+ const wd = await mkdtemp(join(tmpdir(), "omx-runtime-history-group-only-"));
2354
+ let runtimeCodexHome;
2355
+ try {
2356
+ const sourceCodexHome = join(wd, "source-codex-home");
2357
+ const sourceSessions = join(wd, "source-sessions");
2358
+ await mkdir(sourceSessions, { recursive: true });
2359
+ await writeFile(join(sourceSessions, "rollout.jsonl"), "group-only-session\n");
2360
+ if (process.platform !== "win32") {
2361
+ chownSync(sourceSessions, process.getuid(), process.getgid());
2362
+ chownSync(join(sourceSessions, "rollout.jsonl"), process.getuid(), process.getgid());
2363
+ }
2364
+ chmodSync(join(sourceSessions, "rollout.jsonl"), 0o404);
2365
+ chmodSync(sourceSessions, 0o505);
2366
+ await mkdir(sourceCodexHome, { recursive: true });
2367
+ await symlink(sourceSessions, join(sourceCodexHome, "sessions"), "dir");
2368
+ runtimeCodexHome = await prepareRuntimeCodexHomeForProjectLaunch(wd, "session-history-group-only", sourceCodexHome, { includeHistoryArtifacts: true });
2369
+ assert.equal(await readFile(join(runtimeCodexHome, "sessions", "rollout.jsonl"), "utf-8"), "group-only-session\n");
2370
+ if (process.platform !== "win32") {
2371
+ assert.equal((await stat(join(runtimeCodexHome, "sessions"))).mode & 0o700, 0o500);
2372
+ assert.equal((await stat(join(runtimeCodexHome, "sessions", "rollout.jsonl"))).mode & 0o700, 0o400);
2373
+ }
2374
+ }
2375
+ finally {
2376
+ if (existsSync(join(wd, "source-sessions", "rollout.jsonl")))
2377
+ chmodSync(join(wd, "source-sessions", "rollout.jsonl"), 0o600);
2378
+ if (existsSync(join(wd, "source-sessions")))
2379
+ chmodSync(join(wd, "source-sessions"), 0o700);
2380
+ if (runtimeCodexHome && existsSync(join(runtimeCodexHome, "sessions"))) {
2381
+ chmodSync(join(runtimeCodexHome, "sessions"), 0o700);
2382
+ if (existsSync(join(runtimeCodexHome, "sessions", "rollout.jsonl"))) {
2383
+ chmodSync(join(runtimeCodexHome, "sessions", "rollout.jsonl"), 0o600);
2384
+ }
2385
+ }
2386
+ await rm(wd, { recursive: true, force: true });
2387
+ }
2388
+ });
2389
+ it("normalizes group-owned replacement modes for the new owner", async () => {
2390
+ const wd = await mkdtemp(join(tmpdir(), "omx-runtime-history-owner-mode-"));
2391
+ try {
2392
+ const groupWritable = join(wd, "group-writable.jsonl");
2393
+ const groupReadable = join(wd, "group-readable.jsonl");
2394
+ await writeFile(groupWritable, "group-writable\n");
2395
+ await writeFile(groupReadable, "group-readable\n");
2396
+ chmodSync(groupWritable, 0o660);
2397
+ chmodSync(groupReadable, 0o440);
2398
+ const writableMode = historyDestinationMode(0o060);
2399
+ const readableMode = historyDestinationMode(0o040);
2400
+ assert.equal(writableMode & 0o700, 0o600);
2401
+ assert.equal(readableMode & 0o700, 0o400);
2402
+ chmodSync(groupWritable, writableMode);
2403
+ chmodSync(groupReadable, readableMode);
2404
+ await writeFile(groupWritable, "owner-can-append\n", { flag: "a" });
2405
+ assert.match(await readFile(groupWritable, "utf-8"), /owner-can-append/);
2406
+ assert.equal(await readFile(groupReadable, "utf-8"), "group-readable\n");
2407
+ }
2408
+ finally {
2409
+ await rm(wd, { recursive: true, force: true });
2410
+ }
2411
+ });
2412
+ it("skips cyclic durable history links without aborting preparation", async () => {
2413
+ const wd = await mkdtemp(join(tmpdir(), "omx-runtime-history-cyclic-link-"));
2414
+ try {
2415
+ const sourceCodexHome = join(wd, "source-codex-home");
2416
+ await mkdir(join(sourceCodexHome, "sessions"), { recursive: true });
2417
+ await symlink("history.jsonl", join(sourceCodexHome, "history.jsonl"));
2418
+ const runtimeCodexHome = await prepareRuntimeCodexHomeForProjectLaunch(wd, "session-history-cyclic-link", sourceCodexHome, { includeHistoryArtifacts: true });
2419
+ assert.equal(existsSync(join(runtimeCodexHome, "history.jsonl")), false);
2420
+ }
2421
+ finally {
2422
+ await rm(wd, { recursive: true, force: true });
2423
+ }
2424
+ });
2425
+ it("rejects source swaps and replaces nested destination links safely", async () => {
2426
+ const wd = await mkdtemp(join(tmpdir(), "omx-runtime-history-toctou-"));
2427
+ try {
2428
+ const sourceCodexHome = join(wd, "source-codex-home");
2429
+ const sourceSessions = join(wd, "source-sessions");
2430
+ const replacementSessions = join(wd, "replacement-sessions");
2431
+ const outsideDestination = join(wd, "outside-destination.jsonl");
2432
+ await mkdir(sourceCodexHome, { recursive: true });
2433
+ await mkdir(sourceSessions, { recursive: true });
2434
+ await mkdir(replacementSessions, { recursive: true });
2435
+ await writeFile(join(sourceSessions, "rollout.jsonl"), "original\n");
2436
+ await writeFile(join(replacementSessions, "rollout.jsonl"), "replacement\n");
2437
+ await writeFile(outsideDestination, "must-remain\n");
2438
+ await symlink(sourceSessions, join(sourceCodexHome, "sessions"), "dir");
2439
+ const swappedSourceRuntime = await prepareRuntimeCodexHomeForProjectLaunch(wd, "session-history-source-swap", sourceCodexHome, {
2440
+ includeHistoryArtifacts: true,
2441
+ afterHistoryEntryValidation: async (entryName, source) => {
2442
+ if (entryName !== "sessions")
2443
+ return;
2444
+ await rm(source, { force: true });
2445
+ await symlink(replacementSessions, source, "dir");
2446
+ },
2447
+ });
2448
+ assert.equal(existsSync(join(swappedSourceRuntime, "sessions")), false);
2449
+ assert.equal(existsSync(join(swappedSourceRuntime, "sessions", "rollout.jsonl")), false);
2450
+ const destinationSourceHome = join(wd, "destination-source-home");
2451
+ const destinationSessions = join(wd, "destination-sessions");
2452
+ await mkdir(destinationSourceHome, { recursive: true });
2453
+ await mkdir(destinationSessions, { recursive: true });
2454
+ await writeFile(join(destinationSessions, "rollout.jsonl"), "destination-safe\n");
2455
+ await symlink(destinationSessions, join(destinationSourceHome, "sessions"), "dir");
2456
+ const safeDestinationRuntime = await prepareRuntimeCodexHomeForProjectLaunch(wd, "session-history-destination-swap", destinationSourceHome, {
2457
+ includeHistoryArtifacts: true,
2458
+ afterHistoryEntryValidation: async (entryName, _source, destination) => {
2459
+ if (entryName !== "sessions")
2460
+ return;
2461
+ await mkdir(destination, { recursive: true });
2462
+ await symlink(outsideDestination, join(destination, "rollout.jsonl"));
2463
+ },
2464
+ });
2465
+ assert.equal(await readFile(join(outsideDestination), "utf-8"), "must-remain\n");
2466
+ assert.equal(await readFile(join(safeDestinationRuntime, "sessions", "rollout.jsonl"), "utf-8"), "destination-safe\n");
2467
+ assert.equal((await lstat(join(safeDestinationRuntime, "sessions", "rollout.jsonl"))).isSymbolicLink(), false);
2468
+ const emptyDestinationSourceHome = join(wd, "empty-destination-source-home");
2469
+ const emptyDestinationSessions = join(wd, "empty-destination-sessions");
2470
+ await mkdir(emptyDestinationSourceHome, { recursive: true });
2471
+ await mkdir(emptyDestinationSessions, { recursive: true });
2472
+ await writeFile(join(emptyDestinationSessions, "rollout.jsonl"), "empty-destination-safe\n");
2473
+ await symlink(emptyDestinationSessions, join(emptyDestinationSourceHome, "sessions"), "dir");
2474
+ const emptyDestinationRuntime = await prepareRuntimeCodexHomeForProjectLaunch(wd, "session-history-empty-destination-swap", emptyDestinationSourceHome, {
2475
+ includeHistoryArtifacts: true,
2476
+ afterHistoryEntryValidation: async (entryName, _source, destination) => {
2477
+ if (entryName === "sessions")
2478
+ await mkdir(destination, { recursive: true });
2479
+ },
2480
+ });
2481
+ assert.equal(await readFile(join(emptyDestinationRuntime, "sessions", "rollout.jsonl"), "utf-8"), "empty-destination-safe\n");
2482
+ const stagedSourceHome = join(wd, "staged-source-home");
2483
+ const stagedSessions = join(wd, "staged-sessions");
2484
+ const outsideStageDirectory = join(wd, "outside-stage-directory");
2485
+ await mkdir(stagedSourceHome, { recursive: true });
2486
+ await mkdir(stagedSessions, { recursive: true });
2487
+ await mkdir(outsideStageDirectory, { recursive: true });
2488
+ await writeFile(join(stagedSessions, "rollout.jsonl"), "staged-safe\n");
2489
+ await writeFile(join(outsideStageDirectory, "sentinel"), "must-remain\n");
2490
+ await symlink(stagedSessions, join(stagedSourceHome, "sessions"), "dir");
2491
+ await assert.rejects(() => prepareRuntimeCodexHomeForProjectLaunch(wd, "session-history-staging-swap", stagedSourceHome, {
2492
+ includeHistoryArtifacts: true,
2493
+ afterHistoryEntryStage: async (entryName, temporary) => {
2494
+ if (entryName !== "sessions")
2495
+ return;
2496
+ await rm(temporary, { recursive: true, force: true });
2497
+ await symlink(outsideStageDirectory, temporary, "dir");
2498
+ },
2499
+ }), /history staging path is not a directory/);
2500
+ assert.equal(await readFile(join(outsideStageDirectory, "sentinel"), "utf-8"), "must-remain\n");
2501
+ }
2502
+ finally {
2503
+ await rm(wd, { recursive: true, force: true });
2504
+ }
2505
+ });
2088
2506
  it("copies non-symlink runtime Codex transcript artifacts before cleanup", async () => {
2089
2507
  const wd = await mkdtemp(join(tmpdir(), "omx-runtime-history-copyback-"));
2090
2508
  try {
@@ -2113,6 +2531,127 @@ describe("project launch scope helpers", () => {
2113
2531
  await rm(wd, { recursive: true, force: true });
2114
2532
  }
2115
2533
  });
2534
+ it("persists materialized sessions through symlinked authorized roots", async () => {
2535
+ const wd = await mkdtemp(join(tmpdir(), "omx-runtime-history-persist-symlink-"));
2536
+ try {
2537
+ const projectCodexHome = join(wd, ".codex");
2538
+ const projectCodexHomeReal = join(wd, ".codex-real");
2539
+ const projectSessions = join(projectCodexHomeReal, "sessions-target");
2540
+ const runtimeCodexHome = join(wd, "runtime-codex-home");
2541
+ await mkdir(projectSessions, { recursive: true });
2542
+ await mkdir(join(runtimeCodexHome, "sessions", "2026"), { recursive: true });
2543
+ await symlink(projectCodexHomeReal, projectCodexHome, "dir");
2544
+ await symlink(projectSessions, join(projectCodexHomeReal, "sessions"), "dir");
2545
+ await writeFile(join(runtimeCodexHome, "sessions", "2026", "rollout.jsonl"), "persisted-session\n");
2546
+ await cleanupRuntimeCodexHome(runtimeCodexHome, projectCodexHome);
2547
+ assert.equal(await readFile(join(projectSessions, "2026", "rollout.jsonl"), "utf-8"), "persisted-session\n");
2548
+ }
2549
+ finally {
2550
+ await rm(wd, { recursive: true, force: true });
2551
+ }
2552
+ });
2553
+ it("does not rewrite canonical history when runtime and project paths alias", async () => {
2554
+ const wd = await mkdtemp(join(tmpdir(), "omx-runtime-history-self-copyback-"));
2555
+ try {
2556
+ const projectCodexHome = join(wd, ".codex");
2557
+ const runtimeAlias = join(wd, "runtime-alias");
2558
+ await mkdir(projectCodexHome, { recursive: true });
2559
+ await writeFile(join(projectCodexHome, "history.jsonl"), '{"session_id":"existing"}\n');
2560
+ await symlink(projectCodexHome, runtimeAlias, "dir");
2561
+ await cleanupRuntimeCodexHome(runtimeAlias, projectCodexHome);
2562
+ assert.equal(await readFile(join(projectCodexHome, "history.jsonl"), "utf-8"), '{"session_id":"existing"}\n');
2563
+ assert.equal(existsSync(runtimeAlias), false);
2564
+ }
2565
+ finally {
2566
+ await rm(wd, { recursive: true, force: true });
2567
+ }
2568
+ });
2569
+ it("serializes concurrent JSONL cleanup appends without losing either source", async () => {
2570
+ const wd = await mkdtemp(join(tmpdir(), "omx-runtime-history-concurrent-copyback-"));
2571
+ try {
2572
+ const projectCodexHome = join(wd, ".codex");
2573
+ const runtimeOne = join(wd, "runtime-one");
2574
+ const runtimeTwo = join(wd, "runtime-two");
2575
+ await mkdir(runtimeOne, { recursive: true });
2576
+ await mkdir(runtimeTwo, { recursive: true });
2577
+ await writeFile(join(runtimeOne, "history.jsonl"), '{"session_id":"one"}\n');
2578
+ await writeFile(join(runtimeTwo, "history.jsonl"), '{"session_id":"two"}\n');
2579
+ await Promise.all([
2580
+ cleanupRuntimeCodexHome(runtimeOne, projectCodexHome),
2581
+ cleanupRuntimeCodexHome(runtimeTwo, projectCodexHome),
2582
+ ]);
2583
+ const persisted = await readFile(join(projectCodexHome, "history.jsonl"), "utf-8");
2584
+ assert.match(persisted, /"session_id":"one"/);
2585
+ assert.match(persisted, /"session_id":"two"/);
2586
+ }
2587
+ finally {
2588
+ await rm(wd, { recursive: true, force: true });
2589
+ }
2590
+ });
2591
+ it("keeps a live history lock through its stale age", async () => {
2592
+ const wd = await mkdtemp(join(tmpdir(), "omx-runtime-history-lock-lease-"));
2593
+ try {
2594
+ const first = await acquireHistoryPersistenceLock(wd, { staleMs: 30, heartbeatMs: 5 });
2595
+ let contenderAcquired = false;
2596
+ const contender = acquireHistoryPersistenceLock(wd, { timeoutMs: 500, staleMs: 30, heartbeatMs: 5 }).then((lease) => {
2597
+ contenderAcquired = true;
2598
+ return lease;
2599
+ });
2600
+ await new Promise((resolve) => setTimeout(resolve, 100));
2601
+ assert.equal(contenderAcquired, false);
2602
+ await releaseHistoryPersistenceLock(first);
2603
+ const second = await contender;
2604
+ await releaseHistoryPersistenceLock(second);
2605
+ }
2606
+ finally {
2607
+ await rm(wd, { recursive: true, force: true });
2608
+ }
2609
+ });
2610
+ it("does not let an old lock owner remove a replacement lock", async () => {
2611
+ const wd = await mkdtemp(join(tmpdir(), "omx-runtime-history-lock-owner-"));
2612
+ try {
2613
+ const oldLease = await acquireHistoryPersistenceLock(wd);
2614
+ await rm(oldLease.lockPath, { recursive: true, force: true });
2615
+ const newLease = await acquireHistoryPersistenceLock(wd);
2616
+ await releaseHistoryPersistenceLock(oldLease);
2617
+ assert.equal((await lstat(newLease.lockPath)).isDirectory(), true);
2618
+ await releaseHistoryPersistenceLock(newLease);
2619
+ }
2620
+ finally {
2621
+ await rm(wd, { recursive: true, force: true });
2622
+ }
2623
+ });
2624
+ it("fails closed when a heartbeat owner record is interposed", async () => {
2625
+ const wd = await mkdtemp(join(tmpdir(), "omx-runtime-history-lock-heartbeat-"));
2626
+ try {
2627
+ const first = await acquireHistoryPersistenceLock(wd, { staleMs: 30, heartbeatMs: 5 });
2628
+ const ownerPath = join(first.lockPath, "owner.json");
2629
+ await writeFile(ownerPath, "{\"token\":");
2630
+ utimesSync(ownerPath, new Date(Date.now() - 1_000), new Date(Date.now() - 1_000));
2631
+ await assert.rejects(() => acquireHistoryPersistenceLock(wd, { timeoutMs: 100, staleMs: 30, heartbeatMs: 5 }), /timed out waiting for history persistence lock/);
2632
+ await releaseHistoryPersistenceLock(first);
2633
+ }
2634
+ finally {
2635
+ await rm(wd, { recursive: true, force: true });
2636
+ }
2637
+ });
2638
+ it("reaps a stale lock only when process-start identity proves PID reuse", async () => {
2639
+ if (process.platform === "win32")
2640
+ return;
2641
+ const wd = await mkdtemp(join(tmpdir(), "omx-runtime-history-lock-pid-"));
2642
+ try {
2643
+ const lockPath = join(wd, ".omx-history.lock");
2644
+ const ownerPath = join(lockPath, "owner.json");
2645
+ await mkdir(lockPath, { mode: 0o700 });
2646
+ await writeFile(ownerPath, JSON.stringify({ token: "old", pid: process.pid, startIdentity: "reused" }));
2647
+ utimesSync(ownerPath, new Date(Date.now() - 1_000), new Date(Date.now() - 1_000));
2648
+ const lease = await acquireHistoryPersistenceLock(wd, { staleMs: 30 });
2649
+ await releaseHistoryPersistenceLock(lease);
2650
+ }
2651
+ finally {
2652
+ await rm(wd, { recursive: true, force: true });
2653
+ }
2654
+ });
2116
2655
  it("uses a session-scoped CODEX_HOME mirror for project launch config writes", async () => {
2117
2656
  const wd = await mkdtemp(join(tmpdir(), "omx-launch-runtime-codex-home-"));
2118
2657
  try {
@@ -3262,7 +3801,9 @@ exit 0
3262
3801
  const hud = {
3263
3802
  paneId: "%77",
3264
3803
  panePid: 7700,
3804
+ sessionName: "omx-detached",
3265
3805
  sessionId: "$1",
3806
+ sessionCreated: "1700000000",
3266
3807
  windowId: "@1",
3267
3808
  operationMarker: "operation-1",
3268
3809
  };