oh-my-codex 0.20.1 → 0.20.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (791) hide show
  1. package/Cargo.lock +7 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +36 -1
  4. package/crates/omx-runtime/Cargo.toml +1 -0
  5. package/crates/omx-runtime/src/main.rs +25 -2
  6. package/crates/omx-runtime/tests/execution.rs +219 -0
  7. package/crates/omx-runtime-core/src/dispatch.rs +96 -10
  8. package/crates/omx-runtime-core/src/engine.rs +839 -29
  9. package/crates/omx-runtime-core/src/lib.rs +8 -0
  10. package/dist/adapt/__tests__/hermes.test.js.map +1 -1
  11. package/dist/adapt/contracts.d.ts.map +1 -1
  12. package/dist/agents/__tests__/definitions.test.js +81 -0
  13. package/dist/agents/__tests__/definitions.test.js.map +1 -1
  14. package/dist/agents/__tests__/native-config.test.js +25 -0
  15. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  16. package/dist/agents/native-config.d.ts +2 -1
  17. package/dist/agents/native-config.d.ts.map +1 -1
  18. package/dist/agents/native-config.js.map +1 -1
  19. package/dist/auth/__tests__/hotswap.test.d.ts +2 -0
  20. package/dist/auth/__tests__/hotswap.test.d.ts.map +1 -0
  21. package/dist/auth/__tests__/hotswap.test.js +183 -0
  22. package/dist/auth/__tests__/hotswap.test.js.map +1 -0
  23. package/dist/auth/hotswap.d.ts +6 -0
  24. package/dist/auth/hotswap.d.ts.map +1 -1
  25. package/dist/auth/hotswap.js +102 -19
  26. package/dist/auth/hotswap.js.map +1 -1
  27. package/dist/auth/paths.js.map +1 -1
  28. package/dist/auth/storage.js.map +1 -1
  29. package/dist/autopilot/__tests__/ralplan-gate.test.js +5 -5
  30. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
  31. package/dist/autopilot/completion-gate.js.map +1 -1
  32. package/dist/autopilot/fsm.d.ts +1 -1
  33. package/dist/autopilot/fsm.d.ts.map +1 -1
  34. package/dist/autoresearch/goal.js.map +1 -1
  35. package/dist/autoresearch/runtime.js.map +1 -1
  36. package/dist/capabilities/lockfile.js.map +1 -1
  37. package/dist/catalog/reader.js.map +1 -1
  38. package/dist/catalog/skill-mirror.js.map +1 -1
  39. package/dist/cli/__tests__/adapt-help.test.js.map +1 -1
  40. package/dist/cli/__tests__/agents-init.test.js.map +1 -1
  41. package/dist/cli/__tests__/agents.test.js.map +1 -1
  42. package/dist/cli/__tests__/api.test.js.map +1 -1
  43. package/dist/cli/__tests__/ask.test.js.map +1 -1
  44. package/dist/cli/__tests__/auth.test.js +60 -6
  45. package/dist/cli/__tests__/auth.test.js.map +1 -1
  46. package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
  47. package/dist/cli/__tests__/autoresearch.test.js.map +1 -1
  48. package/dist/cli/__tests__/capabilities.test.js.map +1 -1
  49. package/dist/cli/__tests__/codex-plugin-layout.test.js +80 -0
  50. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  51. package/dist/cli/__tests__/doctor-context-window-warning.test.js.map +1 -1
  52. package/dist/cli/__tests__/doctor-invalid-config.test.js +47 -4
  53. package/dist/cli/__tests__/doctor-invalid-config.test.js.map +1 -1
  54. package/dist/cli/__tests__/doctor-team.test.js.map +1 -1
  55. package/dist/cli/__tests__/doctor-warning-copy.test.js +752 -15
  56. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  57. package/dist/cli/__tests__/exec.test.js +136 -0
  58. package/dist/cli/__tests__/exec.test.js.map +1 -1
  59. package/dist/cli/__tests__/imagegen-continuation.test.js.map +1 -1
  60. package/dist/cli/__tests__/index.test.js +473 -14
  61. package/dist/cli/__tests__/index.test.js.map +1 -1
  62. package/dist/cli/__tests__/launch-fallback.test.js +336 -4
  63. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  64. package/dist/cli/__tests__/mcp-parity.test.js +87 -12
  65. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  66. package/dist/cli/__tests__/native-hook-claim-journal.test.d.ts +2 -0
  67. package/dist/cli/__tests__/native-hook-claim-journal.test.d.ts.map +1 -0
  68. package/dist/cli/__tests__/native-hook-claim-journal.test.js +206 -0
  69. package/dist/cli/__tests__/native-hook-claim-journal.test.js.map +1 -0
  70. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  71. package/dist/cli/__tests__/question.test.js.map +1 -1
  72. package/dist/cli/__tests__/ralph-deslop-contract.test.js.map +1 -1
  73. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +6 -0
  74. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  75. package/dist/cli/__tests__/ralph-prd-deep-interview.test.js.map +1 -1
  76. package/dist/cli/__tests__/ralph-prd-smoke.test.js.map +1 -1
  77. package/dist/cli/__tests__/ralph.test.js +8 -3
  78. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  79. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.d.ts +2 -0
  80. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.d.ts.map +1 -0
  81. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js +72 -0
  82. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js.map +1 -0
  83. package/dist/cli/__tests__/ralplan.test.d.ts +2 -0
  84. package/dist/cli/__tests__/ralplan.test.d.ts.map +1 -0
  85. package/dist/cli/__tests__/ralplan.test.js +55 -0
  86. package/dist/cli/__tests__/ralplan.test.js.map +1 -0
  87. package/dist/cli/__tests__/resume.test.js +17 -11
  88. package/dist/cli/__tests__/resume.test.js.map +1 -1
  89. package/dist/cli/__tests__/session-scoped-runtime.test.js +25 -0
  90. package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
  91. package/dist/cli/__tests__/session-search-help.test.js +25 -1
  92. package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
  93. package/dist/cli/__tests__/session-search.test.js.map +1 -1
  94. package/dist/cli/__tests__/setup-agents-overwrite.test.js +79 -0
  95. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  96. package/dist/cli/__tests__/setup-gh-star.test.js.map +1 -1
  97. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +77 -3
  98. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +1 -1
  99. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.d.ts +2 -0
  100. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.d.ts.map +1 -0
  101. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.js +285 -0
  102. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.js.map +1 -0
  103. package/dist/cli/__tests__/setup-install-mode.test.js +2579 -294
  104. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  105. package/dist/cli/__tests__/setup-preferences.test.d.ts +2 -0
  106. package/dist/cli/__tests__/setup-preferences.test.d.ts.map +1 -0
  107. package/dist/cli/__tests__/setup-preferences.test.js +79 -0
  108. package/dist/cli/__tests__/setup-preferences.test.js.map +1 -0
  109. package/dist/cli/__tests__/setup-refresh.test.js +33 -2
  110. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  111. package/dist/cli/__tests__/setup-scope.test.js +46 -3
  112. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  113. package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
  114. package/dist/cli/__tests__/state.test.js +37 -0
  115. package/dist/cli/__tests__/state.test.js.map +1 -1
  116. package/dist/cli/__tests__/team.test.js +105 -7
  117. package/dist/cli/__tests__/team.test.js.map +1 -1
  118. package/dist/cli/__tests__/ultragoal.test.js +61 -1
  119. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  120. package/dist/cli/__tests__/uninstall.test.js +1817 -24
  121. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  122. package/dist/cli/__tests__/update.test.js +58 -0
  123. package/dist/cli/__tests__/update.test.js.map +1 -1
  124. package/dist/cli/__tests__/url.test.js.map +1 -1
  125. package/dist/cli/__tests__/windows-popup-loop-contract.test.js +2 -1
  126. package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
  127. package/dist/cli/adapt.js.map +1 -1
  128. package/dist/cli/agents-init.js.map +1 -1
  129. package/dist/cli/agents.js.map +1 -1
  130. package/dist/cli/api.js.map +1 -1
  131. package/dist/cli/ask.d.ts +1 -1
  132. package/dist/cli/ask.d.ts.map +1 -1
  133. package/dist/cli/ask.js.map +1 -1
  134. package/dist/cli/auth.js.map +1 -1
  135. package/dist/cli/autoresearch-guided.js.map +1 -1
  136. package/dist/cli/autoresearch-intake.js.map +1 -1
  137. package/dist/cli/capabilities.js.map +1 -1
  138. package/dist/cli/cleanup.js.map +1 -1
  139. package/dist/cli/codex-feature-probe.js.map +1 -1
  140. package/dist/cli/codex-home.js.map +1 -1
  141. package/dist/cli/doctor.d.ts +21 -0
  142. package/dist/cli/doctor.d.ts.map +1 -1
  143. package/dist/cli/doctor.js +607 -130
  144. package/dist/cli/doctor.js.map +1 -1
  145. package/dist/cli/explore.js.map +1 -1
  146. package/dist/cli/index.d.ts +14 -3
  147. package/dist/cli/index.d.ts.map +1 -1
  148. package/dist/cli/index.js +245 -57
  149. package/dist/cli/index.js.map +1 -1
  150. package/dist/cli/mcp-parity.js +2 -2
  151. package/dist/cli/mcp-parity.js.map +1 -1
  152. package/dist/cli/mcp-serve.js.map +1 -1
  153. package/dist/cli/mission.js.map +1 -1
  154. package/dist/cli/native-assets.js.map +1 -1
  155. package/dist/cli/native-hook-claim-journal.d.ts +29 -0
  156. package/dist/cli/native-hook-claim-journal.d.ts.map +1 -0
  157. package/dist/cli/native-hook-claim-journal.js +252 -0
  158. package/dist/cli/native-hook-claim-journal.js.map +1 -0
  159. package/dist/cli/omx.js.map +1 -1
  160. package/dist/cli/plugin-marketplace.js.map +1 -1
  161. package/dist/cli/project-runtime-codex-homes.js.map +1 -1
  162. package/dist/cli/question.js.map +1 -1
  163. package/dist/cli/ralph.d.ts.map +1 -1
  164. package/dist/cli/ralph.js +3 -2
  165. package/dist/cli/ralph.js.map +1 -1
  166. package/dist/cli/ralplan.d.ts +10 -0
  167. package/dist/cli/ralplan.d.ts.map +1 -0
  168. package/dist/cli/ralplan.js +110 -0
  169. package/dist/cli/ralplan.js.map +1 -0
  170. package/dist/cli/session-search.d.ts +5 -0
  171. package/dist/cli/session-search.d.ts.map +1 -1
  172. package/dist/cli/session-search.js +87 -0
  173. package/dist/cli/session-search.js.map +1 -1
  174. package/dist/cli/setup-preferences.d.ts +11 -0
  175. package/dist/cli/setup-preferences.d.ts.map +1 -1
  176. package/dist/cli/setup-preferences.js +32 -0
  177. package/dist/cli/setup-preferences.js.map +1 -1
  178. package/dist/cli/setup.d.ts +58 -0
  179. package/dist/cli/setup.d.ts.map +1 -1
  180. package/dist/cli/setup.js +1450 -398
  181. package/dist/cli/setup.js.map +1 -1
  182. package/dist/cli/sparkshell.js.map +1 -1
  183. package/dist/cli/star-prompt.js.map +1 -1
  184. package/dist/cli/state.d.ts.map +1 -1
  185. package/dist/cli/state.js +2 -1
  186. package/dist/cli/state.js.map +1 -1
  187. package/dist/cli/team.js.map +1 -1
  188. package/dist/cli/ultragoal.d.ts +5 -1
  189. package/dist/cli/ultragoal.d.ts.map +1 -1
  190. package/dist/cli/ultragoal.js +34 -2
  191. package/dist/cli/ultragoal.js.map +1 -1
  192. package/dist/cli/uninstall.d.ts +17 -0
  193. package/dist/cli/uninstall.d.ts.map +1 -1
  194. package/dist/cli/uninstall.js +986 -95
  195. package/dist/cli/uninstall.js.map +1 -1
  196. package/dist/cli/update.d.ts.map +1 -1
  197. package/dist/cli/update.js +8 -1
  198. package/dist/cli/update.js.map +1 -1
  199. package/dist/cli/url.js.map +1 -1
  200. package/dist/compat/__tests__/doctor-contract.test.js +5 -1
  201. package/dist/compat/__tests__/doctor-contract.test.js.map +1 -1
  202. package/dist/compat/__tests__/rust-runtime-compat.test.js +1 -1
  203. package/dist/compat/__tests__/rust-runtime-compat.test.js.map +1 -1
  204. package/dist/config/__tests__/codex-hooks.test.js +1287 -11
  205. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  206. package/dist/config/__tests__/generator-idempotent.test.js +1179 -32
  207. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  208. package/dist/config/__tests__/generator-notify.test.js +6 -1
  209. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  210. package/dist/config/__tests__/models.test.js +67 -3
  211. package/dist/config/__tests__/models.test.js.map +1 -1
  212. package/dist/config/codex-feature-flags.d.ts.map +1 -1
  213. package/dist/config/codex-feature-flags.js.map +1 -1
  214. package/dist/config/codex-hooks.d.ts +124 -1
  215. package/dist/config/codex-hooks.d.ts.map +1 -1
  216. package/dist/config/codex-hooks.js +2142 -265
  217. package/dist/config/codex-hooks.js.map +1 -1
  218. package/dist/config/commit-lore-guard.js.map +1 -1
  219. package/dist/config/generator.d.ts +23 -34
  220. package/dist/config/generator.d.ts.map +1 -1
  221. package/dist/config/generator.js +1480 -246
  222. package/dist/config/generator.js.map +1 -1
  223. package/dist/config/mcp-registry.js.map +1 -1
  224. package/dist/config/models.d.ts +14 -4
  225. package/dist/config/models.d.ts.map +1 -1
  226. package/dist/config/models.js +15 -1
  227. package/dist/config/models.js.map +1 -1
  228. package/dist/config/omx-first-party-mcp.js.map +1 -1
  229. package/dist/config/team-mode.d.ts.map +1 -1
  230. package/dist/document-refresh/__tests__/enforcer.test.js.map +1 -1
  231. package/dist/document-refresh/enforcer.js.map +1 -1
  232. package/dist/exec/followup.js.map +1 -1
  233. package/dist/goal-workflows/artifacts.js.map +1 -1
  234. package/dist/hooks/__tests__/agents-overlay.test.js +7 -4
  235. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  236. package/dist/hooks/__tests__/analyze-routing-contract.test.js.map +1 -1
  237. package/dist/hooks/__tests__/analyze-skill-contract.test.js.map +1 -1
  238. package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
  239. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +9 -3
  240. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  241. package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
  242. package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
  243. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  244. package/dist/hooks/__tests__/design-skill.test.js.map +1 -1
  245. package/dist/hooks/__tests__/keyword-detector.test.js +1376 -6
  246. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  247. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +252 -543
  248. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  249. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +38 -4
  250. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  251. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +765 -10
  252. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  253. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +2 -0
  254. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
  255. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +142 -1
  256. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  257. package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
  258. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js +48 -2
  259. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js.map +1 -1
  260. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +61 -44
  261. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
  262. package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
  263. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js +3 -2
  264. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js.map +1 -1
  265. package/dist/hooks/__tests__/notify-hook-session-scope.test.js +243 -4
  266. package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
  267. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +531 -12
  268. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  269. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +163 -24
  270. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  271. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +482 -32
  272. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  273. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +68 -1
  274. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  275. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +7 -1
  276. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
  277. package/dist/hooks/__tests__/notify-hook-visual-verdict.test.js.map +1 -1
  278. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +43 -4
  279. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  280. package/dist/hooks/__tests__/openclaw-setup-contract.test.js.map +1 -1
  281. package/dist/hooks/__tests__/pre-context-gate-skills.test.js.map +1 -1
  282. package/dist/hooks/__tests__/prometheus-strict-contract.test.js.map +1 -1
  283. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js.map +1 -1
  284. package/dist/hooks/__tests__/prompt-guidance-test-helpers.js.map +1 -1
  285. package/dist/hooks/__tests__/prompt-session-provenance.test.d.ts +2 -0
  286. package/dist/hooks/__tests__/prompt-session-provenance.test.d.ts.map +1 -0
  287. package/dist/hooks/__tests__/prompt-session-provenance.test.js +82 -0
  288. package/dist/hooks/__tests__/prompt-session-provenance.test.js.map +1 -0
  289. package/dist/hooks/__tests__/session.test.js +878 -11
  290. package/dist/hooks/__tests__/session.test.js.map +1 -1
  291. package/dist/hooks/__tests__/skill-catalog-hygiene.test.js.map +1 -1
  292. package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.js.map +1 -1
  293. package/dist/hooks/__tests__/visual-ralph-skill.test.js.map +1 -1
  294. package/dist/hooks/__tests__/visual-verdict-loop.test.js.map +1 -1
  295. package/dist/hooks/agents-overlay.js +5 -5
  296. package/dist/hooks/agents-overlay.js.map +1 -1
  297. package/dist/hooks/code-simplifier/index.js.map +1 -1
  298. package/dist/hooks/explore-routing.js.map +1 -1
  299. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  300. package/dist/hooks/extensibility/events.js.map +1 -1
  301. package/dist/hooks/extensibility/loader.js.map +1 -1
  302. package/dist/hooks/extensibility/plugin-runner-stdin.js.map +1 -1
  303. package/dist/hooks/extensibility/sdk/logging.js.map +1 -1
  304. package/dist/hooks/keyword-detector.d.ts +33 -7
  305. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  306. package/dist/hooks/keyword-detector.js +2228 -119
  307. package/dist/hooks/keyword-detector.js.map +1 -1
  308. package/dist/hooks/keyword-registry.d.ts +12 -0
  309. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  310. package/dist/hooks/keyword-registry.js +41 -0
  311. package/dist/hooks/keyword-registry.js.map +1 -1
  312. package/dist/hooks/prompt-session-provenance.d.ts +87 -0
  313. package/dist/hooks/prompt-session-provenance.d.ts.map +1 -0
  314. package/dist/hooks/prompt-session-provenance.js +180 -0
  315. package/dist/hooks/prompt-session-provenance.js.map +1 -0
  316. package/dist/hooks/session.d.ts +163 -34
  317. package/dist/hooks/session.d.ts.map +1 -1
  318. package/dist/hooks/session.js +1395 -317
  319. package/dist/hooks/session.js.map +1 -1
  320. package/dist/hooks/task-size-detector.js.map +1 -1
  321. package/dist/hud/__tests__/state.test.js.map +1 -1
  322. package/dist/hud/authority.js.map +1 -1
  323. package/dist/hud/constants.js.map +1 -1
  324. package/dist/hud/index.js.map +1 -1
  325. package/dist/hud/reconcile.js.map +1 -1
  326. package/dist/hud/render.js.map +1 -1
  327. package/dist/hud/state.js.map +1 -1
  328. package/dist/hud/tmux.js.map +1 -1
  329. package/dist/leader/__tests__/contract.test.js +153 -1
  330. package/dist/leader/__tests__/contract.test.js.map +1 -1
  331. package/dist/leader/contract.d.ts +42 -5
  332. package/dist/leader/contract.d.ts.map +1 -1
  333. package/dist/leader/contract.js +184 -3
  334. package/dist/leader/contract.js.map +1 -1
  335. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  336. package/dist/mcp/__tests__/code-intel-server.test.js +31 -3
  337. package/dist/mcp/__tests__/code-intel-server.test.js.map +1 -1
  338. package/dist/mcp/__tests__/server-lifecycle.test.js +15 -2
  339. package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
  340. package/dist/mcp/__tests__/state-paths.test.js +98 -1
  341. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  342. package/dist/mcp/__tests__/wiki-server.test.js +18 -0
  343. package/dist/mcp/__tests__/wiki-server.test.js.map +1 -1
  344. package/dist/mcp/bootstrap.js.map +1 -1
  345. package/dist/mcp/code-intel-server.d.ts +58 -58
  346. package/dist/mcp/code-intel-server.d.ts.map +1 -1
  347. package/dist/mcp/code-intel-server.js +13 -3
  348. package/dist/mcp/code-intel-server.js.map +1 -1
  349. package/dist/mcp/hermes-bridge.js.map +1 -1
  350. package/dist/mcp/hermes-server.d.ts +70 -70
  351. package/dist/mcp/hermes-server.d.ts.map +1 -1
  352. package/dist/mcp/lifecycle-telemetry.js.map +1 -1
  353. package/dist/mcp/memory-server.d.ts +34 -34
  354. package/dist/mcp/memory-server.d.ts.map +1 -1
  355. package/dist/mcp/memory-server.js.map +1 -1
  356. package/dist/mcp/state-paths.d.ts +14 -0
  357. package/dist/mcp/state-paths.d.ts.map +1 -1
  358. package/dist/mcp/state-paths.js +96 -20
  359. package/dist/mcp/state-paths.js.map +1 -1
  360. package/dist/mcp/state-server.d.ts +47 -47
  361. package/dist/mcp/state-server.d.ts.map +1 -1
  362. package/dist/mcp/trace-server.d.ts +4 -4
  363. package/dist/mcp/trace-server.d.ts.map +1 -1
  364. package/dist/mcp/trace-server.js.map +1 -1
  365. package/dist/mcp/wiki-server.d.ts +34 -23
  366. package/dist/mcp/wiki-server.d.ts.map +1 -1
  367. package/dist/mcp/wiki-server.js +9 -2
  368. package/dist/mcp/wiki-server.js.map +1 -1
  369. package/dist/modes/__tests__/base-session-scope.test.js +53 -1
  370. package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
  371. package/dist/modes/__tests__/base-tmux-pane.test.js +4 -4
  372. package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
  373. package/dist/modes/base.d.ts.map +1 -1
  374. package/dist/modes/base.js +5 -5
  375. package/dist/modes/base.js.map +1 -1
  376. package/dist/notifications/__tests__/lifecycle-dedupe.test.js.map +1 -1
  377. package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
  378. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js +177 -85
  379. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js.map +1 -1
  380. package/dist/notifications/__tests__/session-registry.test.js.map +1 -1
  381. package/dist/notifications/__tests__/template-engine.test.js.map +1 -1
  382. package/dist/notifications/__tests__/verbosity.test.js.map +1 -1
  383. package/dist/notifications/http-client.js.map +1 -1
  384. package/dist/notifications/index.d.ts +4 -1
  385. package/dist/notifications/index.d.ts.map +1 -1
  386. package/dist/notifications/index.js +3 -9
  387. package/dist/notifications/index.js.map +1 -1
  388. package/dist/notifications/lifecycle-dedupe.js.map +1 -1
  389. package/dist/notifications/reply-listener.d.ts.map +1 -1
  390. package/dist/notifications/reply-listener.js.map +1 -1
  391. package/dist/notifications/session-registry.js.map +1 -1
  392. package/dist/notifications/session-status.js.map +1 -1
  393. package/dist/notifications/temp-contract.d.ts.map +1 -1
  394. package/dist/notifications/temp-contract.js +4 -0
  395. package/dist/notifications/temp-contract.js.map +1 -1
  396. package/dist/notifications/tmux-detector.js.map +1 -1
  397. package/dist/notifications/tmux.js.map +1 -1
  398. package/dist/openclaw/config.js.map +1 -1
  399. package/dist/openclaw/dispatcher.js.map +1 -1
  400. package/dist/pipeline/__tests__/orchestrator.test.js +4 -4
  401. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  402. package/dist/pipeline/__tests__/stages.test.js +16 -13
  403. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  404. package/dist/pipeline/stages/code-review.js.map +1 -1
  405. package/dist/pipeline/stages/ralph-verify.js.map +1 -1
  406. package/dist/pipeline/stages/ralplan.js.map +1 -1
  407. package/dist/pipeline/stages/team-exec.js.map +1 -1
  408. package/dist/pipeline/stages/ultragoal.js.map +1 -1
  409. package/dist/pipeline/stages/ultraqa.js.map +1 -1
  410. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js.map +1 -1
  411. package/dist/planning/artifact-names.js.map +1 -1
  412. package/dist/planning/artifacts.js.map +1 -1
  413. package/dist/question/__tests__/state.test.js.map +1 -1
  414. package/dist/question/__tests__/ui.test.js.map +1 -1
  415. package/dist/question/autopilot-wait.js.map +1 -1
  416. package/dist/question/client.d.ts.map +1 -1
  417. package/dist/question/client.js.map +1 -1
  418. package/dist/question/deep-interview.js.map +1 -1
  419. package/dist/question/events.js.map +1 -1
  420. package/dist/question/renderer.js.map +1 -1
  421. package/dist/question/state.d.ts.map +1 -1
  422. package/dist/question/state.js.map +1 -1
  423. package/dist/question/ui.js.map +1 -1
  424. package/dist/ralph/contract.d.ts +1 -1
  425. package/dist/ralph/contract.d.ts.map +1 -1
  426. package/dist/ralplan/__tests__/consensus-gate.test.js +527 -8
  427. package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
  428. package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts +2 -0
  429. package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts.map +1 -0
  430. package/dist/ralplan/__tests__/documented-leader-preflight.test.js +46 -0
  431. package/dist/ralplan/__tests__/documented-leader-preflight.test.js.map +1 -0
  432. package/dist/ralplan/__tests__/runtime.test.js +116 -14
  433. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  434. package/dist/ralplan/consensus-gate.d.ts +3 -3
  435. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  436. package/dist/ralplan/consensus-gate.js +173 -70
  437. package/dist/ralplan/consensus-gate.js.map +1 -1
  438. package/dist/ralplan/documented-leader-preflight.d.ts +34 -0
  439. package/dist/ralplan/documented-leader-preflight.d.ts.map +1 -0
  440. package/dist/ralplan/documented-leader-preflight.js +58 -0
  441. package/dist/ralplan/documented-leader-preflight.js.map +1 -0
  442. package/dist/ralplan/runtime.d.ts +3 -2
  443. package/dist/ralplan/runtime.d.ts.map +1 -1
  444. package/dist/ralplan/runtime.js +12 -9
  445. package/dist/ralplan/runtime.js.map +1 -1
  446. package/dist/runtime/__tests__/bridge.test.js +42 -1
  447. package/dist/runtime/__tests__/bridge.test.js.map +1 -1
  448. package/dist/runtime/bridge.d.ts +12 -0
  449. package/dist/runtime/bridge.d.ts.map +1 -1
  450. package/dist/runtime/bridge.js +52 -1
  451. package/dist/runtime/bridge.js.map +1 -1
  452. package/dist/runtime/process-tree.js.map +1 -1
  453. package/dist/runtime/run-loop.js.map +1 -1
  454. package/dist/runtime/run-outcome.d.ts +3 -3
  455. package/dist/runtime/run-outcome.d.ts.map +1 -1
  456. package/dist/runtime/run-outcome.js.map +1 -1
  457. package/dist/runtime/run-state.js.map +1 -1
  458. package/dist/scripts/__tests__/codex-native-hook.test.js +6657 -995
  459. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  460. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.d.ts +2 -0
  461. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.d.ts.map +1 -0
  462. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.js +634 -0
  463. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.js.map +1 -0
  464. package/dist/scripts/__tests__/generate-release-body.test.js.map +1 -1
  465. package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -1
  466. package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -1
  467. package/dist/scripts/__tests__/notify-fallback-delivery.test.d.ts +2 -0
  468. package/dist/scripts/__tests__/notify-fallback-delivery.test.d.ts.map +1 -0
  469. package/dist/scripts/__tests__/notify-fallback-delivery.test.js +174 -0
  470. package/dist/scripts/__tests__/notify-fallback-delivery.test.js.map +1 -0
  471. package/dist/scripts/__tests__/notify-state-io.test.js +57 -1
  472. package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
  473. package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts +2 -0
  474. package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts.map +1 -0
  475. package/dist/scripts/__tests__/reasoning-artifact-contract.test.js +108 -0
  476. package/dist/scripts/__tests__/reasoning-artifact-contract.test.js.map +1 -0
  477. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.d.ts +2 -0
  478. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.d.ts.map +1 -0
  479. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js +78 -0
  480. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js.map +1 -0
  481. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.d.ts +2 -0
  482. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.d.ts.map +1 -0
  483. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js +55 -0
  484. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js.map +1 -0
  485. package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
  486. package/dist/scripts/__tests__/smoke-packed-install.test.js +1100 -4
  487. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  488. package/dist/scripts/__tests__/test-reply-listener-live.test.js.map +1 -1
  489. package/dist/scripts/build-api.js.map +1 -1
  490. package/dist/scripts/build-explore-harness.js.map +1 -1
  491. package/dist/scripts/build-sparkshell.js.map +1 -1
  492. package/dist/scripts/cleanup-explore-harness.js.map +1 -1
  493. package/dist/scripts/codex-execution-surface.js.map +1 -1
  494. package/dist/scripts/codex-native-hook.d.ts +4 -1
  495. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  496. package/dist/scripts/codex-native-hook.js +11577 -1184
  497. package/dist/scripts/codex-native-hook.js.map +1 -1
  498. package/dist/scripts/generate-native-release-manifest.js.map +1 -1
  499. package/dist/scripts/generate-release-body.js.map +1 -1
  500. package/dist/scripts/hook-payload-guard.d.ts.map +1 -1
  501. package/dist/scripts/hook-payload-guard.js.map +1 -1
  502. package/dist/scripts/notify-fallback-delivery.d.ts +48 -0
  503. package/dist/scripts/notify-fallback-delivery.d.ts.map +1 -0
  504. package/dist/scripts/notify-fallback-delivery.js +419 -0
  505. package/dist/scripts/notify-fallback-delivery.js.map +1 -0
  506. package/dist/scripts/notify-fallback-watcher.js +218 -138
  507. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  508. package/dist/scripts/notify-hook/auto-nudge.d.ts +18 -9
  509. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  510. package/dist/scripts/notify-hook/auto-nudge.js +112 -15
  511. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  512. package/dist/scripts/notify-hook/managed-tmux.d.ts +23 -0
  513. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  514. package/dist/scripts/notify-hook/managed-tmux.js +122 -12
  515. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  516. package/dist/scripts/notify-hook/orchestration-intent.d.ts +7 -7
  517. package/dist/scripts/notify-hook/orchestration-intent.d.ts.map +1 -1
  518. package/dist/scripts/notify-hook/ralph-session-resume.d.ts +5 -2
  519. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
  520. package/dist/scripts/notify-hook/ralph-session-resume.js +33 -37
  521. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  522. package/dist/scripts/notify-hook/state-io.d.ts +17 -0
  523. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  524. package/dist/scripts/notify-hook/state-io.js +73 -11
  525. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  526. package/dist/scripts/notify-hook/team-dispatch.d.ts +10 -1
  527. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  528. package/dist/scripts/notify-hook/team-dispatch.js +234 -79
  529. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  530. package/dist/scripts/notify-hook/team-leader-nudge.d.ts +2 -2
  531. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  532. package/dist/scripts/notify-hook/team-leader-nudge.js +39 -13
  533. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  534. package/dist/scripts/notify-hook/team-tmux-guard.d.ts +16 -8
  535. package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
  536. package/dist/scripts/notify-hook/team-tmux-guard.js +365 -44
  537. package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
  538. package/dist/scripts/notify-hook/team-worker-posttooluse.js.map +1 -1
  539. package/dist/scripts/notify-hook/team-worker-stop.d.ts +1 -1
  540. package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
  541. package/dist/scripts/notify-hook/team-worker-stop.js +19 -18
  542. package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
  543. package/dist/scripts/notify-hook/team-worker.d.ts +5 -2
  544. package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
  545. package/dist/scripts/notify-hook/team-worker.js +37 -28
  546. package/dist/scripts/notify-hook/team-worker.js.map +1 -1
  547. package/dist/scripts/notify-hook/tmux-injection.d.ts +8 -1
  548. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  549. package/dist/scripts/notify-hook/tmux-injection.js +53 -18
  550. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  551. package/dist/scripts/notify-hook/visual-verdict.d.ts +1 -1
  552. package/dist/scripts/notify-hook/visual-verdict.d.ts.map +1 -1
  553. package/dist/scripts/notify-hook/visual-verdict.js +15 -13
  554. package/dist/scripts/notify-hook/visual-verdict.js.map +1 -1
  555. package/dist/scripts/notify-hook.d.ts +24 -1
  556. package/dist/scripts/notify-hook.d.ts.map +1 -1
  557. package/dist/scripts/notify-hook.js +267 -90
  558. package/dist/scripts/notify-hook.js.map +1 -1
  559. package/dist/scripts/postinstall.js.map +1 -1
  560. package/dist/scripts/prompt-inventory.js.map +1 -1
  561. package/dist/scripts/smoke-packed-install.d.ts +927 -5
  562. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  563. package/dist/scripts/smoke-packed-install.js +4317 -13
  564. package/dist/scripts/smoke-packed-install.js.map +1 -1
  565. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  566. package/dist/scripts/test-reply-listener-live.js.map +1 -1
  567. package/dist/scripts/test-sparkshell.js.map +1 -1
  568. package/dist/scripts/verify-native-agents.js.map +1 -1
  569. package/dist/session-history/friction.js.map +1 -1
  570. package/dist/session-history/search.js.map +1 -1
  571. package/dist/sidecar/__tests__/boundary.test.js.map +1 -1
  572. package/dist/sidecar/collector.js.map +1 -1
  573. package/dist/sidecar/index.js.map +1 -1
  574. package/dist/sidecar/render.js.map +1 -1
  575. package/dist/sidecar/tmux.js.map +1 -1
  576. package/dist/state/__tests__/mode-state-context.test.js +54 -0
  577. package/dist/state/__tests__/mode-state-context.test.js.map +1 -1
  578. package/dist/state/__tests__/operations.test.js +66 -15
  579. package/dist/state/__tests__/operations.test.js.map +1 -1
  580. package/dist/state/__tests__/skill-active.test.js +19 -1
  581. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  582. package/dist/state/__tests__/workflow-transition.test.js +61 -0
  583. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  584. package/dist/state/mode-state-context.d.ts +5 -0
  585. package/dist/state/mode-state-context.d.ts.map +1 -1
  586. package/dist/state/mode-state-context.js +59 -0
  587. package/dist/state/mode-state-context.js.map +1 -1
  588. package/dist/state/operations.d.ts +1 -1
  589. package/dist/state/operations.d.ts.map +1 -1
  590. package/dist/state/operations.js +49 -55
  591. package/dist/state/operations.js.map +1 -1
  592. package/dist/state/paths.d.ts +1 -1
  593. package/dist/state/paths.d.ts.map +1 -1
  594. package/dist/state/paths.js +1 -1
  595. package/dist/state/paths.js.map +1 -1
  596. package/dist/state/skill-active.d.ts +13 -1
  597. package/dist/state/skill-active.d.ts.map +1 -1
  598. package/dist/state/skill-active.js +43 -4
  599. package/dist/state/skill-active.js.map +1 -1
  600. package/dist/state/terminal-normalization.js.map +1 -1
  601. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  602. package/dist/state/workflow-transition-reconcile.js +20 -4
  603. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  604. package/dist/state/workflow-transition.d.ts +1 -1
  605. package/dist/state/workflow-transition.d.ts.map +1 -1
  606. package/dist/state/workflow-transition.js.map +1 -1
  607. package/dist/subagents/__tests__/leader-bootstrap-3181.test.d.ts +2 -0
  608. package/dist/subagents/__tests__/leader-bootstrap-3181.test.d.ts.map +1 -0
  609. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js +52 -0
  610. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js.map +1 -0
  611. package/dist/subagents/__tests__/tracker.test.js +1550 -10
  612. package/dist/subagents/__tests__/tracker.test.js.map +1 -1
  613. package/dist/subagents/role-routing-marker.d.ts +10 -0
  614. package/dist/subagents/role-routing-marker.d.ts.map +1 -0
  615. package/dist/subagents/role-routing-marker.js +136 -0
  616. package/dist/subagents/role-routing-marker.js.map +1 -0
  617. package/dist/subagents/tracker.d.ts +91 -0
  618. package/dist/subagents/tracker.d.ts.map +1 -1
  619. package/dist/subagents/tracker.js +806 -62
  620. package/dist/subagents/tracker.js.map +1 -1
  621. package/dist/team/__tests__/api-interop.test.js +91 -1
  622. package/dist/team/__tests__/api-interop.test.js.map +1 -1
  623. package/dist/team/__tests__/approved-execution.test.js.map +1 -1
  624. package/dist/team/__tests__/delivery-e2e-smoke.test.js +52 -10
  625. package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
  626. package/dist/team/__tests__/idle-nudge.test.js +45 -15
  627. package/dist/team/__tests__/idle-nudge.test.js.map +1 -1
  628. package/dist/team/__tests__/mcp-comm.test.js +93 -27
  629. package/dist/team/__tests__/mcp-comm.test.js.map +1 -1
  630. package/dist/team/__tests__/model-contract.test.js +341 -4
  631. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  632. package/dist/team/__tests__/role-router.test.js.map +1 -1
  633. package/dist/team/__tests__/runtime-cli.test.js +9 -0
  634. package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
  635. package/dist/team/__tests__/runtime.test.js +3624 -355
  636. package/dist/team/__tests__/runtime.test.js.map +1 -1
  637. package/dist/team/__tests__/scaling.test.js +2516 -118
  638. package/dist/team/__tests__/scaling.test.js.map +1 -1
  639. package/dist/team/__tests__/state-root.test.js.map +1 -1
  640. package/dist/team/__tests__/state.test.js +804 -36
  641. package/dist/team/__tests__/state.test.js.map +1 -1
  642. package/dist/team/__tests__/team-ops-contract.test.js +6 -0
  643. package/dist/team/__tests__/team-ops-contract.test.js.map +1 -1
  644. package/dist/team/__tests__/tmux-session.test.js +3664 -969
  645. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  646. package/dist/team/__tests__/tmux-test-fixture.js.map +1 -1
  647. package/dist/team/__tests__/worker-runtime-identity.test.js +38 -2
  648. package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
  649. package/dist/team/__tests__/worktree.test.js +6 -0
  650. package/dist/team/__tests__/worktree.test.js.map +1 -1
  651. package/dist/team/api-interop.d.ts +2 -2
  652. package/dist/team/api-interop.d.ts.map +1 -1
  653. package/dist/team/api-interop.js +15 -9
  654. package/dist/team/api-interop.js.map +1 -1
  655. package/dist/team/commit-hygiene.js.map +1 -1
  656. package/dist/team/contracts.d.ts +5 -5
  657. package/dist/team/contracts.d.ts.map +1 -1
  658. package/dist/team/delivery-log.js.map +1 -1
  659. package/dist/team/exact-pane.d.ts +23 -0
  660. package/dist/team/exact-pane.d.ts.map +1 -0
  661. package/dist/team/exact-pane.js +133 -0
  662. package/dist/team/exact-pane.js.map +1 -0
  663. package/dist/team/followup-planner.js.map +1 -1
  664. package/dist/team/goal-workflow.js.map +1 -1
  665. package/dist/team/idle-nudge.d.ts +12 -1
  666. package/dist/team/idle-nudge.d.ts.map +1 -1
  667. package/dist/team/idle-nudge.js +41 -14
  668. package/dist/team/idle-nudge.js.map +1 -1
  669. package/dist/team/model-contract.d.ts +18 -2
  670. package/dist/team/model-contract.d.ts.map +1 -1
  671. package/dist/team/model-contract.js +314 -35
  672. package/dist/team/model-contract.js.map +1 -1
  673. package/dist/team/orchestrator.js.map +1 -1
  674. package/dist/team/pane-status.js.map +1 -1
  675. package/dist/team/phase-controller.js.map +1 -1
  676. package/dist/team/reminder-intents.d.ts +1 -1
  677. package/dist/team/reminder-intents.d.ts.map +1 -1
  678. package/dist/team/reminder-intents.js.map +1 -1
  679. package/dist/team/runtime-cli.js.map +1 -1
  680. package/dist/team/runtime.d.ts +1 -0
  681. package/dist/team/runtime.d.ts.map +1 -1
  682. package/dist/team/runtime.js +1326 -231
  683. package/dist/team/runtime.js.map +1 -1
  684. package/dist/team/scaling.d.ts +22 -8
  685. package/dist/team/scaling.d.ts.map +1 -1
  686. package/dist/team/scaling.js +1463 -376
  687. package/dist/team/scaling.js.map +1 -1
  688. package/dist/team/state/dispatch-lock.js.map +1 -1
  689. package/dist/team/state/dispatch.d.ts.map +1 -1
  690. package/dist/team/state/dispatch.js +26 -8
  691. package/dist/team/state/dispatch.js.map +1 -1
  692. package/dist/team/state/events.js.map +1 -1
  693. package/dist/team/state/locks.d.ts.map +1 -1
  694. package/dist/team/state/locks.js +61 -13
  695. package/dist/team/state/locks.js.map +1 -1
  696. package/dist/team/state/tasks.d.ts.map +1 -1
  697. package/dist/team/state/tasks.js +4 -3
  698. package/dist/team/state/tasks.js.map +1 -1
  699. package/dist/team/state-root.js.map +1 -1
  700. package/dist/team/state.d.ts +67 -1
  701. package/dist/team/state.d.ts.map +1 -1
  702. package/dist/team/state.js +636 -129
  703. package/dist/team/state.js.map +1 -1
  704. package/dist/team/team-identity.d.ts.map +1 -1
  705. package/dist/team/team-identity.js.map +1 -1
  706. package/dist/team/team-ops.d.ts +5 -0
  707. package/dist/team/team-ops.d.ts.map +1 -1
  708. package/dist/team/team-ops.js +4 -0
  709. package/dist/team/team-ops.js.map +1 -1
  710. package/dist/team/tmux-session.d.ts +125 -23
  711. package/dist/team/tmux-session.d.ts.map +1 -1
  712. package/dist/team/tmux-session.js +1340 -333
  713. package/dist/team/tmux-session.js.map +1 -1
  714. package/dist/team/worker-bootstrap.js.map +1 -1
  715. package/dist/team/worktree.d.ts.map +1 -1
  716. package/dist/team/worktree.js +4 -0
  717. package/dist/team/worktree.js.map +1 -1
  718. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  719. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
  720. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  721. package/dist/ultragoal/artifacts.js +4 -1
  722. package/dist/ultragoal/artifacts.js.map +1 -1
  723. package/dist/url-reader/index.js.map +1 -1
  724. package/dist/utils/__tests__/file-durability.test.d.ts +2 -0
  725. package/dist/utils/__tests__/file-durability.test.d.ts.map +1 -0
  726. package/dist/utils/__tests__/file-durability.test.js +57 -0
  727. package/dist/utils/__tests__/file-durability.test.js.map +1 -0
  728. package/dist/utils/agents-model-table.js.map +1 -1
  729. package/dist/utils/file-durability.d.ts +15 -0
  730. package/dist/utils/file-durability.d.ts.map +1 -0
  731. package/dist/utils/file-durability.js +37 -0
  732. package/dist/utils/file-durability.js.map +1 -0
  733. package/dist/utils/package.js.map +1 -1
  734. package/dist/utils/paths.js.map +1 -1
  735. package/dist/utils/platform-command.js.map +1 -1
  736. package/dist/utils/repo-deps.js.map +1 -1
  737. package/dist/utils/version.js.map +1 -1
  738. package/dist/verification/__tests__/ci-rust-gates.test.js +1 -1
  739. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +3 -0
  740. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
  741. package/dist/verification/verifier.js.map +1 -1
  742. package/dist/visual/constants.d.ts +1 -1
  743. package/dist/visual/constants.d.ts.map +1 -1
  744. package/dist/vscode/__tests__/index.test.js.map +1 -1
  745. package/dist/vscode/index.js.map +1 -1
  746. package/dist/wiki/__tests__/cjk-tokenize.test.js.map +1 -1
  747. package/dist/wiki/__tests__/lint.test.js.map +1 -1
  748. package/dist/wiki/__tests__/query.test.js.map +1 -1
  749. package/dist/wiki/__tests__/storage.test.js.map +1 -1
  750. package/dist/wiki/__tests__/test-helpers.d.ts.map +1 -1
  751. package/dist/wiki/lint.d.ts +4 -1
  752. package/dist/wiki/lint.d.ts.map +1 -1
  753. package/dist/wiki/lint.js +3 -2
  754. package/dist/wiki/lint.js.map +1 -1
  755. package/dist/wiki/query.js.map +1 -1
  756. package/dist/wiki/storage.js.map +1 -1
  757. package/package.json +2 -2
  758. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  759. package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +24 -0
  760. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +10 -5
  761. package/plugins/oh-my-codex/skills/ralph/SKILL.md +6 -2
  762. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +6 -2
  763. package/plugins/oh-my-codex/skills/team/SKILL.md +13 -9
  764. package/skills/omx-setup/SKILL.md +10 -5
  765. package/skills/ralph/SKILL.md +6 -2
  766. package/skills/ralplan/SKILL.md +6 -2
  767. package/skills/team/SKILL.md +13 -9
  768. package/src/scripts/__tests__/codex-native-hook.test.ts +20048 -10827
  769. package/src/scripts/__tests__/fixtures/issue-3138-installed-smoke.ts +734 -0
  770. package/src/scripts/__tests__/notify-fallback-delivery.test.ts +177 -0
  771. package/src/scripts/__tests__/notify-state-io.test.ts +73 -0
  772. package/src/scripts/__tests__/reasoning-artifact-contract.test.ts +129 -0
  773. package/src/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.ts +78 -0
  774. package/src/scripts/__tests__/role-intent-durable-recovery-3181.test.ts +60 -0
  775. package/src/scripts/__tests__/smoke-packed-install.test.ts +1309 -4
  776. package/src/scripts/codex-native-hook.ts +11429 -1163
  777. package/src/scripts/notify-fallback-delivery.ts +416 -0
  778. package/src/scripts/notify-fallback-watcher.ts +216 -155
  779. package/src/scripts/notify-hook/auto-nudge.ts +113 -14
  780. package/src/scripts/notify-hook/managed-tmux.ts +149 -12
  781. package/src/scripts/notify-hook/ralph-session-resume.ts +37 -36
  782. package/src/scripts/notify-hook/state-io.ts +100 -13
  783. package/src/scripts/notify-hook/team-dispatch.ts +242 -75
  784. package/src/scripts/notify-hook/team-leader-nudge.ts +40 -18
  785. package/src/scripts/notify-hook/team-tmux-guard.ts +366 -39
  786. package/src/scripts/notify-hook/team-worker-stop.ts +20 -16
  787. package/src/scripts/notify-hook/team-worker.ts +39 -26
  788. package/src/scripts/notify-hook/tmux-injection.ts +66 -23
  789. package/src/scripts/notify-hook/visual-verdict.ts +22 -12
  790. package/src/scripts/notify-hook.ts +336 -90
  791. package/src/scripts/smoke-packed-install.ts +5154 -149
