oh-my-codex 0.20.2 → 0.20.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (367) hide show
  1. package/Cargo.lock +7 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +26 -0
  4. package/crates/omx-runtime/Cargo.toml +1 -0
  5. package/crates/omx-runtime/src/main.rs +25 -2
  6. package/crates/omx-runtime/tests/execution.rs +219 -0
  7. package/crates/omx-runtime-core/src/dispatch.rs +96 -10
  8. package/crates/omx-runtime-core/src/engine.rs +839 -29
  9. package/crates/omx-runtime-core/src/lib.rs +8 -0
  10. package/dist/agents/__tests__/definitions.test.js +81 -0
  11. package/dist/agents/__tests__/definitions.test.js.map +1 -1
  12. package/dist/agents/__tests__/native-config.test.js +25 -0
  13. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  14. package/dist/agents/native-config.d.ts +2 -1
  15. package/dist/agents/native-config.d.ts.map +1 -1
  16. package/dist/agents/native-config.js.map +1 -1
  17. package/dist/auth/hotswap.d.ts +6 -0
  18. package/dist/auth/hotswap.d.ts.map +1 -1
  19. package/dist/auth/hotswap.js +85 -15
  20. package/dist/auth/hotswap.js.map +1 -1
  21. package/dist/autopilot/__tests__/ralplan-gate.test.js +1 -1
  22. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
  23. package/dist/cli/__tests__/auth.test.js +60 -6
  24. package/dist/cli/__tests__/auth.test.js.map +1 -1
  25. package/dist/cli/__tests__/codex-plugin-layout.test.js +80 -0
  26. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  27. package/dist/cli/__tests__/doctor-warning-copy.test.js +103 -1
  28. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  29. package/dist/cli/__tests__/exec.test.js +136 -0
  30. package/dist/cli/__tests__/exec.test.js.map +1 -1
  31. package/dist/cli/__tests__/index.test.js +258 -4
  32. package/dist/cli/__tests__/index.test.js.map +1 -1
  33. package/dist/cli/__tests__/launch-fallback.test.js +287 -1
  34. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  35. package/dist/cli/__tests__/mcp-parity.test.js +4 -1
  36. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  37. package/dist/cli/__tests__/native-hook-claim-journal.test.js +84 -6
  38. package/dist/cli/__tests__/native-hook-claim-journal.test.js.map +1 -1
  39. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +3 -3
  40. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  41. package/dist/cli/__tests__/ralph.test.js +3 -3
  42. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  43. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js +56 -171
  44. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js.map +1 -1
  45. package/dist/cli/__tests__/ralplan.test.js +37 -111
  46. package/dist/cli/__tests__/ralplan.test.js.map +1 -1
  47. package/dist/cli/__tests__/resume.test.js +11 -10
  48. package/dist/cli/__tests__/resume.test.js.map +1 -1
  49. package/dist/cli/__tests__/session-search-help.test.js +25 -1
  50. package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
  51. package/dist/cli/__tests__/setup-install-mode.test.js +42 -4
  52. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  53. package/dist/cli/__tests__/team.test.js +105 -7
  54. package/dist/cli/__tests__/team.test.js.map +1 -1
  55. package/dist/cli/__tests__/uninstall.test.js +46 -0
  56. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  57. package/dist/cli/__tests__/windows-popup-loop-contract.test.js +2 -1
  58. package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
  59. package/dist/cli/doctor.d.ts +3 -0
  60. package/dist/cli/doctor.d.ts.map +1 -1
  61. package/dist/cli/doctor.js +15 -2
  62. package/dist/cli/doctor.js.map +1 -1
  63. package/dist/cli/index.d.ts +4 -1
  64. package/dist/cli/index.d.ts.map +1 -1
  65. package/dist/cli/index.js +138 -38
  66. package/dist/cli/index.js.map +1 -1
  67. package/dist/cli/mcp-parity.js +2 -2
  68. package/dist/cli/mcp-parity.js.map +1 -1
  69. package/dist/cli/native-hook-claim-journal.d.ts +16 -5
  70. package/dist/cli/native-hook-claim-journal.d.ts.map +1 -1
  71. package/dist/cli/native-hook-claim-journal.js +42 -29
  72. package/dist/cli/native-hook-claim-journal.js.map +1 -1
  73. package/dist/cli/ralph.js +2 -2
  74. package/dist/cli/ralph.js.map +1 -1
  75. package/dist/cli/ralplan.d.ts +4 -8
  76. package/dist/cli/ralplan.d.ts.map +1 -1
  77. package/dist/cli/ralplan.js +19 -112
  78. package/dist/cli/ralplan.js.map +1 -1
  79. package/dist/cli/session-search.d.ts +5 -0
  80. package/dist/cli/session-search.d.ts.map +1 -1
  81. package/dist/cli/session-search.js +87 -0
  82. package/dist/cli/session-search.js.map +1 -1
  83. package/dist/cli/setup.d.ts +5 -0
  84. package/dist/cli/setup.d.ts.map +1 -1
  85. package/dist/cli/setup.js +80 -31
  86. package/dist/cli/setup.js.map +1 -1
  87. package/dist/cli/uninstall.d.ts +9 -0
  88. package/dist/cli/uninstall.d.ts.map +1 -1
  89. package/dist/cli/uninstall.js +61 -19
  90. package/dist/cli/uninstall.js.map +1 -1
  91. package/dist/compat/__tests__/rust-runtime-compat.test.js +1 -1
  92. package/dist/config/__tests__/generator-idempotent.test.js +330 -1
  93. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  94. package/dist/config/__tests__/generator-notify.test.js +2 -2
  95. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  96. package/dist/config/__tests__/models.test.js +67 -3
  97. package/dist/config/__tests__/models.test.js.map +1 -1
  98. package/dist/config/generator.d.ts +4 -20
  99. package/dist/config/generator.d.ts.map +1 -1
  100. package/dist/config/generator.js +499 -119
  101. package/dist/config/generator.js.map +1 -1
  102. package/dist/config/models.d.ts +12 -2
  103. package/dist/config/models.d.ts.map +1 -1
  104. package/dist/config/models.js +15 -1
  105. package/dist/config/models.js.map +1 -1
  106. package/dist/hooks/__tests__/agents-overlay.test.js +2 -2
  107. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  108. package/dist/hooks/__tests__/keyword-detector.test.js +4 -4
  109. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  110. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +238 -543
  111. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  112. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +38 -4
  113. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  114. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +96 -9
  115. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  116. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +531 -12
  117. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  118. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +155 -23
  119. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  120. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +482 -32
  121. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  122. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +68 -1
  123. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  124. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +7 -1
  125. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
  126. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +43 -4
  127. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  128. package/dist/hooks/__tests__/session.test.js +348 -2
  129. package/dist/hooks/__tests__/session.test.js.map +1 -1
  130. package/dist/hooks/agents-overlay.js +1 -1
  131. package/dist/hooks/session.d.ts +26 -2
  132. package/dist/hooks/session.d.ts.map +1 -1
  133. package/dist/hooks/session.js +169 -13
  134. package/dist/hooks/session.js.map +1 -1
  135. package/dist/leader/__tests__/contract.test.js +57 -7
  136. package/dist/leader/__tests__/contract.test.js.map +1 -1
  137. package/dist/leader/contract.d.ts +16 -0
  138. package/dist/leader/contract.d.ts.map +1 -1
  139. package/dist/leader/contract.js +79 -4
  140. package/dist/leader/contract.js.map +1 -1
  141. package/dist/mcp/__tests__/code-intel-server.test.js +31 -3
  142. package/dist/mcp/__tests__/code-intel-server.test.js.map +1 -1
  143. package/dist/mcp/__tests__/wiki-server.test.js +18 -0
  144. package/dist/mcp/__tests__/wiki-server.test.js.map +1 -1
  145. package/dist/mcp/code-intel-server.d.ts.map +1 -1
  146. package/dist/mcp/code-intel-server.js +13 -3
  147. package/dist/mcp/code-intel-server.js.map +1 -1
  148. package/dist/mcp/wiki-server.d.ts +11 -0
  149. package/dist/mcp/wiki-server.d.ts.map +1 -1
  150. package/dist/mcp/wiki-server.js +9 -2
  151. package/dist/mcp/wiki-server.js.map +1 -1
  152. package/dist/modes/__tests__/base-tmux-pane.test.js +4 -4
  153. package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
  154. package/dist/notifications/temp-contract.d.ts.map +1 -1
  155. package/dist/notifications/temp-contract.js +4 -0
  156. package/dist/notifications/temp-contract.js.map +1 -1
  157. package/dist/pipeline/__tests__/orchestrator.test.js +4 -4
  158. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  159. package/dist/pipeline/__tests__/stages.test.js +8 -8
  160. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  161. package/dist/ralplan/__tests__/consensus-gate.test.js +134 -10
  162. package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
  163. package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts +2 -0
  164. package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts.map +1 -0
  165. package/dist/ralplan/__tests__/documented-leader-preflight.test.js +46 -0
  166. package/dist/ralplan/__tests__/documented-leader-preflight.test.js.map +1 -0
  167. package/dist/ralplan/__tests__/runtime.test.js +8 -4
  168. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  169. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  170. package/dist/ralplan/consensus-gate.js +58 -50
  171. package/dist/ralplan/consensus-gate.js.map +1 -1
  172. package/dist/ralplan/documented-leader-preflight.d.ts +34 -0
  173. package/dist/ralplan/documented-leader-preflight.d.ts.map +1 -0
  174. package/dist/ralplan/documented-leader-preflight.js +58 -0
  175. package/dist/ralplan/documented-leader-preflight.js.map +1 -0
  176. package/dist/ralplan/runtime.d.ts +1 -0
  177. package/dist/ralplan/runtime.d.ts.map +1 -1
  178. package/dist/ralplan/runtime.js +11 -7
  179. package/dist/ralplan/runtime.js.map +1 -1
  180. package/dist/runtime/__tests__/bridge.test.js +42 -1
  181. package/dist/runtime/__tests__/bridge.test.js.map +1 -1
  182. package/dist/runtime/bridge.d.ts +12 -0
  183. package/dist/runtime/bridge.d.ts.map +1 -1
  184. package/dist/runtime/bridge.js +52 -1
  185. package/dist/runtime/bridge.js.map +1 -1
  186. package/dist/scripts/__tests__/codex-native-hook.test.js +2620 -487
  187. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  188. package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts +2 -0
  189. package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts.map +1 -0
  190. package/dist/scripts/__tests__/reasoning-artifact-contract.test.js +108 -0
  191. package/dist/scripts/__tests__/reasoning-artifact-contract.test.js.map +1 -0
  192. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js +46 -173
  193. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js.map +1 -1
  194. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js +39 -159
  195. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js.map +1 -1
  196. package/dist/scripts/__tests__/smoke-packed-install.test.js +254 -2
  197. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  198. package/dist/scripts/codex-native-hook.d.ts +4 -1
  199. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  200. package/dist/scripts/codex-native-hook.js +11013 -1153
  201. package/dist/scripts/codex-native-hook.js.map +1 -1
  202. package/dist/scripts/notify-fallback-watcher.js +82 -108
  203. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  204. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  205. package/dist/scripts/notify-hook/auto-nudge.js +74 -3
  206. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  207. package/dist/scripts/notify-hook/team-dispatch.d.ts +9 -0
  208. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  209. package/dist/scripts/notify-hook/team-dispatch.js +234 -79
  210. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  211. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  212. package/dist/scripts/notify-hook/team-leader-nudge.js +39 -13
  213. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  214. package/dist/scripts/notify-hook/team-tmux-guard.d.ts +12 -4
  215. package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
  216. package/dist/scripts/notify-hook/team-tmux-guard.js +365 -44
  217. package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
  218. package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
  219. package/dist/scripts/notify-hook/team-worker-stop.js +19 -18
  220. package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
  221. package/dist/scripts/notify-hook/team-worker.d.ts +3 -0
  222. package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
  223. package/dist/scripts/notify-hook/team-worker.js +37 -28
  224. package/dist/scripts/notify-hook/team-worker.js.map +1 -1
  225. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  226. package/dist/scripts/notify-hook/tmux-injection.js +28 -2
  227. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  228. package/dist/scripts/notify-hook.js +1 -0
  229. package/dist/scripts/notify-hook.js.map +1 -1
  230. package/dist/scripts/smoke-packed-install.d.ts +48 -3
  231. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  232. package/dist/scripts/smoke-packed-install.js +2796 -13
  233. package/dist/scripts/smoke-packed-install.js.map +1 -1
  234. package/dist/state/__tests__/mode-state-context.test.js +54 -0
  235. package/dist/state/__tests__/mode-state-context.test.js.map +1 -1
  236. package/dist/state/__tests__/operations.test.js +2 -0
  237. package/dist/state/__tests__/operations.test.js.map +1 -1
  238. package/dist/state/mode-state-context.d.ts +5 -0
  239. package/dist/state/mode-state-context.d.ts.map +1 -1
  240. package/dist/state/mode-state-context.js +59 -0
  241. package/dist/state/mode-state-context.js.map +1 -1
  242. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js +24 -245
  243. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js.map +1 -1
  244. package/dist/subagents/tracker.d.ts +1 -82
  245. package/dist/subagents/tracker.d.ts.map +1 -1
  246. package/dist/subagents/tracker.js +3 -275
  247. package/dist/subagents/tracker.js.map +1 -1
  248. package/dist/team/__tests__/api-interop.test.js +91 -1
  249. package/dist/team/__tests__/api-interop.test.js.map +1 -1
  250. package/dist/team/__tests__/delivery-e2e-smoke.test.js +52 -10
  251. package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
  252. package/dist/team/__tests__/idle-nudge.test.js +45 -15
  253. package/dist/team/__tests__/idle-nudge.test.js.map +1 -1
  254. package/dist/team/__tests__/mcp-comm.test.js +93 -27
  255. package/dist/team/__tests__/mcp-comm.test.js.map +1 -1
  256. package/dist/team/__tests__/model-contract.test.js +110 -3
  257. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  258. package/dist/team/__tests__/runtime-cli.test.js +9 -0
  259. package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
  260. package/dist/team/__tests__/runtime.test.js +3204 -335
  261. package/dist/team/__tests__/runtime.test.js.map +1 -1
  262. package/dist/team/__tests__/scaling.test.js +2359 -142
  263. package/dist/team/__tests__/scaling.test.js.map +1 -1
  264. package/dist/team/__tests__/state.test.js +804 -36
  265. package/dist/team/__tests__/state.test.js.map +1 -1
  266. package/dist/team/__tests__/team-ops-contract.test.js +6 -0
  267. package/dist/team/__tests__/team-ops-contract.test.js.map +1 -1
  268. package/dist/team/__tests__/tmux-session.test.js +3049 -784
  269. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  270. package/dist/team/__tests__/worker-runtime-identity.test.js +38 -2
  271. package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
  272. package/dist/team/__tests__/worktree.test.js +6 -0
  273. package/dist/team/__tests__/worktree.test.js.map +1 -1
  274. package/dist/team/api-interop.d.ts.map +1 -1
  275. package/dist/team/api-interop.js +15 -9
  276. package/dist/team/api-interop.js.map +1 -1
  277. package/dist/team/exact-pane.d.ts +23 -0
  278. package/dist/team/exact-pane.d.ts.map +1 -0
  279. package/dist/team/exact-pane.js +133 -0
  280. package/dist/team/exact-pane.js.map +1 -0
  281. package/dist/team/idle-nudge.d.ts +12 -1
  282. package/dist/team/idle-nudge.d.ts.map +1 -1
  283. package/dist/team/idle-nudge.js +41 -14
  284. package/dist/team/idle-nudge.js.map +1 -1
  285. package/dist/team/model-contract.d.ts +2 -5
  286. package/dist/team/model-contract.d.ts.map +1 -1
  287. package/dist/team/model-contract.js +59 -17
  288. package/dist/team/model-contract.js.map +1 -1
  289. package/dist/team/runtime.d.ts +1 -0
  290. package/dist/team/runtime.d.ts.map +1 -1
  291. package/dist/team/runtime.js +1244 -180
  292. package/dist/team/runtime.js.map +1 -1
  293. package/dist/team/scaling.d.ts +13 -1
  294. package/dist/team/scaling.d.ts.map +1 -1
  295. package/dist/team/scaling.js +1430 -384
  296. package/dist/team/scaling.js.map +1 -1
  297. package/dist/team/state/dispatch.d.ts.map +1 -1
  298. package/dist/team/state/dispatch.js +26 -8
  299. package/dist/team/state/dispatch.js.map +1 -1
  300. package/dist/team/state/locks.d.ts.map +1 -1
  301. package/dist/team/state/locks.js +61 -13
  302. package/dist/team/state/locks.js.map +1 -1
  303. package/dist/team/state/tasks.d.ts.map +1 -1
  304. package/dist/team/state/tasks.js +4 -3
  305. package/dist/team/state/tasks.js.map +1 -1
  306. package/dist/team/state.d.ts +67 -1
  307. package/dist/team/state.d.ts.map +1 -1
  308. package/dist/team/state.js +636 -129
  309. package/dist/team/state.js.map +1 -1
  310. package/dist/team/team-ops.d.ts +5 -0
  311. package/dist/team/team-ops.d.ts.map +1 -1
  312. package/dist/team/team-ops.js +4 -0
  313. package/dist/team/team-ops.js.map +1 -1
  314. package/dist/team/tmux-session.d.ts +123 -23
  315. package/dist/team/tmux-session.d.ts.map +1 -1
  316. package/dist/team/tmux-session.js +1250 -291
  317. package/dist/team/tmux-session.js.map +1 -1
  318. package/dist/team/worktree.d.ts.map +1 -1
  319. package/dist/team/worktree.js +4 -0
  320. package/dist/team/worktree.js.map +1 -1
  321. package/dist/utils/__tests__/file-durability.test.d.ts +2 -0
  322. package/dist/utils/__tests__/file-durability.test.d.ts.map +1 -0
  323. package/dist/utils/__tests__/file-durability.test.js +57 -0
  324. package/dist/utils/__tests__/file-durability.test.js.map +1 -0
  325. package/dist/utils/file-durability.d.ts +15 -0
  326. package/dist/utils/file-durability.d.ts.map +1 -0
  327. package/dist/utils/file-durability.js +37 -0
  328. package/dist/utils/file-durability.js.map +1 -0
  329. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +3 -0
  330. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
  331. package/dist/wiki/lint.d.ts +4 -1
  332. package/dist/wiki/lint.d.ts.map +1 -1
  333. package/dist/wiki/lint.js +3 -2
  334. package/dist/wiki/lint.js.map +1 -1
  335. package/package.json +1 -1
  336. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  337. package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +24 -0
  338. package/plugins/oh-my-codex/skills/ralph/SKILL.md +6 -4
  339. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +3 -2
  340. package/plugins/oh-my-codex/skills/team/SKILL.md +13 -9
  341. package/skills/ralph/SKILL.md +6 -4
  342. package/skills/ralplan/SKILL.md +3 -2
  343. package/skills/team/SKILL.md +13 -9
  344. package/src/scripts/__tests__/codex-native-hook.test.ts +2826 -497
  345. package/src/scripts/__tests__/reasoning-artifact-contract.test.ts +129 -0
  346. package/src/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.ts +48 -185
  347. package/src/scripts/__tests__/role-intent-durable-recovery-3181.test.ts +44 -177
  348. package/src/scripts/__tests__/smoke-packed-install.test.ts +320 -2
  349. package/src/scripts/codex-native-hook.ts +10715 -1069
  350. package/src/scripts/notify-fallback-watcher.ts +84 -115
  351. package/src/scripts/notify-hook/auto-nudge.ts +71 -3
  352. package/src/scripts/notify-hook/team-dispatch.ts +242 -75
  353. package/src/scripts/notify-hook/team-leader-nudge.ts +40 -18
  354. package/src/scripts/notify-hook/team-tmux-guard.ts +366 -39
  355. package/src/scripts/notify-hook/team-worker-stop.ts +20 -16
  356. package/src/scripts/notify-hook/team-worker.ts +39 -26
  357. package/src/scripts/notify-hook/tmux-injection.ts +28 -2
  358. package/src/scripts/notify-hook.ts +1 -0
  359. package/src/scripts/smoke-packed-install.ts +3196 -14
  360. package/dist/subagents/__tests__/adapted-role-binding.test.d.ts +0 -2
  361. package/dist/subagents/__tests__/adapted-role-binding.test.d.ts.map +0 -1
  362. package/dist/subagents/__tests__/adapted-role-binding.test.js +0 -988
  363. package/dist/subagents/__tests__/adapted-role-binding.test.js.map +0 -1
  364. package/dist/subagents/adapted-role-binding.d.ts +0 -17
  365. package/dist/subagents/adapted-role-binding.d.ts.map +0 -1
  366. package/dist/subagents/adapted-role-binding.js +0 -75
  367. package/dist/subagents/adapted-role-binding.js.map +0 -1
