oh-my-codex 0.20.0 → 0.20.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (661) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +10 -1
  4. package/dist/adapt/__tests__/hermes.test.js.map +1 -1
  5. package/dist/adapt/contracts.d.ts.map +1 -1
  6. package/dist/agents/native-config.js.map +1 -1
  7. package/dist/auth/__tests__/hotswap.test.d.ts +2 -0
  8. package/dist/auth/__tests__/hotswap.test.d.ts.map +1 -0
  9. package/dist/auth/__tests__/hotswap.test.js +183 -0
  10. package/dist/auth/__tests__/hotswap.test.js.map +1 -0
  11. package/dist/auth/hotswap.d.ts.map +1 -1
  12. package/dist/auth/hotswap.js +17 -4
  13. package/dist/auth/hotswap.js.map +1 -1
  14. package/dist/auth/paths.js.map +1 -1
  15. package/dist/auth/storage.js.map +1 -1
  16. package/dist/autopilot/__tests__/ralplan-gate.test.js +4 -4
  17. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
  18. package/dist/autopilot/completion-gate.js.map +1 -1
  19. package/dist/autopilot/fsm.d.ts +1 -1
  20. package/dist/autopilot/fsm.d.ts.map +1 -1
  21. package/dist/autoresearch/goal.js.map +1 -1
  22. package/dist/autoresearch/runtime.js.map +1 -1
  23. package/dist/capabilities/lockfile.js.map +1 -1
  24. package/dist/catalog/reader.js.map +1 -1
  25. package/dist/catalog/skill-mirror.js.map +1 -1
  26. package/dist/cli/__tests__/adapt-help.test.js.map +1 -1
  27. package/dist/cli/__tests__/agents-init.test.js.map +1 -1
  28. package/dist/cli/__tests__/agents.test.js.map +1 -1
  29. package/dist/cli/__tests__/api.test.js.map +1 -1
  30. package/dist/cli/__tests__/ask.test.js.map +1 -1
  31. package/dist/cli/__tests__/auth.test.js.map +1 -1
  32. package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
  33. package/dist/cli/__tests__/autoresearch.test.js.map +1 -1
  34. package/dist/cli/__tests__/capabilities.test.js.map +1 -1
  35. package/dist/cli/__tests__/codex-plugin-layout.test.js +27 -0
  36. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  37. package/dist/cli/__tests__/doctor-context-window-warning.test.js +83 -78
  38. package/dist/cli/__tests__/doctor-context-window-warning.test.js.map +1 -1
  39. package/dist/cli/__tests__/doctor-invalid-config.test.js +56 -4
  40. package/dist/cli/__tests__/doctor-invalid-config.test.js.map +1 -1
  41. package/dist/cli/__tests__/doctor-team.test.js.map +1 -1
  42. package/dist/cli/__tests__/doctor-warning-copy.test.js +735 -14
  43. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  44. package/dist/cli/__tests__/exec.test.js.map +1 -1
  45. package/dist/cli/__tests__/imagegen-continuation.test.js.map +1 -1
  46. package/dist/cli/__tests__/index.test.js +216 -11
  47. package/dist/cli/__tests__/index.test.js.map +1 -1
  48. package/dist/cli/__tests__/launch-fallback.test.js +49 -3
  49. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  50. package/dist/cli/__tests__/mcp-parity.test.js +84 -12
  51. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  52. package/dist/cli/__tests__/native-hook-claim-journal.test.d.ts +2 -0
  53. package/dist/cli/__tests__/native-hook-claim-journal.test.d.ts.map +1 -0
  54. package/dist/cli/__tests__/native-hook-claim-journal.test.js +128 -0
  55. package/dist/cli/__tests__/native-hook-claim-journal.test.js.map +1 -0
  56. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  57. package/dist/cli/__tests__/question.test.js.map +1 -1
  58. package/dist/cli/__tests__/ralph-deslop-contract.test.js.map +1 -1
  59. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +6 -0
  60. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  61. package/dist/cli/__tests__/ralph-prd-deep-interview.test.js.map +1 -1
  62. package/dist/cli/__tests__/ralph-prd-smoke.test.js.map +1 -1
  63. package/dist/cli/__tests__/ralph.test.js +8 -3
  64. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  65. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.d.ts +2 -0
  66. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.d.ts.map +1 -0
  67. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js +187 -0
  68. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js.map +1 -0
  69. package/dist/cli/__tests__/ralplan.test.d.ts +2 -0
  70. package/dist/cli/__tests__/ralplan.test.d.ts.map +1 -0
  71. package/dist/cli/__tests__/ralplan.test.js +129 -0
  72. package/dist/cli/__tests__/ralplan.test.js.map +1 -0
  73. package/dist/cli/__tests__/resume.test.js +6 -1
  74. package/dist/cli/__tests__/resume.test.js.map +1 -1
  75. package/dist/cli/__tests__/session-scoped-runtime.test.js +25 -0
  76. package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
  77. package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
  78. package/dist/cli/__tests__/session-search.test.js.map +1 -1
  79. package/dist/cli/__tests__/setup-agents-overwrite.test.js +79 -0
  80. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  81. package/dist/cli/__tests__/setup-gh-star.test.js.map +1 -1
  82. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +131 -5
  83. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +1 -1
  84. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.d.ts +2 -0
  85. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.d.ts.map +1 -0
  86. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.js +285 -0
  87. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.js.map +1 -0
  88. package/dist/cli/__tests__/setup-install-mode.test.js +2264 -17
  89. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  90. package/dist/cli/__tests__/setup-preferences.test.d.ts +2 -0
  91. package/dist/cli/__tests__/setup-preferences.test.d.ts.map +1 -0
  92. package/dist/cli/__tests__/setup-preferences.test.js +79 -0
  93. package/dist/cli/__tests__/setup-preferences.test.js.map +1 -0
  94. package/dist/cli/__tests__/setup-refresh.test.js +168 -5
  95. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  96. package/dist/cli/__tests__/setup-scope.test.js +73 -6
  97. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  98. package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
  99. package/dist/cli/__tests__/state.test.js +37 -0
  100. package/dist/cli/__tests__/state.test.js.map +1 -1
  101. package/dist/cli/__tests__/team.test.js.map +1 -1
  102. package/dist/cli/__tests__/ultragoal.test.js +61 -1
  103. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  104. package/dist/cli/__tests__/uninstall.test.js +2067 -26
  105. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  106. package/dist/cli/__tests__/update.test.js +58 -0
  107. package/dist/cli/__tests__/update.test.js.map +1 -1
  108. package/dist/cli/__tests__/url.test.js.map +1 -1
  109. package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
  110. package/dist/cli/adapt.js.map +1 -1
  111. package/dist/cli/agents-init.js.map +1 -1
  112. package/dist/cli/agents.js.map +1 -1
  113. package/dist/cli/api.js.map +1 -1
  114. package/dist/cli/ask.d.ts +1 -1
  115. package/dist/cli/ask.d.ts.map +1 -1
  116. package/dist/cli/ask.js.map +1 -1
  117. package/dist/cli/auth.js.map +1 -1
  118. package/dist/cli/autoresearch-guided.js.map +1 -1
  119. package/dist/cli/autoresearch-intake.js.map +1 -1
  120. package/dist/cli/capabilities.js.map +1 -1
  121. package/dist/cli/cleanup.js.map +1 -1
  122. package/dist/cli/codex-feature-probe.js.map +1 -1
  123. package/dist/cli/codex-home.js.map +1 -1
  124. package/dist/cli/doctor.d.ts +20 -0
  125. package/dist/cli/doctor.d.ts.map +1 -1
  126. package/dist/cli/doctor.js +636 -161
  127. package/dist/cli/doctor.js.map +1 -1
  128. package/dist/cli/explore.js.map +1 -1
  129. package/dist/cli/index.d.ts +11 -3
  130. package/dist/cli/index.d.ts.map +1 -1
  131. package/dist/cli/index.js +108 -20
  132. package/dist/cli/index.js.map +1 -1
  133. package/dist/cli/mcp-serve.js.map +1 -1
  134. package/dist/cli/mission.js.map +1 -1
  135. package/dist/cli/native-assets.js.map +1 -1
  136. package/dist/cli/native-hook-claim-journal.d.ts +18 -0
  137. package/dist/cli/native-hook-claim-journal.d.ts.map +1 -0
  138. package/dist/cli/native-hook-claim-journal.js +239 -0
  139. package/dist/cli/native-hook-claim-journal.js.map +1 -0
  140. package/dist/cli/omx.js.map +1 -1
  141. package/dist/cli/plugin-marketplace.js.map +1 -1
  142. package/dist/cli/project-runtime-codex-homes.js.map +1 -1
  143. package/dist/cli/question.js.map +1 -1
  144. package/dist/cli/ralph.d.ts.map +1 -1
  145. package/dist/cli/ralph.js +3 -2
  146. package/dist/cli/ralph.js.map +1 -1
  147. package/dist/cli/ralplan.d.ts +14 -0
  148. package/dist/cli/ralplan.d.ts.map +1 -0
  149. package/dist/cli/ralplan.js +203 -0
  150. package/dist/cli/ralplan.js.map +1 -0
  151. package/dist/cli/setup-preferences.d.ts +11 -0
  152. package/dist/cli/setup-preferences.d.ts.map +1 -1
  153. package/dist/cli/setup-preferences.js +32 -0
  154. package/dist/cli/setup-preferences.js.map +1 -1
  155. package/dist/cli/setup.d.ts +53 -0
  156. package/dist/cli/setup.d.ts.map +1 -1
  157. package/dist/cli/setup.js +1403 -400
  158. package/dist/cli/setup.js.map +1 -1
  159. package/dist/cli/sparkshell.js.map +1 -1
  160. package/dist/cli/star-prompt.js.map +1 -1
  161. package/dist/cli/state.d.ts.map +1 -1
  162. package/dist/cli/state.js +2 -1
  163. package/dist/cli/state.js.map +1 -1
  164. package/dist/cli/team.js.map +1 -1
  165. package/dist/cli/ultragoal.d.ts +5 -1
  166. package/dist/cli/ultragoal.d.ts.map +1 -1
  167. package/dist/cli/ultragoal.js +34 -2
  168. package/dist/cli/ultragoal.js.map +1 -1
  169. package/dist/cli/uninstall.d.ts +8 -0
  170. package/dist/cli/uninstall.d.ts.map +1 -1
  171. package/dist/cli/uninstall.js +947 -99
  172. package/dist/cli/uninstall.js.map +1 -1
  173. package/dist/cli/update.d.ts.map +1 -1
  174. package/dist/cli/update.js +8 -1
  175. package/dist/cli/update.js.map +1 -1
  176. package/dist/cli/url.js.map +1 -1
  177. package/dist/compat/__tests__/doctor-contract.test.js +5 -1
  178. package/dist/compat/__tests__/doctor-contract.test.js.map +1 -1
  179. package/dist/compat/__tests__/rust-runtime-compat.test.js.map +1 -1
  180. package/dist/config/__tests__/codex-hooks.test.js +1287 -11
  181. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  182. package/dist/config/__tests__/generator-idempotent.test.js +1049 -135
  183. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  184. package/dist/config/__tests__/generator-notify.test.js +28 -49
  185. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  186. package/dist/config/codex-feature-flags.d.ts.map +1 -1
  187. package/dist/config/codex-feature-flags.js.map +1 -1
  188. package/dist/config/codex-hooks.d.ts +124 -1
  189. package/dist/config/codex-hooks.d.ts.map +1 -1
  190. package/dist/config/codex-hooks.js +2142 -265
  191. package/dist/config/codex-hooks.js.map +1 -1
  192. package/dist/config/commit-lore-guard.js.map +1 -1
  193. package/dist/config/generator.d.ts +40 -26
  194. package/dist/config/generator.d.ts.map +1 -1
  195. package/dist/config/generator.js +1159 -288
  196. package/dist/config/generator.js.map +1 -1
  197. package/dist/config/mcp-registry.js.map +1 -1
  198. package/dist/config/models.d.ts +2 -2
  199. package/dist/config/models.d.ts.map +1 -1
  200. package/dist/config/models.js.map +1 -1
  201. package/dist/config/omx-first-party-mcp.js.map +1 -1
  202. package/dist/config/team-mode.d.ts.map +1 -1
  203. package/dist/document-refresh/__tests__/enforcer.test.js.map +1 -1
  204. package/dist/document-refresh/enforcer.js.map +1 -1
  205. package/dist/exec/followup.js.map +1 -1
  206. package/dist/goal-workflows/artifacts.js.map +1 -1
  207. package/dist/hooks/__tests__/agents-overlay.test.js +7 -4
  208. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  209. package/dist/hooks/__tests__/analyze-routing-contract.test.js.map +1 -1
  210. package/dist/hooks/__tests__/analyze-skill-contract.test.js.map +1 -1
  211. package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
  212. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +9 -3
  213. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  214. package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
  215. package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
  216. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  217. package/dist/hooks/__tests__/design-skill.test.js.map +1 -1
  218. package/dist/hooks/__tests__/keyword-detector.test.js +1372 -2
  219. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  220. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +14 -0
  221. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  222. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  223. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +669 -1
  224. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  225. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +2 -0
  226. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
  227. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +142 -1
  228. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  229. package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
  230. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js +48 -2
  231. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js.map +1 -1
  232. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +61 -44
  233. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
  234. package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
  235. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js +3 -2
  236. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js.map +1 -1
  237. package/dist/hooks/__tests__/notify-hook-session-scope.test.js +243 -4
  238. package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
  239. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  240. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +8 -1
  241. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  242. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  243. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  244. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
  245. package/dist/hooks/__tests__/notify-hook-visual-verdict.test.js.map +1 -1
  246. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  247. package/dist/hooks/__tests__/openclaw-setup-contract.test.js.map +1 -1
  248. package/dist/hooks/__tests__/pre-context-gate-skills.test.js.map +1 -1
  249. package/dist/hooks/__tests__/prometheus-strict-contract.test.js.map +1 -1
  250. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js.map +1 -1
  251. package/dist/hooks/__tests__/prompt-guidance-test-helpers.js.map +1 -1
  252. package/dist/hooks/__tests__/prompt-session-provenance.test.d.ts +2 -0
  253. package/dist/hooks/__tests__/prompt-session-provenance.test.d.ts.map +1 -0
  254. package/dist/hooks/__tests__/prompt-session-provenance.test.js +82 -0
  255. package/dist/hooks/__tests__/prompt-session-provenance.test.js.map +1 -0
  256. package/dist/hooks/__tests__/session.test.js +532 -11
  257. package/dist/hooks/__tests__/session.test.js.map +1 -1
  258. package/dist/hooks/__tests__/skill-catalog-hygiene.test.js.map +1 -1
  259. package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.js.map +1 -1
  260. package/dist/hooks/__tests__/visual-ralph-skill.test.js.map +1 -1
  261. package/dist/hooks/__tests__/visual-verdict-loop.test.js.map +1 -1
  262. package/dist/hooks/agents-overlay.js +5 -5
  263. package/dist/hooks/agents-overlay.js.map +1 -1
  264. package/dist/hooks/code-simplifier/index.js.map +1 -1
  265. package/dist/hooks/explore-routing.js.map +1 -1
  266. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  267. package/dist/hooks/extensibility/events.js.map +1 -1
  268. package/dist/hooks/extensibility/loader.js.map +1 -1
  269. package/dist/hooks/extensibility/plugin-runner-stdin.js.map +1 -1
  270. package/dist/hooks/extensibility/sdk/logging.js.map +1 -1
  271. package/dist/hooks/keyword-detector.d.ts +33 -7
  272. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  273. package/dist/hooks/keyword-detector.js +2228 -119
  274. package/dist/hooks/keyword-detector.js.map +1 -1
  275. package/dist/hooks/keyword-registry.d.ts +12 -0
  276. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  277. package/dist/hooks/keyword-registry.js +41 -0
  278. package/dist/hooks/keyword-registry.js.map +1 -1
  279. package/dist/hooks/prompt-session-provenance.d.ts +87 -0
  280. package/dist/hooks/prompt-session-provenance.d.ts.map +1 -0
  281. package/dist/hooks/prompt-session-provenance.js +180 -0
  282. package/dist/hooks/prompt-session-provenance.js.map +1 -0
  283. package/dist/hooks/session.d.ts +139 -34
  284. package/dist/hooks/session.d.ts.map +1 -1
  285. package/dist/hooks/session.js +1239 -317
  286. package/dist/hooks/session.js.map +1 -1
  287. package/dist/hooks/task-size-detector.js.map +1 -1
  288. package/dist/hud/__tests__/state.test.js.map +1 -1
  289. package/dist/hud/authority.js.map +1 -1
  290. package/dist/hud/constants.js.map +1 -1
  291. package/dist/hud/index.js.map +1 -1
  292. package/dist/hud/reconcile.js.map +1 -1
  293. package/dist/hud/render.js.map +1 -1
  294. package/dist/hud/state.js.map +1 -1
  295. package/dist/hud/tmux.js.map +1 -1
  296. package/dist/leader/__tests__/contract.test.js +146 -2
  297. package/dist/leader/__tests__/contract.test.js.map +1 -1
  298. package/dist/leader/contract.d.ts +26 -5
  299. package/dist/leader/contract.d.ts.map +1 -1
  300. package/dist/leader/contract.js +131 -7
  301. package/dist/leader/contract.js.map +1 -1
  302. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  303. package/dist/mcp/__tests__/server-lifecycle.test.js +15 -2
  304. package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
  305. package/dist/mcp/__tests__/state-paths.test.js +98 -1
  306. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  307. package/dist/mcp/bootstrap.js.map +1 -1
  308. package/dist/mcp/code-intel-server.d.ts +58 -58
  309. package/dist/mcp/code-intel-server.d.ts.map +1 -1
  310. package/dist/mcp/code-intel-server.js.map +1 -1
  311. package/dist/mcp/hermes-bridge.js.map +1 -1
  312. package/dist/mcp/hermes-server.d.ts +70 -70
  313. package/dist/mcp/hermes-server.d.ts.map +1 -1
  314. package/dist/mcp/lifecycle-telemetry.js.map +1 -1
  315. package/dist/mcp/memory-server.d.ts +34 -34
  316. package/dist/mcp/memory-server.d.ts.map +1 -1
  317. package/dist/mcp/memory-server.js.map +1 -1
  318. package/dist/mcp/state-paths.d.ts +14 -0
  319. package/dist/mcp/state-paths.d.ts.map +1 -1
  320. package/dist/mcp/state-paths.js +96 -20
  321. package/dist/mcp/state-paths.js.map +1 -1
  322. package/dist/mcp/state-server.d.ts +47 -47
  323. package/dist/mcp/state-server.d.ts.map +1 -1
  324. package/dist/mcp/trace-server.d.ts +4 -4
  325. package/dist/mcp/trace-server.d.ts.map +1 -1
  326. package/dist/mcp/trace-server.js.map +1 -1
  327. package/dist/mcp/wiki-server.d.ts +23 -23
  328. package/dist/mcp/wiki-server.d.ts.map +1 -1
  329. package/dist/mcp/wiki-server.js.map +1 -1
  330. package/dist/modes/__tests__/base-session-scope.test.js +53 -1
  331. package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
  332. package/dist/modes/base.d.ts.map +1 -1
  333. package/dist/modes/base.js +5 -5
  334. package/dist/modes/base.js.map +1 -1
  335. package/dist/notifications/__tests__/lifecycle-dedupe.test.js.map +1 -1
  336. package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
  337. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js +177 -85
  338. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js.map +1 -1
  339. package/dist/notifications/__tests__/session-registry.test.js.map +1 -1
  340. package/dist/notifications/__tests__/template-engine.test.js.map +1 -1
  341. package/dist/notifications/__tests__/verbosity.test.js.map +1 -1
  342. package/dist/notifications/http-client.js.map +1 -1
  343. package/dist/notifications/index.d.ts +4 -1
  344. package/dist/notifications/index.d.ts.map +1 -1
  345. package/dist/notifications/index.js +3 -9
  346. package/dist/notifications/index.js.map +1 -1
  347. package/dist/notifications/lifecycle-dedupe.js.map +1 -1
  348. package/dist/notifications/reply-listener.d.ts.map +1 -1
  349. package/dist/notifications/reply-listener.js.map +1 -1
  350. package/dist/notifications/session-registry.js.map +1 -1
  351. package/dist/notifications/session-status.js.map +1 -1
  352. package/dist/notifications/temp-contract.js.map +1 -1
  353. package/dist/notifications/tmux-detector.js.map +1 -1
  354. package/dist/notifications/tmux.js.map +1 -1
  355. package/dist/openclaw/config.js.map +1 -1
  356. package/dist/openclaw/dispatcher.js.map +1 -1
  357. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  358. package/dist/pipeline/__tests__/stages.test.js +8 -5
  359. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  360. package/dist/pipeline/stages/code-review.js.map +1 -1
  361. package/dist/pipeline/stages/ralph-verify.js.map +1 -1
  362. package/dist/pipeline/stages/ralplan.js.map +1 -1
  363. package/dist/pipeline/stages/team-exec.js.map +1 -1
  364. package/dist/pipeline/stages/ultragoal.js.map +1 -1
  365. package/dist/pipeline/stages/ultraqa.js.map +1 -1
  366. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js.map +1 -1
  367. package/dist/planning/artifact-names.js.map +1 -1
  368. package/dist/planning/artifacts.js.map +1 -1
  369. package/dist/question/__tests__/state.test.js.map +1 -1
  370. package/dist/question/__tests__/ui.test.js.map +1 -1
  371. package/dist/question/autopilot-wait.js.map +1 -1
  372. package/dist/question/client.d.ts.map +1 -1
  373. package/dist/question/client.js.map +1 -1
  374. package/dist/question/deep-interview.js.map +1 -1
  375. package/dist/question/events.js.map +1 -1
  376. package/dist/question/renderer.js.map +1 -1
  377. package/dist/question/state.d.ts.map +1 -1
  378. package/dist/question/state.js.map +1 -1
  379. package/dist/question/ui.js.map +1 -1
  380. package/dist/ralph/contract.d.ts +1 -1
  381. package/dist/ralph/contract.d.ts.map +1 -1
  382. package/dist/ralplan/__tests__/consensus-gate.test.js +403 -8
  383. package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
  384. package/dist/ralplan/__tests__/runtime.test.js +112 -14
  385. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  386. package/dist/ralplan/consensus-gate.d.ts +3 -3
  387. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  388. package/dist/ralplan/consensus-gate.js +155 -60
  389. package/dist/ralplan/consensus-gate.js.map +1 -1
  390. package/dist/ralplan/runtime.d.ts +2 -2
  391. package/dist/ralplan/runtime.d.ts.map +1 -1
  392. package/dist/ralplan/runtime.js +1 -2
  393. package/dist/ralplan/runtime.js.map +1 -1
  394. package/dist/runtime/bridge.d.ts.map +1 -1
  395. package/dist/runtime/bridge.js.map +1 -1
  396. package/dist/runtime/process-tree.js.map +1 -1
  397. package/dist/runtime/run-loop.js.map +1 -1
  398. package/dist/runtime/run-outcome.d.ts +3 -3
  399. package/dist/runtime/run-outcome.d.ts.map +1 -1
  400. package/dist/runtime/run-outcome.js.map +1 -1
  401. package/dist/runtime/run-state.js.map +1 -1
  402. package/dist/scripts/__tests__/codex-native-hook.test.js +5414 -801
  403. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  404. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.d.ts +2 -0
  405. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.d.ts.map +1 -0
  406. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.js +634 -0
  407. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.js.map +1 -0
  408. package/dist/scripts/__tests__/generate-release-body.test.js.map +1 -1
  409. package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -1
  410. package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -1
  411. package/dist/scripts/__tests__/notify-fallback-delivery.test.d.ts +2 -0
  412. package/dist/scripts/__tests__/notify-fallback-delivery.test.d.ts.map +1 -0
  413. package/dist/scripts/__tests__/notify-fallback-delivery.test.js +174 -0
  414. package/dist/scripts/__tests__/notify-fallback-delivery.test.js.map +1 -0
  415. package/dist/scripts/__tests__/notify-state-io.test.js +57 -1
  416. package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
  417. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.d.ts +2 -0
  418. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.d.ts.map +1 -0
  419. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js +205 -0
  420. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js.map +1 -0
  421. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.d.ts +2 -0
  422. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.d.ts.map +1 -0
  423. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js +175 -0
  424. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js.map +1 -0
  425. package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
  426. package/dist/scripts/__tests__/smoke-packed-install.test.js +848 -4
  427. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  428. package/dist/scripts/__tests__/test-reply-listener-live.test.js.map +1 -1
  429. package/dist/scripts/build-api.js.map +1 -1
  430. package/dist/scripts/build-explore-harness.js.map +1 -1
  431. package/dist/scripts/build-sparkshell.js.map +1 -1
  432. package/dist/scripts/cleanup-explore-harness.js.map +1 -1
  433. package/dist/scripts/codex-execution-surface.js.map +1 -1
  434. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  435. package/dist/scripts/codex-native-hook.js +825 -172
  436. package/dist/scripts/codex-native-hook.js.map +1 -1
  437. package/dist/scripts/generate-native-release-manifest.js.map +1 -1
  438. package/dist/scripts/generate-release-body.js.map +1 -1
  439. package/dist/scripts/hook-payload-guard.d.ts.map +1 -1
  440. package/dist/scripts/hook-payload-guard.js.map +1 -1
  441. package/dist/scripts/notify-fallback-delivery.d.ts +48 -0
  442. package/dist/scripts/notify-fallback-delivery.d.ts.map +1 -0
  443. package/dist/scripts/notify-fallback-delivery.js +419 -0
  444. package/dist/scripts/notify-fallback-delivery.js.map +1 -0
  445. package/dist/scripts/notify-fallback-watcher.js +136 -30
  446. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  447. package/dist/scripts/notify-hook/auto-nudge.d.ts +18 -9
  448. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  449. package/dist/scripts/notify-hook/auto-nudge.js +39 -13
  450. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  451. package/dist/scripts/notify-hook/managed-tmux.d.ts +23 -0
  452. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  453. package/dist/scripts/notify-hook/managed-tmux.js +122 -12
  454. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  455. package/dist/scripts/notify-hook/orchestration-intent.d.ts +7 -7
  456. package/dist/scripts/notify-hook/orchestration-intent.d.ts.map +1 -1
  457. package/dist/scripts/notify-hook/ralph-session-resume.d.ts +5 -2
  458. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
  459. package/dist/scripts/notify-hook/ralph-session-resume.js +33 -37
  460. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  461. package/dist/scripts/notify-hook/state-io.d.ts +17 -0
  462. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  463. package/dist/scripts/notify-hook/state-io.js +73 -11
  464. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  465. package/dist/scripts/notify-hook/team-dispatch.d.ts +1 -1
  466. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  467. package/dist/scripts/notify-hook/team-leader-nudge.d.ts +2 -2
  468. package/dist/scripts/notify-hook/team-tmux-guard.d.ts +4 -4
  469. package/dist/scripts/notify-hook/team-worker-posttooluse.js.map +1 -1
  470. package/dist/scripts/notify-hook/team-worker-stop.d.ts +1 -1
  471. package/dist/scripts/notify-hook/team-worker.d.ts +2 -2
  472. package/dist/scripts/notify-hook/tmux-injection.d.ts +8 -1
  473. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  474. package/dist/scripts/notify-hook/tmux-injection.js +25 -16
  475. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  476. package/dist/scripts/notify-hook/visual-verdict.d.ts +1 -1
  477. package/dist/scripts/notify-hook/visual-verdict.d.ts.map +1 -1
  478. package/dist/scripts/notify-hook/visual-verdict.js +15 -13
  479. package/dist/scripts/notify-hook/visual-verdict.js.map +1 -1
  480. package/dist/scripts/notify-hook.d.ts +24 -1
  481. package/dist/scripts/notify-hook.d.ts.map +1 -1
  482. package/dist/scripts/notify-hook.js +266 -90
  483. package/dist/scripts/notify-hook.js.map +1 -1
  484. package/dist/scripts/postinstall.js.map +1 -1
  485. package/dist/scripts/prompt-inventory.js.map +1 -1
  486. package/dist/scripts/smoke-packed-install.d.ts +879 -2
  487. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  488. package/dist/scripts/smoke-packed-install.js +1528 -7
  489. package/dist/scripts/smoke-packed-install.js.map +1 -1
  490. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  491. package/dist/scripts/test-reply-listener-live.js.map +1 -1
  492. package/dist/scripts/test-sparkshell.js.map +1 -1
  493. package/dist/scripts/verify-native-agents.js.map +1 -1
  494. package/dist/session-history/friction.js.map +1 -1
  495. package/dist/session-history/search.js.map +1 -1
  496. package/dist/sidecar/__tests__/boundary.test.js.map +1 -1
  497. package/dist/sidecar/collector.js.map +1 -1
  498. package/dist/sidecar/index.js.map +1 -1
  499. package/dist/sidecar/render.js.map +1 -1
  500. package/dist/sidecar/tmux.js.map +1 -1
  501. package/dist/state/__tests__/operations.test.js +64 -15
  502. package/dist/state/__tests__/operations.test.js.map +1 -1
  503. package/dist/state/__tests__/skill-active.test.js +19 -1
  504. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  505. package/dist/state/__tests__/workflow-transition.test.js +61 -0
  506. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  507. package/dist/state/mode-state-context.js.map +1 -1
  508. package/dist/state/operations.d.ts +1 -1
  509. package/dist/state/operations.d.ts.map +1 -1
  510. package/dist/state/operations.js +49 -55
  511. package/dist/state/operations.js.map +1 -1
  512. package/dist/state/paths.d.ts +1 -1
  513. package/dist/state/paths.d.ts.map +1 -1
  514. package/dist/state/paths.js +1 -1
  515. package/dist/state/paths.js.map +1 -1
  516. package/dist/state/skill-active.d.ts +13 -1
  517. package/dist/state/skill-active.d.ts.map +1 -1
  518. package/dist/state/skill-active.js +43 -4
  519. package/dist/state/skill-active.js.map +1 -1
  520. package/dist/state/terminal-normalization.js.map +1 -1
  521. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  522. package/dist/state/workflow-transition-reconcile.js +20 -4
  523. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  524. package/dist/state/workflow-transition.d.ts +1 -1
  525. package/dist/state/workflow-transition.d.ts.map +1 -1
  526. package/dist/state/workflow-transition.js.map +1 -1
  527. package/dist/subagents/__tests__/adapted-role-binding.test.d.ts +2 -0
  528. package/dist/subagents/__tests__/adapted-role-binding.test.d.ts.map +1 -0
  529. package/dist/subagents/__tests__/adapted-role-binding.test.js +988 -0
  530. package/dist/subagents/__tests__/adapted-role-binding.test.js.map +1 -0
  531. package/dist/subagents/__tests__/leader-bootstrap-3181.test.d.ts +2 -0
  532. package/dist/subagents/__tests__/leader-bootstrap-3181.test.d.ts.map +1 -0
  533. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js +273 -0
  534. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js.map +1 -0
  535. package/dist/subagents/__tests__/tracker.test.js +1550 -10
  536. package/dist/subagents/__tests__/tracker.test.js.map +1 -1
  537. package/dist/subagents/adapted-role-binding.d.ts +17 -0
  538. package/dist/subagents/adapted-role-binding.d.ts.map +1 -0
  539. package/dist/subagents/adapted-role-binding.js +75 -0
  540. package/dist/subagents/adapted-role-binding.js.map +1 -0
  541. package/dist/subagents/role-routing-marker.d.ts +10 -0
  542. package/dist/subagents/role-routing-marker.d.ts.map +1 -0
  543. package/dist/subagents/role-routing-marker.js +136 -0
  544. package/dist/subagents/role-routing-marker.js.map +1 -0
  545. package/dist/subagents/tracker.d.ts +172 -0
  546. package/dist/subagents/tracker.d.ts.map +1 -1
  547. package/dist/subagents/tracker.js +1077 -61
  548. package/dist/subagents/tracker.js.map +1 -1
  549. package/dist/team/__tests__/approved-execution.test.js.map +1 -1
  550. package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
  551. package/dist/team/__tests__/model-contract.test.js +231 -1
  552. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  553. package/dist/team/__tests__/role-router.test.js.map +1 -1
  554. package/dist/team/__tests__/runtime.test.js +401 -1
  555. package/dist/team/__tests__/runtime.test.js.map +1 -1
  556. package/dist/team/__tests__/scaling.test.js +181 -0
  557. package/dist/team/__tests__/scaling.test.js.map +1 -1
  558. package/dist/team/__tests__/state-root.test.js.map +1 -1
  559. package/dist/team/__tests__/tmux-session.test.js +434 -4
  560. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  561. package/dist/team/__tests__/tmux-test-fixture.js.map +1 -1
  562. package/dist/team/api-interop.d.ts +2 -2
  563. package/dist/team/api-interop.d.ts.map +1 -1
  564. package/dist/team/api-interop.js.map +1 -1
  565. package/dist/team/commit-hygiene.js.map +1 -1
  566. package/dist/team/contracts.d.ts +5 -5
  567. package/dist/team/contracts.d.ts.map +1 -1
  568. package/dist/team/delivery-log.js.map +1 -1
  569. package/dist/team/followup-planner.js.map +1 -1
  570. package/dist/team/goal-workflow.js.map +1 -1
  571. package/dist/team/idle-nudge.d.ts.map +1 -1
  572. package/dist/team/model-contract.d.ts +20 -1
  573. package/dist/team/model-contract.d.ts.map +1 -1
  574. package/dist/team/model-contract.js +267 -30
  575. package/dist/team/model-contract.js.map +1 -1
  576. package/dist/team/orchestrator.js.map +1 -1
  577. package/dist/team/pane-status.js.map +1 -1
  578. package/dist/team/phase-controller.js.map +1 -1
  579. package/dist/team/reminder-intents.d.ts +1 -1
  580. package/dist/team/reminder-intents.d.ts.map +1 -1
  581. package/dist/team/reminder-intents.js.map +1 -1
  582. package/dist/team/runtime-cli.js.map +1 -1
  583. package/dist/team/runtime.d.ts.map +1 -1
  584. package/dist/team/runtime.js +83 -52
  585. package/dist/team/runtime.js.map +1 -1
  586. package/dist/team/scaling.d.ts +9 -7
  587. package/dist/team/scaling.d.ts.map +1 -1
  588. package/dist/team/scaling.js +69 -28
  589. package/dist/team/scaling.js.map +1 -1
  590. package/dist/team/state/dispatch-lock.js.map +1 -1
  591. package/dist/team/state/dispatch.js.map +1 -1
  592. package/dist/team/state/events.js.map +1 -1
  593. package/dist/team/state/tasks.js.map +1 -1
  594. package/dist/team/state-root.js.map +1 -1
  595. package/dist/team/state.js.map +1 -1
  596. package/dist/team/team-identity.d.ts.map +1 -1
  597. package/dist/team/team-identity.js.map +1 -1
  598. package/dist/team/tmux-session.d.ts +2 -0
  599. package/dist/team/tmux-session.d.ts.map +1 -1
  600. package/dist/team/tmux-session.js +90 -42
  601. package/dist/team/tmux-session.js.map +1 -1
  602. package/dist/team/worker-bootstrap.js.map +1 -1
  603. package/dist/team/worktree.js.map +1 -1
  604. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  605. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
  606. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  607. package/dist/ultragoal/artifacts.js +4 -1
  608. package/dist/ultragoal/artifacts.js.map +1 -1
  609. package/dist/url-reader/index.js.map +1 -1
  610. package/dist/utils/__tests__/agents-md.test.js +16 -0
  611. package/dist/utils/__tests__/agents-md.test.js.map +1 -1
  612. package/dist/utils/agents-md.d.ts.map +1 -1
  613. package/dist/utils/agents-md.js +4 -3
  614. package/dist/utils/agents-md.js.map +1 -1
  615. package/dist/utils/agents-model-table.js.map +1 -1
  616. package/dist/utils/package.js.map +1 -1
  617. package/dist/utils/paths.js.map +1 -1
  618. package/dist/utils/platform-command.js.map +1 -1
  619. package/dist/utils/repo-deps.js.map +1 -1
  620. package/dist/utils/version.js.map +1 -1
  621. package/dist/verification/__tests__/ci-rust-gates.test.js +1 -1
  622. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
  623. package/dist/verification/verifier.js.map +1 -1
  624. package/dist/visual/constants.d.ts +1 -1
  625. package/dist/visual/constants.d.ts.map +1 -1
  626. package/dist/vscode/__tests__/index.test.js.map +1 -1
  627. package/dist/vscode/index.js.map +1 -1
  628. package/dist/wiki/__tests__/cjk-tokenize.test.js.map +1 -1
  629. package/dist/wiki/__tests__/lint.test.js.map +1 -1
  630. package/dist/wiki/__tests__/query.test.js.map +1 -1
  631. package/dist/wiki/__tests__/storage.test.js.map +1 -1
  632. package/dist/wiki/__tests__/test-helpers.d.ts.map +1 -1
  633. package/dist/wiki/lint.js.map +1 -1
  634. package/dist/wiki/query.js.map +1 -1
  635. package/dist/wiki/storage.js.map +1 -1
  636. package/package.json +2 -2
  637. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  638. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +10 -5
  639. package/plugins/oh-my-codex/skills/ralph/SKILL.md +4 -2
  640. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +5 -2
  641. package/skills/omx-setup/SKILL.md +10 -5
  642. package/skills/ralph/SKILL.md +4 -2
  643. package/skills/ralplan/SKILL.md +5 -2
  644. package/src/scripts/__tests__/codex-native-hook.test.ts +20497 -12377
  645. package/src/scripts/__tests__/fixtures/issue-3138-installed-smoke.ts +734 -0
  646. package/src/scripts/__tests__/notify-fallback-delivery.test.ts +177 -0
  647. package/src/scripts/__tests__/notify-state-io.test.ts +73 -0
  648. package/src/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.ts +215 -0
  649. package/src/scripts/__tests__/role-intent-durable-recovery-3181.test.ts +193 -0
  650. package/src/scripts/__tests__/smoke-packed-install.test.ts +991 -4
  651. package/src/scripts/codex-native-hook.ts +918 -172
  652. package/src/scripts/notify-fallback-delivery.ts +416 -0
  653. package/src/scripts/notify-fallback-watcher.ts +132 -40
  654. package/src/scripts/notify-hook/auto-nudge.ts +43 -12
  655. package/src/scripts/notify-hook/managed-tmux.ts +149 -12
  656. package/src/scripts/notify-hook/ralph-session-resume.ts +37 -36
  657. package/src/scripts/notify-hook/state-io.ts +100 -13
  658. package/src/scripts/notify-hook/tmux-injection.ts +38 -21
  659. package/src/scripts/notify-hook/visual-verdict.ts +22 -12
  660. package/src/scripts/notify-hook.ts +335 -90
  661. package/src/scripts/smoke-packed-install.ts +1829 -6