@@ -8,13 +8,38 @@ import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
8
8
  import { tmpdir } from "node:os";
9
9
  import { isAbsolute, join } from "node:path";
10
10
  import TOML from "@iarna/toml";
11
- import { analyzeLegacyMultiAgentConfig, buildMergedConfig, cleanCodexModelAvailabilityNuxIfNeeded, hasExactOmxSeededBehavioralDefaultsPair, mergeConfig, repairConfigIfNeeded, stripManagedCodexHookTrustState, stripOmxFeatureFlags, upsertManagedCodexHookTrustState, stripOmxSeededBehavioralDefaults, } from "../generator.js";
12
- import { buildManagedCodexHookTrustState } from "../codex-hooks.js";
11
+ import { analyzeLegacyMultiAgentConfig, buildMergedConfig, cleanCodexModelAvailabilityNuxIfNeeded, hasExactOmxSeededBehavioralDefaultsPair, mergeConfig, repairConfigIfNeeded, repairProjectScopeTrustStateForLaunch, stripExistingOmxBlocks, stripManagedCodexHookTrustState, stripOmxFeatureFlags, syncProjectScopeTrustStateFromRuntime, upsertManagedCodexHookTrustState, stripOmxSeededBehavioralDefaults, } from "../generator.js";
12
+ import { MANAGED_HOOK_EVENTS, ManagedCodexHooksPlanError, buildManagedCodexHookTrustState, planManagedCodexHooksMerge, } from "../codex-hooks.js";
13
13
  import { OMX_FIRST_PARTY_MCP_SERVER_NAMES } from "../omx-first-party-mcp.js";