@@ -37,8 +37,8 @@ const LEGACY_SEEDED_MODEL_CONTEXT_WINDOW = 250000;
37
37
  const LEGACY_SEEDED_MODEL_AUTO_COMPACT_TOKEN_LIMIT = 200000;
38
38
  const OMX_SEEDED_BEHAVIORAL_DEFAULTS_START_MARKER = "# oh-my-codex seeded behavioral defaults (uninstall removes unchanged defaults)";
39
39
  const OMX_SEEDED_BEHAVIORAL_DEFAULTS_END_MARKER = "# End oh-my-codex seeded behavioral defaults";
40
- export const OMX_DEVELOPER_INSTRUCTIONS = "You have oh-my-codex installed. AGENTS.md is the orchestration brain and main control surface. Follow AGENTS.md for skill/keyword routing, $name workflow invocation, and role-specialized subagents; when the native surface exposes `agent_type` role routing, set `agent_type` to an installed role and never omit it for OMX work. When it does not (`role_routing_unavailable`, for example a Codex App `spawn_agent` surface exposing only `task_name`, `message`, and `fork_turns`), do not fabricate `agent_type`; follow the OMX adapted role-pass protocol by recording a pre-validated role intent in the OMX subagent ledger, and never fake the role via a prompt label. Use outcome-first, concise progress updates: state the target result, constraints, validation evidence, and stop condition before adding process detail. Native subagents live in .codex/agents and may handle independent parallel subtasks within one Codex session or team pane. Skills load from .codex/skills, not native-agent TOMLs. Treat installed prompts as narrower execution surfaces under AGENTS.md authority.";
41
- export const OMX_PLUGIN_DEVELOPER_INSTRUCTIONS = '<omx version="1">You have oh-my-codex installed through Codex plugin mode. AGENTS.md is the orchestration brain and main control surface. Follow AGENTS.md for skill/keyword routing and $name workflow invocation. When the native surface exposes `agent_type` role routing, set `agent_type` to an installed role and never omit it for OMX work. When it does not (`role_routing_unavailable`, for example a Codex App `spawn_agent` surface exposing only `task_name`, `message`, and `fork_turns`), do not fabricate `agent_type`; follow the OMX adapted role-pass protocol by recording a pre-validated role intent in the OMX subagent ledger, and never fake the role via a prompt label. Registered Codex plugin marketplace surfaces supply OMX workflows and plugin-scoped companion resources when the plugin is installed; native agent roles are installed as setup-owned Codex agent TOML files in plugin mode so agent_type routing works. User-installed skills may still live under ~/.codex/skills. Use outcome-first, concise progress updates: state the target result, constraints, validation evidence, and stop condition before adding process detail.</omx>';
40
+ export const OMX_DEVELOPER_INSTRUCTIONS = "You have oh-my-codex installed. AGENTS.md is the orchestration brain and main control surface. Follow AGENTS.md for skill/keyword routing, $name workflow invocation, and role-specialized subagents; when the native surface exposes `agent_type` role routing, set `agent_type` to an installed role and never omit it for OMX work. When it reports `role_routing_unavailable`, do not fabricate `agent_type`; before Ralplan planning, state, HUD, runtime, or delegation work, run `omx ralplan preflight --json` and stop on `unsupported_documented_leader_proof`. Never fake the role via a prompt label or infer authority from session/thread/pointer/transcript/cwd state. Use outcome-first, concise progress updates: state the target result, constraints, validation evidence, and stop condition before adding process detail. Native subagents live in .codex/agents and may handle independent parallel subtasks within one Codex session or team pane. Skills load from .codex/skills, not native-agent TOMLs. Treat installed prompts as narrower execution surfaces under AGENTS.md authority.";
41
+ export const OMX_PLUGIN_DEVELOPER_INSTRUCTIONS = '<omx version="1">You have oh-my-codex installed through Codex plugin mode. AGENTS.md is the orchestration brain and main control surface. Follow AGENTS.md for skill/keyword routing and $name workflow invocation. When the native surface exposes `agent_type` role routing, set `agent_type` to an installed role and never omit it for OMX work. When it reports `role_routing_unavailable`, do not fabricate `agent_type`; before Ralplan planning, state, HUD, runtime, or delegation work, run `omx ralplan preflight --json` and stop on `unsupported_documented_leader_proof`. Never fake the role via a prompt label or infer authority from session/thread/pointer/transcript/cwd state. Registered Codex plugin marketplace surfaces supply OMX workflows and plugin-scoped companion resources when the plugin is installed; native agent roles are installed as setup-owned Codex agent TOML files in plugin mode so agent_type routing works. User-installed skills may still live under ~/.codex/skills. Use outcome-first, concise progress updates: state the target result, constraints, validation evidence, and stop condition before adding process detail.</omx>';
42
42
  const SHARED_MCP_REGISTRY_MARKER = "oh-my-codex (OMX) Shared MCP Registry Sync";