@@ -13,11 +13,10 @@ import { readFile, writeFile } from "fs/promises";
13
13
  import { existsSync } from "fs";
14
14
  import { join, resolve } from "path";
15
15
  import TOML from "@iarna/toml";
16
- import { AGENT_DEFINITIONS } from "../agents/definitions.js";
17
16
  import { DEFAULT_FRONTIER_MODEL } from "./models.js";
18
17
  import { DEFAULT_CODEX_HOOK_FEATURE_FLAG, CODEX_HOOK_FEATURE_FLAGS, CODEX_PLUGIN_SCOPED_HOOKS_FEATURE_FLAG, formatCodexHookFeatureFlagLine, normalizeCodexHookFeatureFlag, } from "./codex-feature-flags.js";
19
18
  import { OMX_FIRST_PARTY_MCP_SERVER_NAMES, getOmxFirstPartySetupMcpServers, } from "./omx-first-party-mcp.js";
20
- import { buildManagedCodexHookTrustState, escapeTomlBasicString, } from "./codex-hooks.js";
19
+ import { buildManagedCodexHookTrustState, escapeTomlBasicString, ManagedCodexHooksPlanError, scanManagedCodexHookTrustStateFromContent, } from "./codex-hooks.js";
21
20
  function escapeTomlString(value) {
22
21
  return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
23
22
  }