14
14
  /** Count occurrences of a pattern in text */
15
15
  function count(text, pattern) {
16
16
  return (text.match(pattern) ?? []).length;
17
17
  }
18
+ function escapeRegExp(value) {
19
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
20
+ }
21
+ async function writeSetupGeneratedHookTrustFixture(wd) {
22
+ const configPath = join(wd, "config.toml");
23
+ const hooksPath = join(wd, "hooks.json");
24
+ const hooksPlan = planManagedCodexHooksMerge(null, wd, hooksPath);
25
+ if (!hooksPlan.ok || hooksPlan.finalContent === null) {
26
+ throw new Error("Expected setup hook plan to produce a hooks artifact.");
27
+ }
28
+ await writeFile(hooksPath, hooksPlan.finalContent);
29
+ const config = buildMergedConfig("", wd, {
30
+ codexHooksFile: hooksPath,
31
+ codexHooksContent: hooksPlan.finalContent,
32
+ managedHookTrustState: hooksPlan.finalTrustState,
33
+ priorManagedHookTrustState: hooksPlan.priorTrustState,
34
+ });
35
+ await writeFile(configPath, config);
36
+ return {
37
+ configPath,
38
+ hooksPath,
39
+ config,
40
+ trustState: hooksPlan.finalTrustState,
41
+ };
42
+ }
18
43
  /** Assert the current OMX block appears exactly once */