43
43
  const SHARED_MCP_REGISTRY_END_MARKER = "# End oh-my-codex shared MCP registry sync";
44
44
  const LEGACY_MULTI_AGENT_KEYS = [
@@ -653,45 +653,8 @@ const OMX_HOOK_TRUST_START_MARKER = "# OMX-owned Codex hook trust state";
653
653
  const OMX_HOOK_TRUST_END_MARKER = "# End OMX-owned Codex hook trust state";
654
654
  const OMX_PROJECT_TRUST_START_MARKER = "# OMX-synced Codex project trust state (from runtime CODEX_HOME)";
655
655
  const OMX_PROJECT_TRUST_END_MARKER = "# End OMX-synced Codex project trust state";
656
- function extractMarkerBlockContent(config, startMarker, endMarker) {
657
- const lines = config.split(/\r?\n/);
658
- for (let i = 0; i < lines.length; i++) {
659
- if (lines[i].trim() !== startMarker)
660
- continue;
661
- const nextEndIdx = lines.findIndex((line, index) => index > i && line.trim() === endMarker);
662
- const nextStartIdx = lines.findIndex((line, index) => index > i && line.trim() === startMarker);
663
- if (nextEndIdx === -1 || (nextStartIdx !== -1 && nextStartIdx < nextEndIdx)) {
664
- return undefined;
665
- }
666
- return lines.slice(i + 1, nextEndIdx).join("\n").trim();
667
- }
668
- return undefined;
669
- }
670
- function stripMarkerBlock(config, startMarker, endMarker) {
671
- const lines = config.split(/\r?\n/);
672
- const kept = [];
673
- for (let i = 0; i < lines.length;) {
674
- if (lines[i].trim() !== startMarker) {
675
- kept.push(lines[i]);
676
- i += 1;
677
- continue;
678
- }
679
- const nextEndIdx = lines.findIndex((line, index) => index > i && line.trim() === endMarker);
680
- const nextStartIdx = lines.findIndex((line, index) => index > i && line.trim() === startMarker);
681
- if (nextEndIdx === -1 || (nextStartIdx !== -1 && nextStartIdx < nextEndIdx)) {
682
- kept.push(lines[i]);
683
- i += 1;
684
- continue;
685
- }
686
- i = nextEndIdx + 1;
687
- }
688
- return kept.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd();
689
- }
690
656
  function isPlainTomlRecord(value) {
691
- return (typeof value === "object" &&
692
- value !== null &&
693
- !Array.isArray(value) &&
694
- Object.getPrototypeOf(value) === Object.prototype);
657
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
695
658
  }
696
659
  function safeParseToml(content) {
697
660
  try {
@@ -702,97 +665,514 @@ function safeParseToml(content) {
702
665
  return undefined;
703
666
  }
704
667
  }
705
- function collectProjectHookTrustStateKeys(config) {
706
- const keys = new Set();
707
- const parsed = safeParseToml(config);
708
- const hooksTable = isPlainTomlRecord(parsed) ? parsed.hooks : undefined;
709
- const hooksState = isPlainTomlRecord(hooksTable) ? hooksTable.state : undefined;
710
- if (!isPlainTomlRecord(hooksState))
711
- return keys;
712
- for (const [key, entry] of Object.entries(hooksState)) {
713
- if (!isPlainTomlRecord(entry))
714
- continue;
715
- keys.add(key);
668
+ function projectHookTrustState(config) {
669
+ const hooks = safeParseToml(config)?.hooks;
670
+ const state = isPlainTomlRecord(hooks) ? hooks.state : undefined;
671
+ return isPlainTomlRecord(state) ? state : undefined;
672
+ }
673
+ function projectTrustSemanticView(config) {
674
+ const hasLeadingBom = config.startsWith("\uFEFF");
675
+ const source = hasLeadingBom ? config.slice(1) : config;
676
+ if (source.includes("\uFEFF"))
677
+ return undefined;
678
+ return { source, prefix: hasLeadingBom ? "\uFEFF" : "" };
679
+ }
680
+ function projectTrustMarkerRange(config) {
681
+ const lexical = analyzeTomlSource(config);
682
+ if (!lexical.isUnambiguous)
683
+ return undefined;
684
+ const lines = sourceLines(config);
685
+ const starts = lines.filter((line, index) => lexical.lineStartsOutsideMultiline[index] && line.content.trim() === OMX_PROJECT_TRUST_START_MARKER);
686
+ const ends = lines.filter((line, index) => lexical.lineStartsOutsideMultiline[index] && line.content.trim() === OMX_PROJECT_TRUST_END_MARKER);
687
+ if (starts.length === 0 && ends.length === 0)
688
+ return { start: config.length, end: config.length, payloadStart: config.length, payloadEnd: config.length };
689
+ if (starts.length !== 1 || ends.length !== 1 || starts[0].start >= ends[0].start)
690
+ return undefined;
691
+ const start = starts[0];
692
+ const end = ends[0];
693
+ return { start: start.start, end: end.end, payloadStart: start.end, payloadEnd: end.start };
694
+ }
695
+ function hasInlineProjectTrustMarkerComment(config) {
696
+ const lexical = analyzeTomlSource(config);
697
+ return sourceLines(config).some((line, index) => {
698
+ if (!lexical.lineStartsOutsideMultiline[index])
699
+ return false;
700
+ const trimmed = line.content.trim();
701
+ return (trimmed.startsWith(OMX_PROJECT_TRUST_START_MARKER) && trimmed !== OMX_PROJECT_TRUST_START_MARKER) ||
702
+ (trimmed.startsWith(OMX_PROJECT_TRUST_END_MARKER) && trimmed !== OMX_PROJECT_TRUST_END_MARKER);
703
+ });
704
+ }
705
+ function projectTrustHeaderPath(line) {
706
+ if (/^\s*\[\[/.test(line))
707
+ return undefined;
708
+ const header = parseTomlSourceTableHeader(line);
709
+ if (!header?.parsed)
710
+ return undefined;
711
+ const path = [];
712
+ let cursor = header.parsed;
713
+ while (isPlainTomlRecord(cursor)) {
714
+ const keys = Object.keys(cursor);
715
+ if (keys.length === 0)
716
+ return path;
717
+ if (keys.length !== 1)
718
+ return undefined;
719
+ const key = keys[0];
720
+ path.push(key);
721
+ cursor = cursor[key];
716
722
  }
717
- return keys;
723
+ return path;
718
724
  }
719
- /**
720
- * Repairs project configs from the 0.18.3 relaunch regression where a
721
- * project-synced trust block could duplicate setup-owned hook trust tables and
722
- * make the next runtime CODEX_HOME config.toml invalid before Codex started.
723
- */
724
- export function repairProjectScopeTrustStateForLaunch(projectConfig, projectHooksPath) {
725
- const syncedTrustBlock = extractMarkerBlockContent(projectConfig, OMX_PROJECT_TRUST_START_MARKER, OMX_PROJECT_TRUST_END_MARKER);
726
- if (!syncedTrustBlock)
727
- return projectConfig;
728
- const stripped = stripMarkerBlock(projectConfig, OMX_PROJECT_TRUST_START_MARKER, OMX_PROJECT_TRUST_END_MARKER);
729
- const repaired = syncProjectScopeTrustStateFromRuntime(stripped, syncedTrustBlock, projectHooksPath);
730
- return repaired === stripped ? projectConfig : repaired;
725
+ function dominantProjectTrustEol(source) {
726
+ const crlfCount = (source.match(/\r\n/g) ?? []).length;
727
+ const lfCount = (source.match(/(?<!\r)\n/g) ?? []).length;
728
+ return crlfCount > lfCount ? "\r\n" : "\n";
729
+ }
730
+ function projectTrustPayloadIsSemanticallyEmpty(payload) {
731
+ const parsed = safeParseToml(payload);
732
+ return parsed !== undefined && Object.keys(parsed).length === 0;
733
+ }
734
+ function projectTrustValueAtPath(source, path) {
735
+ let value = source;
736
+ for (const key of path) {
737
+ if (!isPlainTomlRecord(value))
738
+ return undefined;
739
+ value = value[key];
740
+ }
741
+ return value;
731
742
  }
732
743
  /**
733
- * Project-scope launches mirror the durable project config.toml into an
734
- * ephemeral runtime CODEX_HOME. Codex writes its workspace-trust ledger and
735
- * hook trust ledger into the runtime config.toml during the session. Without
736
- * persistence, those entries die with the runtime, so Codex prompts to trust
737
- * the workspace and hooks on every launch (issue #2470).
738
- *
739
- * This function extracts only trust-state tables (`[projects."<cwd>"]` and
740
- * `[hooks.state."<projectHooksPath>:..."]`) from the runtime config.toml and
741
- * upserts them into the durable project config.toml inside a marker-fenced
742
- * block, preserving any surrounding user-managed content and comments and
743
- * ignoring Codex's NUX counters or other ephemeral runtime-only writes.
744
+ * Marker contents are an ownership boundary, not general TOML. Reject every
745
+ * source form that OMX did not emit rather than rebuilding it and losing data.
744
746
  */
745
- export function syncProjectScopeTrustStateFromRuntime(projectConfig, runtimeConfig, projectHooksPath) {
746
- const parsed = safeParseToml(runtimeConfig);
747
- if (!parsed)
748
- return projectConfig;
749
- const stripped = stripMarkerBlock(projectConfig, OMX_PROJECT_TRUST_START_MARKER, OMX_PROJECT_TRUST_END_MARKER);
750
- const existingHookTrustStateKeys = collectProjectHookTrustStateKeys(stripped);
751
- const trustBlockLines = [];
752
- const projectsTable = parsed.projects;
753
- if (isPlainTomlRecord(projectsTable)) {
754
- for (const [projectKey, entry] of Object.entries(projectsTable).sort(([a], [b]) => a.localeCompare(b))) {
755
- if (!isPlainTomlRecord(entry))
756
- continue;
757
- const serialized = TOML.stringify({ [projectKey]: entry });
758
- const renderedHeader = `[projects."${escapeTomlBasicString(projectKey)}"]`;
759
- const body = serialized
760
- .split(/\r?\n/)
761
- .filter((line) => !/^\s*\[/.test(line) && line.trim() !== "")
762
- .join("\n");
763
- if (body.length === 0)
764
- continue;
765
- trustBlockLines.push(renderedHeader, body, "");
747
+ function inventoryProjectTrustPayload(payload, projectHooksPath) {
748
+ const parsed = safeParseToml(payload);
749
+ const lexical = analyzeTomlSource(payload);
750
+ const headers = projectTrustHeaders(payload, { start: 0, end: payload.length });
751
+ if (!parsed || !lexical.isUnambiguous || !headers)
752
+ return undefined;
753
+ if (Object.keys(parsed).some((key) => key !== "projects" && key !== "hooks"))
754
+ return undefined;
755
+ const lines = sourceLines(payload);
756
+ const headerStarts = new Set(headers.map((header) => header.start));
757
+ for (const [index, line] of lines.entries()) {
758
+ if (!lexical.lineStartsOutsideMultiline[index])
759
+ return undefined;
760
+ const isHeader = headerStarts.has(line.start);
761
+ const isBlankOrComment = line.content.trim() === "" || /^\s*#/.test(line.content);
762
+ if (isHeader || isBlankOrComment) {
763
+ if (isHeader && lexical.lineHasTomlComment[index])
764
+ return undefined;
765
+ continue;
766
766
  }
767
+ if (lexical.lineHasTomlComment[index])
768
+ return undefined;
769
+ const priorHeader = headers.some((header) => header.start < line.start);
770
+ if (!priorHeader)
771
+ return undefined;
767
772
  }
768
- const hooksTable = parsed.hooks;
769
- const hooksState = isPlainTomlRecord(hooksTable) ? hooksTable.state : undefined;
770
- if (isPlainTomlRecord(hooksState)) {
771
- for (const [stateKey, entry] of Object.entries(hooksState).sort(([a], [b]) => a.localeCompare(b))) {
772
- if (!isPlainTomlRecord(entry))
773
- continue;
774
- if (!stateKey.startsWith(`${projectHooksPath}:`))
775
- continue;
776
- if (existingHookTrustStateKeys.has(stateKey))
773
+ const managedHeaders = [];
774
+ for (const header of headers) {
775
+ if (header.path[0] === "projects" && header.path.length >= 2) {
776
+ if (!isPlainTomlRecord(projectTrustValueAtPath(parsed, header.path)))
777
+ return undefined;
778
+ managedHeaders.push({ ...header, kind: "project" });
779
+ continue;
780
+ }
781
+ if (header.path[0] === "hooks" &&
782
+ header.path[1] === "state" &&
783
+ header.path.length === 3 &&
784
+ header.path[2].startsWith(`${projectHooksPath}:`)) {
785
+ const value = projectTrustValueAtPath(parsed, header.path);
786
+ if (!isPlainTomlRecord(value) || typeof value.trusted_hash !== "string" || value.trusted_hash.length === 0 || Object.keys(value).some((key) => key !== "trusted_hash"))
787
+ return undefined;
788
+ managedHeaders.push({ ...header, kind: "hook" });
789
+ continue;
790
+ }
791
+ return undefined;
792
+ }
793
+ if (!projectTrustPayloadProjectFamiliesAreValid(managedHeaders))
794
+ return undefined;
795
+ if (!projectTrustPayloadNestedStructuresAreExplicit(parsed, managedHeaders))
796
+ return undefined;
797
+ return { parsed, headers: managedHeaders };
798
+ }
799
+ function projectTrustSourceNestedStructuresAreExplicit(parsed, headers) {
800
+ const projects = parsed.projects;
801
+ if (projects === undefined)
802
+ return true;
803
+ if (!isPlainTomlRecord(projects))
804
+ return false;
805
+ const hasHeader = (path) => headers.some((header) => projectTrustEqual(header.path, path));
806
+ const visit = (value, path) => {
807
+ for (const [key, child] of Object.entries(value)) {
808
+ if (!isPlainTomlRecord(child))
777
809
  continue;
778
- const trusted = entry.trusted_hash;
779
- if (typeof trusted !== "string" || trusted.length === 0)
810
+ const childPath = [...path, key];
811
+ if (!hasHeader(childPath) || !visit(child, childPath))
812
+ return false;
813
+ }
814
+ return true;
815
+ };
816
+ return Object.entries(projects).every(([key, value]) => isPlainTomlRecord(value) && hasHeader(["projects", key]) && visit(value, ["projects", key]));
817
+ }
818
+ function projectTrustPayloadNestedStructuresAreExplicit(parsed, headers) {
819
+ const hasHeader = (path) => headers.some((header) => header.kind === "project" && projectTrustEqual(header.path, path));
820
+ const visit = (value, path) => {
821
+ for (const [key, child] of Object.entries(value)) {
822
+ if (!isPlainTomlRecord(child))
780
823
  continue;
781
- trustBlockLines.push(`[hooks.state."${escapeTomlBasicString(stateKey)}"]`, `trusted_hash = "${escapeTomlBasicString(trusted)}"`, "");
824
+ const childPath = [...path, key];
825
+ if (!hasHeader(childPath) || !visit(child, childPath))
826
+ return false;
827
+ }
828
+ return true;
829
+ };
830
+ return headers
831
+ .filter((header) => header.kind === "project" && header.path.length === 2)
832
+ .every((header) => {
833
+ const value = projectTrustValueAtPath(parsed, header.path);
834
+ return isPlainTomlRecord(value) && visit(value, header.path);
835
+ });
836
+ }
837
+ function projectTrustPayloadTableSpan(source) {
838
+ const lines = sourceLines(source);
839
+ const lastBodyLine = lines
840
+ .slice(1)
841
+ .reverse()
842
+ .find((line) => line.content.trim() !== "" && !/^\s*#/.test(line.content));
843
+ if (!lastBodyLine)
844
+ return { source, gap: "" };
845
+ const bodyEnd = lastBodyLine.end;
846
+ return { source: source.slice(0, bodyEnd), gap: source.slice(bodyEnd) };
847
+ }
848
+ function projectTrustPayloadTableBodyHasCommentsOrBlanks(source) {
849
+ return sourceLines(source).some((line) => line.content.trim() === "" || /^\s*#/.test(line.content));
850
+ }
851
+ function reconcileProjectTrustPayload(payload, payloadInventory, externalProjects, runtimeProjects, runtimeHooksState, externalHookTrustState, projectHooksPath, eol) {
852
+ const { headers } = payloadInventory;
853
+ const payloadProjectKeys = new Set(headers.filter((header) => header.kind === "project").map((header) => header.path[1]));
854
+ const replacementByHeader = new Map();
855
+ for (const key of payloadProjectKeys) {
856
+ const projectHeaders = headers.filter((header) => header.kind === "project" && header.path[1] === key);
857
+ const parent = projectHeaders.find((header) => header.path.length === 2);
858
+ const payloadValue = projectTrustValueAtPath(payloadInventory.parsed, ["projects", key]);
859
+ const externalValue = externalProjects[key];
860
+ const runtimeValue = runtimeProjects[key];
861
+ if (externalValue !== undefined) {
862
+ if (!projectTrustEqual(payloadValue, externalValue) || (runtimeValue !== undefined && !projectTrustEqual(runtimeValue, externalValue)))
863
+ return undefined;
864
+ for (const header of projectHeaders)
865
+ replacementByHeader.set(header.start, undefined);
866
+ continue;
867
+ }
868
+ if (runtimeValue === undefined) {
869
+ for (const header of projectHeaders)
870
+ replacementByHeader.set(header.start, undefined);
871
+ continue;
872
+ }
873
+ if (!parent || projectHeaders.length !== 1 || !isPlainTomlRecord(runtimeValue))
874
+ return undefined;
875
+ const rendered = renderProjectTrust(key, runtimeValue);
876
+ if (!rendered)
877
+ return undefined;
878
+ replacementByHeader.set(parent.start, rendered.replaceAll("\n", eol));
879
+ }
880
+ for (const header of headers.filter((candidate) => candidate.kind === "hook")) {
881
+ const key = header.path[2];
882
+ const payloadValue = projectTrustValueAtPath(payloadInventory.parsed, header.path);
883
+ const runtimeValue = runtimeHooksState?.[key];
884
+ const externalValue = externalHookTrustState?.[key];
885
+ if (externalValue !== undefined) {
886
+ if (!projectTrustEqual(payloadValue, externalValue))
887
+ return undefined;
888
+ replacementByHeader.set(header.start, undefined);
889
+ continue;
782
890
  }
891
+ if (!isPlainTomlRecord(runtimeValue) || typeof runtimeValue.trusted_hash !== "string" || runtimeValue.trusted_hash.length === 0) {
892
+ replacementByHeader.set(header.start, undefined);
893
+ continue;
894
+ }
895
+ replacementByHeader.set(header.start, `[hooks.state."${escapeTomlBasicString(key)}"]${eol}trusted_hash = "${escapeTomlBasicString(runtimeValue.trusted_hash)}"`);
896
+ }
897
+ const leadingGap = payload.slice(0, headers[0]?.start ?? payload.length);
898
+ const parts = headers.length === 0 ? [] : [leadingGap];
899
+ let trailingGap = headers.length === 0 ? leadingGap : "";
900
+ const appendPart = (part) => {
901
+ if (!part)
902
+ return;
903
+ const previous = parts.at(-1);
904
+ if (previous && !/(?:\r\n|\n)$/.test(previous) && !/^(?:\r\n|\n)/.test(part))
905
+ parts.push(eol);
906
+ parts.push(part);
907
+ };
908
+ for (const [index, header] of headers.entries()) {
909
+ const next = headers[index + 1];
910
+ const segmentEnd = next?.start ?? payload.length;
911
+ const replacement = replacementByHeader.get(header.start);
912
+ const span = projectTrustPayloadTableSpan(payload.slice(header.start, segmentEnd));
913
+ const tableBody = span.source.slice(header.end - header.start);
914
+ if (replacement !== undefined &&
915
+ replacement !== span.source &&
916
+ projectTrustPayloadTableBodyHasCommentsOrBlanks(tableBody)) {
917
+ return undefined;
918
+ }
919
+ if (!next) {
920
+ trailingGap = span.gap;
921
+ if (replacement !== undefined)
922
+ appendPart(replacement);
923
+ continue;
924
+ }
925
+ if (replacement !== undefined)
926
+ appendPart(replacement);
927
+ if (span.gap)
928
+ appendPart(span.gap);
783
929
  }
784
- if (trustBlockLines.length === 0) {
785
- return stripped.length === 0 ? "" : `${stripped}\n`;
930
+ const additions = [];
931
+ for (const [key, value] of Object.entries(runtimeProjects).sort(([left], [right]) => left.localeCompare(right))) {
932
+ if (externalProjects[key] !== undefined || payloadProjectKeys.has(key))
933
+ continue;
934
+ if (!isPlainTomlRecord(value))
935
+ return undefined;
936
+ const rendered = renderProjectTrust(key, value);
937
+ if (!rendered)
938
+ return undefined;
939
+ additions.push(`${rendered.replaceAll("\n", eol)}${eol}`);
786
940
  }
787
- const block = [
788
- OMX_PROJECT_TRUST_START_MARKER,
789
- ...trustBlockLines,
790
- OMX_PROJECT_TRUST_END_MARKER,
791
- "",
792
- ].join("\n");
793
- if (stripped.length === 0)
794
- return block;
795
- return `${stripped}\n\n${block}`;
941
+ for (const [key, value] of Object.entries(runtimeHooksState ?? {}).sort(([left], [right]) => left.localeCompare(right))) {
942
+ if (!key.startsWith(`${projectHooksPath}:`) || externalHookTrustState?.[key] !== undefined || headers.some((header) => header.kind === "hook" && header.path[2] === key))
943
+ continue;
944
+ if (!isPlainTomlRecord(value) || typeof value.trusted_hash !== "string" || value.trusted_hash.length === 0)
945
+ continue;
946
+ additions.push(`[hooks.state."${escapeTomlBasicString(key)}"]${eol}trusted_hash = "${escapeTomlBasicString(value.trusted_hash)}"${eol}`);
947
+ }
948
+ let reconciled = parts.join("");
949
+ if (additions.length > 0 && reconciled && !/(?:\r\n|\n)$/.test(reconciled))
950
+ reconciled += eol;
951
+ reconciled += additions.join("");
952
+ if (trailingGap && reconciled && !/(?:\r\n|\n)$/.test(reconciled) && !/^(?:\r\n|\n)/.test(trailingGap))
953
+ reconciled += eol;
954
+ reconciled += trailingGap;
955
+ reconciled = reconciled.replace(/(?:\r\n|\n)$/, "");
956
+ return Object.keys(safeParseToml(reconciled) ?? {}).length === 0 ? "" : reconciled;
957
+ }
958
+ function projectTrustHeaders(config, region) {
959
+ const lexical = analyzeTomlSource(config);
960
+ if (!lexical.isUnambiguous)
961
+ return undefined;
962
+ const lines = sourceLines(config);
963
+ const headers = [];
964
+ for (const [index, line] of lines.entries()) {
965
+ if (line.start < region.start || line.start >= region.end || !lexical.lineStartsOutsideMultiline[index])
966
+ continue;
967
+ if (!/^\s*\[/.test(line.content))
968
+ continue;
969
+ const path = projectTrustHeaderPath(line.content);
970
+ if (!path) {
971
+ if (/^\s*\[\[?\s*(?:projects|"projects"|'projects')/.test(line.content))
972
+ return undefined;
973
+ continue;
974
+ }
975
+ headers.push({ start: line.start, end: line.end, path });
976
+ }
977
+ return headers;
978
+ }
979
+ function inventoryProjectTrustSource(config) {
980
+ const parsed = safeParseToml(config);
981
+ if (!parsed)
982
+ return undefined;
983
+ const projects = parsed.projects;
984
+ if (projects !== undefined && !isPlainTomlRecord(projects))
985
+ return undefined;
986
+ const headers = projectTrustHeaders(config, { start: 0, end: config.length });
987
+ if (!headers)
988
+ return undefined;
989
+ const projectHeaders = headers.filter((header) => header.path[0] === "projects");
990
+ if (projectHeaders.some((header) => header.path.length < 2))
991
+ return undefined;
992
+ for (const key of Object.keys(projects ?? {})) {
993
+ if (projectHeaders.filter((header) => header.path.length === 2 && header.path[1] === key).length !== 1) {
994
+ return undefined;
995
+ }
996
+ }
997
+ if (!projectTrustSourceNestedStructuresAreExplicit(parsed, projectHeaders))
998
+ return undefined;
999
+ return { projects: projects ?? {}, headers };
1000
+ }
1001
+ function projectTrustCandidateIsSafe(candidate, external, runtimeProjects) {
1002
+ const marker = projectTrustMarkerRange(candidate);
1003
+ const inventory = inventoryProjectTrustSource(candidate);
1004
+ const externalInventory = inventoryProjectTrustSource(external);
1005
+ if (!inventory || !externalInventory)
1006
+ return false;
1007
+ const hasMarker = marker !== undefined && marker.start !== marker.end;
1008
+ const hasEquivalentExternalBytes = (actual) => {
1009
+ if (actual === external)
1010
+ return true;
1011
+ return splitProjectTrustTrailingEols(actual).body === splitProjectTrustTrailingEols(external).body;
1012
+ };
1013
+ if (hasMarker) {
1014
+ const candidateExternal = `${candidate.slice(0, marker.start)}${candidate.slice(marker.end)}`;
1015
+ if (!hasEquivalentExternalBytes(candidateExternal) && !(candidate.startsWith(external) && candidate.slice(marker.end).length === 0 && /^(?:\r\n|\n)+$/.test(candidate.slice(external.length, marker.start))))
1016
+ return false;
1017
+ }
1018
+ else if (!hasEquivalentExternalBytes(candidate)) {
1019
+ return false;
1020
+ }
1021
+ const expectedProjects = { ...externalInventory.projects, ...runtimeProjects };
1022
+ if (!projectTrustEqual(inventory.projects, expectedProjects))
1023
+ return false;
1024
+ for (const [key, value] of Object.entries(expectedProjects)) {
1025
+ if (!projectTrustEqual(inventory.projects[key], value))
1026
+ return false;
1027
+ if (inventory.headers.filter((header) => header.path[0] === "projects" && header.path.length === 2 && header.path[1] === key).length !== 1)
1028
+ return false;
1029
+ }
1030
+ return true;
1031
+ }
1032
+ function projectTrustPayloadProjectFamiliesAreValid(headers) {
1033
+ const projectKeys = new Set(headers.filter((header) => header.kind === "project").map((header) => header.path[1]));
1034
+ for (const key of projectKeys) {
1035
+ const family = headers.filter((header) => header.kind === "project" && header.path[1] === key);
1036
+ const parents = family.filter((header) => header.path.length === 2);
1037
+ if (parents.length !== 1 || !projectTrustFamilyIsContiguous(headers, parents[0]))
1038
+ return false;
1039
+ }
1040
+ return true;
1041
+ }
1042
+ function projectTrustFamilyIsContiguous(headers, parent) {
1043
+ const first = headers.indexOf(parent);
1044
+ let ended = false;
1045
+ for (let index = first + 1; index < headers.length; index += 1) {
1046
+ const path = headers[index].path;
1047
+ const belongsToFamily = path[0] === "projects" && path[1] === parent.path[1];
1048
+ if (!belongsToFamily) {
1049
+ ended = true;
1050
+ continue;
1051
+ }
1052
+ if (ended || path.length < 3)
1053
+ return false;
1054
+ }
1055
+ return true;
1056
+ }
1057
+ function projectTrustEqual(left, right) {
1058
+ if (Object.is(left, right))
1059
+ return true;
1060
+ if (left instanceof Date || right instanceof Date) {
1061
+ return left instanceof Date && right instanceof Date && left.constructor === right.constructor && String(left) === String(right);
1062
+ }
1063
+ if (Array.isArray(left) || Array.isArray(right)) {
1064
+ return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((value, index) => projectTrustEqual(value, right[index]));
1065
+ }
1066
+ if (!isPlainTomlRecord(left) || !isPlainTomlRecord(right))
1067
+ return false;
1068
+ const leftKeys = Object.keys(left).sort();
1069
+ const rightKeys = Object.keys(right).sort();
1070
+ return leftKeys.length === rightKeys.length && leftKeys.every((key, index) => key === rightKeys[index] && projectTrustEqual(left[key], right[key]));
1071
+ }
1072
+ function canRenderFlatProjectTrust(entry) {
1073
+ const isValue = (value) => {
1074
+ if (Array.isArray(value))
1075
+ return value.every(isValue);
1076
+ return !isPlainTomlRecord(value);
1077
+ };
1078
+ return Object.values(entry).every(isValue);
1079
+ }
1080
+ function renderProjectTrust(projectKey, entry) {
1081
+ if (!canRenderFlatProjectTrust(entry))
1082
+ return undefined;
1083
+ const serialized = TOML.stringify({ [projectKey]: entry });
1084
+ const body = serialized.split(/\r?\n/).filter((line) => !/^\s*\[/.test(line) && line.trim() !== "").join("\n");
1085
+ return body ? `[projects."${escapeTomlBasicString(projectKey)}"]\n${body}` : undefined;
1086
+ }
1087
+ function splitProjectTrustTrailingEols(source) {
1088
+ const match = source.match(/(?:(?:\r\n|\n))*$/);
1089
+ const trailingEols = match?.[0] ?? "";
1090
+ return { body: source.slice(0, source.length - trailingEols.length), trailingEols };
1091
+ }
1092
+ function appendProjectTrustMarker(base, payload, eol, trailingEols) {
1093
+ if (!payload)
1094
+ return `${splitProjectTrustTrailingEols(base).body}${trailingEols}`;
1095
+ const { body } = splitProjectTrustTrailingEols(base);
1096
+ const separator = body ? `${eol}${eol}` : "";
1097
+ const block = `${OMX_PROJECT_TRUST_START_MARKER}${eol}${payload}${eol}${OMX_PROJECT_TRUST_END_MARKER}`;
1098
+ return `${body}${separator}${block}${trailingEols}`;
1099
+ }
1100
+ function reconcileProjectScopeTrustState(projectConfig, runtimeConfig, projectHooksPath, requireExistingPayload) {
1101
+ const durable = projectTrustSemanticView(projectConfig);
1102
+ const runtime = projectTrustSemanticView(runtimeConfig);
1103
+ if (!durable || !runtime)
1104
+ return projectConfig;
1105
+ if (hasInlineProjectTrustMarkerComment(durable.source) || hasInlineProjectTrustMarkerComment(runtime.source))
1106
+ return projectConfig;
1107
+ const parsedRuntime = safeParseToml(runtime.source);
1108
+ const runtimeInventory = inventoryProjectTrustSource(runtime.source);
1109
+ if (!parsedRuntime || !runtimeInventory)
1110
+ return projectConfig;
1111
+ const marker = projectTrustMarkerRange(durable.source);
1112
+ if (!marker)
1113
+ return projectConfig;
1114
+ const hasMarker = marker.start !== marker.end;
1115
+ const payload = durable.source.slice(marker.payloadStart, marker.payloadEnd);
1116
+ const payloadIsEmpty = projectTrustPayloadIsSemanticallyEmpty(payload);
1117
+ const payloadInventory = hasMarker
1118
+ ? inventoryProjectTrustPayload(payload, projectHooksPath)
1119
+ : { parsed: {}, headers: [] };
1120
+ if (!payloadInventory)
1121
+ return projectConfig;
1122
+ if (requireExistingPayload && (!hasMarker || payloadIsEmpty))
1123
+ return projectConfig;
1124
+ const external = hasMarker
1125
+ ? `${durable.source.slice(0, marker.start)}${durable.source.slice(marker.end)}`
1126
+ : durable.source;
1127
+ const trailingEols = splitProjectTrustTrailingEols(durable.source).trailingEols;
1128
+ const externalInventory = inventoryProjectTrustSource(external);
1129
+ if (!externalInventory)
1130
+ return projectConfig;
1131
+ const projects = runtimeInventory.projects;
1132
+ const externalProjects = externalInventory.projects;
1133
+ const headersBeforeMarker = projectTrustHeaders(durable.source, { start: 0, end: marker.start });
1134
+ const headersAfterMarker = projectTrustHeaders(durable.source, { start: marker.end, end: durable.source.length });
1135
+ if (!headersBeforeMarker || !headersAfterMarker)
1136
+ return projectConfig;
1137
+ const headers = [...headersBeforeMarker, ...headersAfterMarker];
1138
+ for (const key of Object.keys(externalProjects)) {
1139
+ const family = headers.filter((header) => header.path[0] === "projects" && header.path[1] === key);
1140
+ const parents = family.filter((header) => header.path.length === 2);
1141
+ if (parents.length !== 1)
1142
+ return projectConfig;
1143
+ const parentIsBeforeMarker = parents[0].start < marker.start;
1144
+ if (family.some((header) => (header.start < marker.start) !== parentIsBeforeMarker))
1145
+ return projectConfig;
1146
+ if (!projectTrustFamilyIsContiguous(headers, parents[0]))
1147
+ return projectConfig;
1148
+ }
1149
+ const eol = dominantProjectTrustEol(durable.source);
1150
+ const hooks = parsedRuntime.hooks;
1151
+ const hooksState = isPlainTomlRecord(hooks) && isPlainTomlRecord(hooks.state) ? hooks.state : undefined;
1152
+ const nextPayload = reconcileProjectTrustPayload(payload, payloadInventory, externalProjects, projects, hooksState, projectHookTrustState(external), projectHooksPath, eol);
1153
+ if (nextPayload === undefined)
1154
+ return projectConfig;
1155
+ if (!inventoryProjectTrustPayload(nextPayload, projectHooksPath))
1156
+ return projectConfig;
1157
+ const candidate = appendProjectTrustMarker(external, nextPayload, eol, trailingEols);
1158
+ if (!projectTrustCandidateIsSafe(candidate, external, projects))
1159
+ return projectConfig;
1160
+ return `${durable.prefix}${candidate}`;
1161
+ }
1162
+ /** Repairs only a uniquely parseable, nonempty project-sync payload before mirror creation. */
1163
+ export function repairProjectScopeTrustStateForLaunch(projectConfig, projectHooksPath) {
1164
+ const durable = projectTrustSemanticView(projectConfig);
1165
+ if (!durable)
1166
+ return projectConfig;
1167
+ const marker = projectTrustMarkerRange(durable.source);
1168
+ if (!marker || marker.start === marker.end)
1169
+ return projectConfig;
1170
+ const payload = durable.source.slice(marker.payloadStart, marker.payloadEnd);
1171
+ return reconcileProjectScopeTrustState(projectConfig, payload, projectHooksPath, true);
1172
+ }
1173
+ /** Conservatively persists runtime project and hook trust without rewriting external TOML. */
1174
+ export function syncProjectScopeTrustStateFromRuntime(projectConfig, runtimeConfig, projectHooksPath) {
1175
+ return reconcileProjectScopeTrustState(projectConfig, runtimeConfig, projectHooksPath, false);
796
1176
  }
797
1177
  function isMultilineTomlString(mode) {
798
1178
  return mode === "multiline-basic" || mode === "multiline-literal";