@@ -34,25 +33,65 @@ const OMX_TOP_LEVEL_KEYS = [
34
33
  "developer_instructions",
35
34
  ];
36
35
  export const DEFAULT_SETUP_MODEL = DEFAULT_FRONTIER_MODEL;
37
- export const DEFAULT_SETUP_MODEL_CONTEXT_WINDOW = 250000;
38
- export const DEFAULT_SETUP_MODEL_AUTO_COMPACT_TOKEN_LIMIT = 200000;
39
- export function getModelContextRecommendation(model) {
40
- if (model !== DEFAULT_SETUP_MODEL)
41
- return null;
42
- return {
43
- model,
44
- modelContextWindow: DEFAULT_SETUP_MODEL_CONTEXT_WINDOW,
45
- modelAutoCompactTokenLimit: DEFAULT_SETUP_MODEL_AUTO_COMPACT_TOKEN_LIMIT,
46
- };
47
- }
36
+ const LEGACY_SEEDED_MODEL_CONTEXT_WINDOW = 250000;
37
+ const LEGACY_SEEDED_MODEL_AUTO_COMPACT_TOKEN_LIMIT = 200000;
48
38
  const OMX_SEEDED_BEHAVIORAL_DEFAULTS_START_MARKER = "# oh-my-codex seeded behavioral defaults (uninstall removes unchanged defaults)";
49
39
  const OMX_SEEDED_BEHAVIORAL_DEFAULTS_END_MARKER = "# End oh-my-codex seeded behavioral defaults";
50
- 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 spawning native subagents, set `agent_type` to an installed role and never omit it for OMX work. 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.";
51
- 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 spawning native subagents, set `agent_type` to an installed role and never omit it for OMX work. 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 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>';
52
42
  const SHARED_MCP_REGISTRY_MARKER = "oh-my-codex (OMX) Shared MCP Registry Sync";
53
43
  const SHARED_MCP_REGISTRY_END_MARKER = "# End oh-my-codex shared MCP registry sync";
54
- const OMX_AGENTS_MAX_THREADS = 6;
55
- const OMX_AGENTS_MAX_DEPTH = 2;
44
+ const LEGACY_MULTI_AGENT_KEYS = [
45
+ "features.multi_agent",
46
+ "agents.max_threads",
47
+ "agents.max_depth",
48
+ ];
49
+ const LEGACY_MULTI_AGENT_VALUES = {
50
+ "features.multi_agent": true,
51
+ "agents.max_threads": 6,
52
+ "agents.max_depth": 2,
53
+ };
54
+ export function analyzeLegacyMultiAgentConfig(configText) {
55
+ let parsed;
56
+ try {
57
+ parsed = TOML.parse(configText);
58
+ }
59
+ catch (error) {
60
+ const reasonCode = /duplicate|redefine|already defined/i.test(String(error))
61
+ ? "toml-duplicate-key"
62
+ : "toml-parse-error";
63
+ return {
64
+ assessments: Object.fromEntries(LEGACY_MULTI_AGENT_KEYS.map((key) => [
65
+ key,
66
+ { key, state: "invalid/duplicate", reasonCode },
67
+ ])),
68
+ };
69
+ }
70
+ return {
71
+ assessments: Object.fromEntries(LEGACY_MULTI_AGENT_KEYS.map((key) => {
72
+ const [table, entry] = key.split(".");
73
+ const value = isRecord(parsed[table]) ? parsed[table][entry] : undefined;
74
+ if (value === undefined) {
75
+ return [key, { key, state: "absent", reasonCode: "key-absent" }];
76
+ }
77
+ if (value === LEGACY_MULTI_AGENT_VALUES[key]) {
78
+ return [
79
+ key,
80
+ {
81
+ key,
82
+ state: "retained-legacy",
83
+ reasonCode: "exact-legacy-value",
84
+ value,
85
+ },
86
+ ];
87
+ }
88
+ return [
89
+ key,
90
+ { key, state: "custom", reasonCode: "custom-value", value },
91
+ ];
92
+ })),
93
+ };
94
+ }
56
95
  const OMX_EXPLORE_ROUTING_DEFAULT = "0";
57
96
  const OMX_EXPLORE_CMD_ENV = "USE_OMX_EXPLORE_CMD";
58
97
  const DEFAULT_LAUNCHER_MCP_STARTUP_TIMEOUT_SEC = 15;
@@ -97,6 +136,7 @@ const LEGACY_OMX_STATUS_LINE = statusLineForPreset(DEFAULT_STATUS_LINE_PRESET);
97
136
  const OMX_PRESET_STATUS_LINE_VALUES = new Set(Object.keys(STATUS_LINE_PRESETS).map((preset) => statusLineForPreset(preset)));
98
137
  const LEGACY_OMX_TEAM_RUN_TABLE_PATTERN = /^\s*\[mcp_servers\.(?:"omx_team_run"|omx_team_run)\]\s*$/m;
99
138
  const OMX_CONFIG_MARKER = "oh-my-codex (OMX) Configuration";
139
+ const OMX_CONFIG_START_MARKER = `# ${OMX_CONFIG_MARKER}`;
100
140
  const OMX_CONFIG_END_MARKER = "# End oh-my-codex";
101
141
  const CODEX_MODEL_AVAILABILITY_NUX_TABLE_PATTERN = /^\s*\[tui\.model_availability_nux\]\s*(?:#.*)?$/;
102
142
  const TOML_TABLE_HEADER_PATTERN = /^\s*\[\[?[^\]]+\]?\]\s*(?:#.*)?$/;
@@ -343,71 +383,117 @@ function getOmxTopLevelLines(pkgRoot, existingConfig = "", modelOverride, notify
343
383
  `developer_instructions = "${escapeTomlString(OMX_DEVELOPER_INSTRUCTIONS)}"`,
344
384
  ];
345
385
  const existingModel = rootValues.get("model");
346
- const existingContextWindow = rootValues.get("model_context_window");
347
- const existingAutoCompact = rootValues.get("model_auto_compact_token_limit");
348
386
  const selectedModel = modelOverride ?? unwrapTomlString(existingModel) ?? DEFAULT_SETUP_MODEL;
349
387
  if (modelOverride || !existingModel) {
350
388
  lines.push(`model = "${selectedModel}"`);
351
389
  }
352
- if (selectedModel === DEFAULT_SETUP_MODEL) {
353
- const seededBehavioralDefaults = [];
354
- if (!existingContextWindow) {
355
- seededBehavioralDefaults.push(`model_context_window = ${DEFAULT_SETUP_MODEL_CONTEXT_WINDOW}`);
356
- }
357
- if (!existingAutoCompact) {
358
- seededBehavioralDefaults.push(`model_auto_compact_token_limit = ${DEFAULT_SETUP_MODEL_AUTO_COMPACT_TOKEN_LIMIT}`);
359
- }
360
- if (seededBehavioralDefaults.length > 0) {
361
- lines.push(OMX_SEEDED_BEHAVIORAL_DEFAULTS_START_MARKER);
362
- lines.push(...seededBehavioralDefaults);
363
- lines.push(OMX_SEEDED_BEHAVIORAL_DEFAULTS_END_MARKER);
364
- }
365
- }
366
390
  return lines;
367
391
  }