19
44
  function assertSingleOmxBlock(toml, options = {}) {
20
45
  assert.equal(count(toml, /# oh-my-codex \(OMX\) Configuration/g), 1, "OMX marker should appear once");
@@ -788,6 +813,106 @@ describe("config generator idempotency (#384)", () => {
788
813
  await rm(wd, { recursive: true, force: true });
789
814
  }
790
815
  });
816
+ it("strips only complete exact OMX marker blocks outside TOML values", () => {
817
+ const start = "# oh-my-codex (OMX) Configuration";
818
+ const end = "# End oh-my-codex";
819
+ const decoys = [
820
+ `basic = "${start}"`,
821
+ `literal = '${end}'`,
822
+ 'multiline_basic = """',
823
+ start,
824
+ end,
825
+ '"""',
826
+ "multiline_literal = '''",
827
+ start,
828
+ end,
829
+ "'''",
830
+ `# Marker-shaped text: ${start}`,
831
+ `# Marker-shaped text: ${end}`,
832
+ ];
833
+ const decoyOnly = [...decoys, ""].join("\n");
834
+ assert.deepEqual(stripExistingOmxBlocks(decoyOnly), {
835
+ cleaned: decoyOnly,
836
+ removed: 0,
837
+ });
838
+ const incomplete = [...decoys, "", start, "[user.incomplete]", "enabled = true", ""].join("\n");
839
+ assert.deepEqual(stripExistingOmxBlocks(incomplete), {
840
+ cleaned: incomplete,
841
+ removed: 0,
842
+ });
843
+ const managed = [
844
+ ...decoys,
845
+ "",
846
+ "# ============================================================",
847
+ start,
848
+ "[mcp_servers.omx_state]",
849
+ 'command = "node"',
850
+ end,
851
+ "",
852
+ "[user.after]",
853
+ 'name = "kept"',
854
+ "",
855
+ ].join("\n");
856
+ const expected = [...decoys, "", "[user.after]", 'name = "kept"', ""].join("\n");
857
+ assert.deepEqual(stripExistingOmxBlocks(managed), {
858
+ cleaned: expected,
859
+ removed: 1,
860
+ });
861
+ assert.doesNotThrow(() => TOML.parse(expected));
862
+ });
863
+ it("keeps OMX delimiter comments inert inside complete TOML assignments", () => {
864
+ const start = "# oh-my-codex (OMX) Configuration";
865
+ const end = "# End oh-my-codex";
866
+ const fixtures = [
867
+ ["root multiline array", ["root = [", start, end, "]"].join("\n")],
868
+ ["dotted multiline array", ["outer.value = [", start, end, "]"].join("\n")],
869
+ ["quoted multiline array", ['"quoted" = [', start, end, "]"].join("\n")],
870
+ ["empty-quoted multiline array", ['"" = [', start, end, "]"].join("\n")],
871
+ ["root multiline basic value", ['root_value = """', start, end, '"""'].join("\n")],
872
+ ["dotted multiline literal value", ["outer.literal = '''", start, end, "'''"].join("\n")],
873
+ ];
874
+ for (const [name, assignment] of fixtures) {
875
+ const config = `${assignment}\n`;
876
+ assert.doesNotThrow(() => TOML.parse(config), `${name} fixture must be valid TOML`);
877
+ assert.deepEqual(stripExistingOmxBlocks(config), { cleaned: config, removed: 0 }, name);
878
+ const merged = buildMergedConfig(config, "/tmp/omx");
879
+ assert.ok(merged.includes(assignment), `${name} bytes must survive merging`);
880
+ assert.doesNotThrow(() => TOML.parse(merged), `${name} merged config must be valid TOML`);
881
+ }
882
+ });
883
+ it("rejects marker-contained hook trust state without ownership proof", () => {
884
+ const start = "# oh-my-codex (OMX) Configuration";
885
+ const end = "# End oh-my-codex";
886
+ const fixtures = [
887
+ ["table", ['[hooks.state."foreign"]', 'trusted_hash = "sha256:foreign"'].join("\n")],
888
+ ["dotted", 'hooks.state."foreign" = { trusted_hash = "sha256:foreign" }'],
889
+ ["inline", 'hooks = { state = { foreign = { trusted_hash = "sha256:foreign" } } }'],
890
+ ["scalar", 'hooks.state = "foreign"'],
891
+ ];
892
+ const isManagedTrustConflict = (error) => error instanceof ManagedCodexHooksPlanError &&
893
+ error.code === "managed_trust_key_conflict";
894
+ for (const [name, content] of fixtures) {
895
+ const config = [start, content, end, ""].join("\n");
896
+ assert.doesNotThrow(() => TOML.parse(config), `${name} fixture must be valid TOML`);
897
+ assert.throws(() => stripExistingOmxBlocks(config), isManagedTrustConflict, name);
898
+ assert.throws(() => buildMergedConfig(config, "/tmp/omx"), isManagedTrustConflict, name);
899
+ }
900
+ });
901
+ it("matches managed hooks.state keys case-sensitively", () => {
902
+ const start = "# oh-my-codex (OMX) Configuration";
903
+ const end = "# End oh-my-codex";
904
+ const variants = [
905
+ 'Hooks.state = { foreign = { trusted_hash = "sha256:foreign" } }',
906
+ 'hooks.State = { foreign = { trusted_hash = "sha256:foreign" } }',
907
+ '"Hooks"."State" = { foreign = { trusted_hash = "sha256:foreign" } }',
908
+ ];
909
+ for (const variant of variants) {
910
+ const config = [start, variant, end, ""].join("\n");
911
+ assert.doesNotThrow(() => TOML.parse(config));
912
+ assert.equal(stripManagedCodexHookTrustState(config, { managedTrustState: {} }), config, `${variant} must remain foreign`);
913
+ assert.doesNotThrow(() => buildMergedConfig(config, "/tmp/omx"));
914
+ }
915
+ });
791
916
  it("mergeConfig removes legacy omx_team_run tables during setup upgrade", async () => {
792
917
  const wd = await mkdtemp(join(tmpdir(), "omx-idem-"));
793
918
  try {
@@ -887,6 +1012,74 @@ describe("config generator idempotency (#384)", () => {
887
1012
  await rm(wd, { recursive: true, force: true });
888
1013
  }
889
1014
  });
1015
+ it("repairs duplicate TUI config with setup-generated hook trust from the current hooks artifact", async () => {
1016
+ const wd = await mkdtemp(join(tmpdir(), "omx-idem-"));
1017
+ try {
1018
+ const { configPath, config: setupConfig } = await writeSetupGeneratedHookTrustFixture(wd);
1019
+ const trustBlock = setupConfig.match(/# OMX-owned Codex hook trust state\n[\s\S]*?# End OMX-owned Codex hook trust state/)?.[0];
1020
+ assert.ok(trustBlock, "setup fixture must contain fenced managed hook trust");
1021
+ const setupTrustState = TOML.parse(setupConfig).hooks?.state;
1022
+ await writeFile(configPath, `${setupConfig}\n[tui]\nstatus_line = ["git-branch"]\n`);
1023
+ assert.equal(await repairConfigIfNeeded(configPath, wd), true);
1024
+ const repaired = await readFile(configPath, "utf-8");
1025
+ const repairedTrustState = TOML.parse(repaired).hooks?.state;
1026
+ assert.equal(count(repaired, /^\[tui\]$/gm), 1);
1027
+ assert.ok(repaired.includes(trustBlock), "repair must preserve setup-generated trust bytes");
1028
+ assert.deepEqual(repairedTrustState, setupTrustState);
1029
+ }
1030
+ finally {
1031
+ await rm(wd, { recursive: true, force: true });
1032
+ }
1033
+ });
1034
+ it("fails closed without rewriting managed hook trust when launch repair cannot prove it", async () => {
1035
+ const wd = await mkdtemp(join(tmpdir(), "omx-idem-"));
1036
+ try {
1037
+ const { configPath, hooksPath, config: setupConfig, trustState } = await writeSetupGeneratedHookTrustFixture(wd);
1038
+ const [firstTrustState] = Object.values(trustState);
1039
+ assert.ok(firstTrustState);
1040
+ const repairTarget = `${setupConfig}\n[tui]\nstatus_line = ["git-branch"]\n`;
1041
+ const conflicting = repairTarget.replace(firstTrustState.trusted_hash, "sha256:conflicting-user-trust");
1042
+ await writeFile(configPath, conflicting);
1043
+ await assert.rejects(repairConfigIfNeeded(configPath, wd), (error) => error instanceof ManagedCodexHooksPlanError &&
1044
+ error.code === "managed_trust_key_conflict");
1045
+ assert.equal(await readFile(configPath, "utf-8"), conflicting);
1046
+ const unprovenHooks = [
1047
+ ["invalid", "{ not valid JSON"],
1048
+ [
1049
+ "ambiguous",
1050
+ JSON.stringify({
1051
+ hooks: {
1052
+ SessionStart: [
1053
+ {
1054
+ hooks: [
1055
+ {
1056
+ type: "command",
1057
+ command: `NODE ${join(wd, "dist", "scripts", "codex-native-hook.js")}`,
1058
+ },
1059
+ ],
1060
+ },
1061
+ ],
1062
+ },
1063
+ }),
1064
+ ],
1065
+ ["missing", null],
1066
+ ];
1067
+ for (const [name, hooksContent] of unprovenHooks) {
1068
+ await writeFile(configPath, repairTarget);
1069
+ if (hooksContent === null) {
1070
+ await rm(hooksPath);
1071
+ }
1072
+ else {
1073
+ await writeFile(hooksPath, hooksContent);
1074
+ }
1075
+ await assert.rejects(repairConfigIfNeeded(configPath, wd), (error) => error instanceof ManagedCodexHooksPlanError, name);
1076
+ assert.equal(await readFile(configPath, "utf-8"), repairTarget, name);
1077
+ }
1078
+ }
1079
+ finally {
1080
+ await rm(wd, { recursive: true, force: true });
1081
+ }
1082
+ });
890
1083
  it("preserves trailing user config after an unmatched managed hook trust-state start marker", () => {
891
1084
  const malformed = [
892
1085
  'model = "gpt-5.6-sol"',
@@ -952,7 +1145,7 @@ describe("config generator idempotency (#384)", () => {
952
1145
  assert.match(stripped, /^\[desktop\]$/m);
953
1146
  assert.doesNotThrow(() => TOML.parse(stripped));
954
1147
  });
955
- it("preserves same-key user hook trust state and suppresses generated duplicates", () => {
1148
+ it("fails closed on conflicting same-key hook trust state", () => {
956
1149
  const hooksPath = "/tmp/codex/hooks.json";
957
1150
  const config = [
958
1151
  'model = "gpt-5.6-sol"',
@@ -962,13 +1155,10 @@ describe("config generator idempotency (#384)", () => {
962
1155
  "enabled = false",
963
1156
  "",
964
1157
  ].join("\n");
965
- const refreshed = upsertManagedCodexHookTrustState(config, "/tmp/omx", hooksPath);
966
- assert.match(refreshed, /^trusted_hash = "sha256:user"$/m);
967
- assert.match(refreshed, /^enabled = false$/m);
968
- assert.equal(count(refreshed, /^\[hooks\.state\."\/tmp\/codex\/hooks\.json:post_compact:0:0"\]$/gm), 1, "preserved same-key user trust state must not be duplicated");
969
- assert.doesNotThrow(() => TOML.parse(refreshed));
1158
+ assert.throws(() => upsertManagedCodexHookTrustState(config, "/tmp/omx", hooksPath), (error) => error instanceof ManagedCodexHooksPlanError &&
1159
+ error.code === "managed_trust_key_conflict");
970
1160
  });
971
- it("preserves unproven same-key hook trust-state tables", () => {
1161
+ it("fails closed on unproven same-key hook trust-state tables", () => {
972
1162
  const hooksPath = "/tmp/codex/hooks.json";
973
1163
  const managedTrustState = buildManagedCodexHookTrustState(hooksPath, "/tmp/omx");
974
1164
  const fixtures = [
@@ -1018,10 +1208,534 @@ describe("config generator idempotency (#384)", () => {
1018
1208
  ],
1019
1209
  ];
1020
1210
  for (const [name, table] of fixtures) {
1021
- const stripped = stripManagedCodexHookTrustState(table.join("\n"), {
1211
+ assert.throws(() => stripManagedCodexHookTrustState(table.join("\n"), {
1022
1212
  managedTrustState,
1023
- });
1024
- assert.match(stripped, /^\[hooks\.state\."\/tmp\/codex\/hooks\.json:post_compact:0:0"\]/m, `${name} should be preserved`);
1213
+ }), (error) => error instanceof ManagedCodexHooksPlanError &&
1214
+ error.code === "managed_trust_key_conflict", name);
1215
+ }
1216
+ });
1217
+ it("handles semantically equivalent TOML trust-key variants before writing a replacement", () => {
1218
+ const hooksPath = "/tmp/codex/hooks.json";
1219
+ const managedTrustState = buildManagedCodexHookTrustState(hooksPath, "/tmp/omx");
1220
+ const key = `${hooksPath}:post_compact:0:0`;
1221
+ const hash = managedTrustState[key]?.trusted_hash;
1222
+ assert.ok(hash);
1223
+ const matchingHeaders = [
1224
+ `[hooks.state.'${key}']`,
1225
+ `[hooks.state."${key.replace(/\//g, "\\u002f")}"]`,
1226
+ ];
1227
+ for (const header of matchingHeaders) {
1228
+ const matching = [header, `trusted_hash = "${hash}"`, ""].join("\n");
1229
+ const refreshed = upsertManagedCodexHookTrustState(matching, "/tmp/omx", hooksPath, { managedTrustState });
1230
+ assert.equal(count(refreshed, /^\[hooks\.state\./gm), Object.keys(managedTrustState).length);
1231
+ assert.equal(TOML.parse(refreshed)
1232
+ .hooks?.state?.[key] !== undefined, true);
1233
+ }
1234
+ const conflicting = [
1235
+ matchingHeaders[0],
1236
+ 'trusted_hash = "sha256:foreign"',
1237
+ "",
1238
+ ].join("\n");
1239
+ assert.throws(() => upsertManagedCodexHookTrustState(conflicting, "/tmp/omx", hooksPath, { managedTrustState }), (error) => error instanceof ManagedCodexHooksPlanError &&
1240
+ error.code === "managed_trust_key_conflict");
1241
+ });
1242
+ it("removes only a single exact proven managed dotted hook trust assignment", () => {
1243
+ const hooksPath = "/tmp/codex/hooks.json";
1244
+ const managedTrustState = buildManagedCodexHookTrustState(hooksPath, "/tmp/omx");
1245
+ const key = `${hooksPath}:post_compact:0:0`;
1246
+ const hash = managedTrustState[key]?.trusted_hash;
1247
+ assert.ok(hash);
1248
+ const exactAssignments = [
1249
+ `hooks.state."${key}".trusted_hash = "${hash}"`,
1250
+ `hooks . state . '${key}' . trusted_hash = "${hash}"`,
1251
+ ];
1252
+ for (const exact of exactAssignments) {
1253
+ assert.equal(stripManagedCodexHookTrustState(exact, { managedTrustState }), "");
1254
+ }
1255
+ const exact = exactAssignments[1];
1256
+ const refreshed = upsertManagedCodexHookTrustState(exact, "/tmp/omx", hooksPath, { managedTrustState });
1257
+ assert.doesNotMatch(refreshed, /^hooks\s*\.\s*state\s*\./m);
1258
+ assert.equal(count(refreshed, /^\[hooks\.state\./gm), Object.keys(managedTrustState).length);
1259
+ });
1260
+ it("fails closed for conflicting, commented, or multi-field managed dotted hook trust assignments", () => {
1261
+ const hooksPath = "/tmp/codex/hooks.json";
1262
+ const managedTrustState = buildManagedCodexHookTrustState(hooksPath, "/tmp/omx");
1263
+ const key = `${hooksPath}:post_compact:0:0`;
1264
+ const hash = managedTrustState[key]?.trusted_hash;
1265
+ assert.ok(hash);
1266
+ const assignment = `hooks.state.'${key}'.trusted_hash = "${hash}"`;
1267
+ const fixtures = [
1268
+ assignment.replace(hash, "sha256:foreign"),
1269
+ `${assignment}\nhooks.state.'${key}'.enabled = true`,
1270
+ `${assignment} # user comment`,
1271
+ ];
1272
+ for (const fixture of fixtures) {
1273
+ assert.throws(() => stripManagedCodexHookTrustState(fixture, { managedTrustState }), (error) => error instanceof ManagedCodexHooksPlanError &&
1274
+ error.code === "managed_trust_key_conflict");
1275
+ }
1276
+ });
1277
+ it("removes exact inline hook trust state without changing adjacent TOML", () => {
1278
+ const hooksPath = "/tmp/codex/hooks.json";
1279
+ const managedTrustState = buildManagedCodexHookTrustState(hooksPath, "/tmp/omx");
1280
+ const key = `${hooksPath}:post_compact:0:0`;
1281
+ const hash = managedTrustState[key]?.trusted_hash;
1282
+ assert.ok(hash);
1283
+ const exactWholeStatement = `hooks = { state = { "${key}" = { trusted_hash = "${hash}" } } }`;
1284
+ assert.equal(stripManagedCodexHookTrustState(exactWholeStatement, { managedTrustState }), "", "an exact single-field inline hooks state remains removable");
1285
+ const inline = [
1286
+ 'model = "gpt-5.6-sol"',
1287
+ `hooks.state.'${key}' = { trusted_hash = '${hash}' }`,
1288
+ "",
1289
+ "[desktop]",
1290
+ "git-create-pull-request-as-draft = true",
1291
+ "",
1292
+ ].join("\n");
1293
+ const stripped = stripManagedCodexHookTrustState(inline, { managedTrustState });
1294
+ assert.doesNotMatch(stripped, /hooks\.state/);
1295
+ assert.match(stripped, /^model = "gpt-5\.6-sol"$/m);
1296
+ assert.match(stripped, /^\[desktop\]$/m);
1297
+ assert.match(stripped, /^git-create-pull-request-as-draft = true$/m);
1298
+ assert.doesNotThrow(() => TOML.parse(stripped));
1299
+ const refreshed = upsertManagedCodexHookTrustState(inline, "/tmp/omx", hooksPath, { managedTrustState });
1300
+ assert.equal(Object.keys(TOML.parse(refreshed)
1301
+ .hooks?.state ?? {}).filter((stateKey) => stateKey === key).length, 1);
1302
+ });
1303
+ it("fails closed for inline, dotted, and table hook trust representations with foreign siblings", () => {
1304
+ const hooksPath = "/tmp/codex/hooks.json";
1305
+ const managedTrustState = buildManagedCodexHookTrustState(hooksPath, "/tmp/omx");
1306
+ const key = `${hooksPath}:post_compact:0:0`;
1307
+ const hash = managedTrustState[key]?.trusted_hash;
1308
+ assert.ok(hash);
1309
+ const fixtures = [
1310
+ [
1311
+ "inline",
1312
+ `hooks = { state = { "${key}" = { trusted_hash = "${hash}" } }, foreign = "keep" }`,
1313
+ ],
1314
+ [
1315
+ "dotted",
1316
+ `hooks.state = { "${key}" = { trusted_hash = "${hash}" }, foreign = "keep" }`,
1317
+ ],
1318
+ [
1319
+ "table",
1320
+ [
1321
+ "[hooks]",
1322
+ `state = { "${key}" = { trusted_hash = "${hash}" } }`,
1323
+ 'foreign = "keep"',
1324
+ ].join("\n"),
1325
+ ],
1326
+ ];
1327
+ for (const [name, fixture] of fixtures) {
1328
+ assert.doesNotThrow(() => TOML.parse(fixture), `${name} fixture must be valid TOML`);
1329
+ assert.throws(() => stripManagedCodexHookTrustState(fixture, { managedTrustState }), (error) => error instanceof ManagedCodexHooksPlanError &&
1330
+ error.code === "managed_trust_key_conflict", `${name} cleanup must preserve the entire foreign-containing representation`);
1331
+ assert.throws(() => upsertManagedCodexHookTrustState(fixture, "/tmp/omx", hooksPath, { managedTrustState }), (error) => error instanceof ManagedCodexHooksPlanError &&
1332
+ error.code === "managed_trust_key_conflict", `${name} refresh must preserve the original bytes`);
1333
+ }
1334
+ });
1335
+ it("fails closed for commented trust assignments inside a managed config marker", () => {
1336
+ const fixture = [
1337
+ "# oh-my-codex (OMX) Configuration",
1338
+ 'hooks.state."foreign-key".trusted_hash = "sha256:foreign" # preserve me',
1339
+ "# End oh-my-codex",
1340
+ "",
1341
+ ].join("\n");
1342
+ assert.throws(() => stripManagedCodexHookTrustState(fixture, { managedTrustState: {} }), (error) => error instanceof ManagedCodexHooksPlanError &&
1343
+ error.code === "managed_trust_key_conflict");
1344
+ });
1345
+ it("fails closed on every non-record hooks.state form inside managed markers", () => {
1346
+ const hooksPath = "/tmp/codex/hooks.json";
1347
+ const managedTrustState = buildManagedCodexHookTrustState(hooksPath, "/tmp/omx");
1348
+ const fixtures = [
1349
+ ["scalar", 'hooks.state = "foreign"'],
1350
+ ["array", "hooks.state = []"],
1351
+ ["quoted state key", 'hooks."state" = "foreign"'],
1352
+ ["inline non-record", 'hooks = { state = "foreign" }'],
1353
+ [
1354
+ "array of tables",
1355
+ ["[[hooks.state]]", 'trusted_hash = "sha256:foreign"'].join("\n"),
1356
+ ],
1357
+ ];
1358
+ for (const [name, value] of fixtures) {
1359
+ const fixture = [
1360
+ "# oh-my-codex (OMX) Configuration",
1361
+ value,
1362
+ "# End oh-my-codex",
1363
+ "",
1364
+ ].join("\n");
1365
+ assert.doesNotThrow(() => TOML.parse(fixture), `${name} fixture must be valid TOML`);
1366
+ assert.throws(() => stripManagedCodexHookTrustState(fixture, { managedTrustState }), (error) => error instanceof ManagedCodexHooksPlanError &&
1367
+ error.code === "managed_trust_key_conflict", name);
1368
+ }
1369
+ });
1370
+ it("fails closed before stripping malformed hooks.state spellings", () => {
1371
+ const hooksPath = "/tmp/codex/hooks.json";
1372
+ const managedTrustState = buildManagedCodexHookTrustState(hooksPath, "/tmp/omx");
1373
+ const key = `${hooksPath}:post_compact:0:0`;
1374
+ const hash = managedTrustState[key]?.trusted_hash;
1375
+ assert.ok(hash);
1376
+ const spellings = [
1377
+ ["quoted state key", 'hooks."state" ='],
1378
+ ["quoted hooks key", '"hooks".state ='],
1379
+ ["fully quoted dotted key", '"hooks"."state" ='],
1380
+ ["whitespace-delimited malformed array", 'hooks \t. "state" \t= ['],
1381
+ ["malformed inline table", 'hooks = { "state" = {'],
1382
+ ["incomplete table header", '[ "hooks" . "state"'],
1383
+ ["incomplete array table header", '[[ "hooks" . "state" ]'],
1384
+ ["incomplete hooks table header prefix", "[hooks"],
1385
+ ["incomplete hooks array table header prefix", "[[hooks"],
1386
+ ["partial hooks array-table closer", "[[hooks] # continuation"],
1387
+ ["whitespace-split hooks array-table closer", "[[hooks] ]"],
1388
+ [
1389
+ "split hooks array-table closer",
1390
+ ["[[hooks]", "]"].join("\n"),
1391
+ ],
1392
+ [
1393
+ "comment-separated hooks assignment continuation",
1394
+ [
1395
+ "hooks # continuation",
1396
+ '= { "state" = { trusted_hash = "sha256:foreign" } }',
1397
+ ].join("\n"),
1398
+ ],
1399
+ [
1400
+ "bare hooks assignment continuation",
1401
+ [
1402
+ "hooks",
1403
+ '= { "state" = { trusted_hash = "sha256:foreign" } }',
1404
+ ].join("\n"),
1405
+ ],
1406
+ [
1407
+ "comment-separated hooks dotted-key continuation",
1408
+ [
1409
+ "hooks # continuation",
1410
+ '. "state" = { trusted_hash = "sha256:foreign" }',
1411
+ ].join("\n"),
1412
+ ],
1413
+ [
1414
+ "comment-separated hooks table-header continuation",
1415
+ ["hooks # continuation", '[ "state" ]'].join("\n"),
1416
+ ],
1417
+ [
1418
+ "comment-separated hooks array-table-header continuation",
1419
+ ["hooks # continuation", '[[ "state" ]]'].join("\n"),
1420
+ ],
1421
+ [
1422
+ "split dotted key",
1423
+ ["hooks .", '"state" = { trusted_hash = "sha256:foreign" }'].join("\n"),
1424
+ ],
1425
+ [
1426
+ "unclosed multiline inline table",
1427
+ ["hooks = {", '"state" = { trusted_hash = "sha256:foreign" }'].join("\n"),
1428
+ ],
1429
+ [
1430
+ "comment-ended inline table continuation",
1431
+ [
1432
+ "hooks = { # continuation",
1433
+ '"state" = { trusted_hash = "sha256:foreign" }',
1434
+ ].join("\n"),
1435
+ ],
1436
+ [
1437
+ "bare hooks assignment with inline-table continuation",
1438
+ [
1439
+ "hooks =",
1440
+ '{ "state" = { trusted_hash = "sha256:foreign" } }',
1441
+ ].join("\n"),
1442
+ ],
1443
+ ];
1444
+ const isManagedTrustConflict = (error) => error instanceof ManagedCodexHooksPlanError &&
1445
+ error.code === "managed_trust_key_conflict";
1446
+ for (const [name, spelling] of spellings) {
1447
+ const fixture = [
1448
+ "# oh-my-codex (OMX) Configuration",
1449
+ spelling,
1450
+ "# End oh-my-codex",
1451
+ "",
1452
+ "[user.after]",
1453
+ 'value = "preserved"',
1454
+ "",
1455
+ ].join("\n");
1456
+ const original = fixture;
1457
+ assert.throws(() => buildMergedConfig(fixture, "/tmp/omx", { managedHookTrustState: managedTrustState }), isManagedTrustConflict, `${name} must fail before OMX block stripping`);
1458
+ assert.equal(fixture, original, `${name} planning must not mutate input`);
1459
+ assert.throws(() => stripExistingOmxBlocks(fixture), isManagedTrustConflict, `${name} must not be erased by direct OMX block stripping`);
1460
+ assert.equal(fixture, original, `${name} stripping must not mutate input`);
1461
+ }
1462
+ const exactQuoted = [
1463
+ "# oh-my-codex (OMX) Configuration",
1464
+ `["hooks"."state"."${key}"]`,
1465
+ `trusted_hash = "${hash}"`,
1466
+ "# End oh-my-codex",
1467
+ "",
1468
+ ].join("\n");
1469
+ assert.throws(() => stripExistingOmxBlocks(exactQuoted), isManagedTrustConflict, "direct stripping must not infer ownership without trust expectations");
1470
+ assert.deepEqual(stripExistingOmxBlocks(exactQuoted, { managedTrustState }), {
1471
+ cleaned: "",
1472
+ removed: 1,
1473
+ });
1474
+ assert.doesNotThrow(() => buildMergedConfig(exactQuoted, "/tmp/omx", { managedHookTrustState: managedTrustState }));
1475
+ });
1476
+ it("fails closed when hooks scope crosses parsed assignments or malformed headers", () => {
1477
+ const hooksPath = "/tmp/codex/hooks.json";
1478
+ const managedTrustState = buildManagedCodexHookTrustState(hooksPath, "/tmp/omx");
1479
+ const validNestedArrayAssignment = [
1480
+ "values = [",
1481
+ ' ["unrelated"]',
1482
+ "]",
1483
+ ];
1484
+ assert.doesNotThrow(() => TOML.parse(["[hooks]", ...validNestedArrayAssignment].join("\n")));
1485
+ const fixtures = [
1486
+ [
1487
+ "parsed nested assignment",
1488
+ ["[hooks]", ...validNestedArrayAssignment, "state ="].join("\n"),
1489
+ ],
1490
+ [
1491
+ "malformed bracket-prefixed line",
1492
+ ["[hooks]", "[broken", "state ="].join("\n"),
1493
+ ],
1494
+ ];
1495
+ const isManagedTrustConflict = (error) => error instanceof ManagedCodexHooksPlanError &&
1496
+ error.code === "managed_trust_key_conflict";
1497
+ for (const [name, content] of fixtures) {
1498
+ const fixture = [
1499
+ "# oh-my-codex (OMX) Configuration",
1500
+ content,
1501
+ "# End oh-my-codex",
1502
+ "",
1503
+ "[user.after]",
1504
+ 'value = "preserved"',
1505
+ "",
1506
+ ].join("\n");
1507
+ const original = fixture;
1508
+ assert.throws(() => stripExistingOmxBlocks(fixture), isManagedTrustConflict, `${name} must not be erased by direct OMX block stripping`);
1509
+ assert.equal(fixture, original, `${name} stripping must not mutate input`);
1510
+ assert.throws(() => buildMergedConfig(fixture, "/tmp/omx", { managedHookTrustState: managedTrustState }), isManagedTrustConflict, `${name} must fail before OMX block stripping`);
1511
+ assert.equal(fixture, original, `${name} planning must not mutate input`);
1512
+ }
1513
+ const validNonHooksHeaderExit = [
1514
+ "# oh-my-codex (OMX) Configuration",
1515
+ "[hooks]",
1516
+ "[unrelated]",
1517
+ "state =",
1518
+ "# End oh-my-codex",
1519
+ "",
1520
+ "[user.after]",
1521
+ 'value = "preserved"',
1522
+ "",
1523
+ ].join("\n");
1524
+ const expectedExit = ["[user.after]", 'value = "preserved"', ""].join("\n");
1525
+ assert.deepEqual(stripExistingOmxBlocks(validNonHooksHeaderExit), {
1526
+ cleaned: expectedExit,
1527
+ removed: 1,
1528
+ });
1529
+ assert.doesNotThrow(() => buildMergedConfig(validNonHooksHeaderExit, "/tmp/omx", {
1530
+ managedHookTrustState: managedTrustState,
1531
+ }));
1532
+ });
1533
+ it("fails closed for hooks array-table openers and preserves unrelated arrays", () => {
1534
+ const hooksPath = "/tmp/codex/hooks.json";
1535
+ const managedTrustState = buildManagedCodexHookTrustState(hooksPath, "/tmp/omx");
1536
+ const arrayTableOpeners = [
1537
+ ["adjacent", "[[hooks]]"],
1538
+ ["space-split", "[ [hooks]]"],
1539
+ ["tab-split", "[\t[hooks]]"],
1540
+ ["newline-split", ["[[", "hooks]]"].join("\n")],
1541
+ [
1542
+ "two standalone opening brackets followed by independently parsed state",
1543
+ [
1544
+ "[",
1545
+ "[",
1546
+ "hooks]]",
1547
+ 'state = { trusted_hash = "sha256:foreign" }',
1548
+ ].join("\n"),
1549
+ ],
1550
+ [
1551
+ "three standalone opening brackets followed by independently parsed state",
1552
+ [
1553
+ "[",
1554
+ "[",
1555
+ "[",
1556
+ "hooks]]]",
1557
+ 'state = { trusted_hash = "sha256:foreign" }',
1558
+ ].join("\n"),
1559
+ ],
1560
+ [
1561
+ "comment- and blank-separated standalone opening brackets",
1562
+ [
1563
+ "[",
1564
+ "# opening bracket continuation",
1565
+ "",
1566
+ "[",
1567
+ "",
1568
+ "# another opening bracket continuation",
1569
+ "[",
1570
+ "hooks]]]",
1571
+ 'state = { trusted_hash = "sha256:foreign" }',
1572
+ ].join("\n"),
1573
+ ],
1574
+ ["triple", "[[[hooks]]]"],
1575
+ ["extra", "[[[[hooks]]]]"],
1576
+ ["space-newline-split", ["[ [", "hooks]]"].join("\n")],
1577
+ ["tab-newline-split", ["[\t[", "hooks]]"].join("\n")],
1578
+ ["triple-newline-split", ["[[[", "hooks]]]"].join("\n")],
1579
+ ["comment-ended", ["[[ # continuation", "hooks]]"].join("\n")],
1580
+ ];
1581
+ const isManagedTrustConflict = (error) => error instanceof ManagedCodexHooksPlanError &&
1582
+ error.code === "managed_trust_key_conflict";
1583
+ for (const [name, opener] of arrayTableOpeners) {
1584
+ const fixture = [
1585
+ "# oh-my-codex (OMX) Configuration",
1586
+ opener,
1587
+ "# End oh-my-codex",
1588
+ "",
1589
+ "[user.after]",
1590
+ 'value = "preserved"',
1591
+ "",
1592
+ ].join("\n");
1593
+ const original = fixture;
1594
+ assert.throws(() => buildMergedConfig(fixture, "/tmp/omx", {
1595
+ managedHookTrustState: managedTrustState,
1596
+ }), isManagedTrustConflict, `${name} opener must fail before OMX block stripping`);
1597
+ assert.equal(fixture, original, `${name} planning must not mutate input`);
1598
+ assert.throws(() => stripExistingOmxBlocks(fixture), isManagedTrustConflict, `${name} opener must not be erased by direct OMX block stripping`);
1599
+ assert.equal(fixture, original, `${name} stripping must not mutate input`);
1600
+ }
1601
+ const unrelatedArrays = [
1602
+ "[[user.before]]",
1603
+ 'name = "before"',
1604
+ "",
1605
+ "# oh-my-codex (OMX) Configuration",
1606
+ "[[unrelated]]",
1607
+ 'name = "inside managed marker"',
1608
+ "# End oh-my-codex",
1609
+ "",
1610
+ "[[user.after]]",
1611
+ 'name = "after"',
1612
+ "",
1613
+ ].join("\n");
1614
+ const preservedArrays = [
1615
+ "[[user.before]]",
1616
+ 'name = "before"',
1617
+ "",
1618
+ "[[user.after]]",
1619
+ 'name = "after"',
1620
+ "",
1621
+ ].join("\n");
1622
+ assert.deepEqual(stripExistingOmxBlocks(unrelatedArrays), {
1623
+ cleaned: preservedArrays,
1624
+ removed: 1,
1625
+ });
1626
+ const merged = buildMergedConfig(unrelatedArrays, "/tmp/omx");
1627
+ assert.match(merged, /^\[\[user\.before\]\]$/m);
1628
+ assert.match(merged, /^\[\[user\.after\]\]$/m);
1629
+ assert.doesNotMatch(merged, /^\[\[unrelated\]\]$/m);
1630
+ assert.doesNotThrow(() => TOML.parse(merged));
1631
+ });
1632
+ it("removes valid nested hooks arrays inside exact managed marker ranges", () => {
1633
+ const fixture = [
1634
+ "# oh-my-codex (OMX) Configuration",
1635
+ "values = [",
1636
+ " [",
1637
+ "",
1638
+ " # a value named hooks is not a table prefix",
1639
+ ' "hooks",',
1640
+ " ],",
1641
+ " # nor is this independently nested array",
1642
+ " [",
1643
+ ' "unrelated",',
1644
+ " ],",
1645
+ " [",
1646
+ ' [["hooks"]]',
1647
+ " ],",
1648
+ " [",
1649
+ ' [ [ "hooks" ] ]',
1650
+ " ],",
1651
+ "]",
1652
+ "outer.values = [",
1653
+ ' [["hooks"]]',
1654
+ "]",
1655
+ '"" = [',
1656
+ ' [ [ "hooks" ] ]',
1657
+ "]",
1658
+ "# End oh-my-codex",
1659
+ "",
1660
+ "[user.after]",
1661
+ 'value = "preserved"',
1662
+ "",
1663
+ ].join("\n");
1664
+ const expected = ["[user.after]", 'value = "preserved"', ""].join("\n");
1665
+ assert.doesNotThrow(() => TOML.parse(fixture));
1666
+ assert.deepEqual(stripExistingOmxBlocks(fixture), { cleaned: expected, removed: 1 });
1667
+ const merged = buildMergedConfig(fixture, "/tmp/omx");
1668
+ assert.doesNotMatch(merged, /^values = \[/m);
1669
+ assert.match(merged, /^\[user\.after\]$/m);
1670
+ assert.doesNotThrow(() => TOML.parse(merged));
1671
+ });
1672
+ it("ignores inert hooks.state text inside exact managed marker ranges", () => {
1673
+ const fixture = [
1674
+ "# oh-my-codex (OMX) Configuration",
1675
+ '# hooks."state" =',
1676
+ 'basic = "hooks.\'state\' ="',
1677
+ "literal = 'hooks = { state = {'",
1678
+ 'multiline = """',
1679
+ '[[ "hooks" . "state" ]',
1680
+ 'hooks = { "state" = {',
1681
+ '"""',
1682
+ "# End oh-my-codex",
1683
+ "",
1684
+ "[user.after]",
1685
+ 'value = "preserved"',
1686
+ "",
1687
+ ].join("\n");
1688
+ const expected = ["[user.after]", 'value = "preserved"', ""].join("\n");
1689
+ assert.deepEqual(stripExistingOmxBlocks(fixture), { cleaned: expected, removed: 1 });
1690
+ assert.doesNotThrow(() => buildMergedConfig(fixture, "/tmp/omx"));
1691
+ });
1692
+ it("removes an exact inline state entry within a hooks.state table", () => {
1693
+ const hooksPath = "/tmp/codex/hooks.json";
1694
+ const managedTrustState = buildManagedCodexHookTrustState(hooksPath, "/tmp/omx");
1695
+ const key = `${hooksPath}:post_compact:0:0`;
1696
+ const hash = managedTrustState[key]?.trusted_hash;
1697
+ assert.ok(hash);
1698
+ const inline = [
1699
+ "[hooks.state]",
1700
+ `"${key}" = { trusted_hash = "${hash}" }`,
1701
+ "",
1702
+ "[desktop]",
1703
+ "git-create-pull-request-as-draft = true",
1704
+ "",
1705
+ ].join("\n");
1706
+ const stripped = stripManagedCodexHookTrustState(inline, { managedTrustState });
1707
+ assert.doesNotMatch(stripped, new RegExp(key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")));
1708
+ assert.match(stripped, /^\[hooks\.state\]$/m);
1709
+ assert.match(stripped, /^\[desktop\]$/m);
1710
+ assert.doesNotThrow(() => TOML.parse(stripped));
1711
+ });
1712
+ it("fails closed on multi-field, array-like, and mixed managed trust representations", () => {
1713
+ const hooksPath = "/tmp/codex/hooks.json";
1714
+ const managedTrustState = buildManagedCodexHookTrustState(hooksPath, "/tmp/omx");
1715
+ const key = `${hooksPath}:post_compact:0:0`;
1716
+ const hash = managedTrustState[key]?.trusted_hash;
1717
+ assert.ok(hash);
1718
+ const fixtures = [
1719
+ [
1720
+ `hooks.state."${key}" = { trusted_hash = "${hash}", enabled = false }`,
1721
+ "",
1722
+ ].join("\n"),
1723
+ [
1724
+ `hooks.state."${key}" = [{ trusted_hash = "${hash}" }]`,
1725
+ "",
1726
+ ].join("\n"),
1727
+ [
1728
+ `hooks.state."${key}" = { trusted_hash = "${hash}" }`,
1729
+ `[hooks.state."${key}"]`,
1730
+ `trusted_hash = "${hash}"`,
1731
+ "",
1732
+ ].join("\n"),
1733
+ ];
1734
+ for (const fixture of fixtures) {
1735
+ assert.throws(() => stripManagedCodexHookTrustState(fixture, { managedTrustState }), (error) => error instanceof ManagedCodexHooksPlanError &&
1736
+ error.code === "managed_trust_key_conflict");
1737
+ assert.throws(() => buildMergedConfig(fixture, "/tmp/omx", { managedHookTrustState: managedTrustState }), (error) => error instanceof ManagedCodexHooksPlanError &&
1738
+ error.code === "managed_trust_key_conflict");
1025
1739
  }
1026
1740
  });
1027
1741
  it("does not remove unfenced hook trust-state tables without a proof map", () => {
@@ -1051,31 +1765,138 @@ describe("config generator idempotency (#384)", () => {
1051
1765
  assert.match(repaired, /^enabled = false$/m);
1052
1766
  assertSingleManagedHookTrustState(repaired);
1053
1767
  });
1054
- it("dedupes stale unfenced managed hook trust-state tables during legacy setup refresh", () => {
1768
+ it("preserves managed-looking trust blocks inside multiline TOML values", () => {
1055
1769
  const hooksPath = "/tmp/codex/hooks.json";
1056
- const stalePluginModeConfig = [
1057
- "[features]",
1058
- "codex_hooks = true",
1059
- "goals = true",
1060
- "",
1061
- '[hooks.state."custom:/hooks.json:stop:0:0"]',
1062
- 'trusted_hash = "sha256:user"',
1063
- "enabled = false",
1064
- "",
1065
- // Regression fixture for #2225: plugin-mode state could be left outside
1066
- // the managed fence, then legacy setup appended the same table again.
1067
- '[hooks.state."/tmp/codex/hooks.json:post_compact:0:0"]',
1068
- 'trusted_hash = "sha256:stale-plugin-mode"',
1069
- "",
1770
+ const managedTrustState = buildManagedCodexHookTrustState(hooksPath, "/tmp/omx");
1771
+ const embeddedTrustBlock = [
1772
+ "# OMX-owned Codex hook trust state",
1773
+ "# Trusts only setup-managed native hook wrappers.",
1774
+ ...Object.entries(managedTrustState)
1775
+ .sort(([left], [right]) => left.localeCompare(right))
1776
+ .flatMap(([key, state]) => [
1777
+ `[hooks.state."${key}"]`,
1778
+ `trusted_hash = "${state.trusted_hash}"`,
1779
+ "",
1780
+ ]),
1781
+ "# End OMX-owned Codex hook trust state",
1070
1782
  ].join("\n");
1071
- const refreshed = buildMergedConfig(stalePluginModeConfig, "/tmp/omx", {
1783
+ const multilineBasicValue = `basic = """\n${embeddedTrustBlock}\n"""`;
1784
+ const multilineLiteralValue = `literal = '''\n${embeddedTrustBlock}\n'''`;
1785
+ const config = [multilineBasicValue, "", multilineLiteralValue, ""].join("\n");
1786
+ const first = upsertManagedCodexHookTrustState(config, "/tmp/omx", hooksPath, {
1787
+ managedTrustState,
1788
+ });
1789
+ const refreshed = upsertManagedCodexHookTrustState(first, "/tmp/omx", hooksPath, {
1790
+ managedTrustState,
1791
+ });
1792
+ const stripped = stripManagedCodexHookTrustState(refreshed, { managedTrustState });
1793
+ assert.equal(refreshed, first, "the real managed block should still be replaced idempotently");
1794
+ for (const preserved of [first, stripped]) {
1795
+ assert.ok(preserved.includes(multilineBasicValue));
1796
+ assert.ok(preserved.includes(multilineLiteralValue));
1797
+ }
1798
+ const parsed = TOML.parse(stripped);
1799
+ assert.equal(parsed.basic, `${embeddedTrustBlock}\n`);
1800
+ assert.equal(parsed.literal, `${embeddedTrustBlock}\n`);
1801
+ assert.equal(parsed.hooks, undefined, "only the real managed trust tables should be stripped");
1802
+ const unterminatedMultilineValue = multilineBasicValue.slice(0, -3);
1803
+ assert.equal(stripManagedCodexHookTrustState(unterminatedMultilineValue, { managedTrustState }), unterminatedMultilineValue, "an ambiguous unterminated multiline value must not lose managed-looking content");
1804
+ });
1805
+ it("uses strict final hook coordinates for precomputed managed trust", () => {
1806
+ const hooksPath = "/tmp/codex/hooks.json";
1807
+ const foreignGroup = {
1808
+ hooks: [
1809
+ {
1810
+ type: "command",
1811
+ command: "/usr/bin/python3 /tmp/user-hook.py",
1812
+ timeout: 5,
1813
+ },
1814
+ ],
1815
+ };
1816
+ const existingHooks = JSON.stringify({
1817
+ hooks: Object.fromEntries(MANAGED_HOOK_EVENTS.map((eventName) => [eventName, [foreignGroup]])),
1818
+ });
1819
+ const plan = planManagedCodexHooksMerge(existingHooks, "/tmp/omx", hooksPath);
1820
+ assert.equal(plan.ok, true);
1821
+ if (!plan.ok)
1822
+ return;
1823
+ const refreshed = buildMergedConfig("", "/tmp/omx", {
1072
1824
  codexHooksFile: hooksPath,
1825
+ managedHookTrustState: plan.finalTrustState,
1826
+ priorManagedHookTrustState: plan.priorTrustState,
1827
+ legacyHookTrustState: plan.legacyTrustState,
1073
1828
  });
1074
- assert.doesNotThrow(() => TOML.parse(refreshed));
1075
- assert.equal(count(refreshed, /^\[hooks\.state\."\/tmp\/codex\/hooks\.json:post_compact:0:0"\]$/gm), 1, "legacy refresh should replace the stale plugin-mode managed hook state");
1076
- assert.match(refreshed, /^\[hooks\.state\."custom:\/hooks\.json:stop:0:0"\]$/m, "unrelated user hook state should be preserved");
1829
+ const parsed = TOML.parse(refreshed);
1830
+ const trustKeys = Object.keys(parsed.hooks?.state ?? {}).sort();
1831
+ assert.deepEqual(trustKeys, Object.keys(plan.finalTrustState).sort());
1832
+ for (const eventName of MANAGED_HOOK_EVENTS) {
1833
+ const eventLabel = eventName
1834
+ .replace(/([A-Z])/g, "_$1")
1835
+ .toLowerCase()
1836
+ .replace(/^_/, "");
1837
+ assert.ok(trustKeys.some((key) => key.includes(`:${eventLabel}:1:0`)), `${eventName} should retain the actual final group index after foreign hooks`);
1838
+ }
1839
+ });
1840
+ it("migrates exact legacy hooks.json state in the same precomputed config plan", () => {
1841
+ const hooksPath = "/tmp/codex/hooks.json";
1842
+ const plan = planManagedCodexHooksMerge(JSON.stringify({
1843
+ state: {
1844
+ "legacy:/hooks.json:stop:0:0": {
1845
+ trusted_hash: "sha256:legacy",
1846
+ enabled: false,
1847
+ },
1848
+ },
1849
+ hooks: {},
1850
+ }), "/tmp/omx", hooksPath);
1851
+ assert.equal(plan.ok, true);
1852
+ if (!plan.ok)
1853
+ return;
1854
+ const refreshed = buildMergedConfig("", "/tmp/omx", {
1855
+ codexHooksFile: hooksPath,
1856
+ managedHookTrustState: plan.finalTrustState,
1857
+ priorManagedHookTrustState: plan.priorTrustState,
1858
+ legacyHookTrustState: plan.legacyTrustState,
1859
+ });
1860
+ assert.doesNotMatch(plan.finalContent ?? "", /"state"/);
1861
+ assert.match(refreshed, /^\[hooks\.state\."legacy:\/hooks\.json:stop:0:0"\]$/m);
1862
+ assert.match(refreshed, /^trusted_hash = "sha256:legacy"$/m);
1077
1863
  assert.match(refreshed, /^enabled = false$/m);
1078
- assertSingleManagedHookTrustState(refreshed);
1864
+ });
1865
+ it("migrates a legacy __proto__ trust key into config.toml", () => {
1866
+ const plan = planManagedCodexHooksMerge('{"state":{"__proto__":{"trusted_hash":"sha256:legacy","enabled":true}}}', "/tmp/omx", "/tmp/codex/hooks.json");
1867
+ assert.equal(plan.ok, true);
1868
+ if (!plan.ok)
1869
+ return;
1870
+ const config = buildMergedConfig("", "/tmp/omx", {
1871
+ legacyHookTrustState: plan.legacyTrustState,
1872
+ });
1873
+ assert.match(config, /^\[hooks\.state\."__proto__"\]$/m);
1874
+ assert.match(config, /^trusted_hash = "sha256:legacy"$/m);
1875
+ assert.match(config, /^enabled = true$/m);
1876
+ });
1877
+ it("coalesces matching legacy hook trust config tables and rejects hash or enabled collisions", () => {
1878
+ const key = "legacy:/hooks.json:stop:0:0";
1879
+ const legacyHookTrustState = {
1880
+ [key]: { trusted_hash: "sha256:legacy", enabled: false },
1881
+ };
1882
+ const matchingConfig = [
1883
+ `[hooks.state."${key}"]`,
1884
+ 'trusted_hash = "sha256:legacy"',
1885
+ "enabled = false",
1886
+ "",
1887
+ ].join("\n");
1888
+ const matching = buildMergedConfig(matchingConfig, "/tmp/omx", {
1889
+ legacyHookTrustState,
1890
+ });
1891
+ assert.equal(count(matching, /^\[hooks\.state\."legacy:\/hooks\.json:stop:0:0"\]$/gm), 1);
1892
+ for (const conflictingConfig of [
1893
+ matchingConfig.replace("sha256:legacy", "sha256:other"),
1894
+ matchingConfig.replace("enabled = false", "enabled = true"),
1895
+ matchingConfig.replace("\nenabled = false", ""),
1896
+ ]) {
1897
+ assert.throws(() => buildMergedConfig(conflictingConfig, "/tmp/omx", { legacyHookTrustState }), (error) => error instanceof ManagedCodexHooksPlanError &&
1898
+ error.code === "managed_trust_key_conflict");
1899
+ }
1079
1900
  });
1080
1901
  it("syncs shared MCP registry entries in a dedicated managed block", async () => {
1081
1902
  const wd = await mkdtemp(join(tmpdir(), "omx-idem-"));
@@ -1243,4 +2064,330 @@ describe("config generator idempotency (#384)", () => {
1243
2064
  }
1244
2065
  });
1245
2066
  });
2067
+ describe("project trust sync ownership reconciliation (#3199)", () => {
2068
+ const markerStart = "# OMX-synced Codex project trust state (from runtime CODEX_HOME)";
2069
+ const markerEnd = "# End OMX-synced Codex project trust state";
2070
+ it("retains an equal external project family byte-for-byte and removes only its fenced duplicate", () => {
2071
+ const key = "/tmp/외부.project";
2072
+ const external = `[projects."${key}"] # retain this spelling\ntrust_level = "trusted"\n\n[projects."${key}".child]\nanswer = 42\n`;
2073
+ const durable = `${external}\n${markerStart}\n[projects."${key}"]\ntrust_level = "trusted"\n\n[projects."${key}".child]\nanswer = 42\n${markerEnd}\n`;
2074
+ const runtime = `[projects."${key}"]\ntrust_level = "trusted"\n\n[projects."${key}".child]\nanswer = 42\n`;
2075
+ const synced = syncProjectScopeTrustStateFromRuntime(durable, runtime, "/tmp/hooks.json");
2076
+ assert.equal(synced, external);
2077
+ assert.deepEqual(TOML.parse(synced), TOML.parse(external));
2078
+ assert.equal(syncProjectScopeTrustStateFromRuntime(synced, runtime, "/tmp/hooks.json"), synced);
2079
+ assert.equal(syncProjectScopeTrustStateFromRuntime(synced, runtime, "/tmp/hooks.json"), synced);
2080
+ });
2081
+ it("fails closed atomically for mismatched, nested, and ambiguous project source", () => {
2082
+ const runtime = '[projects."/tmp/a"]\ntrust_level = "trusted"\n\n[projects."/tmp/b"]\ntrust_level = "trusted"\n';
2083
+ const mismatch = '[projects."/tmp/a"]\ntrust_level = "untrusted"\n';
2084
+ assert.equal(syncProjectScopeTrustStateFromRuntime(mismatch, runtime, "/tmp/hooks.json"), mismatch);
2085
+ const nested = '[projects."/tmp/a"]\n[projects."/tmp/a".child]\nvalue = "x"\n';
2086
+ assert.equal(syncProjectScopeTrustStateFromRuntime("", nested, "/tmp/hooks.json"), "");
2087
+ const duplicateMarker = `${markerStart}\n${markerEnd}\n${markerStart}\n${markerEnd}\n`;
2088
+ assert.equal(syncProjectScopeTrustStateFromRuntime(duplicateMarker, runtime, "/tmp/hooks.json"), duplicateMarker);
2089
+ });
2090
+ it("preserves BOM and hook-only marker state through launch repair", () => {
2091
+ const hook = "/tmp/hooks.json:pre_tool_use:0:0";
2092
+ const runtime = `[hooks.state."${hook}"]\ntrusted_hash = "sha256:ok"\n`;
2093
+ const bom = `\uFEFF# non-ascii: 외부\r\n`;
2094
+ const synced = syncProjectScopeTrustStateFromRuntime(bom, runtime, "/tmp/hooks.json");
2095
+ assert.ok(synced.startsWith("\uFEFF# non-ascii: 외부\r\n"));
2096
+ assert.ok(synced.includes(`${markerStart}\r\n`));
2097
+ assert.doesNotThrow(() => TOML.parse(synced.slice(1)));
2098
+ assert.equal(repairProjectScopeTrustStateForLaunch(synced, "/tmp/hooks.json"), synced);
2099
+ });
2100
+ it("preserves unrelated external tables and distinguishes quoted dotted keys from dotted paths", () => {
2101
+ const target = "org.example/project";
2102
+ const external = [
2103
+ "# user-owned leading comment",
2104
+ `[projects.'${target}']`,
2105
+ 'trust_level = "trusted" # user-owned trailing comment',
2106
+ "",
2107
+ '[projects."other.key"]',
2108
+ 'trust_level = "untrusted"',
2109
+ "",
2110
+ "[user.section]",
2111
+ 'note = "keep"',
2112
+ "",
2113
+ ].join("\n");
2114
+ const runtime = `[projects."${target}"]\ntrust_level = "trusted"\n`;
2115
+ const synced = syncProjectScopeTrustStateFromRuntime(external, runtime, "/tmp/hooks.json");
2116
+ assert.equal(synced, external);
2117
+ assert.equal(TOML.parse(synced).projects["other.key"] !== undefined, true);
2118
+ });
2119
+ it("normal sync removes semantically empty markers while launch repair leaves them untouched", () => {
2120
+ const empty = `${markerStart}\n${markerEnd}\n`;
2121
+ assert.equal(syncProjectScopeTrustStateFromRuntime(empty, "", "/tmp/hooks.json"), "\n");
2122
+ const comments = `${markerStart}\n# OMX payload comment\n\n${markerEnd}\n`;
2123
+ assert.equal(syncProjectScopeTrustStateFromRuntime(comments, "", "/tmp/hooks.json"), "\n");
2124
+ assert.equal(repairProjectScopeTrustStateForLaunch(comments, "/tmp/hooks.json"), comments);
2125
+ const hook = "/tmp/hooks.json:post_tool_use:0:0";
2126
+ const runtime = `[hooks.state."${hook}"]\ntrusted_hash = "sha256:hook"\n`;
2127
+ const hookOnly = syncProjectScopeTrustStateFromRuntime(comments, runtime, "/tmp/hooks.json");
2128
+ assert.ok(hookOnly.includes(markerStart));
2129
+ assert.match(hookOnly, new RegExp(`^\\[hooks\\.state\\."${escapeRegExp(hook)}"\\]$`, "m"));
2130
+ assert.equal(syncProjectScopeTrustStateFromRuntime(hookOnly, runtime, "/tmp/hooks.json"), hookOnly);
2131
+ });
2132
+ it("keeps project and hook mutation atomic across runtime shape and multi-key failures", () => {
2133
+ const hook = "/tmp/hooks.json:pre_tool_use:0:0";
2134
+ const hookRuntime = `[hooks.state."${hook}"]\ntrusted_hash = "sha256:hook"\n`;
2135
+ const nonRecord = `projects = "invalid"\n\n${hookRuntime}`;
2136
+ assert.equal(syncProjectScopeTrustStateFromRuntime("# durable\n", nonRecord, "/tmp/hooks.json"), "# durable\n");
2137
+ const mixed = `${hookRuntime}\n[projects."/safe"]\ntrust_level = "trusted"\n\n[projects."/unsafe"]\n[projects."/unsafe".child]\nvalue = "nested"\n`;
2138
+ const durable = '[projects."/unsafe"]\ntrust_level = "untrusted"\n';
2139
+ assert.equal(syncProjectScopeTrustStateFromRuntime(durable, mixed, "/tmp/hooks.json"), durable);
2140
+ const absentProjects = syncProjectScopeTrustStateFromRuntime("", hookRuntime, "/tmp/hooks.json");
2141
+ assert.match(absentProjects, /hooks\.state/);
2142
+ const emptyProjects = syncProjectScopeTrustStateFromRuntime("", `projects = {}\n\n${hookRuntime}`, "/tmp/hooks.json");
2143
+ assert.match(emptyProjects, /hooks\.state/);
2144
+ });
2145
+ it("accepts nested external retention but refuses nested rendering and malformed project forms", () => {
2146
+ const nested = '[projects."/nested"]\ntrust_level = "trusted"\n\n[projects."/nested".child]\nvalue = "x"\n';
2147
+ assert.equal(syncProjectScopeTrustStateFromRuntime(nested, nested, "/tmp/hooks.json"), nested);
2148
+ assert.equal(syncProjectScopeTrustStateFromRuntime("", nested, "/tmp/hooks.json"), "");
2149
+ for (const unsafe of [
2150
+ 'projects = { "/inline" = { trust_level = "trusted" } }\n',
2151
+ '[projects]\n"/dotted".trust_level = "trusted"\n',
2152
+ '[[projects."/array"]]\ntrust_level = "trusted"\n',
2153
+ '[projects."/bad"\ntrust_level = "trusted"\n',
2154
+ ]) {
2155
+ assert.equal(syncProjectScopeTrustStateFromRuntime(unsafe, '[projects."/bad"]\ntrust_level = "trusted"\n', "/tmp/hooks.json"), unsafe);
2156
+ }
2157
+ });
2158
+ it("fails closed for equal external state with parent-only dotted or inline nested marker bodies", () => {
2159
+ const external = '[projects."/nested"]\nchild.value = "x"\n';
2160
+ const runtime = '[projects."/nested"]\n[projects."/nested".child]\nvalue = "x"\n';
2161
+ const inlineExternal = '[projects."/nested"]\nchild = { value = "x" }\n';
2162
+ const inlineRuntime = '[projects."/nested"]\n[projects."/nested".child]\nvalue = "x"\n';
2163
+ for (const [outside, source] of [
2164
+ [external, external],
2165
+ [inlineExternal, inlineExternal],
2166
+ ]) {
2167
+ const durable = `${outside}\n${markerStart}\n${source}${markerEnd}\n`;
2168
+ assert.equal(syncProjectScopeTrustStateFromRuntime(durable, source === external ? runtime : inlineRuntime, "/tmp/hooks.json"), durable);
2169
+ assert.equal(repairProjectScopeTrustStateForLaunch(durable, "/tmp/hooks.json"), durable);
2170
+ }
2171
+ });
2172
+ it("fails closed for implicit nested runtime and external sources before any hook mutation", () => {
2173
+ const hook = "/tmp/hooks.json:post_tool_use:0:0";
2174
+ const hookRuntime = `[hooks.state."${hook}"]\ntrusted_hash = "sha256:hook"\n`;
2175
+ const durable = '# durable bytes\n';
2176
+ for (const runtime of [
2177
+ '[projects."/dotted"]\nchild.value = "x"\n',
2178
+ '[projects."/inline"]\nchild = { value = "x" }\n',
2179
+ ]) {
2180
+ assert.equal(syncProjectScopeTrustStateFromRuntime(durable, `${runtime}\n${hookRuntime}`, "/tmp/hooks.json"), durable);
2181
+ }
2182
+ for (const external of [
2183
+ '[projects."/dotted"]\nchild.value = "x"\n',
2184
+ '[projects."/inline"]\nchild = { value = "x" }\n',
2185
+ ]) {
2186
+ assert.equal(syncProjectScopeTrustStateFromRuntime(external, `${external}\n${hookRuntime}`, "/tmp/hooks.json"), external);
2187
+ }
2188
+ });
2189
+ it("preserves managed marker headers and assignments with inline comments exactly", () => {
2190
+ const hook = "/tmp/hooks.json:pre_tool_use:0:0";
2191
+ const runtime = `[projects."/commented"]\ntrust_level = "trusted"\n\n[hooks.state."${hook}"]\ntrusted_hash = "sha256:hook"\n`;
2192
+ for (const managedLine of [
2193
+ '[projects."/commented"] # user-owned header comment',
2194
+ 'trust_level = "trusted" # user-owned assignment comment',
2195
+ ]) {
2196
+ const durable = `${markerStart}\n${managedLine}\n${managedLine.startsWith("[") ? 'trust_level = "trusted"' : '[projects."/commented"]'}\n${markerEnd}\n`;
2197
+ assert.equal(syncProjectScopeTrustStateFromRuntime(durable, runtime, "/tmp/hooks.json"), durable);
2198
+ assert.equal(repairProjectScopeTrustStateForLaunch(durable, "/tmp/hooks.json"), durable);
2199
+ }
2200
+ const inlineMarker = `${markerStart} # user-owned marker comment\n${markerEnd}\n`;
2201
+ assert.equal(syncProjectScopeTrustStateFromRuntime(inlineMarker, runtime, "/tmp/hooks.json"), inlineMarker);
2202
+ });
2203
+ it("fails closed rather than moving comments before or between table assignments", () => {
2204
+ const durable = [
2205
+ markerStart,
2206
+ '[projects."/commented"]',
2207
+ "# before trust level",
2208
+ 'trust_level = "trusted"',
2209
+ "# between assignments",
2210
+ 'approval = "trusted"',
2211
+ markerEnd,
2212
+ "",
2213
+ ].join("\n");
2214
+ const runtime = '[projects."/commented"]\ntrust_level = "trusted"\napproval = "trusted"\n';
2215
+ assert.equal(syncProjectScopeTrustStateFromRuntime(durable, runtime, "/tmp/hooks.json"), durable);
2216
+ assert.equal(repairProjectScopeTrustStateForLaunch(durable, "/tmp/hooks.json"), durable);
2217
+ });
2218
+ it("fails closed for noncanonical runtime and marker project source forms", () => {
2219
+ const canonical = '[projects."/safe"]\ntrust_level = "trusted"\n';
2220
+ const runtimeForms = [
2221
+ 'projects = { "/inline" = { trust_level = "trusted" } }\n',
2222
+ '[projects]\n"/dotted".trust_level = "trusted"\n',
2223
+ '[projects]\n"/implicit" = { trust_level = "trusted" }\n',
2224
+ ];
2225
+ for (const runtime of runtimeForms) {
2226
+ assert.equal(syncProjectScopeTrustStateFromRuntime("# durable\n", runtime, "/tmp/hooks.json"), "# durable\n");
2227
+ }
2228
+ for (const payload of runtimeForms) {
2229
+ const durable = `${markerStart}\n${payload}${markerEnd}\n`;
2230
+ assert.equal(syncProjectScopeTrustStateFromRuntime(durable, canonical, "/tmp/hooks.json"), durable);
2231
+ }
2232
+ const malformed = `${markerStart}\n[projects."/bad"\ntrust_level = "trusted"\n${markerEnd}\n`;
2233
+ const noncontiguous = '[projects."/safe"]\ntrust_level = "trusted"\n\n[user]\nkeep = true\n\n[projects."/safe".child]\nvalue = "x"\n';
2234
+ const multiline = 'note = """\n[projects."/not-a-header"]\n"""\n';
2235
+ assert.equal(syncProjectScopeTrustStateFromRuntime(malformed, canonical, "/tmp/hooks.json"), malformed);
2236
+ assert.equal(syncProjectScopeTrustStateFromRuntime(noncontiguous, noncontiguous, "/tmp/hooks.json"), noncontiguous);
2237
+ const multilineSynced = syncProjectScopeTrustStateFromRuntime(multiline, canonical, "/tmp/hooks.json");
2238
+ assert.match(multilineSynced, /^note = """\n\[projects\."\/not-a-header"\]\n"""$/m);
2239
+ assert.equal(syncProjectScopeTrustStateFromRuntime(multilineSynced, canonical, "/tmp/hooks.json"), multilineSynced);
2240
+ });
2241
+ it("preserves zero and multiple durable trailing newlines without accumulating separators", () => {
2242
+ const runtime = '[projects."/eol"]\ntrust_level = "trusted"\n';
2243
+ for (const trailingEols of ["", "\n\n\n", "\r\n\n\r\n"]) {
2244
+ const durable = `# external${trailingEols}`;
2245
+ const synced = syncProjectScopeTrustStateFromRuntime(durable, runtime, "/tmp/hooks.json");
2246
+ const replaced = syncProjectScopeTrustStateFromRuntime(synced, runtime, "/tmp/hooks.json");
2247
+ const removed = syncProjectScopeTrustStateFromRuntime(replaced, "", "/tmp/hooks.json");
2248
+ assert.equal(synced.match(/(?:\r\n|\n)*$/)?.[0], trailingEols);
2249
+ assert.equal(replaced, synced);
2250
+ assert.equal(removed, durable);
2251
+ }
2252
+ });
2253
+ it("repairs an equal external-and-fenced project duplicate without moving external bytes", () => {
2254
+ const external = '# before external\n[projects."/repair"]\ntrust_level = "trusted"\n';
2255
+ const durable = `${external}\n${markerStart}\n[projects."/repair"]\ntrust_level = "trusted"\n${markerEnd}\n`;
2256
+ const repaired = repairProjectScopeTrustStateForLaunch(durable, "/tmp/hooks.json");
2257
+ assert.equal(repaired, external);
2258
+ assert.doesNotThrow(() => TOML.parse(repaired));
2259
+ });
2260
+ it("preserves BOM and escaped project keys through second and third sync", () => {
2261
+ const key = '__omx_project_sync_probe__\\"quoted';
2262
+ const runtime = `[projects."${key.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"]\ntrust_level = "trusted"\n`;
2263
+ const first = syncProjectScopeTrustStateFromRuntime("\uFEFF# é\n", runtime, "/tmp/hooks.json");
2264
+ const second = syncProjectScopeTrustStateFromRuntime(first, runtime, "/tmp/hooks.json");
2265
+ const third = syncProjectScopeTrustStateFromRuntime(second, runtime, "/tmp/hooks.json");
2266
+ const projects = TOML.parse(third.slice(1)).projects;
2267
+ assert.equal(second, first);
2268
+ assert.equal(third, first);
2269
+ assert.deepEqual(Object.keys(projects), [key]);
2270
+ assert.equal(syncProjectScopeTrustStateFromRuntime("# x\uFEFF\n", runtime, "/tmp/hooks.json"), "# x\uFEFF\n");
2271
+ assert.equal(syncProjectScopeTrustStateFromRuntime("\uFEFF\uFEFF# x\n", runtime, "/tmp/hooks.json"), "\uFEFF\uFEFF# x\n");
2272
+ });
2273
+ it("fails closed for unsupported marker tables and assignments", () => {
2274
+ const runtime = '[projects."/safe"]\ntrust_level = "trusted"\n';
2275
+ for (const unsupported of [
2276
+ '[omx_unknown]\nvalue = true\n',
2277
+ 'unknown_managed_assignment = true\n',
2278
+ ]) {
2279
+ const durable = `${markerStart}\n${unsupported}${markerEnd}\n`;
2280
+ assert.equal(syncProjectScopeTrustStateFromRuntime(durable, runtime, "/tmp/hooks.json"), durable);
2281
+ assert.equal(repairProjectScopeTrustStateForLaunch(durable, "/tmp/hooks.json"), durable);
2282
+ }
2283
+ });
2284
+ it("fails closed for descendant-only and hook-interleaved marker project families", () => {
2285
+ const hook = "/tmp/hooks.json:post_tool_use:0:0";
2286
+ const external = [
2287
+ '[projects."/safe"]',
2288
+ 'trust_level = "trusted"',
2289
+ "",
2290
+ '[projects."/safe".child]',
2291
+ 'value = "nested"',
2292
+ "",
2293
+ ].join("\n");
2294
+ const runtime = `${external}[hooks.state."${hook}"]\ntrusted_hash = "sha256:hook"\n`;
2295
+ const descendantOnly = `${external}${markerStart}\n[projects."/safe".child]\nvalue = "nested"\n${markerEnd}\n`;
2296
+ const hookInterleaved = `${external}${markerStart}\n[projects."/safe"]\ntrust_level = "trusted"\n\n[hooks.state."${hook}"]\ntrusted_hash = "sha256:hook"\n\n[projects."/safe".child]\nvalue = "nested"\n${markerEnd}\n`;
2297
+ for (const durable of [descendantOnly, hookInterleaved]) {
2298
+ assert.equal(syncProjectScopeTrustStateFromRuntime(durable, runtime, "/tmp/hooks.json"), durable);
2299
+ assert.equal(repairProjectScopeTrustStateForLaunch(durable, "/tmp/hooks.json"), durable);
2300
+ }
2301
+ });
2302
+ it("inserts new runtime project and hook tables before the trailing owned payload gap", () => {
2303
+ const hook = "/tmp/hooks.json:post_tool_use:0:0";
2304
+ const durable = [
2305
+ markerStart,
2306
+ '[projects."/existing"]',
2307
+ 'trust_level = "trusted"',
2308
+ "# trailing owned gap",
2309
+ "",
2310
+ markerEnd,
2311
+ "",
2312
+ ].join("\n");
2313
+ const runtime = [
2314
+ '[projects."/existing"]',
2315
+ 'trust_level = "trusted"',
2316
+ "",
2317
+ '[projects."/new"]',
2318
+ 'trust_level = "trusted"',
2319
+ "",
2320
+ `[hooks.state."${hook}"]`,
2321
+ 'trusted_hash = "sha256:hook"',
2322
+ "",
2323
+ ].join("\n");
2324
+ const expected = [
2325
+ markerStart,
2326
+ '[projects."/existing"]',
2327
+ 'trust_level = "trusted"',
2328
+ '[projects."/new"]',
2329
+ 'trust_level = "trusted"',
2330
+ `[hooks.state."${hook}"]`,
2331
+ 'trusted_hash = "sha256:hook"',
2332
+ "# trailing owned gap",
2333
+ "",
2334
+ markerEnd,
2335
+ "",
2336
+ ].join("\n");
2337
+ const synced = syncProjectScopeTrustStateFromRuntime(durable, runtime, "/tmp/hooks.json");
2338
+ assert.equal(synced, expected);
2339
+ assert.equal(syncProjectScopeTrustStateFromRuntime(synced, runtime, "/tmp/hooks.json"), synced);
2340
+ });
2341
+ it("preserves owned payload gaps while removing first, middle, and last project duplicates", () => {
2342
+ const hook = "/tmp/hooks.json:post_tool_use:0:0";
2343
+ const external = [
2344
+ '[projects."/first"]',
2345
+ 'trust_level = "trusted"',
2346
+ "",
2347
+ '[projects."/middle"]',
2348
+ 'trust_level = "trusted"',
2349
+ "",
2350
+ '[projects."/last"]',
2351
+ 'trust_level = "trusted"',
2352
+ "",
2353
+ ].join("\n");
2354
+ const runtime = `${external}[hooks.state."${hook}"]\ntrusted_hash = "sha256:hook"\n`;
2355
+ const durable = [
2356
+ external,
2357
+ markerStart,
2358
+ "# before first",
2359
+ '[projects."/first"]',
2360
+ 'trust_level = "trusted"',
2361
+ "# between first and middle",
2362
+ '[projects."/middle"]',
2363
+ 'trust_level = "trusted"',
2364
+ "# between middle and hook",
2365
+ `[hooks.state."${hook}"]`,
2366
+ 'trusted_hash = "sha256:hook"',
2367
+ "# between hook and last",
2368
+ '[projects."/last"]',
2369
+ 'trust_level = "trusted"',
2370
+ "# after last",
2371
+ markerEnd,
2372
+ "",
2373
+ ].join("\n");
2374
+ const expected = [
2375
+ external,
2376
+ markerStart,
2377
+ "# before first",
2378
+ "# between first and middle",
2379
+ "# between middle and hook",
2380
+ `[hooks.state."${hook}"]`,
2381
+ 'trusted_hash = "sha256:hook"',
2382
+ "# between hook and last",
2383
+ "# after last",
2384
+ markerEnd,
2385
+ "",
2386
+ ].join("\n");
2387
+ const synced = syncProjectScopeTrustStateFromRuntime(durable, runtime, "/tmp/hooks.json");
2388
+ assert.equal(synced, expected);
2389
+ assert.equal(syncProjectScopeTrustStateFromRuntime(synced, runtime, "/tmp/hooks.json"), synced);
2390
+ assert.doesNotThrow(() => TOML.parse(synced));
2391
+ });
2392
+ });
1246
2393
  //# sourceMappingURL=generator-idempotent.test.js.map