368
- function isUnchangedOmxSeededBehavioralDefaultsBlock(lines) {
369
- const relevant = lines.filter((line) => {
370
- const trimmed = line.trim();
371
- return trimmed.length > 0 && !trimmed.startsWith("#");
372
- });
373
- if (relevant.length !== 2)
374
- return false;
375
- const parsed = parseRootKeyValues(relevant.join("\n"));
376
- return (parsed.size === 2 &&
377
- parsed.get("model_context_window") ===
378
- String(DEFAULT_SETUP_MODEL_CONTEXT_WINDOW) &&
379
- parsed.get("model_auto_compact_token_limit") ===
380
- String(DEFAULT_SETUP_MODEL_AUTO_COMPACT_TOKEN_LIMIT));
392
+ function sourceLines(config) {
393
+ const lines = [];
394
+ let start = 0;
395
+ for (let index = 0; index < config.length; index += 1) {
396
+ if (config[index] !== "\n")
397
+ continue;
398
+ lines.push({
399
+ content: config.slice(start, index - (index > start && config[index - 1] === "\r" ? 1 : 0)),
400
+ start,
401
+ end: index + 1,
402
+ });
403
+ start = index + 1;
404
+ }
405
+ if (start < config.length)
406
+ lines.push({ content: config.slice(start), start, end: config.length });
407
+ return lines;
381
408
  }
382
- export function stripOmxSeededBehavioralDefaults(config) {
383
- const lines = config.split(/\r?\n/);
384
- const firstTable = lines.findIndex((line) => /^\s*\[/.test(line));
385
- const boundary = firstTable >= 0 ? firstTable : lines.length;
386
- const result = [];
387
- for (let index = 0; index < lines.length; index += 1) {
388
- const trimmed = lines[index].trim();
389
- if (index < boundary &&
390
- trimmed === OMX_SEEDED_BEHAVIORAL_DEFAULTS_START_MARKER) {
391
- const endIndex = lines.findIndex((line, candidateIndex) => candidateIndex > index &&
392
- candidateIndex < boundary &&
393
- line.trim() === OMX_SEEDED_BEHAVIORAL_DEFAULTS_END_MARKER);
394
- if (endIndex < 0) {
395
- continue;
396
- }
397
- const blockLines = lines.slice(index + 1, endIndex);
398
- if (!isUnchangedOmxSeededBehavioralDefaultsBlock(blockLines)) {
399
- result.push(...blockLines);
409
+ function analyzeOmxSeededBehavioralDefaults(config) {
410
+ const lines = sourceLines(config);
411
+ const firstTable = lines.findIndex((line) => TOML_TABLE_HEADER_PATTERN.test(line.content));
412
+ const boundary = firstTable < 0 ? lines.length : firstTable;
413
+ const starts = lines.map((line, index) => ({ line, index }))
414
+ .filter(({ line }) => line.content === OMX_SEEDED_BEHAVIORAL_DEFAULTS_START_MARKER);
415
+ const ends = lines.map((line, index) => ({ line, index }))
416
+ .filter(({ line }) => line.content === OMX_SEEDED_BEHAVIORAL_DEFAULTS_END_MARKER);
417
+ if (starts.length === 0 && ends.length === 0)
418
+ return { state: "absent", spans: [] };
419
+ if (starts.length !== 1 || ends.length !== 1 || starts[0].index >= ends[0].index) {
420
+ return { state: "malformed-or-ambiguous", spans: [] };
421
+ }
422
+ const rootText = lines.slice(0, boundary).map((line) => line.content).join("\n");
423
+ let entryStart = 0;
424
+ const rootEntries = splitRootLevelEntries(rootText).entries.map((entry) => {
425
+ const indexed = { entry, start: entryStart, end: entryStart + entry.lines.length };
426
+ entryStart = indexed.end;
427
+ return indexed;
428
+ });
429
+ const isStandaloneRootMarker = (index) => index < boundary && rootEntries.some(({ entry, start, end }) => !entry.key && start === index && end === index + 1);
430
+ const start = starts[0];
431
+ const end = ends[0];
432
+ if (!isStandaloneRootMarker(start.index) || !isStandaloneRootMarker(end.index)) {
433
+ return { state: "malformed-or-ambiguous", spans: [] };
434
+ }
435
+ const block = lines.slice(start.index, end.index + 1);
436
+ const exact = (body) => block.length === body.length + 2 &&
437
+ block[0].content === OMX_SEEDED_BEHAVIORAL_DEFAULTS_START_MARKER &&
438
+ block.at(-1)?.content === OMX_SEEDED_BEHAVIORAL_DEFAULTS_END_MARKER &&
439
+ body.every((line, index) => block[index + 1].content === line);
440
+ const blockSpan = [{ start: start.line.start, end: end.line.end }];
441
+ const markerSpans = [
442
+ { start: start.line.start, end: start.line.end },
443
+ { start: end.line.start, end: end.line.end },
444
+ ];
445
+ if (exact([
446
+ `model_context_window = ${LEGACY_SEEDED_MODEL_CONTEXT_WINDOW}`,
447
+ `model_auto_compact_token_limit = ${LEGACY_SEEDED_MODEL_AUTO_COMPACT_TOKEN_LIMIT}`,
448
+ ])) {
449
+ const hasOutsideDuplicate = rootEntries.some(({ entry, start: entryStart, end: entryEnd }) => (entryEnd <= start.index || entryStart > end.index) &&
450
+ (entry.key === "model_context_window" || entry.key === "model_auto_compact_token_limit"));
451
+ return hasOutsideDuplicate
452
+ ? { state: "malformed-or-ambiguous", spans: [] }
453
+ : { state: "exact-pair", spans: blockSpan };
454
+ }
455
+ const singletonState = (singletonKey, siblingKey) => {
456
+ let singletonDuplicates = 0;
457
+ let siblingCount = 0;
458
+ let invalidSibling = false;
459
+ for (const { entry, start: entryStart, end: entryEnd } of rootEntries) {
460
+ const outside = entryEnd <= start.index || entryStart > end.index;
461
+ if (outside && entry.key === singletonKey)
462
+ singletonDuplicates += 1;
463
+ if (outside && entry.key === siblingKey) {
464
+ siblingCount += 1;
465
+ invalidSibling ||= !parseStandaloneToml(entry.lines.join("\n"));
400
466
  }
401
- index = endIndex;
402
- continue;
403
- }
404
- if (index < boundary &&
405
- trimmed === OMX_SEEDED_BEHAVIORAL_DEFAULTS_END_MARKER) {
406
- continue;
407
467
  }
408
- result.push(lines[index]);
409
- }
410
- return result.join("\n");
468
+ if (singletonDuplicates > 0 || siblingCount > 1 || invalidSibling)
469
+ return "malformed-or-ambiguous";
470
+ return siblingCount === 1 ? "exact" : "bounded-nonremovable";
471
+ };
472
+ if (exact([`model_context_window = ${LEGACY_SEEDED_MODEL_CONTEXT_WINDOW}`])) {
473
+ const state = singletonState("model_context_window", "model_auto_compact_token_limit");
474
+ return state === "exact"
475
+ ? { state: "exact-context-singleton", spans: blockSpan }
476
+ : state === "bounded-nonremovable"
477
+ ? { state, spans: markerSpans }
478
+ : { state, spans: [] };
479
+ }
480
+ if (exact([`model_auto_compact_token_limit = ${LEGACY_SEEDED_MODEL_AUTO_COMPACT_TOKEN_LIMIT}`])) {
481
+ const state = singletonState("model_auto_compact_token_limit", "model_context_window");
482
+ return state === "exact"
483
+ ? { state: "exact-auto-singleton", spans: blockSpan }
484
+ : state === "bounded-nonremovable"
485
+ ? { state, spans: markerSpans }
486
+ : { state, spans: [] };
487
+ }
488
+ return { state: "bounded-nonremovable", spans: markerSpans };
489
+ }
490
+ export function hasExactOmxSeededBehavioralDefaultsPair(config) {
491
+ return analyzeOmxSeededBehavioralDefaults(config).state === "exact-pair";
492
+ }
493
+ export function stripOmxSeededBehavioralDefaults(config) {
494
+ return [...analyzeOmxSeededBehavioralDefaults(config).spans]
495
+ .sort((left, right) => right.start - left.start)
496
+ .reduce((result, span) => result.slice(0, span.start) + result.slice(span.end), config);
411
497
  }
412
498
  function stripRootLevelKeys(config, keys) {
413
499
  const { entries, remainder } = splitRootLevelEntries(config);
@@ -511,7 +597,6 @@ function upsertFeatureFlags(config, codexHookFeatureFlag = DEFAULT_CODEX_HOOK_FE
511
597
  const base = config.trimEnd();
512
598
  const featureBlock = [
513
599
  "[features]",
514
- "multi_agent = true",
515
600
  "child_agents_md = true",
516
601
  hookFeatureFlagLine,
517
602
  "goals = true",
@@ -529,31 +614,19 @@ function upsertFeatureFlags(config, codexHookFeatureFlag = DEFAULT_CODEX_HOOK_FE
529
614
  break;
530
615
  }
531
616
  }
532
- // Remove deprecated 'collab' key (superseded by multi_agent) and
533
- // the misspelled singular 'goal' flag written by unreleased PR builds.
617
+ // Remove deprecated collab and unreleased singular goal flags.
534
618
  for (let i = sectionEnd - 1; i > featuresStart; i--) {
535
619
  if (/^\s*(?:collab|goal)\s*=/.test(lines[i])) {
536
620
  lines.splice(i, 1);
537
621
  sectionEnd -= 1;
538
622
  }
539
623
  }
540
- let multiAgentIdx = -1;
541
624
  let childAgentsIdx = -1;
542
625
  for (let i = featuresStart + 1; i < sectionEnd; i++) {
543
- if (/^\s*multi_agent\s*=/.test(lines[i])) {
544
- multiAgentIdx = i;
545
- }
546
- else if (/^\s*child_agents_md\s*=/.test(lines[i])) {
626
+ if (/^\s*child_agents_md\s*=/.test(lines[i])) {
547
627
  childAgentsIdx = i;
548
628
  }
549
629
  }
550
- if (multiAgentIdx >= 0) {
551
- lines[multiAgentIdx] = "multi_agent = true";
552
- }
553
- else {
554
- lines.splice(sectionEnd, 0, "multi_agent = true");
555
- sectionEnd += 1;
556
- }
557
630
  if (childAgentsIdx >= 0) {
558
631
  lines[childAgentsIdx] = "child_agents_md = true";
559
632
  }
@@ -721,105 +794,845 @@ export function syncProjectScopeTrustStateFromRuntime(projectConfig, runtimeConf
721
794
  return block;
722
795
  return `${stripped}\n\n${block}`;
723
796
  }
724
- function decodeTomlBasicString(raw) {
725
- try {
726
- const parsed = TOML.parse(`value = "${raw}"`);
727
- return typeof parsed.value === "string" ? parsed.value : undefined;
797
+ function isMultilineTomlString(mode) {
798
+ return mode === "multiline-basic" || mode === "multiline-literal";
799
+ }
800
+ /**
801
+ * Tracks only lexical boundaries needed to safely associate source lines with
802
+ * TOML statements. TOML's parser gives us semantics; this prevents source
803
+ * repair from treating table-like text in a multiline value as syntax.
804
+ */
805
+ function analyzeTomlSource(config) {
806
+ const lines = config.split(/\r?\n/);
807
+ const lineStartsOutsideMultiline = [];
808
+ const lineHasTomlComment = [];
809
+ let mode;
810
+ let isUnambiguous = true;
811
+ for (const line of lines) {
812
+ lineStartsOutsideMultiline.push(!isMultilineTomlString(mode));
813
+ let hasComment = false;
814
+ for (let index = 0; index < line.length; index += 1) {
815
+ const character = line[index];
816
+ if (mode === "basic") {
817
+ if (character === "\\") {
818
+ index += 1;
819
+ }
820
+ else if (character === "\"") {
821
+ mode = undefined;
822
+ }
823
+ continue;
824
+ }
825
+ if (mode === "literal") {
826
+ if (character === "'")
827
+ mode = undefined;
828
+ continue;
829
+ }
830
+ if (mode === "multiline-basic" || mode === "multiline-literal") {
831
+ const delimiter = mode === "multiline-basic" ? "\"" : "'";
832
+ if (mode === "multiline-basic" && character === "\\") {
833
+ index += 1;
834
+ continue;
835
+ }
836
+ if (character !== delimiter)
837
+ continue;
838
+ let runEnd = index + 1;
839
+ while (line[runEnd] === delimiter)
840
+ runEnd += 1;
841
+ if (runEnd - index >= 3) {
842
+ // TOML permits one or two delimiter characters immediately before
843
+ // the closing triple delimiter as string content.
844
+ mode = undefined;
845
+ index = runEnd - 1;
846
+ }
847
+ continue;
848
+ }
849
+ if (character === "#") {
850
+ hasComment = true;
851
+ break;
852
+ }
853
+ if (character !== "\"" && character !== "'")
854
+ continue;
855
+ const isMultiline = line[index + 1] === character && line[index + 2] === character;
856
+ if (isMultiline) {
857
+ mode = character === "\"" ? "multiline-basic" : "multiline-literal";
858
+ index += 2;
859
+ }
860
+ else {
861
+ mode = character === "\"" ? "basic" : "literal";
862
+ }
863
+ }
864
+ lineHasTomlComment.push(hasComment);
865
+ if (mode === "basic" || mode === "literal")
866
+ isUnambiguous = false;
728
867
  }
729
- catch {
868
+ if (isMultilineTomlString(mode))
869
+ isUnambiguous = false;
870
+ return { lines, lineStartsOutsideMultiline, lineHasTomlComment, isUnambiguous };
871
+ }
872
+ function skipTomlSourceWhitespace(line, index) {
873
+ let cursor = index;
874
+ while (line[cursor] === " " || line[cursor] === "\t")
875
+ cursor += 1;
876
+ return cursor;
877
+ }
878
+ function parseTomlSourceKeySegment(line, index) {
879
+ const start = skipTomlSourceWhitespace(line, index);
880
+ const first = line[start];
881
+ if (!first)
882
+ return undefined;
883
+ if (first !== '"' && first !== "'") {
884
+ const match = /^[A-Za-z0-9_-]+/.exec(line.slice(start));
885
+ if (!match)
886
+ return undefined;
887
+ return { key: match[0], end: start + match[0].length, incomplete: false };
888
+ }
889
+ let cursor = start + 1;
890
+ let escaped = false;
891
+ for (; cursor < line.length; cursor += 1) {
892
+ const character = line[cursor];
893
+ if (first === '"' && escaped) {
894
+ escaped = false;
895
+ continue;
896
+ }
897
+ if (first === '"' && character === "\\") {
898
+ escaped = true;
899
+ continue;
900
+ }
901
+ if (character !== first)
902
+ continue;
903
+ const token = line.slice(start, cursor + 1);
904
+ const parsed = safeParseToml(`${token} = true`);
905
+ const keys = parsed ? Object.keys(parsed) : [];
906
+ const hasKey = keys.length === 1;
907
+ return {
908
+ ...(hasKey ? { key: keys[0] } : {}),
909
+ end: cursor + 1,
910
+ incomplete: !hasKey,
911
+ };
912
+ }
913
+ return { end: line.length, incomplete: true };
914
+ }
915
+ function isTomlSourceKey(segment, key) {
916
+ return segment.key === key;
917
+ }
918
+ function findTomlSourceAssignmentSeparator(line) {
919
+ let segment = parseTomlSourceKeySegment(line, 0);
920
+ if (!segment || segment.incomplete)
921
+ return undefined;
922
+ let cursor = skipTomlSourceWhitespace(line, segment.end);
923
+ while (line[cursor] === ".") {
924
+ segment = parseTomlSourceKeySegment(line, cursor + 1);
925
+ if (!segment || segment.incomplete)
926
+ return undefined;
927
+ cursor = skipTomlSourceWhitespace(line, segment.end);
928
+ }
929
+ return line[cursor] === "=" ? cursor : undefined;
930
+ }
931
+ function advancePastInlineTomlValue(line, index) {
932
+ let braces = 0;
933
+ let brackets = 0;
934
+ let quote;
935
+ let escaped = false;
936
+ for (let cursor = index; cursor < line.length; cursor += 1) {
937
+ const character = line[cursor];
938
+ if (quote) {
939
+ if (quote === '"' && escaped) {
940
+ escaped = false;
941
+ }
942
+ else if (quote === '"' && character === "\\") {
943
+ escaped = true;
944
+ }
945
+ else if (character === quote) {
946
+ quote = undefined;
947
+ }
948
+ continue;
949
+ }
950
+ if (character === '"' || character === "'") {
951
+ quote = character;
952
+ continue;
953
+ }
954
+ if (character === "#")
955
+ return line.length;
956
+ if (character === "{") {
957
+ braces += 1;
958
+ continue;
959
+ }
960
+ if (character === "}") {
961
+ if (braces === 0 && brackets === 0)
962
+ return cursor;
963
+ braces = Math.max(0, braces - 1);
964
+ continue;
965
+ }
966
+ if (character === "[") {
967
+ brackets += 1;
968
+ continue;
969
+ }
970
+ if (character === "]") {
971
+ brackets = Math.max(0, brackets - 1);
972
+ continue;
973
+ }
974
+ if (character === "," && braces === 0 && brackets === 0)
975
+ return cursor + 1;
976
+ }
977
+ return line.length;
978
+ }
979
+ function inlineTomlTableMaySpellStateKey(line, index) {
980
+ let cursor = index;
981
+ while (cursor < line.length) {
982
+ cursor = skipTomlSourceWhitespace(line, cursor);
983
+ // A comment cannot close the surrounding inline table.
984
+ if (line[cursor] === "#")
985
+ return true;
986
+ if (line[cursor] === "}")
987
+ return false;
988
+ const segment = parseTomlSourceKeySegment(line, cursor);
989
+ if (!segment)
990
+ return true;
991
+ if (segment.incomplete || isTomlSourceKey(segment, "state"))
992
+ return true;
993
+ cursor = skipTomlSourceWhitespace(line, segment.end);
994
+ if (line[cursor] !== "=")
995
+ return true;
996
+ cursor = advancePastInlineTomlValue(line, cursor + 1);
997
+ if (line[cursor] === "}")
998
+ return false;
999
+ }
1000
+ // The unclosed inline table can acquire a state member on a following
1001
+ // unparsed line, so it is not safe to strip the containing marker block.
1002
+ return true;
1003
+ }
1004
+ function lineMaySpellHooksState(line) {
1005
+ let cursor = skipTomlSourceWhitespace(line, 0);
1006
+ if (line[cursor] === "[") {
1007
+ if (line[cursor + 1] === "[") {
1008
+ cursor = skipTomlSourceWhitespace(line, cursor + 2);
1009
+ }
1010
+ else {
1011
+ cursor = skipTomlSourceWhitespace(line, cursor + 1);
1012
+ }
1013
+ }
1014
+ const hooks = parseTomlSourceKeySegment(line, cursor);
1015
+ if (!hooks || !isTomlSourceKey(hooks, "hooks"))
1016
+ return false;
1017
+ cursor = skipTomlSourceWhitespace(line, hooks.end);
1018
+ if (line[cursor] === ".") {
1019
+ const state = parseTomlSourceKeySegment(line, cursor + 1);
1020
+ // A dangling dot can be completed by a following unparsed state key.
1021
+ return state === undefined || state.incomplete || isTomlSourceKey(state, "state");
1022
+ }
1023
+ if (line[cursor] !== "=")
1024
+ return false;
1025
+ cursor = skipTomlSourceWhitespace(line, cursor + 1);
1026
+ // A bare assignment or comment cannot prove that a following unparsed line
1027
+ // does not continue an inline hooks table with a state member.
1028
+ if (cursor >= line.length || line[cursor] === "#")
1029
+ return true;
1030
+ return line[cursor] === "{" && inlineTomlTableMaySpellStateKey(line, cursor + 1);
1031
+ }
1032
+ /**
1033
+ * Detects a potential array-table opener without accepting malformed spacing
1034
+ * or extra opening brackets as TOML syntax. Array tables rooted at `hooks`
1035
+ * have no proof-owned representation, so their first key segment is enough
1036
+ * to retain a managed marker block rather than strip it.
1037
+ */
1038
+ function parseTomlSourceArrayTableOpener(line) {
1039
+ let cursor = skipTomlSourceWhitespace(line, 0);
1040
+ if (line[cursor] !== "[")
730
1041
  return undefined;
1042
+ const hasExactAdjacentOpener = line[cursor + 1] === "[";
1043
+ if (hasExactAdjacentOpener) {
1044
+ cursor += 2;
1045
+ }
1046
+ else {
1047
+ cursor = skipTomlSourceWhitespace(line, cursor + 1);
1048
+ if (line[cursor] !== "[")
1049
+ return undefined;
1050
+ cursor += 1;
1051
+ }
1052
+ cursor = skipTomlSourceWhitespace(line, cursor);
1053
+ while (line[cursor] === "[") {
1054
+ cursor = skipTomlSourceWhitespace(line, cursor + 1);
731
1055
  }
1056
+ const firstKey = parseTomlSourceKeySegment(line, cursor);
1057
+ return {
1058
+ firstKey,
1059
+ openerOnly: firstKey === undefined &&
1060
+ (cursor >= line.length || line[cursor] === "#"),
1061
+ };
1062
+ }
1063
+ function hasIncompleteTomlSourceHooksKeyPrefix(line) {
1064
+ let cursor = skipTomlSourceWhitespace(line, 0);
1065
+ let tableHeader = false;
1066
+ let arrayTableHeader = false;
1067
+ if (line[cursor] === "[") {
1068
+ tableHeader = true;
1069
+ if (line[cursor + 1] === "[") {
1070
+ arrayTableHeader = true;
1071
+ cursor = skipTomlSourceWhitespace(line, cursor + 2);
1072
+ }
1073
+ else {
1074
+ cursor = skipTomlSourceWhitespace(line, cursor + 1);
1075
+ }
1076
+ }
1077
+ const hooks = parseTomlSourceKeySegment(line, cursor);
1078
+ if (!hooks || !isTomlSourceKey(hooks, "hooks"))
1079
+ return false;
1080
+ cursor = skipTomlSourceWhitespace(line, hooks.end);
1081
+ if (!tableHeader)
1082
+ return cursor >= line.length || line[cursor] === "#";
1083
+ if (line[cursor] !== "]" || (arrayTableHeader && line[cursor + 1] !== "]")) {
1084
+ return true;
1085
+ }
1086
+ const suffix = line.slice(cursor + (arrayTableHeader ? 2 : 1)).trimStart();
1087
+ return suffix.length > 0 && !suffix.startsWith("#");
1088
+ }
1089
+ function isTomlSourceHooksKeyContinuationSeparator(line) {
1090
+ const cursor = skipTomlSourceWhitespace(line, 0);
1091
+ return line[cursor] === "." || line[cursor] === "=" || line[cursor] === "[";
732
1092
  }
733
- function parseHooksStateHeader(line) {
734
- const match = line.match(/^\s*\[hooks\.state\."((?:\\.|[^"\\])*)"\]\s*(#.*)?$/);
735
- if (!match)
1093
+ function isTomlSourceBlankOrComment(line) {
1094
+ return /^\s*(?:#.*)?$/.test(line);
1095
+ }
1096
+ function isTomlSourceStandaloneOpeningBracket(line) {
1097
+ const cursor = skipTomlSourceWhitespace(line, 0);
1098
+ if (line[cursor] !== "[")
1099
+ return false;
1100
+ const next = skipTomlSourceWhitespace(line, cursor + 1);
1101
+ return next >= line.length || line[next] === "#";
1102
+ }
1103
+ function firstTomlSourceKeyFollowingOpeningBrackets(line) {
1104
+ let cursor = skipTomlSourceWhitespace(line, 0);
1105
+ while (line[cursor] === "[") {
1106
+ cursor = skipTomlSourceWhitespace(line, cursor + 1);
1107
+ }
1108
+ return parseTomlSourceKeySegment(line, cursor);
1109
+ }
1110
+ function lineMaySpellStateKey(line) {
1111
+ const state = parseTomlSourceKeySegment(line, 0);
1112
+ return state !== undefined && (state.incomplete || isTomlSourceKey(state, "state"));
1113
+ }
1114
+ function isTomlSourceHooksTableHeader(parsed) {
1115
+ const hooks = parsed?.hooks;
1116
+ return parsed !== undefined &&
1117
+ Object.keys(parsed).length === 1 &&
1118
+ isPlainTomlRecord(hooks) &&
1119
+ Object.keys(hooks).length === 0;
1120
+ }
1121
+ function hasUnprovenManagedMarkerHooksStateSyntax(source, managedMarkerRanges, parsedStatementSpans, parsedAssignmentSpans) {
1122
+ const parsedLines = new Set();
1123
+ const parsedStatementStartLines = new Set();
1124
+ for (const span of parsedStatementSpans) {
1125
+ parsedStatementStartLines.add(span.start);
1126
+ for (let index = span.start; index < span.end; index += 1)
1127
+ parsedLines.add(index);
1128
+ }
1129
+ const parsedAssignmentLines = new Set();
1130
+ for (const span of parsedAssignmentSpans) {
1131
+ for (let index = span.start; index < span.end; index += 1) {
1132
+ parsedAssignmentLines.add(index);
1133
+ }
1134
+ }
1135
+ const managedContentLines = new Set();
1136
+ for (const range of managedMarkerRanges) {
1137
+ for (let index = range.start + 1; index < range.end; index += 1) {
1138
+ managedContentLines.add(index);
1139
+ }
1140
+ }
1141
+ let insideHooksTable = false;
1142
+ let hasIncompleteHooksKeyPrefix = false;
1143
+ let hasIncompleteHooksArrayTableOpener = false;
1144
+ for (let index = 0; index < source.lines.length; index += 1) {
1145
+ if (!source.lineStartsOutsideMultiline[index]) {
1146
+ hasIncompleteHooksKeyPrefix = false;
1147
+ hasIncompleteHooksArrayTableOpener = false;
1148
+ continue;
1149
+ }
1150
+ const line = source.lines[index] ?? "";
1151
+ const isParsedAssignmentLine = parsedAssignmentLines.has(index);
1152
+ if (!isParsedAssignmentLine) {
1153
+ const header = parseTomlSourceTableHeader(line);
1154
+ if (header?.parsed) {
1155
+ insideHooksTable = isTomlSourceHooksTableHeader(header.parsed);
1156
+ }
1157
+ }
1158
+ if (!managedContentLines.has(index)) {
1159
+ hasIncompleteHooksKeyPrefix = false;
1160
+ hasIncompleteHooksArrayTableOpener = false;
1161
+ continue;
1162
+ }
1163
+ const isParsedStatementLine = parsedLines.has(index);
1164
+ const isParsedStatementStartLine = parsedStatementStartLines.has(index);
1165
+ if (hasIncompleteHooksKeyPrefix) {
1166
+ if (isTomlSourceBlankOrComment(line))
1167
+ continue;
1168
+ if (isTomlSourceHooksKeyContinuationSeparator(line))
1169
+ return true;
1170
+ hasIncompleteHooksKeyPrefix = false;
1171
+ }
1172
+ // Parsed statements can contain nested arrays whose standalone brackets and
1173
+ // quoted "hooks" values must not contribute to malformed-header tracking.
1174
+ if (hasIncompleteHooksArrayTableOpener && !isParsedStatementLine) {
1175
+ if (isTomlSourceBlankOrComment(line))
1176
+ continue;
1177
+ const firstKey = firstTomlSourceKeyFollowingOpeningBrackets(line);
1178
+ if (firstKey) {
1179
+ if (isTomlSourceKey(firstKey, "hooks"))
1180
+ return true;
1181
+ hasIncompleteHooksArrayTableOpener = false;
1182
+ }
1183
+ }
1184
+ const arrayTableOpener = !isParsedStatementLine || isParsedStatementStartLine
1185
+ ? parseTomlSourceArrayTableOpener(line)
1186
+ : undefined;
1187
+ if (arrayTableOpener?.firstKey && isTomlSourceKey(arrayTableOpener.firstKey, "hooks")) {
1188
+ return true;
1189
+ }
1190
+ if (arrayTableOpener?.openerOnly && !isParsedStatementLine) {
1191
+ hasIncompleteHooksArrayTableOpener = true;
1192
+ }
1193
+ if (!isParsedStatementLine && isTomlSourceStandaloneOpeningBracket(line)) {
1194
+ hasIncompleteHooksArrayTableOpener = true;
1195
+ }
1196
+ if (isParsedStatementLine)
1197
+ continue;
1198
+ if (lineMaySpellHooksState(line) || (insideHooksTable && lineMaySpellStateKey(line))) {
1199
+ return true;
1200
+ }
1201
+ const hasIncompleteHooksPrefix = hasIncompleteTomlSourceHooksKeyPrefix(line);
1202
+ if (hasIncompleteHooksPrefix && /^\s*\[\[?/.test(line))
1203
+ return true;
1204
+ hasIncompleteHooksKeyPrefix = hasIncompleteHooksPrefix;
1205
+ }
1206
+ return false;
1207
+ }
1208
+ function managedHookTrustStateExpectations(...states) {
1209
+ const expectations = new Map();
1210
+ for (const state of states) {
1211
+ if (!state)
1212
+ continue;
1213
+ for (const [key, entry] of Object.entries(state)) {
1214
+ const hashes = expectations.get(key) ?? new Set();
1215
+ hashes.add(entry.trusted_hash);
1216
+ expectations.set(key, hashes);
1217
+ }
1218
+ }
1219
+ return expectations;
1220
+ }
1221
+ function managedHookTrustCoordinateFamily(key) {
1222
+ const match = /^(.*):([a-z_]+):\d+:\d+$/.exec(key);
1223
+ return match ? `${match[1]}:${match[2]}` : undefined;
1224
+ }
1225
+ function expectedManagedHookTrustHashes(expectations, key, allowCoordinateDrift) {
1226
+ const exact = expectations.get(key);
1227
+ if (exact || !allowCoordinateDrift)
1228
+ return exact;
1229
+ const family = managedHookTrustCoordinateFamily(key);
1230
+ if (!family)
736
1231
  return undefined;
737
- const key = decodeTomlBasicString(match[1] ?? "");
738
- if (key === undefined)
1232
+ const hashes = new Set();
1233
+ for (const [expectedKey, expectedHashes] of expectations) {
1234
+ if (managedHookTrustCoordinateFamily(expectedKey) !== family)
1235
+ continue;
1236
+ for (const hash of expectedHashes)
1237
+ hashes.add(hash);
1238
+ }
1239
+ return hashes.size > 0 ? hashes : undefined;
1240
+ }
1241
+ function tomlHooksStateValue(parsed) {
1242
+ if (!parsed)
739
1243
  return undefined;
740
- return { key, hasInlineComment: match[2] !== undefined };
1244
+ const hooks = parsed.hooks;
1245
+ if (!isPlainTomlRecord(hooks) || !Object.hasOwn(hooks, "state")) {
1246
+ return undefined;
1247
+ }
1248
+ return hooks.state;
741
1249
  }
742
- function isTomlTableHeader(line) {
743
- return /^\s*\[\[?[^\]]+\]?\]\s*(?:#.*)?$/.test(line);
1250
+ function tomlHooksStateEntries(parsed) {
1251
+ const state = tomlHooksStateValue(parsed);
1252
+ return isPlainTomlRecord(state) ? state : undefined;
744
1253
  }
745
- function isExactlyManagedHookTrustBody(bodyLines, expectedHash) {
746
- const nonBlank = bodyLines.filter((line) => line.trim().length > 0);
747
- if (nonBlank.length !== 1)
1254
+ function hasOnlyManagedHookTrustStateField(parsed, key) {
1255
+ if (!parsed || Object.keys(parsed).length !== 1 || !Object.hasOwn(parsed, "hooks")) {
748
1256
  return false;
749
- const expected = expectedHash.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
750
- return new RegExp(`^\\s*trusted_hash\\s*=\\s*"${expected}"\\s*$`).test(nonBlank[0] ?? "");
1257
+ }
1258
+ const hooks = parsed.hooks;
1259
+ if (!isPlainTomlRecord(hooks) || Object.keys(hooks).length !== 1 || !Object.hasOwn(hooks, "state")) {
1260
+ return false;
1261
+ }
1262
+ const state = hooks.state;
1263
+ return isPlainTomlRecord(state) &&
1264
+ Object.keys(state).length === 1 &&
1265
+ Object.hasOwn(state, key);
751
1266
  }
752
- function stripProofManagedCodexHookTrustStateTables(config, managedTrustState) {
753
- if (Object.keys(managedTrustState).length === 0) {
754
- return { config, preservedConflictKeys: new Set() };
1267
+ function parseTomlSourceTableHeader(line) {
1268
+ const start = line.search(/\S/);
1269
+ if (start === -1 || line[start] !== "[")
1270
+ return undefined;
1271
+ const array = line[start + 1] === "[";
1272
+ let quote;
1273
+ let escaped = false;
1274
+ for (let index = start + (array ? 2 : 1); index < line.length; index += 1) {
1275
+ const character = line[index];
1276
+ if (quote === "\"") {
1277
+ if (escaped) {
1278
+ escaped = false;
1279
+ }
1280
+ else if (character === "\\") {
1281
+ escaped = true;
1282
+ }
1283
+ else if (character === quote) {
1284
+ quote = undefined;
1285
+ }
1286
+ continue;
1287
+ }
1288
+ if (quote === "'") {
1289
+ if (character === quote)
1290
+ quote = undefined;
1291
+ continue;
1292
+ }
1293
+ if (character === "\"" || character === "'") {
1294
+ quote = character;
1295
+ continue;
1296
+ }
1297
+ if (character !== "]")
1298
+ continue;
1299
+ const end = array ? index + 2 : index + 1;
1300
+ if (array && line[index + 1] !== "]")
1301
+ return undefined;
1302
+ const suffix = line.slice(end).trimStart();
1303
+ if (suffix.length > 0 && !suffix.startsWith("#"))
1304
+ return undefined;
1305
+ return { parsed: safeParseToml(line.slice(start, end)) };
755
1306
  }
756
- const lines = config.split(/\r?\n/);
757
- const kept = [];
758
- const preservedConflictKeys = new Set();
759
- for (let i = 0; i < lines.length;) {
760
- const header = parseHooksStateHeader(lines[i] ?? "");
761
- if (!header) {
762
- kept.push(lines[i]);
763
- i += 1;
1307
+ return undefined;
1308
+ }
1309
+ function sourceRangeHasTomlComment(source, start, end) {
1310
+ for (let index = start; index < end; index += 1) {
1311
+ if (source.lineHasTomlComment[index])
1312
+ return true;
1313
+ }
1314
+ return false;
1315
+ }
1316
+ function collectMarkerRanges(source, startMarker, endMarker, includeUnterminatedRange = false, inertAssignmentSpans = []) {
1317
+ const isInsideInertAssignment = (line) => inertAssignmentSpans.some((span) => span.start <= line && line < span.end);
1318
+ const ranges = [];
1319
+ for (let start = 0; start < source.lines.length; start += 1) {
1320
+ if (!source.lineStartsOutsideMultiline[start] ||
1321
+ isInsideInertAssignment(start) ||
1322
+ source.lines[start]?.trim() !== startMarker)
1323
+ continue;
1324
+ const end = source.lines.findIndex((line, index) => index > start &&
1325
+ source.lineStartsOutsideMultiline[index] &&
1326
+ !isInsideInertAssignment(index) &&
1327
+ line.trim() === endMarker);
1328
+ const nestedStart = source.lines.findIndex((line, index) => index > start &&
1329
+ source.lineStartsOutsideMultiline[index] &&
1330
+ !isInsideInertAssignment(index) &&
1331
+ line.trim() === startMarker);
1332
+ if (end === -1 || (nestedStart !== -1 && nestedStart < end)) {
1333
+ if (includeUnterminatedRange)
1334
+ ranges.push({ start, end: source.lines.length });
764
1335
  continue;
765
1336
  }
766
- let tableEnd = lines.length;
767
- for (let next = i + 1; next < lines.length; next += 1) {
768
- if (isTomlTableHeader(lines[next] ?? "")) {
769
- tableEnd = next;
770
- break;
1337
+ ranges.push({ start, end });
1338
+ start = end;
1339
+ }
1340
+ return ranges;
1341
+ }
1342
+ function recordManagedMarkerHooksStateSourceSpan(spans, invalidSpans, start, end, parsed, insideManagedMarkerBlock) {
1343
+ if (!insideManagedMarkerBlock)
1344
+ return;
1345
+ const state = tomlHooksStateValue(parsed);
1346
+ if (state === undefined)
1347
+ return;
1348
+ const span = { start, end };
1349
+ spans.push(span);
1350
+ if (!isPlainTomlRecord(state) || Object.keys(state).length === 0) {
1351
+ invalidSpans.push(span);
1352
+ }
1353
+ }
1354
+ function addManagedHookTrustStateSourceRepresentations(representations, markerContainedHooksStateSpans, invalidMarkerContainedHooksStateSpans, source, start, end, parsed, insideManagedMarkerBlock, containingTableScope, fallbackKeys = []) {
1355
+ recordManagedMarkerHooksStateSourceSpan(markerContainedHooksStateSpans, invalidMarkerContainedHooksStateSpans, start, end, parsed, insideManagedMarkerBlock);
1356
+ const state = tomlHooksStateEntries(parsed);
1357
+ const hasComment = sourceRangeHasTomlComment(source, start, end);
1358
+ if (state) {
1359
+ const stateEntryCount = Object.keys(state).length;
1360
+ for (const [key, value] of Object.entries(state)) {
1361
+ const hasExactSemanticScope = hasOnlyManagedHookTrustStateField(parsed, key) &&
1362
+ (!containingTableScope ||
1363
+ (!containingTableScope.hasComment &&
1364
+ hasOnlyManagedHookTrustStateField(containingTableScope.parsed, key)));
1365
+ const entries = representations.get(key) ?? [];
1366
+ entries.push({
1367
+ start,
1368
+ end,
1369
+ stateEntryCount,
1370
+ value,
1371
+ hasComment,
1372
+ hasExactSemanticScope,
1373
+ insideManagedMarkerBlock,
1374
+ });
1375
+ representations.set(key, entries);
1376
+ }
1377
+ return;
1378
+ }
1379
+ for (const key of fallbackKeys) {
1380
+ const entries = representations.get(key) ?? [];
1381
+ entries.push({
1382
+ start,
1383
+ end,
1384
+ stateEntryCount: 0,
1385
+ value: undefined,
1386
+ hasComment,
1387
+ hasExactSemanticScope: false,
1388
+ insideManagedMarkerBlock,
1389
+ });
1390
+ representations.set(key, entries);
1391
+ }
1392
+ }
1393
+ function collectManagedHookTrustStateSourceRepresentations(config) {
1394
+ const source = analyzeTomlSource(config);
1395
+ const { lines } = source;
1396
+ const parsedAssignmentSpans = [];
1397
+ for (let cursor = 0; cursor < lines.length; cursor += 1) {
1398
+ if (!source.lineStartsOutsideMultiline[cursor])
1399
+ continue;
1400
+ const line = lines[cursor] ?? "";
1401
+ if (findTomlSourceAssignmentSeparator(line) === undefined)
1402
+ continue;
1403
+ for (let statementEnd = cursor + 1; statementEnd <= lines.length; statementEnd += 1) {
1404
+ if (!safeParseToml(lines.slice(cursor, statementEnd).join("\n")))
1405
+ continue;
1406
+ parsedAssignmentSpans.push({ start: cursor, end: statementEnd });
1407
+ cursor = statementEnd - 1;
1408
+ break;
1409
+ }
1410
+ }
1411
+ const headers = [];
1412
+ for (const [index, line] of lines.entries()) {
1413
+ if (!source.lineStartsOutsideMultiline[index])
1414
+ continue;
1415
+ const header = parseTomlSourceTableHeader(line);
1416
+ if (parsedAssignmentSpans.some((span) => span.start < index && index < span.end))
1417
+ continue;
1418
+ if (header)
1419
+ headers.push({ index, header });
1420
+ }
1421
+ const boundaries = new Set(headers.map(({ index }) => index));
1422
+ for (const [index, line] of lines.entries()) {
1423
+ if (source.lineStartsOutsideMultiline[index] &&
1424
+ (line.trim() === OMX_HOOK_TRUST_END_MARKER ||
1425
+ line.trim() === OMX_CONFIG_END_MARKER)) {
1426
+ boundaries.add(index);
1427
+ }
1428
+ }
1429
+ const sortedBoundaries = [...boundaries].sort((left, right) => left - right);
1430
+ const representations = new Map();
1431
+ const markerContainedHooksStateSpans = [];
1432
+ const invalidMarkerContainedHooksStateSpans = [];
1433
+ const parsedStatementSpans = [];
1434
+ const managedMarkerRanges = [
1435
+ ...collectMarkerRanges(source, OMX_HOOK_TRUST_START_MARKER, OMX_HOOK_TRUST_END_MARKER, false, parsedAssignmentSpans),
1436
+ ...collectMarkerRanges(source, OMX_CONFIG_START_MARKER, OMX_CONFIG_END_MARKER, true, parsedAssignmentSpans),
1437
+ ];
1438
+ const isInsideManagedMarkerBlock = (start, end) => managedMarkerRanges.some((range) => start > range.start && end <= range.end);
1439
+ const collectAssignments = (start, end, prefix, containingTableScope) => {
1440
+ for (let cursor = start; cursor < end;) {
1441
+ const line = lines[cursor] ?? "";
1442
+ if (!source.lineStartsOutsideMultiline[cursor] ||
1443
+ line.trim().length === 0 ||
1444
+ line.trim() === OMX_HOOK_TRUST_START_MARKER ||
1445
+ line.trim() === OMX_CONFIG_START_MARKER) {
1446
+ cursor += 1;
1447
+ continue;
1448
+ }
1449
+ let parsed;
1450
+ let statementEnd = cursor + 1;
1451
+ for (; statementEnd <= end; statementEnd += 1) {
1452
+ const statement = lines.slice(cursor, statementEnd).join("\n");
1453
+ parsed = safeParseToml(prefix ? `${prefix}\n${statement}` : statement);
1454
+ if (parsed)
1455
+ break;
771
1456
  }
1457
+ if (!parsed) {
1458
+ cursor += 1;
1459
+ continue;
1460
+ }
1461
+ parsedStatementSpans.push({ start: cursor, end: statementEnd });
1462
+ addManagedHookTrustStateSourceRepresentations(representations, markerContainedHooksStateSpans, invalidMarkerContainedHooksStateSpans, source, cursor, statementEnd, parsed, isInsideManagedMarkerBlock(cursor, statementEnd), containingTableScope);
1463
+ cursor = statementEnd;
772
1464
  }
773
- const expectedState = managedTrustState[header.key];
774
- const removable = expectedState !== undefined &&
775
- !header.hasInlineComment &&
776
- isExactlyManagedHookTrustBody(lines.slice(i + 1, tableEnd), expectedState.trusted_hash);
777
- if (removable) {
778
- i = tableEnd;
1465
+ };
1466
+ const rootEnd = sortedBoundaries[0] ?? lines.length;
1467
+ collectAssignments(0, rootEnd, undefined);
1468
+ for (const { index, header } of headers) {
1469
+ const boundaryIndex = sortedBoundaries.findIndex((boundary) => boundary === index);
1470
+ const end = boundaryIndex === -1
1471
+ ? lines.length
1472
+ : (sortedBoundaries[boundaryIndex + 1] ?? lines.length);
1473
+ const tableScope = {
1474
+ parsed: safeParseToml(lines.slice(index, end).join("\n")),
1475
+ hasComment: sourceRangeHasTomlComment(source, index, end),
1476
+ };
1477
+ if (tableScope.parsed)
1478
+ parsedStatementSpans.push({ start: index, end });
1479
+ const insideManagedMarkerBlock = isInsideManagedMarkerBlock(index, end);
1480
+ recordManagedMarkerHooksStateSourceSpan(markerContainedHooksStateSpans, invalidMarkerContainedHooksStateSpans, index, end, tableScope.parsed, insideManagedMarkerBlock);
1481
+ const headerState = tomlHooksStateEntries(header.parsed);
1482
+ const directKeys = Object.keys(headerState ?? {});
1483
+ if (directKeys.length > 0) {
1484
+ addManagedHookTrustStateSourceRepresentations(representations, markerContainedHooksStateSpans, invalidMarkerContainedHooksStateSpans, source, index, end, tableScope.parsed, insideManagedMarkerBlock, undefined, directKeys);
779
1485
  continue;
780
1486
  }
781
- if (expectedState !== undefined) {
782
- preservedConflictKeys.add(header.key);
1487
+ collectAssignments(index + 1, end, lines[index], tableScope);
1488
+ }
1489
+ return {
1490
+ source,
1491
+ representations,
1492
+ managedMarkerRanges,
1493
+ markerContainedHooksStateSpans,
1494
+ invalidMarkerContainedHooksStateSpans,
1495
+ parsedStatementSpans,
1496
+ parsedAssignmentSpans,
1497
+ };
1498
+ }
1499
+ function isExactlyManagedHookTrustStateValue(value, expectedHashes) {
1500
+ return isPlainTomlRecord(value) &&
1501
+ Object.keys(value).length === 1 &&
1502
+ Object.hasOwn(value, "trusted_hash") &&
1503
+ typeof value.trusted_hash === "string" &&
1504
+ expectedHashes.has(value.trusted_hash);
1505
+ }
1506
+ function managedMarkerTrustStateConflicts(source, representations, managedMarkerRanges, markerContainedHooksStateSpans, invalidMarkerContainedHooksStateSpans, parsedStatementSpans, parsedAssignmentSpans, expectations) {
1507
+ const conflicts = new Set();
1508
+ const isInsideManagedMarkerRange = (span) => managedMarkerRanges.some((range) => span.start > range.start && span.end <= range.end);
1509
+ if (invalidMarkerContainedHooksStateSpans.some(isInsideManagedMarkerRange) ||
1510
+ hasUnprovenManagedMarkerHooksStateSyntax(source, managedMarkerRanges, [...markerContainedHooksStateSpans, ...parsedStatementSpans], parsedAssignmentSpans)) {
1511
+ conflicts.add("marker-contained hooks.state");
1512
+ }
1513
+ for (const [key, sourceRepresentations] of representations) {
1514
+ for (const representation of sourceRepresentations) {
1515
+ if (!isInsideManagedMarkerRange(representation))
1516
+ continue;
1517
+ const expectedHashes = expectedManagedHookTrustHashes(expectations, key, true);
1518
+ const isExactOwnedRepresentation = expectedHashes !== undefined &&
1519
+ representation.stateEntryCount === 1 &&
1520
+ !representation.hasComment &&
1521
+ representation.hasExactSemanticScope &&
1522
+ isExactlyManagedHookTrustStateValue(representation.value, expectedHashes);
1523
+ if (!source.isUnambiguous || !isExactOwnedRepresentation)
1524
+ conflicts.add(key);
783
1525
  }
784
- kept.push(...lines.slice(i, tableEnd));
785
- i = tableEnd;
786
1526
  }
787
- return { config: kept.join("\n"), preservedConflictKeys };
1527
+ return conflicts;
788
1528
  }
789
- function stripManagedCodexHookTrustStateWithResult(config, options = {}) {
790
- const lines = config.split(/\r?\n/);
791
- const kept = [];
792
- for (let i = 0; i < lines.length;) {
793
- const trimmed = lines[i].trim();
794
- if (trimmed !== OMX_HOOK_TRUST_START_MARKER) {
795
- kept.push(lines[i]);
796
- i += 1;
1529
+ function removeEmptyManagedHookTrustStateMarkerBlocks(source, removed) {
1530
+ const { lines } = source;
1531
+ for (let start = 0; start < lines.length; start += 1) {
1532
+ if (!source.lineStartsOutsideMultiline[start] ||
1533
+ lines[start]?.trim() !== OMX_HOOK_TRUST_START_MARKER)
797
1534
  continue;
1535
+ const end = lines.findIndex((line, index) => index > start &&
1536
+ source.lineStartsOutsideMultiline[index] &&
1537
+ line.trim() === OMX_HOOK_TRUST_END_MARKER);
1538
+ const nestedStart = lines.findIndex((line, index) => index > start &&
1539
+ source.lineStartsOutsideMultiline[index] &&
1540
+ line.trim() === OMX_HOOK_TRUST_START_MARKER);
1541
+ if (end === -1 || (nestedStart !== -1 && nestedStart < end))
1542
+ continue;
1543
+ let onlyOwnedMarkerContent = true;
1544
+ for (let index = start + 1; index < end; index += 1) {
1545
+ const trimmed = lines[index].trim();
1546
+ if (removed.has(index) ||
1547
+ trimmed.length === 0 ||
1548
+ trimmed === "# Trusts only setup-managed native hook wrappers.")
1549
+ continue;
1550
+ onlyOwnedMarkerContent = false;
1551
+ break;
798
1552
  }
799
- const nextEndIdx = lines.findIndex((line, index) => index > i && line.trim() === OMX_HOOK_TRUST_END_MARKER);
800
- const nextStartIdx = lines.findIndex((line, index) => index > i && line.trim() === OMX_HOOK_TRUST_START_MARKER);
801
- if (nextEndIdx === -1 || (nextStartIdx !== -1 && nextStartIdx < nextEndIdx)) {
802
- kept.push(lines[i]);
803
- i += 1;
1553
+ if (!onlyOwnedMarkerContent)
1554
+ continue;
1555
+ removed.add(start);
1556
+ removed.add(end);
1557
+ for (let index = start + 1; index < end; index += 1) {
1558
+ if (lines[index]?.trim() === "# Trusts only setup-managed native hook wrappers.") {
1559
+ removed.add(index);
1560
+ }
1561
+ }
1562
+ start = end;
1563
+ }
1564
+ }
1565
+ function stripProofManagedCodexHookTrustStateTables(config, expectations) {
1566
+ const { source, representations, managedMarkerRanges, markerContainedHooksStateSpans, invalidMarkerContainedHooksStateSpans, parsedStatementSpans, parsedAssignmentSpans, } = collectManagedHookTrustStateSourceRepresentations(config);
1567
+ const preservedConflictKeys = managedMarkerTrustStateConflicts(source, representations, managedMarkerRanges, markerContainedHooksStateSpans, invalidMarkerContainedHooksStateSpans, parsedStatementSpans, parsedAssignmentSpans, expectations);
1568
+ if (!source.isUnambiguous) {
1569
+ return { config, preservedConflictKeys };
1570
+ }
1571
+ const { lines } = source;
1572
+ const removals = new Map();
1573
+ const parsedConfigState = tomlHooksStateEntries(safeParseToml(config));
1574
+ for (const key of Object.keys(parsedConfigState ?? {})) {
1575
+ if (expectations.has(key) && !representations.has(key)) {
1576
+ preservedConflictKeys.add(key);
1577
+ }
1578
+ }
1579
+ for (const [key, sourceRepresentations] of representations) {
1580
+ const isExactOwnedRepresentation = (representation) => {
1581
+ const expectedHashes = expectedManagedHookTrustHashes(expectations, key, representation.insideManagedMarkerBlock);
1582
+ return expectedHashes !== undefined &&
1583
+ representation.stateEntryCount === 1 &&
1584
+ !representation.hasComment &&
1585
+ representation.hasExactSemanticScope &&
1586
+ isExactlyManagedHookTrustStateValue(representation.value, expectedHashes);
1587
+ };
1588
+ if (!sourceRepresentations.some((representation) => expectedManagedHookTrustHashes(expectations, key, representation.insideManagedMarkerBlock) !== undefined))
1589
+ continue;
1590
+ const mayRemoveAll = sourceRepresentations.length === 1
1591
+ ? isExactOwnedRepresentation(sourceRepresentations[0])
1592
+ : sourceRepresentations.every((representation) => representation.insideManagedMarkerBlock &&
1593
+ isExactOwnedRepresentation(representation));
1594
+ if (!mayRemoveAll) {
1595
+ preservedConflictKeys.add(key);
804
1596
  continue;
805
1597
  }
806
- i = nextEndIdx + 1;
1598
+ for (const representation of sourceRepresentations) {
1599
+ removals.set(`${representation.start}:${representation.end}`, {
1600
+ start: representation.start,
1601
+ end: representation.end,
1602
+ });
1603
+ }
1604
+ }
1605
+ if (removals.size === 0) {
1606
+ return { config, preservedConflictKeys };
1607
+ }
1608
+ const removed = new Set();
1609
+ for (const range of removals.values()) {
1610
+ for (let index = range.start; index < range.end; index += 1) {
1611
+ removed.add(index);
1612
+ }
807
1613
  }
808
- const withoutFenced = kept.join("\n");
809
- const proofStripped = options.managedTrustState
810
- ? stripProofManagedCodexHookTrustStateTables(withoutFenced, options.managedTrustState)
811
- : { config: withoutFenced, preservedConflictKeys: new Set() };
1614
+ removeEmptyManagedHookTrustStateMarkerBlocks(source, removed);
812
1615
  return {
813
- config: proofStripped.config.replace(/\n{3,}/g, "\n\n").trimEnd(),
814
- preservedConflictKeys: proofStripped.preservedConflictKeys,
1616
+ config: lines.filter((_, index) => !removed.has(index)).join("\n").trimEnd(),
1617
+ preservedConflictKeys,
815
1618
  };
816
1619
  }
1620
+ function stripManagedCodexHookTrustStateForRefresh(config, priorManagedTrustState, finalManagedTrustState) {
1621
+ return stripProofManagedCodexHookTrustStateTables(config, managedHookTrustStateExpectations(priorManagedTrustState, finalManagedTrustState));
1622
+ }
1623
+ function rejectManagedCodexHookTrustStateConflicts(conflicts) {
1624
+ if (conflicts.size === 0)
1625
+ return;
1626
+ const keys = [...conflicts].sort((left, right) => left.localeCompare(right));
1627
+ throw new ManagedCodexHooksPlanError("managed_trust_key_conflict", `Refusing to replace existing Codex hook trust state for ${keys.join(", ")}. Remove or reconcile the conflicting [hooks.state] entry before running setup.`, { keys });
1628
+ }
817
1629
  export function stripManagedCodexHookTrustState(config, options = {}) {
818
- return stripManagedCodexHookTrustStateWithResult(config, options).config;
1630
+ const stripped = stripManagedCodexHookTrustStateForRefresh(config, options.priorManagedHookTrustState, options.managedTrustState ?? {});
1631
+ rejectManagedCodexHookTrustStateConflicts(stripped.preservedConflictKeys);
1632
+ return stripped.config;
819
1633
  }
820
- function renderManagedCodexHookTrustToml(managedTrustState, excludedKeys = new Set()) {
1634
+ function renderManagedCodexHookTrustToml(managedTrustState) {
821
1635
  return Object.entries(managedTrustState)
822
- .filter(([key]) => !excludedKeys.has(key))
823
1636
  .sort(([left], [right]) => left.localeCompare(right))
824
1637
  .flatMap(([key, hookState]) => [
825
1638
  `[hooks.state."${escapeTomlBasicString(key)}"]`,
@@ -829,21 +1642,92 @@ function renderManagedCodexHookTrustToml(managedTrustState, excludedKeys = new S
829
1642
  .join("\n")
830
1643
  .trimEnd();
831
1644
  }
832
- function buildManagedCodexHookTrustStateForConfig(codexHooksFile, pkgRoot, options = {}) {
1645
+ function identicalLegacyHookTrustStateEntry(existing, expected) {
1646
+ if (!isPlainTomlRecord(existing))
1647
+ return false;
1648
+ if (!Object.hasOwn(existing, "trusted_hash") ||
1649
+ typeof existing.trusted_hash !== "string" ||
1650
+ existing.trusted_hash !== expected.trusted_hash ||
1651
+ Object.keys(existing).some((key) => key !== "trusted_hash" && key !== "enabled")) {
1652
+ return false;
1653
+ }
1654
+ return Object.hasOwn(existing, "enabled") === Object.hasOwn(expected, "enabled") &&
1655
+ existing.enabled === expected.enabled;
1656
+ }
1657
+ function legacyHookTrustStateConflict(keys, reason) {
1658
+ throw new ManagedCodexHooksPlanError("managed_trust_key_conflict", `Refusing to migrate legacy Codex hook trust state into conflicting config.toml entries for ${keys.join(", ")}.`, { keys: [...keys], ...(reason ? { reason } : {}) });
1659
+ }
1660
+ function appendLegacyHookTrustState(config, legacyTrustState) {
1661
+ if (!legacyTrustState || Object.keys(legacyTrustState).length === 0) {
1662
+ return config;
1663
+ }
1664
+ const parsed = safeParseToml(config);
1665
+ const keys = Object.keys(legacyTrustState).sort((left, right) => left.localeCompare(right));
1666
+ if (!parsed)
1667
+ legacyHookTrustStateConflict(keys, "config_parse_failure");
1668
+ const hooks = parsed.hooks;
1669
+ if (hooks !== undefined && !isPlainTomlRecord(hooks)) {
1670
+ legacyHookTrustStateConflict(keys, "hooks_table_invalid");
1671
+ }
1672
+ const hookState = isPlainTomlRecord(hooks) ? hooks.state : undefined;
1673
+ if (hookState !== undefined && !isPlainTomlRecord(hookState)) {
1674
+ legacyHookTrustStateConflict(keys, "hooks_state_table_invalid");
1675
+ }
1676
+ const existingTrustState = isPlainTomlRecord(hookState) ? hookState : {};
1677
+ const entriesToAppend = Object.entries(legacyTrustState)
1678
+ .filter(([key]) => !Object.hasOwn(existingTrustState, key));
1679
+ const conflictingKeys = Object.entries(legacyTrustState)
1680
+ .filter(([key, expected]) => Object.hasOwn(existingTrustState, key) &&
1681
+ !identicalLegacyHookTrustStateEntry(existingTrustState[key], expected))
1682
+ .map(([key]) => key)
1683
+ .sort((left, right) => left.localeCompare(right));
1684
+ if (conflictingKeys.length > 0)
1685
+ legacyHookTrustStateConflict(conflictingKeys);
1686
+ const trustToml = entriesToAppend
1687
+ .sort(([left], [right]) => left.localeCompare(right))
1688
+ .flatMap(([key, state]) => [
1689
+ `[hooks.state."${escapeTomlBasicString(key)}"]`,
1690
+ `trusted_hash = "${escapeTomlBasicString(state.trusted_hash)}"`,
1691
+ ...(typeof state.enabled === "boolean" ? [`enabled = ${state.enabled}`] : []),
1692
+ "",
1693
+ ])
1694
+ .join("\n")
1695
+ .trimEnd();
1696
+ if (!trustToml)
1697
+ return config;
1698
+ const base = config.trimEnd();
1699
+ return [
1700
+ base,
1701
+ base ? "" : null,
1702
+ "# Migrated from legacy hooks.json state; kept in Codex config.toml because Codex 0.140 rejects top-level hooks.json state.",
1703
+ trustToml,
1704
+ "",
1705
+ ].filter((line) => line !== null).join("\n");
1706
+ }
1707
+ function managedCodexHookOptionsFromMergeOptions(options) {
1708
+ return {
1709
+ codexHomeDir: options.codexHomeDir,
1710
+ platform: options.hookCommandPlatform,
1711
+ hooksContent: options.codexHooksContent,
1712
+ };
1713
+ }
1714
+ function buildManagedCodexHookTrustStateForConfig(codexHooksFile, pkgRoot, options = {}, precomputedTrustState) {
1715
+ if (precomputedTrustState)
1716
+ return precomputedTrustState;
833
1717
  if (!codexHooksFile)
834
1718
  return {};
835
1719
  return buildManagedCodexHookTrustState(codexHooksFile, pkgRoot, options);
836
1720
  }
837
1721
  export function upsertManagedCodexHookTrustState(config, pkgRoot, codexHooksFile, options = {}) {
838
- const managedTrustState = buildManagedCodexHookTrustStateForConfig(codexHooksFile, pkgRoot, options);
839
- const strippedResult = stripManagedCodexHookTrustStateWithResult(config, {
840
- managedTrustState,
841
- });
842
- const stripped = strippedResult.config;
843
- const hookTrustToml = renderManagedCodexHookTrustToml(managedTrustState, strippedResult.preservedConflictKeys);
844
- if (!hookTrustToml)
845
- return `${stripped}\n`;
846
- return [
1722
+ const managedTrustState = buildManagedCodexHookTrustStateForConfig(codexHooksFile, pkgRoot, options, options.managedTrustState);
1723
+ const strippedResult = stripManagedCodexHookTrustStateForRefresh(config, options.priorManagedHookTrustState, managedTrustState);
1724
+ rejectManagedCodexHookTrustStateConflicts(strippedResult.preservedConflictKeys);
1725
+ const stripped = strippedResult.config.trimEnd();
1726
+ const hookTrustToml = renderManagedCodexHookTrustToml(managedTrustState);
1727
+ if (!hookTrustToml) {
1728
+ return appendLegacyHookTrustState(`${stripped}\n`, options.legacyHookTrustState);
1729
+ }
1730
+ return appendLegacyHookTrustState([
847
1731
  stripped,
848
1732
  "",
849
1733
  OMX_HOOK_TRUST_START_MARKER,
@@ -851,7 +1735,7 @@ export function upsertManagedCodexHookTrustState(config, pkgRoot, codexHooksFile
851
1735
  hookTrustToml,
852
1736
  OMX_HOOK_TRUST_END_MARKER,
853
1737
  "",
854
- ].filter((line, index) => index !== 0 || line.length > 0).join("\n");
1738
+ ].filter((line, index) => index !== 0 || line.length > 0).join("\n"), options.legacyHookTrustState);
855
1739
  }
856
1740
  export function upsertPluginModeRuntimeFeatureFlags(config, codexHookFeatureFlag = DEFAULT_CODEX_HOOK_FEATURE_FLAG, options = {}) {
857
1741
  const lines = config.split(/\r?\n/);
@@ -864,6 +1748,9 @@ export function upsertPluginModeRuntimeFeatureFlags(config, codexHookFeatureFlag
864
1748
  const featureBlock = [
865
1749
  "[features]",
866
1750
  hookFeatureFlagLine,
1751
+ ...(options.pluginScopedHooks && options.preserveNativeHooks
1752
+ ? [formatCodexHookFeatureFlagLine(codexHookFeatureFlag)]
1753
+ : []),
867
1754
  "goals = true",
868
1755
  "",
869
1756
  ].join("\n");
@@ -887,9 +1774,15 @@ export function upsertPluginModeRuntimeFeatureFlags(config, codexHookFeatureFlag
887
1774
  sectionEnd -= 1;
888
1775
  }
889
1776
  }
890
- ({ sectionEnd } = options.pluginScopedHooks
891
- ? upsertPluginScopedHookFeatureFlagInSection(lines, featuresStart, sectionEnd)
892
- : upsertCodexHookFeatureFlagInSection(lines, featuresStart, sectionEnd, codexHookFeatureFlag));
1777
+ if (options.pluginScopedHooks) {
1778
+ ({ sectionEnd } = upsertPluginScopedHookFeatureFlagInSection(lines, featuresStart, sectionEnd));
1779
+ if (options.preserveNativeHooks) {
1780
+ ({ sectionEnd } = upsertCodexHookFeatureFlagInSection(lines, featuresStart, sectionEnd, codexHookFeatureFlag));
1781
+ }
1782
+ }
1783
+ else {
1784
+ ({ sectionEnd } = upsertCodexHookFeatureFlagInSection(lines, featuresStart, sectionEnd, codexHookFeatureFlag));
1785
+ }
893
1786
  let goalsIdx = -1;
894
1787
  for (let i = featuresStart + 1; i < sectionEnd; i++) {
895
1788
  if (/^\s*goals\s*=/.test(lines[i])) {
@@ -1015,52 +1908,12 @@ function upsertEnvSettings(config) {
1015
1908
  }
1016
1909
  return lines.join("\n");
1017
1910
  }
1018
- function upsertAgentsSettings(config) {
1019
- const lines = config.split(/\r?\n/);
1020
- const agentsStart = lines.findIndex((line) => /^\s*\[agents\]\s*$/.test(line));
1021
- if (agentsStart < 0) {
1022
- const base = config.trimEnd();
1023
- const agentsBlock = [
1024
- "[agents]",
1025
- `max_threads = ${OMX_AGENTS_MAX_THREADS}`,
1026
- `max_depth = ${OMX_AGENTS_MAX_DEPTH}`,
1027
- "",
1028
- ].join("\n");
1029
- if (base.length === 0)
1030
- return agentsBlock;
1031
- return `${base}\n\n${agentsBlock}`;
1032
- }
1033
- let sectionEnd = lines.length;
1034
- for (let i = agentsStart + 1; i < lines.length; i++) {
1035
- if (/^\s*\[\[?[^\]]+\]?\]\s*$/.test(lines[i])) {
1036
- sectionEnd = i;
1037
- break;
1038
- }
1039
- }
1040
- let maxThreadsIdx = -1;
1041
- let maxDepthIdx = -1;
1042
- for (let i = agentsStart + 1; i < sectionEnd; i++) {
1043
- if (/^\s*max_threads\s*=/.test(lines[i])) {
1044
- maxThreadsIdx = i;
1045
- }
1046
- else if (/^\s*max_depth\s*=/.test(lines[i])) {
1047
- maxDepthIdx = i;
1048
- }
1049
- }
1050
- if (maxThreadsIdx < 0) {
1051
- lines.splice(sectionEnd, 0, `max_threads = ${OMX_AGENTS_MAX_THREADS}`);
1052
- sectionEnd += 1;
1053
- }
1054
- if (maxDepthIdx < 0) {
1055
- lines.splice(sectionEnd, 0, `max_depth = ${OMX_AGENTS_MAX_DEPTH}`);
1056
- }
1057
- return lines.join("\n");
1058
- }
1059
1911
  /**
1060
1912
  * Remove OMX-owned feature flags from the [features] section.
1913
+ * If `preserveMultiAgent` is set, retain multi_agent unchanged.
1061
1914
  * If the section becomes empty after removal, remove the section header too.
1062
1915
  */
1063
- export function stripOmxFeatureFlags(config) {
1916
+ export function stripOmxFeatureFlags(config, options = {}) {
1064
1917
  const lines = config.split(/\r?\n/);
1065
1918
  const featuresStart = lines.findIndex((line) => /^\s*\[features\]\s*$/.test(line));
1066
1919
  if (featuresStart < 0)
@@ -1073,7 +1926,7 @@ export function stripOmxFeatureFlags(config) {
1073
1926
  }
1074
1927
  }
1075
1928
  const omxFlags = [
1076
- "multi_agent",
1929
+ ...(options.preserveMultiAgent ? [] : ["multi_agent"]),
1077
1930
  "child_agents_md",
1078
1931
  "hooks",
1079
1932
  "codex_hooks",
@@ -1139,10 +1992,6 @@ export function stripOmxEnvSettings(config) {
1139
1992
  // ---------------------------------------------------------------------------
1140
1993
  // Orphaned OMX table sections (no marker block)
1141
1994
  // ---------------------------------------------------------------------------
1142
- /**
1143
- * Check whether a TOML table name belongs to a legacy OMX-managed agent entry.
1144
- * Handles both `agents.name` and `agents."name"` forms.
1145
- */
1146
1995
  function isOmxFirstPartyMcpSection(tableName) {
1147
1996
  const match = tableName.match(/^mcp_servers\.(?:"([^"]+)"|([A-Za-z0-9_-]+))$/);
1148
1997
  const name = match?.[1] ?? match?.[2];
@@ -1150,20 +1999,10 @@ function isOmxFirstPartyMcpSection(tableName) {
1150
1999
  (OMX_FIRST_PARTY_MCP_SERVER_NAMES.includes(name) ||
1151
2000
  name === "omx_team_run"));
1152
2001
  }
1153
- function isLegacyOmxAgentSection(tableName) {
1154
- const m = tableName.match(/^agents\.(?:"([^"]+)"|(\w[\w-]*))$/);
1155
- if (!m)
1156
- return false;
1157
- const name = m[1] || m[2] || "";
1158
- return Object.prototype.hasOwnProperty.call(AGENT_DEFINITIONS, name);
1159
- }
1160
2002
  /**
1161
- * Strip OMX-owned table sections that exist outside the marker block.
2003
+ * Strip first-party OMX MCP table sections that exist outside the marker block.
1162
2004
  * This covers legacy configs that were written before markers were added,
1163
2005
  * or configs where the marker was accidentally removed.
1164
- *
1165
- * Targets: exact first-party [mcp_servers.<name>] entries, retired
1166
- * [mcp_servers.omx_team_run], and legacy [agents.<name>] entries.
1167
2006
  */
1168
2007
  function stripOrphanedOmxSections(config) {
1169
2008
  const lines = config.split(/\r?\n/);
@@ -1177,8 +2016,7 @@ function stripOrphanedOmxSections(config) {
1177
2016
  // Note: [tui] is NOT stripped here because it could be user-owned.
1178
2017
  // The marker-based stripExistingOmxBlocks already handles [tui]
1179
2018
  // when it lives inside the OMX marker block.
1180
- const isOmxSection = isOmxFirstPartyMcpSection(tableName) ||
1181
- isLegacyOmxAgentSection(tableName);
2019
+ const isOmxSection = isOmxFirstPartyMcpSection(tableName);
1182
2020
  if (isOmxSection) {
1183
2021
  // Remove preceding OMX comment lines and blank lines
1184
2022
  while (result.length > 0) {
@@ -1408,35 +2246,45 @@ function upsertTuiStatusLine(config, preset = DEFAULT_STATUS_LINE_PRESET, option
1408
2246
  // ---------------------------------------------------------------------------
1409
2247
  // OMX [table] sections block (appended at end of file)
1410
2248
  // ---------------------------------------------------------------------------
1411
- export function stripExistingOmxBlocks(config) {
1412
- let cleaned = config;
1413
- let removed = 0;
1414
- while (true) {
1415
- const markerIdx = cleaned.indexOf(OMX_CONFIG_MARKER);
1416
- if (markerIdx < 0)
1417
- break;
1418
- let blockStart = cleaned.lastIndexOf("\n", markerIdx);
1419
- blockStart = blockStart >= 0 ? blockStart + 1 : 0;
1420
- const previousLineEnd = blockStart - 1;
1421
- if (previousLineEnd >= 0) {
1422
- const previousLineStart = cleaned.lastIndexOf("\n", previousLineEnd - 1);
1423
- const previousLine = cleaned.slice(previousLineStart + 1, previousLineEnd);
1424
- if (/^# =+$/.test(previousLine.trim())) {
1425
- blockStart = previousLineStart >= 0 ? previousLineStart + 1 : 0;
1426
- }
1427
- }
1428
- let blockEnd = cleaned.length;
1429
- const endIdx = cleaned.indexOf(OMX_CONFIG_END_MARKER, markerIdx);
1430
- if (endIdx >= 0) {
1431
- const endLineBreak = cleaned.indexOf("\n", endIdx);
1432
- blockEnd = endLineBreak >= 0 ? endLineBreak + 1 : cleaned.length;
1433
- }
1434
- const before = cleaned.slice(0, blockStart).trimEnd();
1435
- const after = cleaned.slice(blockEnd).trimStart();
1436
- cleaned = [before, after].filter(Boolean).join("\n\n");
1437
- removed += 1;
1438
- }
1439
- return { cleaned, removed };
2249
+ export function stripExistingOmxBlocks(config, options = {}) {
2250
+ const inventory = collectManagedHookTrustStateSourceRepresentations(config);
2251
+ const { source } = inventory;
2252
+ const markerRanges = collectMarkerRanges(source, OMX_CONFIG_START_MARKER, OMX_CONFIG_END_MARKER, false, inventory.parsedAssignmentSpans);
2253
+ if (markerRanges.length === 0)
2254
+ return { cleaned: config, removed: 0 };
2255
+ const conflicts = managedMarkerTrustStateConflicts(source, inventory.representations, markerRanges, inventory.markerContainedHooksStateSpans, inventory.invalidMarkerContainedHooksStateSpans, inventory.parsedStatementSpans, inventory.parsedAssignmentSpans, managedHookTrustStateExpectations(options.priorManagedHookTrustState, options.managedTrustState));
2256
+ rejectManagedCodexHookTrustStateConflicts(conflicts);
2257
+ if (!source.isUnambiguous)
2258
+ return { cleaned: config, removed: 0 };
2259
+ const lines = sourceLines(config);
2260
+ if (markerRanges.some((range) => !lines[range.start] || !lines[range.end])) {
2261
+ return { cleaned: config, removed: 0 };
2262
+ }
2263
+ const removalRanges = markerRanges.map((range) => {
2264
+ const startLine = lines[range.start];
2265
+ const endLine = lines[range.end];
2266
+ const previousLine = lines[range.start - 1];
2267
+ const start = previousLine && /^# =+$/.test(previousLine.content.trim())
2268
+ ? previousLine.start
2269
+ : startLine.start;
2270
+ return { start, end: endLine.end };
2271
+ });
2272
+ const segments = [];
2273
+ let cursor = 0;
2274
+ for (const range of removalRanges) {
2275
+ segments.push(config.slice(cursor, range.start));
2276
+ cursor = range.end;
2277
+ }
2278
+ segments.push(config.slice(cursor));
2279
+ const cleaned = segments
2280
+ .map((segment, index) => index === 0
2281
+ ? segment.trimEnd()
2282
+ : index === segments.length - 1
2283
+ ? segment.trimStart()
2284
+ : segment.trim())
2285
+ .filter(Boolean)
2286
+ .join("\n\n");
2287
+ return { cleaned, removed: markerRanges.length };
1440
2288
  }
1441
2289
  export function stripExistingSharedMcpRegistryBlock(config) {
1442
2290
  let cleaned = config;
@@ -1671,7 +2519,7 @@ export function mergeSharedMcpRegistryBlock(config, servers, sourcePath) {
1671
2519
  * OMX table-section block (MCP servers, TUI).
1672
2520
  * Contains ONLY [table] sections — no bare keys.
1673
2521
  */
1674
- function getOmxTablesBlock(pkgRoot, includeTui = true, statusLinePreset = DEFAULT_STATUS_LINE_PRESET, codexHooksFile, hookOptions = {}, includeFirstPartyMcp = false, excludedHookTrustStateKeys = new Set()) {
2522
+ function getOmxTablesBlock(pkgRoot, includeTui = true, statusLinePreset = DEFAULT_STATUS_LINE_PRESET, codexHooksFile, hookOptions = {}, managedHookTrustState, includeFirstPartyMcp = false) {
1675
2523
  const lines = [
1676
2524
  "",
1677
2525
  "# ============================================================",
@@ -1694,7 +2542,7 @@ function getOmxTablesBlock(pkgRoot, includeTui = true, statusLinePreset = DEFAUL
1694
2542
  }
1695
2543
  }
1696
2544
  }
1697
- const hookTrustToml = renderManagedCodexHookTrustToml(buildManagedCodexHookTrustStateForConfig(codexHooksFile, pkgRoot, hookOptions), excludedHookTrustStateKeys);
2545
+ const hookTrustToml = renderManagedCodexHookTrustToml(buildManagedCodexHookTrustStateForConfig(codexHooksFile, pkgRoot, hookOptions, managedHookTrustState));
1698
2546
  if (hookTrustToml) {
1699
2547
  lines.push("");
1700
2548
  lines.push("# OMX-owned Codex hook trust state");
@@ -1726,13 +2574,17 @@ function getOmxTablesBlock(pkgRoot, includeTui = true, statusLinePreset = DEFAUL
1726
2574
  *
1727
2575
  * Layout:
1728
2576
  * 1. OMX top-level keys (notify, model_reasoning_effort, developer_instructions)
1729
- * 2. [features] with multi_agent + child_agents_md + hooks + goals
2577
+ * 2. [features] with child_agents_md + hooks + goals
1730
2578
  * 3. [shell_environment_policy.set] with defaulted deprecated explore-routing opt-out
1731
2579
  * 4. … user sections …
1732
2580
  * 5. OMX [table] sections (mcp_servers, tui)
1733
2581
  */
1734
2582
  export function buildMergedConfig(existingConfig, pkgRoot, options = {}) {
1735
- let existing = existingConfig;
2583
+ let existing = stripOmxSeededBehavioralDefaults(existingConfig);
2584
+ const hookOptions = managedCodexHookOptionsFromMergeOptions(options);
2585
+ const managedTrustState = buildManagedCodexHookTrustStateForConfig(options.codexHooksFile, pkgRoot, hookOptions, options.managedHookTrustState);
2586
+ const preflightHookTrustStrip = stripManagedCodexHookTrustStateForRefresh(existing, options.priorManagedHookTrustState, managedTrustState);
2587
+ rejectManagedCodexHookTrustStateConflicts(preflightHookTrustStrip.preservedConflictKeys);
1736
2588
  const preservedFirstPartyMcpSections = options.preserveExistingFirstPartyMcp === true &&
1737
2589
  options.includeFirstPartyMcp !== true
1738
2590
  ? extractFirstPartyOmxMcpSections(existing)
@@ -1741,7 +2593,10 @@ export function buildMergedConfig(existingConfig, pkgRoot, options = {}) {
1741
2593
  const statusLinePreset = options.statusLinePreset ?? DEFAULT_STATUS_LINE_PRESET;
1742
2594
  const customizedManagedTuiSections = extractCustomizedTuiSectionsFromOmxBlocks(existing);
1743
2595
  if (existing.includes(OMX_CONFIG_MARKER)) {
1744
- const stripped = stripExistingOmxBlocks(existing);
2596
+ const stripped = stripExistingOmxBlocks(existing, {
2597
+ managedTrustState,
2598
+ priorManagedHookTrustState: options.priorManagedHookTrustState,
2599
+ });
1745
2600
  existing = stripped.cleaned;
1746
2601
  if (customizedManagedTuiSections.length > 0) {
1747
2602
  existing = `${existing.trimEnd()}\n\n${customizedManagedTuiSections.join("\n\n")}\n`;
@@ -1755,18 +2610,13 @@ export function buildMergedConfig(existingConfig, pkgRoot, options = {}) {
1755
2610
  !isOmxManagedNotifyCommand(getRootTomlArray(existing, "notify"), pkgRoot)
1756
2611
  ? getRootTomlArray(existing, "notify")
1757
2612
  : null;
1758
- existing = stripOmxTopLevelKeys(existing);
2613
+ existing = stripOmxTopLevelKeys(existing).trimStart();
1759
2614
  if (userNotifyToPreserve) {
1760
2615
  existing = `${`notify = ${formatTomlStringArray(userNotifyToPreserve)}`}\n${existing.trimStart()}`;
1761
2616
  }
1762
- existing = stripOrphanedManagedNotify(existing, pkgRoot);
1763
- const managedTrustState = buildManagedCodexHookTrustStateForConfig(options.codexHooksFile, pkgRoot, {
1764
- codexHomeDir: options.codexHomeDir,
1765
- platform: options.hookCommandPlatform,
1766
- });
1767
- const hookTrustStrip = stripManagedCodexHookTrustStateWithResult(existing, {
1768
- managedTrustState,
1769
- });
2617
+ existing = stripOrphanedManagedNotify(existing, pkgRoot).trimStart();
2618
+ const hookTrustStrip = stripManagedCodexHookTrustStateForRefresh(existing, options.priorManagedHookTrustState, managedTrustState);
2619
+ rejectManagedCodexHookTrustStateConflicts(hookTrustStrip.preservedConflictKeys);
1770
2620
  existing = hookTrustStrip.config;
1771
2621
  if (options.modelOverride) {
1772
2622
  existing = stripRootLevelKeys(existing, ["model"]);
@@ -1777,7 +2627,6 @@ export function buildMergedConfig(existingConfig, pkgRoot, options = {}) {
1777
2627
  }
1778
2628
  existing = upsertFeatureFlags(existing, options.codexHookFeatureFlag);
1779
2629
  existing = upsertEnvSettings(existing);
1780
- existing = upsertAgentsSettings(existing);
1781
2630
  const tuiUpsert = includeTui
1782
2631
  ? upsertTuiStatusLine(existing, statusLinePreset, {
1783
2632
  forceStatusLinePreset: options.forceStatusLinePreset,
@@ -1787,16 +2636,15 @@ export function buildMergedConfig(existingConfig, pkgRoot, options = {}) {
1787
2636
  const topLines = getOmxTopLevelLines(pkgRoot, existing, options.modelOverride, options.notifyCommand === undefined
1788
2637
  ? getDefaultNotifyCommand(pkgRoot)
1789
2638
  : options.notifyCommand);
1790
- const tablesBlock = getOmxTablesBlock(pkgRoot, includeTui && !tuiUpsert.hadExistingTui, statusLinePreset, options.codexHooksFile, {
1791
- codexHomeDir: options.codexHomeDir,
1792
- platform: options.hookCommandPlatform,
1793
- }, options.includeFirstPartyMcp === true, hookTrustStrip.preservedConflictKeys);
2639
+ const tablesBlock = getOmxTablesBlock(pkgRoot, includeTui && !tuiUpsert.hadExistingTui, statusLinePreset, options.codexHooksFile, hookOptions, managedTrustState, options.includeFirstPartyMcp === true);
1794
2640
  const sharedRegistryBlock = getSharedMcpRegistryBlock(options.sharedMcpServers ?? [], options.sharedMcpRegistrySource, existing);
1795
- let body = existing.trimEnd();
2641
+ let body = existing.trim();
1796
2642
  if (sharedRegistryBlock) {
1797
2643
  body = body ? `${body}\n\n${sharedRegistryBlock}` : sharedRegistryBlock;
1798
2644
  }
1799
- return addDefaultLauncherMcpStartupTimeouts(topLines.join("\n") + "\n\n" + body + "\n" + tablesBlock);
2645
+ const bodySeparator = body.length > 0 && !/^\s*\[/.test(body) ? "\n" : "\n\n";
2646
+ const merged = addDefaultLauncherMcpStartupTimeouts(topLines.join("\n") + bodySeparator + body + "\n" + tablesBlock);
2647
+ return appendLegacyHookTrustState(merged, options.legacyHookTrustState);
1800
2648
  }
1801
2649
  /**
1802
2650
  * Detect and repair upgrade-era managed config incompatibilities in config.toml.
@@ -1809,6 +2657,33 @@ export function buildMergedConfig(existingConfig, pkgRoot, options = {}) {
1809
2657
  *
1810
2658
  * Returns `true` if a repair was performed.
1811
2659
  */
2660
+ function managedHookTrustProofRequiredForRepair(config) {
2661
+ const inventory = collectManagedHookTrustStateSourceRepresentations(config);
2662
+ return managedMarkerTrustStateConflicts(inventory.source, inventory.representations, inventory.managedMarkerRanges, inventory.markerContainedHooksStateSpans, inventory.invalidMarkerContainedHooksStateSpans, inventory.parsedStatementSpans, inventory.parsedAssignmentSpans, managedHookTrustStateExpectations()).size > 0;
2663
+ }
2664
+ async function launchRepairOptionsWithManagedHookTrustProof(configPath, config, options) {
2665
+ if (!managedHookTrustProofRequiredForRepair(config)) {
2666
+ return options;
2667
+ }
2668
+ const hooksPath = options.codexHooksFile ?? resolve(configPath, "..", "hooks.json");
2669
+ let hooksContent;
2670
+ try {
2671
+ hooksContent = await readFile(hooksPath, "utf-8");
2672
+ }
2673
+ catch (error) {
2674
+ throw new ManagedCodexHooksPlanError("invalid_document", `Cannot safely repair config.toml because the current hooks artifact at ${hooksPath} could not be read.`, { cause: String(error), hooksPath });
2675
+ }
2676
+ const trustScan = scanManagedCodexHookTrustStateFromContent(hooksContent, hooksPath, managedCodexHookOptionsFromMergeOptions(options));
2677
+ if (!trustScan.ok)
2678
+ throw trustScan.error;
2679
+ return {
2680
+ ...options,
2681
+ codexHooksFile: hooksPath,
2682
+ codexHooksContent: hooksContent,
2683
+ managedHookTrustState: trustScan.trustState,
2684
+ priorManagedHookTrustState: trustScan.trustState,
2685
+ };
2686
+ }
1812
2687
  export async function repairConfigIfNeeded(configPath, pkgRoot, options = {}) {
1813
2688
  if (!existsSync(configPath))
1814
2689
  return false;
@@ -1818,8 +2693,10 @@ export async function repairConfigIfNeeded(configPath, pkgRoot, options = {}) {
1818
2693
  const hasLauncherTimeoutGap = findLauncherTimeoutRepairTargets(content).length > 0;
1819
2694
  if (tuiCount <= 1 && !hasLegacyTeamRunTable && !hasLauncherTimeoutGap)
1820
2695
  return false;
1821
- // Managed config compatibility issue detected — run full merge to repair
1822
- const repaired = buildMergedConfig(content, pkgRoot, options);
2696
+ // Managed config compatibility issue detected — derive proof from the current
2697
+ // hooks artifact before any marker stripping can replace managed trust state.
2698
+ const repairOptions = await launchRepairOptionsWithManagedHookTrustProof(configPath, content, options);
2699
+ const repaired = buildMergedConfig(content, pkgRoot, repairOptions);
1823
2700
  if (repaired === content)
1824
2701
  return false;
1825
2702
  await writeFile(configPath, repaired);
@@ -1830,12 +2707,6 @@ export async function mergeConfig(configPath, pkgRoot, options = {}) {
1830
2707
  if (existsSync(configPath)) {
1831
2708
  existing = await readFile(configPath, "utf-8");
1832
2709
  }
1833
- if (existing.includes("oh-my-codex (OMX) Configuration")) {
1834
- const stripped = stripExistingOmxBlocks(existing);
1835
- if (options.verbose && stripped.removed > 0) {
1836
- console.log(" Updating existing OMX config block.");
1837
- }
1838
- }
1839
2710
  const finalConfig = buildMergedConfig(existing, pkgRoot, options);
1840
2711
  await writeFile(configPath, finalConfig);
1841
2712
  if (options.verbose) {