oh-my-codex 0.20.1 → 0.20.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (654) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +10 -1
  4. package/dist/adapt/__tests__/hermes.test.js.map +1 -1
  5. package/dist/adapt/contracts.d.ts.map +1 -1
  6. package/dist/agents/native-config.js.map +1 -1
  7. package/dist/auth/__tests__/hotswap.test.d.ts +2 -0
  8. package/dist/auth/__tests__/hotswap.test.d.ts.map +1 -0
  9. package/dist/auth/__tests__/hotswap.test.js +183 -0
  10. package/dist/auth/__tests__/hotswap.test.js.map +1 -0
  11. package/dist/auth/hotswap.d.ts.map +1 -1
  12. package/dist/auth/hotswap.js +17 -4
  13. package/dist/auth/hotswap.js.map +1 -1
  14. package/dist/auth/paths.js.map +1 -1
  15. package/dist/auth/storage.js.map +1 -1
  16. package/dist/autopilot/__tests__/ralplan-gate.test.js +4 -4
  17. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
  18. package/dist/autopilot/completion-gate.js.map +1 -1
  19. package/dist/autopilot/fsm.d.ts +1 -1
  20. package/dist/autopilot/fsm.d.ts.map +1 -1
  21. package/dist/autoresearch/goal.js.map +1 -1
  22. package/dist/autoresearch/runtime.js.map +1 -1
  23. package/dist/capabilities/lockfile.js.map +1 -1
  24. package/dist/catalog/reader.js.map +1 -1
  25. package/dist/catalog/skill-mirror.js.map +1 -1
  26. package/dist/cli/__tests__/adapt-help.test.js.map +1 -1
  27. package/dist/cli/__tests__/agents-init.test.js.map +1 -1
  28. package/dist/cli/__tests__/agents.test.js.map +1 -1
  29. package/dist/cli/__tests__/api.test.js.map +1 -1
  30. package/dist/cli/__tests__/ask.test.js.map +1 -1
  31. package/dist/cli/__tests__/auth.test.js.map +1 -1
  32. package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
  33. package/dist/cli/__tests__/autoresearch.test.js.map +1 -1
  34. package/dist/cli/__tests__/capabilities.test.js.map +1 -1
  35. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  36. package/dist/cli/__tests__/doctor-context-window-warning.test.js.map +1 -1
  37. package/dist/cli/__tests__/doctor-invalid-config.test.js +47 -4
  38. package/dist/cli/__tests__/doctor-invalid-config.test.js.map +1 -1
  39. package/dist/cli/__tests__/doctor-team.test.js.map +1 -1
  40. package/dist/cli/__tests__/doctor-warning-copy.test.js +650 -15
  41. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  42. package/dist/cli/__tests__/exec.test.js.map +1 -1
  43. package/dist/cli/__tests__/imagegen-continuation.test.js.map +1 -1
  44. package/dist/cli/__tests__/index.test.js +216 -11
  45. package/dist/cli/__tests__/index.test.js.map +1 -1
  46. package/dist/cli/__tests__/launch-fallback.test.js +49 -3
  47. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  48. package/dist/cli/__tests__/mcp-parity.test.js +84 -12
  49. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  50. package/dist/cli/__tests__/native-hook-claim-journal.test.d.ts +2 -0
  51. package/dist/cli/__tests__/native-hook-claim-journal.test.d.ts.map +1 -0
  52. package/dist/cli/__tests__/native-hook-claim-journal.test.js +128 -0
  53. package/dist/cli/__tests__/native-hook-claim-journal.test.js.map +1 -0
  54. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  55. package/dist/cli/__tests__/question.test.js.map +1 -1
  56. package/dist/cli/__tests__/ralph-deslop-contract.test.js.map +1 -1
  57. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +6 -0
  58. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  59. package/dist/cli/__tests__/ralph-prd-deep-interview.test.js.map +1 -1
  60. package/dist/cli/__tests__/ralph-prd-smoke.test.js.map +1 -1
  61. package/dist/cli/__tests__/ralph.test.js +8 -3
  62. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  63. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.d.ts +2 -0
  64. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.d.ts.map +1 -0
  65. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js +187 -0
  66. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js.map +1 -0
  67. package/dist/cli/__tests__/ralplan.test.d.ts +2 -0
  68. package/dist/cli/__tests__/ralplan.test.d.ts.map +1 -0
  69. package/dist/cli/__tests__/ralplan.test.js +129 -0
  70. package/dist/cli/__tests__/ralplan.test.js.map +1 -0
  71. package/dist/cli/__tests__/resume.test.js +6 -1
  72. package/dist/cli/__tests__/resume.test.js.map +1 -1
  73. package/dist/cli/__tests__/session-scoped-runtime.test.js +25 -0
  74. package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
  75. package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
  76. package/dist/cli/__tests__/session-search.test.js.map +1 -1
  77. package/dist/cli/__tests__/setup-agents-overwrite.test.js +79 -0
  78. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  79. package/dist/cli/__tests__/setup-gh-star.test.js.map +1 -1
  80. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +77 -3
  81. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +1 -1
  82. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.d.ts +2 -0
  83. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.d.ts.map +1 -0
  84. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.js +285 -0
  85. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.js.map +1 -0
  86. package/dist/cli/__tests__/setup-install-mode.test.js +2261 -14
  87. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  88. package/dist/cli/__tests__/setup-preferences.test.d.ts +2 -0
  89. package/dist/cli/__tests__/setup-preferences.test.d.ts.map +1 -0
  90. package/dist/cli/__tests__/setup-preferences.test.js +79 -0
  91. package/dist/cli/__tests__/setup-preferences.test.js.map +1 -0
  92. package/dist/cli/__tests__/setup-refresh.test.js +33 -2
  93. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  94. package/dist/cli/__tests__/setup-scope.test.js +46 -3
  95. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  96. package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
  97. package/dist/cli/__tests__/state.test.js +37 -0
  98. package/dist/cli/__tests__/state.test.js.map +1 -1
  99. package/dist/cli/__tests__/team.test.js.map +1 -1
  100. package/dist/cli/__tests__/ultragoal.test.js +61 -1
  101. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  102. package/dist/cli/__tests__/uninstall.test.js +1771 -24
  103. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  104. package/dist/cli/__tests__/update.test.js +58 -0
  105. package/dist/cli/__tests__/update.test.js.map +1 -1
  106. package/dist/cli/__tests__/url.test.js.map +1 -1
  107. package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
  108. package/dist/cli/adapt.js.map +1 -1
  109. package/dist/cli/agents-init.js.map +1 -1
  110. package/dist/cli/agents.js.map +1 -1
  111. package/dist/cli/api.js.map +1 -1
  112. package/dist/cli/ask.d.ts +1 -1
  113. package/dist/cli/ask.d.ts.map +1 -1
  114. package/dist/cli/ask.js.map +1 -1
  115. package/dist/cli/auth.js.map +1 -1
  116. package/dist/cli/autoresearch-guided.js.map +1 -1
  117. package/dist/cli/autoresearch-intake.js.map +1 -1
  118. package/dist/cli/capabilities.js.map +1 -1
  119. package/dist/cli/cleanup.js.map +1 -1
  120. package/dist/cli/codex-feature-probe.js.map +1 -1
  121. package/dist/cli/codex-home.js.map +1 -1
  122. package/dist/cli/doctor.d.ts +18 -0
  123. package/dist/cli/doctor.d.ts.map +1 -1
  124. package/dist/cli/doctor.js +594 -130
  125. package/dist/cli/doctor.js.map +1 -1
  126. package/dist/cli/explore.js.map +1 -1
  127. package/dist/cli/index.d.ts +11 -3
  128. package/dist/cli/index.d.ts.map +1 -1
  129. package/dist/cli/index.js +108 -20
  130. package/dist/cli/index.js.map +1 -1
  131. package/dist/cli/mcp-serve.js.map +1 -1
  132. package/dist/cli/mission.js.map +1 -1
  133. package/dist/cli/native-assets.js.map +1 -1
  134. package/dist/cli/native-hook-claim-journal.d.ts +18 -0
  135. package/dist/cli/native-hook-claim-journal.d.ts.map +1 -0
  136. package/dist/cli/native-hook-claim-journal.js +239 -0
  137. package/dist/cli/native-hook-claim-journal.js.map +1 -0
  138. package/dist/cli/omx.js.map +1 -1
  139. package/dist/cli/plugin-marketplace.js.map +1 -1
  140. package/dist/cli/project-runtime-codex-homes.js.map +1 -1
  141. package/dist/cli/question.js.map +1 -1
  142. package/dist/cli/ralph.d.ts.map +1 -1
  143. package/dist/cli/ralph.js +3 -2
  144. package/dist/cli/ralph.js.map +1 -1
  145. package/dist/cli/ralplan.d.ts +14 -0
  146. package/dist/cli/ralplan.d.ts.map +1 -0
  147. package/dist/cli/ralplan.js +203 -0
  148. package/dist/cli/ralplan.js.map +1 -0
  149. package/dist/cli/setup-preferences.d.ts +11 -0
  150. package/dist/cli/setup-preferences.d.ts.map +1 -1
  151. package/dist/cli/setup-preferences.js +32 -0
  152. package/dist/cli/setup-preferences.js.map +1 -1
  153. package/dist/cli/setup.d.ts +53 -0
  154. package/dist/cli/setup.d.ts.map +1 -1
  155. package/dist/cli/setup.js +1401 -398
  156. package/dist/cli/setup.js.map +1 -1
  157. package/dist/cli/sparkshell.js.map +1 -1
  158. package/dist/cli/star-prompt.js.map +1 -1
  159. package/dist/cli/state.d.ts.map +1 -1
  160. package/dist/cli/state.js +2 -1
  161. package/dist/cli/state.js.map +1 -1
  162. package/dist/cli/team.js.map +1 -1
  163. package/dist/cli/ultragoal.d.ts +5 -1
  164. package/dist/cli/ultragoal.d.ts.map +1 -1
  165. package/dist/cli/ultragoal.js +34 -2
  166. package/dist/cli/ultragoal.js.map +1 -1
  167. package/dist/cli/uninstall.d.ts +8 -0
  168. package/dist/cli/uninstall.d.ts.map +1 -1
  169. package/dist/cli/uninstall.js +944 -95
  170. package/dist/cli/uninstall.js.map +1 -1
  171. package/dist/cli/update.d.ts.map +1 -1
  172. package/dist/cli/update.js +8 -1
  173. package/dist/cli/update.js.map +1 -1
  174. package/dist/cli/url.js.map +1 -1
  175. package/dist/compat/__tests__/doctor-contract.test.js +5 -1
  176. package/dist/compat/__tests__/doctor-contract.test.js.map +1 -1
  177. package/dist/compat/__tests__/rust-runtime-compat.test.js.map +1 -1
  178. package/dist/config/__tests__/codex-hooks.test.js +1287 -11
  179. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  180. package/dist/config/__tests__/generator-idempotent.test.js +850 -32
  181. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  182. package/dist/config/__tests__/generator-notify.test.js +6 -1
  183. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  184. package/dist/config/codex-feature-flags.d.ts.map +1 -1
  185. package/dist/config/codex-feature-flags.js.map +1 -1
  186. package/dist/config/codex-hooks.d.ts +124 -1
  187. package/dist/config/codex-hooks.d.ts.map +1 -1
  188. package/dist/config/codex-hooks.js +2142 -265
  189. package/dist/config/codex-hooks.js.map +1 -1
  190. package/dist/config/commit-lore-guard.js.map +1 -1
  191. package/dist/config/generator.d.ts +21 -16
  192. package/dist/config/generator.d.ts.map +1 -1
  193. package/dist/config/generator.js +991 -137
  194. package/dist/config/generator.js.map +1 -1
  195. package/dist/config/mcp-registry.js.map +1 -1
  196. package/dist/config/models.d.ts +2 -2
  197. package/dist/config/models.d.ts.map +1 -1
  198. package/dist/config/models.js.map +1 -1
  199. package/dist/config/omx-first-party-mcp.js.map +1 -1
  200. package/dist/config/team-mode.d.ts.map +1 -1
  201. package/dist/document-refresh/__tests__/enforcer.test.js.map +1 -1
  202. package/dist/document-refresh/enforcer.js.map +1 -1
  203. package/dist/exec/followup.js.map +1 -1
  204. package/dist/goal-workflows/artifacts.js.map +1 -1
  205. package/dist/hooks/__tests__/agents-overlay.test.js +7 -4
  206. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  207. package/dist/hooks/__tests__/analyze-routing-contract.test.js.map +1 -1
  208. package/dist/hooks/__tests__/analyze-skill-contract.test.js.map +1 -1
  209. package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
  210. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +9 -3
  211. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  212. package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
  213. package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
  214. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  215. package/dist/hooks/__tests__/design-skill.test.js.map +1 -1
  216. package/dist/hooks/__tests__/keyword-detector.test.js +1372 -2
  217. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  218. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +14 -0
  219. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  220. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  221. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +669 -1
  222. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  223. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +2 -0
  224. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
  225. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +142 -1
  226. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  227. package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
  228. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js +48 -2
  229. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js.map +1 -1
  230. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +61 -44
  231. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
  232. package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
  233. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js +3 -2
  234. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js.map +1 -1
  235. package/dist/hooks/__tests__/notify-hook-session-scope.test.js +243 -4
  236. package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
  237. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  238. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +8 -1
  239. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  240. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  241. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  242. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
  243. package/dist/hooks/__tests__/notify-hook-visual-verdict.test.js.map +1 -1
  244. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  245. package/dist/hooks/__tests__/openclaw-setup-contract.test.js.map +1 -1
  246. package/dist/hooks/__tests__/pre-context-gate-skills.test.js.map +1 -1
  247. package/dist/hooks/__tests__/prometheus-strict-contract.test.js.map +1 -1
  248. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js.map +1 -1
  249. package/dist/hooks/__tests__/prompt-guidance-test-helpers.js.map +1 -1
  250. package/dist/hooks/__tests__/prompt-session-provenance.test.d.ts +2 -0
  251. package/dist/hooks/__tests__/prompt-session-provenance.test.d.ts.map +1 -0
  252. package/dist/hooks/__tests__/prompt-session-provenance.test.js +82 -0
  253. package/dist/hooks/__tests__/prompt-session-provenance.test.js.map +1 -0
  254. package/dist/hooks/__tests__/session.test.js +532 -11
  255. package/dist/hooks/__tests__/session.test.js.map +1 -1
  256. package/dist/hooks/__tests__/skill-catalog-hygiene.test.js.map +1 -1
  257. package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.js.map +1 -1
  258. package/dist/hooks/__tests__/visual-ralph-skill.test.js.map +1 -1
  259. package/dist/hooks/__tests__/visual-verdict-loop.test.js.map +1 -1
  260. package/dist/hooks/agents-overlay.js +5 -5
  261. package/dist/hooks/agents-overlay.js.map +1 -1
  262. package/dist/hooks/code-simplifier/index.js.map +1 -1
  263. package/dist/hooks/explore-routing.js.map +1 -1
  264. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  265. package/dist/hooks/extensibility/events.js.map +1 -1
  266. package/dist/hooks/extensibility/loader.js.map +1 -1
  267. package/dist/hooks/extensibility/plugin-runner-stdin.js.map +1 -1
  268. package/dist/hooks/extensibility/sdk/logging.js.map +1 -1
  269. package/dist/hooks/keyword-detector.d.ts +33 -7
  270. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  271. package/dist/hooks/keyword-detector.js +2228 -119
  272. package/dist/hooks/keyword-detector.js.map +1 -1
  273. package/dist/hooks/keyword-registry.d.ts +12 -0
  274. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  275. package/dist/hooks/keyword-registry.js +41 -0
  276. package/dist/hooks/keyword-registry.js.map +1 -1
  277. package/dist/hooks/prompt-session-provenance.d.ts +87 -0
  278. package/dist/hooks/prompt-session-provenance.d.ts.map +1 -0
  279. package/dist/hooks/prompt-session-provenance.js +180 -0
  280. package/dist/hooks/prompt-session-provenance.js.map +1 -0
  281. package/dist/hooks/session.d.ts +139 -34
  282. package/dist/hooks/session.d.ts.map +1 -1
  283. package/dist/hooks/session.js +1239 -317
  284. package/dist/hooks/session.js.map +1 -1
  285. package/dist/hooks/task-size-detector.js.map +1 -1
  286. package/dist/hud/__tests__/state.test.js.map +1 -1
  287. package/dist/hud/authority.js.map +1 -1
  288. package/dist/hud/constants.js.map +1 -1
  289. package/dist/hud/index.js.map +1 -1
  290. package/dist/hud/reconcile.js.map +1 -1
  291. package/dist/hud/render.js.map +1 -1
  292. package/dist/hud/state.js.map +1 -1
  293. package/dist/hud/tmux.js.map +1 -1
  294. package/dist/leader/__tests__/contract.test.js +103 -1
  295. package/dist/leader/__tests__/contract.test.js.map +1 -1
  296. package/dist/leader/contract.d.ts +26 -5
  297. package/dist/leader/contract.d.ts.map +1 -1
  298. package/dist/leader/contract.js +109 -3
  299. package/dist/leader/contract.js.map +1 -1
  300. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  301. package/dist/mcp/__tests__/server-lifecycle.test.js +15 -2
  302. package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
  303. package/dist/mcp/__tests__/state-paths.test.js +98 -1
  304. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  305. package/dist/mcp/bootstrap.js.map +1 -1
  306. package/dist/mcp/code-intel-server.d.ts +58 -58
  307. package/dist/mcp/code-intel-server.d.ts.map +1 -1
  308. package/dist/mcp/code-intel-server.js.map +1 -1
  309. package/dist/mcp/hermes-bridge.js.map +1 -1
  310. package/dist/mcp/hermes-server.d.ts +70 -70
  311. package/dist/mcp/hermes-server.d.ts.map +1 -1
  312. package/dist/mcp/lifecycle-telemetry.js.map +1 -1
  313. package/dist/mcp/memory-server.d.ts +34 -34
  314. package/dist/mcp/memory-server.d.ts.map +1 -1
  315. package/dist/mcp/memory-server.js.map +1 -1
  316. package/dist/mcp/state-paths.d.ts +14 -0
  317. package/dist/mcp/state-paths.d.ts.map +1 -1
  318. package/dist/mcp/state-paths.js +96 -20
  319. package/dist/mcp/state-paths.js.map +1 -1
  320. package/dist/mcp/state-server.d.ts +47 -47
  321. package/dist/mcp/state-server.d.ts.map +1 -1
  322. package/dist/mcp/trace-server.d.ts +4 -4
  323. package/dist/mcp/trace-server.d.ts.map +1 -1
  324. package/dist/mcp/trace-server.js.map +1 -1
  325. package/dist/mcp/wiki-server.d.ts +23 -23
  326. package/dist/mcp/wiki-server.d.ts.map +1 -1
  327. package/dist/mcp/wiki-server.js.map +1 -1
  328. package/dist/modes/__tests__/base-session-scope.test.js +53 -1
  329. package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
  330. package/dist/modes/base.d.ts.map +1 -1
  331. package/dist/modes/base.js +5 -5
  332. package/dist/modes/base.js.map +1 -1
  333. package/dist/notifications/__tests__/lifecycle-dedupe.test.js.map +1 -1
  334. package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
  335. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js +177 -85
  336. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js.map +1 -1
  337. package/dist/notifications/__tests__/session-registry.test.js.map +1 -1
  338. package/dist/notifications/__tests__/template-engine.test.js.map +1 -1
  339. package/dist/notifications/__tests__/verbosity.test.js.map +1 -1
  340. package/dist/notifications/http-client.js.map +1 -1
  341. package/dist/notifications/index.d.ts +4 -1
  342. package/dist/notifications/index.d.ts.map +1 -1
  343. package/dist/notifications/index.js +3 -9
  344. package/dist/notifications/index.js.map +1 -1
  345. package/dist/notifications/lifecycle-dedupe.js.map +1 -1
  346. package/dist/notifications/reply-listener.d.ts.map +1 -1
  347. package/dist/notifications/reply-listener.js.map +1 -1
  348. package/dist/notifications/session-registry.js.map +1 -1
  349. package/dist/notifications/session-status.js.map +1 -1
  350. package/dist/notifications/temp-contract.js.map +1 -1
  351. package/dist/notifications/tmux-detector.js.map +1 -1
  352. package/dist/notifications/tmux.js.map +1 -1
  353. package/dist/openclaw/config.js.map +1 -1
  354. package/dist/openclaw/dispatcher.js.map +1 -1
  355. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  356. package/dist/pipeline/__tests__/stages.test.js +8 -5
  357. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  358. package/dist/pipeline/stages/code-review.js.map +1 -1
  359. package/dist/pipeline/stages/ralph-verify.js.map +1 -1
  360. package/dist/pipeline/stages/ralplan.js.map +1 -1
  361. package/dist/pipeline/stages/team-exec.js.map +1 -1
  362. package/dist/pipeline/stages/ultragoal.js.map +1 -1
  363. package/dist/pipeline/stages/ultraqa.js.map +1 -1
  364. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js.map +1 -1
  365. package/dist/planning/artifact-names.js.map +1 -1
  366. package/dist/planning/artifacts.js.map +1 -1
  367. package/dist/question/__tests__/state.test.js.map +1 -1
  368. package/dist/question/__tests__/ui.test.js.map +1 -1
  369. package/dist/question/autopilot-wait.js.map +1 -1
  370. package/dist/question/client.d.ts.map +1 -1
  371. package/dist/question/client.js.map +1 -1
  372. package/dist/question/deep-interview.js.map +1 -1
  373. package/dist/question/events.js.map +1 -1
  374. package/dist/question/renderer.js.map +1 -1
  375. package/dist/question/state.d.ts.map +1 -1
  376. package/dist/question/state.js.map +1 -1
  377. package/dist/question/ui.js.map +1 -1
  378. package/dist/ralph/contract.d.ts +1 -1
  379. package/dist/ralph/contract.d.ts.map +1 -1
  380. package/dist/ralplan/__tests__/consensus-gate.test.js +403 -8
  381. package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
  382. package/dist/ralplan/__tests__/runtime.test.js +112 -14
  383. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  384. package/dist/ralplan/consensus-gate.d.ts +3 -3
  385. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  386. package/dist/ralplan/consensus-gate.js +155 -60
  387. package/dist/ralplan/consensus-gate.js.map +1 -1
  388. package/dist/ralplan/runtime.d.ts +2 -2
  389. package/dist/ralplan/runtime.d.ts.map +1 -1
  390. package/dist/ralplan/runtime.js +1 -2
  391. package/dist/ralplan/runtime.js.map +1 -1
  392. package/dist/runtime/bridge.d.ts.map +1 -1
  393. package/dist/runtime/bridge.js.map +1 -1
  394. package/dist/runtime/process-tree.js.map +1 -1
  395. package/dist/runtime/run-loop.js.map +1 -1
  396. package/dist/runtime/run-outcome.d.ts +3 -3
  397. package/dist/runtime/run-outcome.d.ts.map +1 -1
  398. package/dist/runtime/run-outcome.js.map +1 -1
  399. package/dist/runtime/run-state.js.map +1 -1
  400. package/dist/scripts/__tests__/codex-native-hook.test.js +4121 -592
  401. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  402. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.d.ts +2 -0
  403. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.d.ts.map +1 -0
  404. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.js +634 -0
  405. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.js.map +1 -0
  406. package/dist/scripts/__tests__/generate-release-body.test.js.map +1 -1
  407. package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -1
  408. package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -1
  409. package/dist/scripts/__tests__/notify-fallback-delivery.test.d.ts +2 -0
  410. package/dist/scripts/__tests__/notify-fallback-delivery.test.d.ts.map +1 -0
  411. package/dist/scripts/__tests__/notify-fallback-delivery.test.js +174 -0
  412. package/dist/scripts/__tests__/notify-fallback-delivery.test.js.map +1 -0
  413. package/dist/scripts/__tests__/notify-state-io.test.js +57 -1
  414. package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
  415. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.d.ts +2 -0
  416. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.d.ts.map +1 -0
  417. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js +205 -0
  418. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js.map +1 -0
  419. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.d.ts +2 -0
  420. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.d.ts.map +1 -0
  421. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js +175 -0
  422. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js.map +1 -0
  423. package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
  424. package/dist/scripts/__tests__/smoke-packed-install.test.js +848 -4
  425. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  426. package/dist/scripts/__tests__/test-reply-listener-live.test.js.map +1 -1
  427. package/dist/scripts/build-api.js.map +1 -1
  428. package/dist/scripts/build-explore-harness.js.map +1 -1
  429. package/dist/scripts/build-sparkshell.js.map +1 -1
  430. package/dist/scripts/cleanup-explore-harness.js.map +1 -1
  431. package/dist/scripts/codex-execution-surface.js.map +1 -1
  432. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  433. package/dist/scripts/codex-native-hook.js +689 -156
  434. package/dist/scripts/codex-native-hook.js.map +1 -1
  435. package/dist/scripts/generate-native-release-manifest.js.map +1 -1
  436. package/dist/scripts/generate-release-body.js.map +1 -1
  437. package/dist/scripts/hook-payload-guard.d.ts.map +1 -1
  438. package/dist/scripts/hook-payload-guard.js.map +1 -1
  439. package/dist/scripts/notify-fallback-delivery.d.ts +48 -0
  440. package/dist/scripts/notify-fallback-delivery.d.ts.map +1 -0
  441. package/dist/scripts/notify-fallback-delivery.js +419 -0
  442. package/dist/scripts/notify-fallback-delivery.js.map +1 -0
  443. package/dist/scripts/notify-fallback-watcher.js +136 -30
  444. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  445. package/dist/scripts/notify-hook/auto-nudge.d.ts +18 -9
  446. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  447. package/dist/scripts/notify-hook/auto-nudge.js +39 -13
  448. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  449. package/dist/scripts/notify-hook/managed-tmux.d.ts +23 -0
  450. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  451. package/dist/scripts/notify-hook/managed-tmux.js +122 -12
  452. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  453. package/dist/scripts/notify-hook/orchestration-intent.d.ts +7 -7
  454. package/dist/scripts/notify-hook/orchestration-intent.d.ts.map +1 -1
  455. package/dist/scripts/notify-hook/ralph-session-resume.d.ts +5 -2
  456. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
  457. package/dist/scripts/notify-hook/ralph-session-resume.js +33 -37
  458. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  459. package/dist/scripts/notify-hook/state-io.d.ts +17 -0
  460. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  461. package/dist/scripts/notify-hook/state-io.js +73 -11
  462. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  463. package/dist/scripts/notify-hook/team-dispatch.d.ts +1 -1
  464. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  465. package/dist/scripts/notify-hook/team-leader-nudge.d.ts +2 -2
  466. package/dist/scripts/notify-hook/team-tmux-guard.d.ts +4 -4
  467. package/dist/scripts/notify-hook/team-worker-posttooluse.js.map +1 -1
  468. package/dist/scripts/notify-hook/team-worker-stop.d.ts +1 -1
  469. package/dist/scripts/notify-hook/team-worker.d.ts +2 -2
  470. package/dist/scripts/notify-hook/tmux-injection.d.ts +8 -1
  471. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  472. package/dist/scripts/notify-hook/tmux-injection.js +25 -16
  473. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  474. package/dist/scripts/notify-hook/visual-verdict.d.ts +1 -1
  475. package/dist/scripts/notify-hook/visual-verdict.d.ts.map +1 -1
  476. package/dist/scripts/notify-hook/visual-verdict.js +15 -13
  477. package/dist/scripts/notify-hook/visual-verdict.js.map +1 -1
  478. package/dist/scripts/notify-hook.d.ts +24 -1
  479. package/dist/scripts/notify-hook.d.ts.map +1 -1
  480. package/dist/scripts/notify-hook.js +266 -90
  481. package/dist/scripts/notify-hook.js.map +1 -1
  482. package/dist/scripts/postinstall.js.map +1 -1
  483. package/dist/scripts/prompt-inventory.js.map +1 -1
  484. package/dist/scripts/smoke-packed-install.d.ts +879 -2
  485. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  486. package/dist/scripts/smoke-packed-install.js +1528 -7
  487. package/dist/scripts/smoke-packed-install.js.map +1 -1
  488. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  489. package/dist/scripts/test-reply-listener-live.js.map +1 -1
  490. package/dist/scripts/test-sparkshell.js.map +1 -1
  491. package/dist/scripts/verify-native-agents.js.map +1 -1
  492. package/dist/session-history/friction.js.map +1 -1
  493. package/dist/session-history/search.js.map +1 -1
  494. package/dist/sidecar/__tests__/boundary.test.js.map +1 -1
  495. package/dist/sidecar/collector.js.map +1 -1
  496. package/dist/sidecar/index.js.map +1 -1
  497. package/dist/sidecar/render.js.map +1 -1
  498. package/dist/sidecar/tmux.js.map +1 -1
  499. package/dist/state/__tests__/operations.test.js +64 -15
  500. package/dist/state/__tests__/operations.test.js.map +1 -1
  501. package/dist/state/__tests__/skill-active.test.js +19 -1
  502. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  503. package/dist/state/__tests__/workflow-transition.test.js +61 -0
  504. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  505. package/dist/state/mode-state-context.js.map +1 -1
  506. package/dist/state/operations.d.ts +1 -1
  507. package/dist/state/operations.d.ts.map +1 -1
  508. package/dist/state/operations.js +49 -55
  509. package/dist/state/operations.js.map +1 -1
  510. package/dist/state/paths.d.ts +1 -1
  511. package/dist/state/paths.d.ts.map +1 -1
  512. package/dist/state/paths.js +1 -1
  513. package/dist/state/paths.js.map +1 -1
  514. package/dist/state/skill-active.d.ts +13 -1
  515. package/dist/state/skill-active.d.ts.map +1 -1
  516. package/dist/state/skill-active.js +43 -4
  517. package/dist/state/skill-active.js.map +1 -1
  518. package/dist/state/terminal-normalization.js.map +1 -1
  519. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  520. package/dist/state/workflow-transition-reconcile.js +20 -4
  521. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  522. package/dist/state/workflow-transition.d.ts +1 -1
  523. package/dist/state/workflow-transition.d.ts.map +1 -1
  524. package/dist/state/workflow-transition.js.map +1 -1
  525. package/dist/subagents/__tests__/adapted-role-binding.test.d.ts +2 -0
  526. package/dist/subagents/__tests__/adapted-role-binding.test.d.ts.map +1 -0
  527. package/dist/subagents/__tests__/adapted-role-binding.test.js +988 -0
  528. package/dist/subagents/__tests__/adapted-role-binding.test.js.map +1 -0
  529. package/dist/subagents/__tests__/leader-bootstrap-3181.test.d.ts +2 -0
  530. package/dist/subagents/__tests__/leader-bootstrap-3181.test.d.ts.map +1 -0
  531. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js +273 -0
  532. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js.map +1 -0
  533. package/dist/subagents/__tests__/tracker.test.js +1550 -10
  534. package/dist/subagents/__tests__/tracker.test.js.map +1 -1
  535. package/dist/subagents/adapted-role-binding.d.ts +17 -0
  536. package/dist/subagents/adapted-role-binding.d.ts.map +1 -0
  537. package/dist/subagents/adapted-role-binding.js +75 -0
  538. package/dist/subagents/adapted-role-binding.js.map +1 -0
  539. package/dist/subagents/role-routing-marker.d.ts +10 -0
  540. package/dist/subagents/role-routing-marker.d.ts.map +1 -0
  541. package/dist/subagents/role-routing-marker.js +136 -0
  542. package/dist/subagents/role-routing-marker.js.map +1 -0
  543. package/dist/subagents/tracker.d.ts +172 -0
  544. package/dist/subagents/tracker.d.ts.map +1 -1
  545. package/dist/subagents/tracker.js +1077 -61
  546. package/dist/subagents/tracker.js.map +1 -1
  547. package/dist/team/__tests__/approved-execution.test.js.map +1 -1
  548. package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
  549. package/dist/team/__tests__/model-contract.test.js +231 -1
  550. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  551. package/dist/team/__tests__/role-router.test.js.map +1 -1
  552. package/dist/team/__tests__/runtime.test.js +401 -1
  553. package/dist/team/__tests__/runtime.test.js.map +1 -1
  554. package/dist/team/__tests__/scaling.test.js +181 -0
  555. package/dist/team/__tests__/scaling.test.js.map +1 -1
  556. package/dist/team/__tests__/state-root.test.js.map +1 -1
  557. package/dist/team/__tests__/tmux-session.test.js +434 -4
  558. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  559. package/dist/team/__tests__/tmux-test-fixture.js.map +1 -1
  560. package/dist/team/api-interop.d.ts +2 -2
  561. package/dist/team/api-interop.d.ts.map +1 -1
  562. package/dist/team/api-interop.js.map +1 -1
  563. package/dist/team/commit-hygiene.js.map +1 -1
  564. package/dist/team/contracts.d.ts +5 -5
  565. package/dist/team/contracts.d.ts.map +1 -1
  566. package/dist/team/delivery-log.js.map +1 -1
  567. package/dist/team/followup-planner.js.map +1 -1
  568. package/dist/team/goal-workflow.js.map +1 -1
  569. package/dist/team/idle-nudge.d.ts.map +1 -1
  570. package/dist/team/model-contract.d.ts +20 -1
  571. package/dist/team/model-contract.d.ts.map +1 -1
  572. package/dist/team/model-contract.js +267 -30
  573. package/dist/team/model-contract.js.map +1 -1
  574. package/dist/team/orchestrator.js.map +1 -1
  575. package/dist/team/pane-status.js.map +1 -1
  576. package/dist/team/phase-controller.js.map +1 -1
  577. package/dist/team/reminder-intents.d.ts +1 -1
  578. package/dist/team/reminder-intents.d.ts.map +1 -1
  579. package/dist/team/reminder-intents.js.map +1 -1
  580. package/dist/team/runtime-cli.js.map +1 -1
  581. package/dist/team/runtime.d.ts.map +1 -1
  582. package/dist/team/runtime.js +83 -52
  583. package/dist/team/runtime.js.map +1 -1
  584. package/dist/team/scaling.d.ts +9 -7
  585. package/dist/team/scaling.d.ts.map +1 -1
  586. package/dist/team/scaling.js +69 -28
  587. package/dist/team/scaling.js.map +1 -1
  588. package/dist/team/state/dispatch-lock.js.map +1 -1
  589. package/dist/team/state/dispatch.js.map +1 -1
  590. package/dist/team/state/events.js.map +1 -1
  591. package/dist/team/state/tasks.js.map +1 -1
  592. package/dist/team/state-root.js.map +1 -1
  593. package/dist/team/state.js.map +1 -1
  594. package/dist/team/team-identity.d.ts.map +1 -1
  595. package/dist/team/team-identity.js.map +1 -1
  596. package/dist/team/tmux-session.d.ts +2 -0
  597. package/dist/team/tmux-session.d.ts.map +1 -1
  598. package/dist/team/tmux-session.js +90 -42
  599. package/dist/team/tmux-session.js.map +1 -1
  600. package/dist/team/worker-bootstrap.js.map +1 -1
  601. package/dist/team/worktree.js.map +1 -1
  602. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  603. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
  604. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  605. package/dist/ultragoal/artifacts.js +4 -1
  606. package/dist/ultragoal/artifacts.js.map +1 -1
  607. package/dist/url-reader/index.js.map +1 -1
  608. package/dist/utils/agents-model-table.js.map +1 -1
  609. package/dist/utils/package.js.map +1 -1
  610. package/dist/utils/paths.js.map +1 -1
  611. package/dist/utils/platform-command.js.map +1 -1
  612. package/dist/utils/repo-deps.js.map +1 -1
  613. package/dist/utils/version.js.map +1 -1
  614. package/dist/verification/__tests__/ci-rust-gates.test.js +1 -1
  615. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
  616. package/dist/verification/verifier.js.map +1 -1
  617. package/dist/visual/constants.d.ts +1 -1
  618. package/dist/visual/constants.d.ts.map +1 -1
  619. package/dist/vscode/__tests__/index.test.js.map +1 -1
  620. package/dist/vscode/index.js.map +1 -1
  621. package/dist/wiki/__tests__/cjk-tokenize.test.js.map +1 -1
  622. package/dist/wiki/__tests__/lint.test.js.map +1 -1
  623. package/dist/wiki/__tests__/query.test.js.map +1 -1
  624. package/dist/wiki/__tests__/storage.test.js.map +1 -1
  625. package/dist/wiki/__tests__/test-helpers.d.ts.map +1 -1
  626. package/dist/wiki/lint.js.map +1 -1
  627. package/dist/wiki/query.js.map +1 -1
  628. package/dist/wiki/storage.js.map +1 -1
  629. package/package.json +2 -2
  630. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  631. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +10 -5
  632. package/plugins/oh-my-codex/skills/ralph/SKILL.md +4 -2
  633. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +5 -2
  634. package/skills/omx-setup/SKILL.md +10 -5
  635. package/skills/ralph/SKILL.md +4 -2
  636. package/skills/ralplan/SKILL.md +5 -2
  637. package/src/scripts/__tests__/codex-native-hook.test.ts +17522 -10630
  638. package/src/scripts/__tests__/fixtures/issue-3138-installed-smoke.ts +734 -0
  639. package/src/scripts/__tests__/notify-fallback-delivery.test.ts +177 -0
  640. package/src/scripts/__tests__/notify-state-io.test.ts +73 -0
  641. package/src/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.ts +215 -0
  642. package/src/scripts/__tests__/role-intent-durable-recovery-3181.test.ts +193 -0
  643. package/src/scripts/__tests__/smoke-packed-install.test.ts +991 -4
  644. package/src/scripts/codex-native-hook.ts +779 -159
  645. package/src/scripts/notify-fallback-delivery.ts +416 -0
  646. package/src/scripts/notify-fallback-watcher.ts +132 -40
  647. package/src/scripts/notify-hook/auto-nudge.ts +43 -12
  648. package/src/scripts/notify-hook/managed-tmux.ts +149 -12
  649. package/src/scripts/notify-hook/ralph-session-resume.ts +37 -36
  650. package/src/scripts/notify-hook/state-io.ts +100 -13
  651. package/src/scripts/notify-hook/tmux-injection.ts +38 -21
  652. package/src/scripts/notify-hook/visual-verdict.ts +22 -12
  653. package/src/scripts/notify-hook.ts +335 -90
  654. package/src/scripts/smoke-packed-install.ts +1829 -6
@@ -1,10 +1,11 @@
1
1
  /**
2
2
  * omx doctor - Validate oh-my-codex installation
3
3
  */
4
+ import { recoverNativeHookClaimJournal } from "./native-hook-claim-journal.js";
4
5
  import { constants, existsSync, readFileSync } from "fs";
5
- import { access, chown, lstat, mkdtemp, readdir, readFile, rm } from "fs/promises";
6
+ import { access, chown, lstat, mkdtemp, readdir, readFile, rmdir, rm } from "fs/promises";
6
7
  import { spawnSync } from "child_process";
7
- import { basename, join, relative } from "path";
8
+ import { basename, dirname, join, relative } from "path";
8
9
  import { tmpdir } from "os";
9
10
  import { codexHome, codexConfigPath, codexPromptsDir, userSkillsDir, projectSkillsDir, omxStateDir, detectLegacySkillRootOverlap, codexAgentsDir, } from "../utils/paths.js";
10
11
  import { classifySpawnError, spawnPlatformCommandSync, } from "../utils/platform-command.js";
@@ -13,7 +14,7 @@ import { parse as parseToml } from "@iarna/toml";
13
14
  import { getBuiltinExploreHarnessUnsupportedReason, resolvePackagedExploreHarnessCommand, EXPLORE_BIN_ENV, } from "./explore.js";
14
15
  import { getPackageRoot } from "../utils/package.js";
15
16
  import { analyzeLegacyMultiAgentConfig, hasExactOmxSeededBehavioralDefaultsPair, hasLegacyOmxTeamRunTable, } from "../config/generator.js";
16
- import { MANAGED_HOOK_EVENTS, buildManagedCodexNativeHookCommand, discoverCodexHookConfigPaths, resolveWindowsPowerShellPath, getManagedCodexHookCommandsForEvent, getMissingManagedCodexHookEvents, hasCodexHooksJsonTopLevelState, } from "../config/codex-hooks.js";
17
+ import { MANAGED_HOOK_EVENTS, buildManagedCodexNativeHookCommand, buildManagedCodexNativeHookWindowsShimContent, classifyManagedCodexNativeHookWindowsShimOwnership, discoverCodexHookConfigPaths, isManagedCodexHookCommand, parseManagedCodexNativeHookWindowsShimCommand, planManagedCodexHooksRemoval, resolveWindowsPowerShellPath, validateCodexHooksConfigStrict, } from "../config/codex-hooks.js";
17
18
  import { OMX_FIRST_PARTY_MCP_SERVER_NAMES } from "../config/omx-first-party-mcp.js";
18
19
  import { getDefaultBridge, isBridgeEnabled } from "../runtime/bridge.js";
19
20
  import { OMX_EXPLORE_CMD_ENV, isExploreCommandRoutingEnabled, } from "../hooks/explore-routing.js";
@@ -124,6 +125,7 @@ export async function doctor(options = {}) {
124
125
  const cwd = process.cwd();
125
126
  const scopeResolution = await resolveDoctorScope(cwd);
126
127
  const paths = resolveDoctorPaths(cwd, scopeResolution.scope);
128
+ await recoverNativeHookClaimJournal(paths.codexHomeDir);
127
129
  const scopeSourceMessage = scopeResolution.source === "persisted"
128
130
  ? " (from .omx/setup-scope.json)"
129
131
  : scopeResolution.source === "config"
@@ -182,13 +184,14 @@ export async function doctor(options = {}) {
182
184
  checks.push(seededContextDefaultsCheck);
183
185
  }
184
186
  // Check 4.25: Native hooks coverage
185
- checks.push(await checkNativeHooks(paths.hooksPath, paths.configPath, {
187
+ const nativeHooksCheck = await checkNativeHooks(paths.hooksPath, paths.configPath, {
186
188
  codexHomeDir: paths.codexHomeDir,
187
189
  installMode: scopeResolution.installMode,
188
- }));
190
+ });
191
+ checks.push(nativeHooksCheck);
189
192
  checks.push(await checkNativeHookDistSmoke());
190
193
  if (options.verbose) {
191
- const postCompactRuntimeCheck = await checkNativePostCompactHookRuntime(paths.hooksPath, cwd, paths.codexHomeDir);
194
+ const postCompactRuntimeCheck = await checkNativePostCompactHookRuntime(paths.hooksPath, cwd, paths.codexHomeDir, { nativeHooksCheck });
192
195
  if (postCompactRuntimeCheck)
193
196
  checks.push(postCompactRuntimeCheck);
194
197
  }
@@ -249,10 +252,10 @@ export async function doctor(options = {}) {
249
252
  }
250
253
  console.log(`\nResults: ${passCount} passed, ${warnCount} warnings, ${failCount} failed`);
251
254
  if (failCount > 0) {
252
- console.log('\nRun "omx setup" to fix installation issues.');
255
+ console.log('\nReview failed checks above. Follow the check-specific recovery guidance; inspect invalid or ambiguous hook documents manually because doctor will not modify them.');
253
256
  }
254
257
  else if (warnCount > 0) {
255
- console.log('\nReview warnings above. Use "omx setup --force" only when a warning recommends full replacement; for AGENTS.md preservation prefer "omx setup --merge-agents".');
258
+ console.log('\nReview warnings above. Follow the check-specific recovery guidance; for AGENTS.md preservation prefer "omx setup --merge-agents".');
256
259
  }
257
260
  else {
258
261
  console.log("\nAll checks passed! oh-my-codex is ready.");
@@ -1229,6 +1232,183 @@ function configEnablesPluginScopedHooks(configContent) {
1229
1232
  return /^\s*plugin_hooks\s*=\s*(?:true|1|"true"|"1"|"yes"|"on")\s*$/m.test(configContent);
1230
1233
  }
1231
1234
  }
1235
+ function trimNativeHookDetailTerminalPeriod(detail) {
1236
+ return detail.endsWith(".") ? detail.slice(0, -1) : detail;
1237
+ }
1238
+ function formatNativeHookDiagnostics(diagnostics) {
1239
+ return diagnostics
1240
+ .map((diagnostic) => {
1241
+ const coordinate = `${diagnostic.eventName}[${diagnostic.groupIndex}]${diagnostic.handlerIndex === undefined
1242
+ ? ""
1243
+ : `.${diagnostic.handlerIndex}`}`;
1244
+ return `${coordinate}: ${trimNativeHookDetailTerminalPeriod(diagnostic.message)}`;
1245
+ })
1246
+ .join("; ");
1247
+ }
1248
+ function isJsonRecord(value) {
1249
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1250
+ }
1251
+ const WINDOWS_SHIM_SCAN_EVENTS = [
1252
+ "PreToolUse",
1253
+ "PermissionRequest",
1254
+ "PostToolUse",
1255
+ "PreCompact",
1256
+ "PostCompact",
1257
+ "SessionStart",
1258
+ "UserPromptSubmit",
1259
+ "SubagentStart",
1260
+ "SubagentStop",
1261
+ "Stop",
1262
+ ];
1263
+ function effectiveWindowsHookCommand(handler) {
1264
+ const commandWindows = handler.commandWindows;
1265
+ if (typeof commandWindows === "string")
1266
+ return commandWindows;
1267
+ const commandWindowsSnakeCase = handler.command_windows;
1268
+ if (typeof commandWindowsSnakeCase === "string")
1269
+ return commandWindowsSnakeCase;
1270
+ return typeof handler.command === "string" ? handler.command : null;
1271
+ }
1272
+ function referencedWindowsNativeHookShimPaths(root, diagnostics, codexHomeDir) {
1273
+ if (!isJsonRecord(root.hooks))
1274
+ return [];
1275
+ const skippedGroups = new Set(diagnostics
1276
+ .filter((diagnostic) => diagnostic.code === "invalid_matcher")
1277
+ .map((diagnostic) => `${diagnostic.eventName}:${diagnostic.groupIndex}`));
1278
+ const skippedHandlers = new Set(diagnostics
1279
+ .filter((diagnostic) => diagnostic.code === "async_command" || diagnostic.code === "empty_command")
1280
+ .map((diagnostic) => `${diagnostic.eventName}:${diagnostic.groupIndex}:${diagnostic.handlerIndex}`));
1281
+ const shimPaths = new Set();
1282
+ for (const eventName of WINDOWS_SHIM_SCAN_EVENTS) {
1283
+ const eventGroups = root.hooks[eventName];
1284
+ if (!Array.isArray(eventGroups))
1285
+ continue;
1286
+ for (const [groupIndex, group] of eventGroups.entries()) {
1287
+ if (skippedGroups.has(`${eventName}:${groupIndex}`) || !isJsonRecord(group) || !Array.isArray(group.hooks))
1288
+ continue;
1289
+ for (const [handlerIndex, handler] of group.hooks.entries()) {
1290
+ if (skippedHandlers.has(`${eventName}:${groupIndex}:${handlerIndex}`) ||
1291
+ !isJsonRecord(handler) ||
1292
+ handler.type !== "command")
1293
+ continue;
1294
+ const command = effectiveWindowsHookCommand(handler);
1295
+ if (!command)
1296
+ continue;
1297
+ const shimPath = parseManagedCodexNativeHookWindowsShimCommand(command, {
1298
+ platform: "win32",
1299
+ codexHomeDir,
1300
+ });
1301
+ if (shimPath)
1302
+ shimPaths.add(shimPath);
1303
+ }
1304
+ }
1305
+ }
1306
+ return [...shimPaths];
1307
+ }
1308
+ async function checkWindowsNativeHookShimParentTopology(shimPath, codexHomeDir) {
1309
+ let ancestorPath = dirname(shimPath);
1310
+ for (;;) {
1311
+ try {
1312
+ const ancestorStat = await lstat(ancestorPath);
1313
+ if (ancestorStat.isSymbolicLink() || !ancestorStat.isDirectory()) {
1314
+ return {
1315
+ name: "Native hooks",
1316
+ status: "fail",
1317
+ message: `referenced Windows native hook shim at ${shimPath} has an unsafe parent topology at ${ancestorPath}; doctor will not follow or modify it`,
1318
+ };
1319
+ }
1320
+ }
1321
+ catch {
1322
+ return {
1323
+ name: "Native hooks",
1324
+ status: "fail",
1325
+ message: `referenced Windows native hook shim at ${shimPath} has a parent topology that cannot be safely validated; doctor will not follow or modify it`,
1326
+ };
1327
+ }
1328
+ if (ancestorPath === codexHomeDir)
1329
+ return null;
1330
+ const parentPath = dirname(ancestorPath);
1331
+ if (parentPath === ancestorPath) {
1332
+ return {
1333
+ name: "Native hooks",
1334
+ status: "fail",
1335
+ message: `referenced Windows native hook shim at ${shimPath} is outside the controlled Codex home topology; doctor will not follow or modify it`,
1336
+ };
1337
+ }
1338
+ ancestorPath = parentPath;
1339
+ }
1340
+ }
1341
+ async function checkWindowsNativeHookShims(root, diagnostics, codexHomeDir, requireCurrent = false) {
1342
+ const expected = Buffer.from(buildManagedCodexNativeHookWindowsShimContent(getPackageRoot()), "utf-8");
1343
+ for (const shimPath of referencedWindowsNativeHookShimPaths(root, diagnostics, codexHomeDir)) {
1344
+ let shimContent;
1345
+ try {
1346
+ const shimStat = await lstat(shimPath);
1347
+ if (shimStat.isSymbolicLink() || !shimStat.isFile()) {
1348
+ return {
1349
+ name: "Native hooks",
1350
+ status: "fail",
1351
+ message: `referenced Windows native hook shim at ${shimPath} is not a regular file; doctor will not follow or modify it`,
1352
+ };
1353
+ }
1354
+ if (shimStat.nlink !== 1) {
1355
+ return {
1356
+ name: "Native hooks",
1357
+ status: "fail",
1358
+ message: `referenced Windows native hook shim at ${shimPath} is hard-linked; doctor will not execute or modify it`,
1359
+ };
1360
+ }
1361
+ shimContent = await readFile(shimPath);
1362
+ const topologyCheck = await checkWindowsNativeHookShimParentTopology(shimPath, codexHomeDir);
1363
+ if (topologyCheck)
1364
+ return topologyCheck;
1365
+ }
1366
+ catch (error) {
1367
+ const code = typeof error === "object" && error !== null && "code" in error
1368
+ ? String(error.code)
1369
+ : undefined;
1370
+ return {
1371
+ name: "Native hooks",
1372
+ status: "fail",
1373
+ message: code === "ENOENT"
1374
+ ? `referenced Windows native hook shim is missing at ${shimPath}; manually reinstall the matching oh-my-codex version because doctor will not create or modify shims`
1375
+ : `cannot read referenced Windows native hook shim at ${shimPath}; inspect it and manually reinstall the matching oh-my-codex version because doctor will not modify shims`,
1376
+ };
1377
+ }
1378
+ const ownership = classifyManagedCodexNativeHookWindowsShimOwnership(shimContent, expected);
1379
+ if (ownership === "modified") {
1380
+ return {
1381
+ name: "Native hooks",
1382
+ status: "fail",
1383
+ message: `referenced Windows native hook shim at ${shimPath} is not an exact current or complete historical generated shim; it may be modified, truncated, have extra content, or use ambiguous encoding. Manually reinstall the matching oh-my-codex version because doctor will not overwrite it`,
1384
+ };
1385
+ }
1386
+ if (requireCurrent && ownership !== "current") {
1387
+ return {
1388
+ name: "Native hooks",
1389
+ status: "warn",
1390
+ message: `referenced Windows native hook shim at ${shimPath} is a complete historical generated shim, but verbose execution requires exact current shim bytes; run "omx setup" to migrate it before retrying`,
1391
+ };
1392
+ }
1393
+ }
1394
+ return null;
1395
+ }
1396
+ const MANAGED_HOOK_TRUST_KEY_LABELS = {
1397
+ SessionStart: "session_start",
1398
+ PreToolUse: "pre_tool_use",
1399
+ PostToolUse: "post_tool_use",
1400
+ UserPromptSubmit: "user_prompt_submit",
1401
+ PreCompact: "pre_compact",
1402
+ PostCompact: "post_compact",
1403
+ Stop: "stop",
1404
+ };
1405
+ function getMissingManagedHookEventsFromPlan(plan) {
1406
+ return MANAGED_HOOK_EVENTS.filter((eventName) => {
1407
+ const label = MANAGED_HOOK_TRUST_KEY_LABELS[eventName];
1408
+ const keyPattern = new RegExp(`:${label}:\\d+:\\d+$`);
1409
+ return !Object.keys(plan.priorTrustState).some((key) => keyPattern.test(key));
1410
+ });
1411
+ }
1232
1412
  function pluginHooksJsonHasNativeCoverage(content) {
1233
1413
  try {
1234
1414
  const parsed = JSON.parse(content);
@@ -1260,7 +1440,7 @@ function pluginHooksJsonHasNativeCoverage(content) {
1260
1440
  }
1261
1441
  async function checkPluginScopedNativeHooks(codexHomeDir, setupHooksPath) {
1262
1442
  const setupHooksPathDescription = existsSync(setupHooksPath)
1263
- ? `existing hooks.json at ${setupHooksPath} is treated as user-owned because plugin-scoped hooks are enabled`
1443
+ ? `existing hooks.json at ${setupHooksPath} is retained read-only and validated separately because plugin-scoped hooks are enabled`
1264
1444
  : `setup-owned hooks.json is intentionally absent at ${setupHooksPath}`;
1265
1445
  const packagedMarketplace = await resolvePackagedOmxMarketplace(getPackageRoot());
1266
1446
  if (!packagedMarketplace) {
@@ -1282,14 +1462,14 @@ async function checkPluginScopedNativeHooks(codexHomeDir, setupHooksPath) {
1282
1462
  return {
1283
1463
  name: "Native hooks",
1284
1464
  status: "warn",
1285
- message: `plugin-scoped hooks are enabled, but the expected Codex plugin cache manifest is missing at ${join(expectedCacheDir, ".codex-plugin", "plugin.json")}; ${setupHooksPathDescription}; run "omx setup --plugin --force" to refresh the plugin cache`,
1465
+ message: `plugin-scoped hooks are enabled, but the expected Codex plugin cache manifest is missing at ${join(expectedCacheDir, ".codex-plugin", "plugin.json")}; ${setupHooksPathDescription}; run "omx setup --plugin" to refresh the plugin cache`,
1286
1466
  };
1287
1467
  }
1288
1468
  if (state.hooksPointer !== "./hooks/hooks.json") {
1289
1469
  return {
1290
1470
  name: "Native hooks",
1291
1471
  status: "warn",
1292
- message: `plugin-scoped hooks are enabled, but the Codex plugin cache manifest points hooks to ${String(state.hooksPointer)} instead of ./hooks/hooks.json at ${expectedHooksPath}; run "omx setup --plugin --force" to refresh the plugin cache`,
1472
+ message: `plugin-scoped hooks are enabled, but the Codex plugin cache manifest points hooks to ${String(state.hooksPointer)} instead of ./hooks/hooks.json at ${expectedHooksPath}; run "omx setup --plugin" to refresh the plugin cache`,
1293
1473
  };
1294
1474
  }
1295
1475
  for (const expectedPath of [expectedHooksPath, expectedHookLauncherPath, expectedPinnedLauncherPath]) {
@@ -1297,7 +1477,7 @@ async function checkPluginScopedNativeHooks(codexHomeDir, setupHooksPath) {
1297
1477
  return {
1298
1478
  name: "Native hooks",
1299
1479
  status: "warn",
1300
- message: `plugin-scoped hooks are enabled, but expected plugin hook file is missing at ${expectedPath}; ${setupHooksPathDescription}; run "omx setup --plugin --force" to refresh the plugin cache`,
1480
+ message: `plugin-scoped hooks are enabled, but expected plugin hook file is missing at ${expectedPath}; ${setupHooksPathDescription}; run "omx setup --plugin" to refresh the plugin cache`,
1301
1481
  };
1302
1482
  }
1303
1483
  }
@@ -1305,7 +1485,7 @@ async function checkPluginScopedNativeHooks(codexHomeDir, setupHooksPath) {
1305
1485
  return {
1306
1486
  name: "Native hooks",
1307
1487
  status: "warn",
1308
- message: `plugin-scoped hooks are enabled, but cached plugin hook files or pinned hook launcher in ${expectedCacheDir} do not match the packaged plugin; ${setupHooksPathDescription}; run "omx setup --plugin --force" to refresh the plugin cache`,
1488
+ message: `plugin-scoped hooks are enabled, but cached plugin hook files or pinned hook launcher in ${expectedCacheDir} do not match the packaged plugin; ${setupHooksPathDescription}; run "omx setup --plugin" to refresh the plugin cache`,
1309
1489
  };
1310
1490
  }
1311
1491
  let hookContent;
@@ -1331,7 +1511,7 @@ async function checkPluginScopedNativeHooks(codexHomeDir, setupHooksPath) {
1331
1511
  return {
1332
1512
  name: "Native hooks",
1333
1513
  status: "warn",
1334
- message: `plugin-scoped hooks.json at ${expectedHooksPath} is missing OMX native coverage for one or more events; run "omx setup --plugin --force" to refresh the plugin cache`,
1514
+ message: `plugin-scoped hooks.json at ${expectedHooksPath} is missing OMX native coverage for one or more events; run "omx setup --plugin" to refresh the plugin cache`,
1335
1515
  };
1336
1516
  }
1337
1517
  const smokeCwd = await mkdtemp(join(tmpdir(), "omx-doctor-plugin-hook-"));
@@ -1384,12 +1564,145 @@ async function checkPluginScopedNativeHooks(codexHomeDir, setupHooksPath) {
1384
1564
  message: `plugin-scoped hooks are enabled; ${setupHooksPathDescription}, and plugin cache native hook coverage smoke passed via ${expectedHooksPath}`,
1385
1565
  };
1386
1566
  }
1387
- async function checkNativeHooks(hooksPath, configPath, context) {
1567
+ function decodeStrictUtf8(bytes) {
1568
+ try {
1569
+ const content = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
1570
+ return Buffer.from(content, "utf-8").equals(bytes) ? content : null;
1571
+ }
1572
+ catch {
1573
+ return null;
1574
+ }
1575
+ }
1576
+ function combinePluginAndGlobalNativeHookChecks(plugin, global) {
1577
+ if (!global)
1578
+ return plugin;
1579
+ const status = plugin.status === "fail" || global.status === "fail"
1580
+ ? "fail"
1581
+ : plugin.status === "warn" || global.status === "warn"
1582
+ ? "warn"
1583
+ : "pass";
1584
+ return {
1585
+ name: "Native hooks",
1586
+ status,
1587
+ message: `${plugin.message}; existing global hooks.json: ${global.message}`,
1588
+ };
1589
+ }
1590
+ function combineNativeHookIntegrityAndRemovalChecks(integrityCheck, removalCheck) {
1591
+ if (!integrityCheck)
1592
+ return removalCheck;
1593
+ return {
1594
+ name: "Native hooks",
1595
+ status: integrityCheck.status === "fail" || removalCheck.status === "fail"
1596
+ ? "fail"
1597
+ : "warn",
1598
+ message: `${integrityCheck.message}; ${removalCheck.message}`,
1599
+ };
1600
+ }
1601
+ async function checkExistingNativeHooks(hooksPath, context) {
1602
+ const platform = context.platform ?? process.platform;
1603
+ try {
1604
+ const hooksStat = await lstat(hooksPath);
1605
+ if (hooksStat.isSymbolicLink() || !hooksStat.isFile()) {
1606
+ return {
1607
+ name: "Native hooks",
1608
+ status: "fail",
1609
+ message: `hooks.json at ${hooksPath} is not a regular file; doctor will not follow or modify it`,
1610
+ };
1611
+ }
1612
+ const content = decodeStrictUtf8(await readFile(hooksPath));
1613
+ if (content === null) {
1614
+ return {
1615
+ name: "Native hooks",
1616
+ status: "fail",
1617
+ message: `hooks.json at ${hooksPath} is not valid UTF-8; inspect the file manually because doctor will not modify it`,
1618
+ };
1619
+ }
1620
+ const validation = validateCodexHooksConfigStrict(content, {
1621
+ platform,
1622
+ codexHomeDir: context.codexHomeDir,
1623
+ });
1624
+ if (!validation.ok) {
1625
+ return {
1626
+ name: "Native hooks",
1627
+ status: "fail",
1628
+ message: `hooks.json failed strict load validation (${validation.error.code}): ${trimNativeHookDetailTerminalPeriod(validation.error.message)}; inspect the file manually because doctor will not modify it`,
1629
+ };
1630
+ }
1631
+ const removalPlan = planManagedCodexHooksRemoval(content, hooksPath, {
1632
+ platform,
1633
+ codexHomeDir: context.codexHomeDir,
1634
+ });
1635
+ const windowsShimCheck = platform === "win32"
1636
+ ? await checkWindowsNativeHookShims(validation.root, validation.diagnostics, context.codexHomeDir)
1637
+ : null;
1638
+ if (!removalPlan.ok) {
1639
+ const removalIsCoordinateOnly = removalPlan.error.code === "unsafe_managed_removal";
1640
+ const removalCheck = {
1641
+ name: "Native hooks",
1642
+ status: removalIsCoordinateOnly ? "warn" : "fail",
1643
+ message: removalIsCoordinateOnly
1644
+ ? `hooks.json has OMX entries that cannot be safely removed (${removalPlan.error.code}): ${trimNativeHookDetailTerminalPeriod(removalPlan.error.message)}; manual cleanup is required because doctor will not overwrite or remove it`
1645
+ : `hooks.json has ambiguous or untrusted OMX ownership (${removalPlan.error.code}): ${trimNativeHookDetailTerminalPeriod(removalPlan.error.message)}; inspect the file manually because doctor will not overwrite or remove it`,
1646
+ };
1647
+ return combineNativeHookIntegrityAndRemovalChecks(windowsShimCheck, removalCheck);
1648
+ }
1649
+ if (windowsShimCheck)
1650
+ return windowsShimCheck;
1651
+ const legacyTrustStateEntries = Object.keys(removalPlan.legacyTrustState).length;
1652
+ if (legacyTrustStateEntries > 0) {
1653
+ return {
1654
+ name: "Native hooks",
1655
+ status: "warn",
1656
+ message: `hooks.json contains ${legacyTrustStateEntries} exact historical OMX hook trust-state ${legacyTrustStateEntries === 1 ? "entry that requires" : "entries that require"} migration; run "omx setup" to migrate ${legacyTrustStateEntries === 1 ? "it" : "them"} after reviewing the configuration`,
1657
+ };
1658
+ }
1659
+ if (validation.diagnostics.length > 0) {
1660
+ return {
1661
+ name: "Native hooks",
1662
+ status: "warn",
1663
+ message: `hooks.json discovery warnings: ${formatNativeHookDiagnostics(validation.diagnostics)}; Codex may ignore the listed entries, and doctor will not modify them`,
1664
+ };
1665
+ }
1666
+ const missingEvents = getMissingManagedHookEventsFromPlan(removalPlan);
1667
+ if (removalPlan.hasForeignHooks &&
1668
+ removalPlan.removedCount === 0 &&
1669
+ missingEvents.length === MANAGED_HOOK_EVENTS.length) {
1670
+ return {
1671
+ name: "Native hooks",
1672
+ status: "pass",
1673
+ message: "hooks.json contains valid foreign hook entries and no OMX-managed wrappers; doctor will preserve the user-owned configuration",
1674
+ };
1675
+ }
1676
+ if (missingEvents.length > 0) {
1677
+ return {
1678
+ name: "Native hooks",
1679
+ status: "warn",
1680
+ message: `hooks.json is missing OMX-managed coverage for ${missingEvents.join(", ")}; run "omx setup" to restore native hooks${removalPlan.hasForeignHooks ? "; valid foreign hooks will be preserved" : ""}`,
1681
+ };
1682
+ }
1683
+ return {
1684
+ name: "Native hooks",
1685
+ status: "pass",
1686
+ message: `hooks.json includes OMX-managed coverage for all native hook events${removalPlan.hasForeignHooks ? "; valid foreign hooks will be preserved" : ""}`,
1687
+ };
1688
+ }
1689
+ catch {
1690
+ return {
1691
+ name: "Native hooks",
1692
+ status: "fail",
1693
+ message: "cannot read hooks.json",
1694
+ };
1695
+ }
1696
+ }
1697
+ export async function checkNativeHooks(hooksPath, configPath, context) {
1388
1698
  if (existsSync(configPath) && context.installMode === "plugin") {
1389
1699
  try {
1390
1700
  const configContent = await readFile(configPath, "utf-8");
1391
1701
  if (configEnablesPluginScopedHooks(configContent)) {
1392
- return checkPluginScopedNativeHooks(context.codexHomeDir, hooksPath);
1702
+ const globalCheck = existsSync(hooksPath)
1703
+ ? await checkExistingNativeHooks(hooksPath, context)
1704
+ : null;
1705
+ return combinePluginAndGlobalNativeHookChecks(await checkPluginScopedNativeHooks(context.codexHomeDir, hooksPath), globalCheck);
1393
1706
  }
1394
1707
  }
1395
1708
  catch {
@@ -1401,25 +1714,23 @@ async function checkNativeHooks(hooksPath, configPath, context) {
1401
1714
  if (existsSync(configPath)) {
1402
1715
  try {
1403
1716
  const configContent = await readFile(configPath, "utf-8");
1404
- if (context.installMode === "plugin") {
1405
- if (configHasOmxEntries(configContent)) {
1406
- return {
1407
- name: "Native hooks",
1408
- status: "warn",
1409
- message: `plugin mode is using legacy native hook fallback, but expected setup-owned hooks.json is missing at ${hooksPath}; run "omx setup --plugin --force" to restore the fallback hook file, or upgrade Codex to plugin_hooks support so setup can use plugin-scoped hooks`,
1410
- };
1411
- }
1717
+ if (context.installMode === "plugin" && configHasOmxEntries(configContent)) {
1718
+ return {
1719
+ name: "Native hooks",
1720
+ status: "warn",
1721
+ message: `plugin mode is using legacy native hook fallback, but expected setup-owned hooks.json is missing at ${hooksPath}; run "omx setup --plugin" to restore the fallback hook file, or upgrade Codex to plugin_hooks support so setup can use plugin-scoped hooks`,
1722
+ };
1412
1723
  }
1413
1724
  if (configHasOmxEntries(configContent)) {
1414
1725
  return {
1415
1726
  name: "Native hooks",
1416
1727
  status: "warn",
1417
- message: `expected setup-owned hooks.json is missing at ${hooksPath} even though config.toml has OMX entries; run "omx setup --force" to restore native hook coverage`,
1728
+ message: `expected setup-owned hooks.json is missing at ${hooksPath} even though config.toml has OMX entries; run "omx setup" to restore native hook coverage`,
1418
1729
  };
1419
1730
  }
1420
1731
  }
1421
1732
  catch {
1422
- // fall through to the neutral first-setup path when config cannot be read here;
1733
+ // Fall through to the neutral first-setup path when config cannot be read here;
1423
1734
  // the dedicated config check will report read failures separately.
1424
1735
  }
1425
1736
  }
@@ -1429,66 +1740,18 @@ async function checkNativeHooks(hooksPath, configPath, context) {
1429
1740
  message: "hooks.json not found yet (expected before first setup)",
1430
1741
  };
1431
1742
  }
1432
- try {
1433
- const content = await readFile(hooksPath, "utf-8");
1434
- const missingEvents = getMissingManagedCodexHookEvents(content);
1435
- if (missingEvents === null) {
1436
- return {
1437
- name: "Native hooks",
1438
- status: "fail",
1439
- message: 'invalid hooks.json; Codex may skip OMX hook coverage until "omx setup --force" repairs it',
1440
- };
1441
- }
1442
- const hasTopLevelState = hasCodexHooksJsonTopLevelState(content);
1443
- if (hasTopLevelState === true) {
1444
- return {
1445
- name: "Native hooks",
1446
- status: "fail",
1447
- message: 'top-level state in hooks.json is incompatible with Codex 0.140 (unknown field state, expected hooks); run "omx setup --force" to migrate trust state to config.toml and repair hooks.json',
1448
- };
1449
- }
1450
- if (missingEvents.length > 0) {
1451
- return {
1452
- name: "Native hooks",
1453
- status: "warn",
1454
- message: `hooks.json is missing OMX-managed coverage for ${missingEvents.join(", ")}; run "omx setup --force" to restore native hooks`,
1455
- };
1456
- }
1457
- return {
1458
- name: "Native hooks",
1459
- status: "pass",
1460
- message: "hooks.json includes OMX-managed coverage for all native hook events",
1461
- };
1462
- }
1463
- catch {
1464
- return {
1465
- name: "Native hooks",
1466
- status: "fail",
1467
- message: "cannot read hooks.json",
1468
- };
1469
- }
1743
+ return checkExistingNativeHooks(hooksPath, context);
1470
1744
  }
1471
1745
  export async function checkNativeHookDistSmoke(options = {}) {
1472
1746
  const packageRoot = options.packageRoot ?? getPackageRoot();
1473
1747
  const nodePath = options.nodePath ?? process.execPath;
1474
1748
  const runner = options.runner ?? spawnSync;
1475
1749
  const scriptPath = join(packageRoot, "dist", "scripts", "codex-native-hook.js");
1476
- const packageJsonPath = join(packageRoot, "package.json");
1477
- let packageVersion = "current";
1478
- try {
1479
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
1480
- if (typeof packageJson.version === "string" && packageJson.version.trim()) {
1481
- packageVersion = packageJson.version.trim();
1482
- }
1483
- }
1484
- catch {
1485
- // Keep the generic recovery copy when package metadata is not readable.
1486
- }
1487
1750
  if (!existsSync(scriptPath)) {
1488
1751
  return {
1489
1752
  name: "Native hook dist smoke",
1490
1753
  status: "fail",
1491
- message: `installed native hook script is missing at ${scriptPath}; reinstall oh-my-codex and run "omx setup --force"`,
1754
+ message: `installed native hook script is missing at ${scriptPath}; reinstall oh-my-codex and run "omx setup"`,
1492
1755
  };
1493
1756
  }
1494
1757
  const smokeCwd = await mkdtemp(join(tmpdir(), "omx-doctor-native-hook-dist-"));
@@ -1518,7 +1781,7 @@ export async function checkNativeHookDistSmoke(options = {}) {
1518
1781
  return {
1519
1782
  name: "Native hook dist smoke",
1520
1783
  status: "fail",
1521
- message: `installed native hook dist smoke failed to run (${result.error.message}); reinstall oh-my-codex and run "omx setup --force"`,
1784
+ message: `installed native hook dist smoke failed to run (${result.error.message}); reinstall oh-my-codex and run "omx setup"`,
1522
1785
  };
1523
1786
  }
1524
1787
  if (result.status !== 0) {
@@ -1528,7 +1791,7 @@ export async function checkNativeHookDistSmoke(options = {}) {
1528
1791
  return {
1529
1792
  name: "Native hook dist smoke",
1530
1793
  status: "fail",
1531
- message: `installed native hook dist failed a minimal UserPromptSubmit smoke (${detail}); reinstall with "npm install -g oh-my-codex@${packageVersion} --force --min-release-age=0 --before=" and then run "omx setup --force"`,
1794
+ message: `installed native hook dist failed a minimal UserPromptSubmit smoke (${detail}); reinstall the matching oh-my-codex version and then run "omx setup"`,
1532
1795
  };
1533
1796
  }
1534
1797
  return {
@@ -1550,14 +1813,14 @@ export function classifyPostCompactHookStdout(stdout) {
1550
1813
  return {
1551
1814
  name: "Native PostCompact hook",
1552
1815
  status: "fail",
1553
- message: "PostCompact hook emitted JSON stdout, but OMX PostCompact must emit no stdout until Codex defines a supported PostCompact output contract; run \"omx setup --force\" after upgrading",
1816
+ message: "PostCompact hook emitted JSON stdout, but OMX PostCompact must emit no stdout until Codex defines a supported PostCompact output contract; rerun \"omx setup\" after upgrading",
1554
1817
  };
1555
1818
  }
1556
1819
  catch (error) {
1557
1820
  return {
1558
1821
  name: "Native PostCompact hook",
1559
1822
  status: "fail",
1560
- message: `PostCompact hook emitted invalid JSON stdout (${error instanceof Error ? error.message : String(error)}); run "omx setup --force" after upgrading`,
1823
+ message: `PostCompact hook emitted invalid JSON stdout (${error instanceof Error ? error.message : String(error)}); rerun "omx setup" after upgrading`,
1561
1824
  };
1562
1825
  }
1563
1826
  }
@@ -1582,77 +1845,278 @@ export function buildPostCompactSmokeSpawnInvocation(expectedCommand, options =
1582
1845
  shell: true,
1583
1846
  };
1584
1847
  }
1585
- async function checkNativePostCompactHookRuntime(hooksPath, cwd, codexHomeDir) {
1586
- if (!existsSync(hooksPath))
1848
+ function buildInMemoryWindowsShimSmokeInvocation(expectedShimContent, options = {}) {
1849
+ const shimSource = decodeStrictUtf8(expectedShimContent);
1850
+ if (shimSource === null)
1587
1851
  return null;
1588
- let content;
1852
+ const encodedShimBytes = expectedShimContent.toString("base64");
1853
+ const command = [
1854
+ "$ErrorActionPreference = 'Stop'",
1855
+ `$omxShimSource = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('${encodedShimBytes}'))`,
1856
+ "if ($omxShimSource.Length -gt 0 -and [int][char]$omxShimSource[0] -eq 0xFEFF) { $omxShimSource = $omxShimSource.Substring(1) }",
1857
+ "& ([ScriptBlock]::Create($omxShimSource))",
1858
+ "exit $LASTEXITCODE",
1859
+ ].join("; ");
1860
+ return {
1861
+ command: resolveWindowsPowerShellPath(options.env),
1862
+ args: [
1863
+ "-NoProfile",
1864
+ "-ExecutionPolicy",
1865
+ "Bypass",
1866
+ "-EncodedCommand",
1867
+ Buffer.from(command, "utf16le").toString("base64"),
1868
+ ],
1869
+ shell: false,
1870
+ };
1871
+ }
1872
+ function smokeDirectoryIdentity(stat) {
1873
+ if (!stat.isDirectory() ||
1874
+ stat.isSymbolicLink() ||
1875
+ (process.platform !== "win32" && (stat.mode & 0o077) !== 0))
1876
+ return null;
1877
+ return { dev: stat.dev, ino: stat.ino };
1878
+ }
1879
+ async function readSmokeDirectoryIdentity(smokeCwd) {
1589
1880
  try {
1590
- content = await readFile(hooksPath, "utf-8");
1881
+ return smokeDirectoryIdentity(await lstat(smokeCwd));
1591
1882
  }
1592
1883
  catch {
1593
1884
  return null;
1594
1885
  }
1595
- const postCompactCommands = getManagedCodexHookCommandsForEvent(content, "PostCompact");
1596
- if (postCompactCommands === null || postCompactCommands.length === 0) {
1886
+ }
1887
+ function sameSmokeDirectoryIdentity(expected, actual) {
1888
+ return actual !== null && actual.dev === expected.dev && actual.ino === expected.ino;
1889
+ }
1890
+ function smokeDirectorySafetyCheck(message) {
1891
+ return {
1892
+ name: "Native PostCompact hook",
1893
+ status: "warn",
1894
+ message,
1895
+ };
1896
+ }
1897
+ async function cleanupSmokeDirectoryIfUnchanged(smokeCwd, identity) {
1898
+ if (!sameSmokeDirectoryIdentity(identity, await readSmokeDirectoryIdentity(smokeCwd))) {
1899
+ return smokeDirectorySafetyCheck("temporary PostCompact smoke directory changed during validation; doctor preserved it for manual recovery and skipped cleanup for safety");
1900
+ }
1901
+ try {
1902
+ // Only remove the empty, identity-checked directory. Never recursively remove
1903
+ // a directory that another process could have replaced or populated.
1904
+ await rmdir(smokeCwd);
1597
1905
  return null;
1598
1906
  }
1599
- const expectedCommand = buildManagedCodexNativeHookCommand(getPackageRoot(), {
1907
+ catch {
1908
+ return smokeDirectorySafetyCheck("temporary PostCompact smoke directory could not be removed without recursive deletion; doctor preserved it for manual recovery");
1909
+ }
1910
+ }
1911
+ function inMemoryWindowsShimSmokeCheck() {
1912
+ return {
1913
+ name: "Native PostCompact hook",
1914
+ status: "warn",
1915
+ message: "doctor could not build an in-memory Windows native hook smoke command from exact validated shim bytes; doctor skipped execution for safety",
1916
+ };
1917
+ }
1918
+ function getManagedPostCompactHookCommands(content, platform, codexHomeDir) {
1919
+ const validation = validateCodexHooksConfigStrict(content, {
1920
+ platform,
1921
+ codexHomeDir,
1922
+ });
1923
+ if (!validation.ok)
1924
+ return null;
1925
+ return validation.discoveredCommands
1926
+ .filter((command) => command.eventName === "PostCompact")
1927
+ .map((command) => command.command)
1928
+ .filter((command) => isManagedCodexHookCommand(command) ||
1929
+ (platform === "win32" && parseManagedCodexNativeHookWindowsShimCommand(command, {
1930
+ platform,
1931
+ codexHomeDir,
1932
+ }) !== null));
1933
+ }
1934
+ function currentPostCompactCommandCheck() {
1935
+ return {
1936
+ name: "Native PostCompact hook",
1937
+ status: "warn",
1938
+ message: "effective PostCompact OMX command does not match this installation's managed hook command; doctor skipped execution for safety, and rerunning \"omx setup\" should refresh stale hooks.json entries",
1939
+ };
1940
+ }
1941
+ function skippedPostCompactIntegrityCheck(integrityCheck) {
1942
+ return {
1943
+ name: "Native PostCompact hook",
1944
+ status: integrityCheck.status === "fail" ? "fail" : "warn",
1945
+ message: `${integrityCheck.message}; doctor skipped execution because native hook integrity validation did not pass`,
1946
+ };
1947
+ }
1948
+ export async function checkNativePostCompactHookRuntime(hooksPath, cwd, codexHomeDir, options = {}) {
1949
+ if (!existsSync(hooksPath))
1950
+ return null;
1951
+ if (options.nativeHooksCheck && options.nativeHooksCheck.status !== "pass")
1952
+ return null;
1953
+ const platform = options.platform ?? process.platform;
1954
+ const expectedCommand = options.expectedCommand ?? buildManagedCodexNativeHookCommand(getPackageRoot(), {
1600
1955
  codexHomeDir,
1956
+ platform,
1601
1957
  });
1958
+ let content;
1959
+ try {
1960
+ content = decodeStrictUtf8(await readFile(hooksPath));
1961
+ }
1962
+ catch {
1963
+ return null;
1964
+ }
1965
+ if (content === null)
1966
+ return null;
1967
+ const postCompactCommands = getManagedPostCompactHookCommands(content, platform, codexHomeDir);
1968
+ if (postCompactCommands === null || postCompactCommands.length === 0)
1969
+ return null;
1602
1970
  const uniqueCommands = [...new Set(postCompactCommands)];
1603
1971
  if (uniqueCommands.length !== 1 || uniqueCommands[0] !== expectedCommand) {
1604
- return {
1605
- name: "Native PostCompact hook",
1606
- status: "warn",
1607
- message: "effective PostCompact OMX command does not match this installation's managed hook command; doctor skipped execution for safety, and \"omx setup --force\" should refresh stale hooks.json entries",
1608
- };
1972
+ return currentPostCompactCommandCheck();
1609
1973
  }
1610
1974
  const smokeCwd = await mkdtemp(join(tmpdir(), "omx-doctor-postcompact-"));
1975
+ const smokeDirectory = await readSmokeDirectoryIdentity(smokeCwd);
1976
+ if (!smokeDirectory) {
1977
+ return smokeDirectorySafetyCheck("temporary PostCompact smoke directory could not be validated; doctor preserved it for manual recovery and skipped execution for safety");
1978
+ }
1979
+ let primaryResult = null;
1980
+ let primaryError = null;
1611
1981
  try {
1612
- const payload = JSON.stringify({
1613
- hook_event_name: "PostCompact",
1614
- cwd: smokeCwd,
1615
- session_id: "omx-doctor-postcompact-smoke",
1616
- });
1617
- const smokeInvocation = buildPostCompactSmokeSpawnInvocation(expectedCommand);
1618
- const result = spawnSync(smokeInvocation.command, smokeInvocation.args, {
1619
- cwd,
1620
- encoding: "utf-8",
1621
- env: {
1622
- ...process.env,
1623
- OMX_NATIVE_HOOK_DOCTOR_SMOKE: "1",
1624
- },
1625
- input: payload,
1626
- shell: smokeInvocation.shell,
1627
- timeout: 5_000,
1628
- });
1629
- if (result.error) {
1630
- return {
1631
- name: "Native PostCompact hook",
1632
- status: "fail",
1633
- message: `PostCompact hook smoke validation failed to run (${result.error.message})`,
1634
- };
1635
- }
1636
- if (result.status !== 0) {
1637
- const stderr = (result.stderr || "").trim();
1982
+ primaryResult = await (async () => {
1983
+ const revalidatedIntegrity = await checkExistingNativeHooks(hooksPath, {
1984
+ codexHomeDir,
1985
+ platform,
1986
+ });
1987
+ if (revalidatedIntegrity.status !== "pass") {
1988
+ return skippedPostCompactIntegrityCheck(revalidatedIntegrity);
1989
+ }
1990
+ let revalidatedContent;
1991
+ try {
1992
+ revalidatedContent = decodeStrictUtf8(await readFile(hooksPath));
1993
+ }
1994
+ catch {
1995
+ return {
1996
+ name: "Native PostCompact hook",
1997
+ status: "warn",
1998
+ message: "hooks.json changed during verbose validation; doctor skipped execution for safety",
1999
+ };
2000
+ }
2001
+ if (revalidatedContent === null) {
2002
+ return {
2003
+ name: "Native PostCompact hook",
2004
+ status: "warn",
2005
+ message: "hooks.json changed during verbose validation; doctor skipped execution for safety",
2006
+ };
2007
+ }
2008
+ const revalidatedCommands = getManagedPostCompactHookCommands(revalidatedContent, platform, codexHomeDir);
2009
+ if (revalidatedCommands === null ||
2010
+ revalidatedCommands.length === 0 ||
2011
+ new Set(revalidatedCommands).size !== 1 ||
2012
+ revalidatedCommands[0] !== expectedCommand) {
2013
+ return currentPostCompactCommandCheck();
2014
+ }
2015
+ if (platform === "win32") {
2016
+ const validation = validateCodexHooksConfigStrict(revalidatedContent, {
2017
+ platform,
2018
+ codexHomeDir,
2019
+ });
2020
+ if (!validation.ok) {
2021
+ return {
2022
+ name: "Native PostCompact hook",
2023
+ status: "warn",
2024
+ message: "hooks.json changed during verbose validation; doctor skipped execution for safety",
2025
+ };
2026
+ }
2027
+ const currentShimCheck = await checkWindowsNativeHookShims(validation.root, validation.diagnostics, codexHomeDir, true);
2028
+ if (currentShimCheck)
2029
+ return skippedPostCompactIntegrityCheck(currentShimCheck);
2030
+ }
2031
+ let smokeInvocation;
2032
+ if (platform === "win32") {
2033
+ const canonicalShimPath = parseManagedCodexNativeHookWindowsShimCommand(expectedCommand, {
2034
+ platform,
2035
+ codexHomeDir,
2036
+ });
2037
+ if (!canonicalShimPath)
2038
+ return currentPostCompactCommandCheck();
2039
+ const expectedShimContent = Buffer.from(buildManagedCodexNativeHookWindowsShimContent(getPackageRoot()), "utf-8");
2040
+ const inMemoryInvocation = buildInMemoryWindowsShimSmokeInvocation(expectedShimContent);
2041
+ if (!inMemoryInvocation)
2042
+ return inMemoryWindowsShimSmokeCheck();
2043
+ smokeInvocation = inMemoryInvocation;
2044
+ try {
2045
+ await options.beforeWindowsShimSmoke?.({ canonicalShimPath, smokeCwd });
2046
+ }
2047
+ catch {
2048
+ return smokeDirectorySafetyCheck("Windows native hook changed during validation; doctor skipped execution for safety");
2049
+ }
2050
+ }
2051
+ else {
2052
+ smokeInvocation = buildPostCompactSmokeSpawnInvocation(expectedCommand, { platform });
2053
+ }
2054
+ if (!sameSmokeDirectoryIdentity(smokeDirectory, await readSmokeDirectoryIdentity(smokeCwd))) {
2055
+ return smokeDirectorySafetyCheck("temporary PostCompact smoke directory changed during validation; doctor preserved it for manual recovery and skipped execution for safety");
2056
+ }
2057
+ const payload = JSON.stringify({
2058
+ hook_event_name: "PostCompact",
2059
+ cwd: smokeCwd,
2060
+ session_id: "omx-doctor-postcompact-smoke",
2061
+ });
2062
+ const result = (options.runner ?? spawnSync)(smokeInvocation.command, smokeInvocation.args, {
2063
+ cwd,
2064
+ encoding: "utf-8",
2065
+ env: {
2066
+ ...process.env,
2067
+ OMX_NATIVE_HOOK_DOCTOR_SMOKE: "1",
2068
+ OMX_ROOT: smokeCwd,
2069
+ OMX_SESSION_ID: "omx-doctor-postcompact-smoke",
2070
+ OMX_SOURCE_CWD: smokeCwd,
2071
+ OMX_STARTUP_CWD: smokeCwd,
2072
+ },
2073
+ input: payload,
2074
+ shell: smokeInvocation.shell,
2075
+ timeout: 5_000,
2076
+ });
2077
+ if (result.error) {
2078
+ return {
2079
+ name: "Native PostCompact hook",
2080
+ status: "fail",
2081
+ message: `PostCompact hook smoke validation failed to run (${result.error.message})`,
2082
+ };
2083
+ }
2084
+ if (result.status !== 0) {
2085
+ const stderr = (result.stderr || "").trim();
2086
+ return {
2087
+ name: "Native PostCompact hook",
2088
+ status: "fail",
2089
+ message: `PostCompact hook smoke validation exited ${result.status}${stderr ? `: ${stderr}` : ""}`,
2090
+ };
2091
+ }
2092
+ const stdoutCheck = classifyPostCompactHookStdout(result.stdout || "");
2093
+ if (stdoutCheck)
2094
+ return stdoutCheck;
1638
2095
  return {
1639
2096
  name: "Native PostCompact hook",
1640
- status: "fail",
1641
- message: `PostCompact hook smoke validation exited ${result.status}${stderr ? `: ${stderr}` : ""}`,
2097
+ status: "pass",
2098
+ message: "verbose smoke validation confirmed the effective PostCompact hook exits successfully with no stdout",
1642
2099
  };
1643
- }
1644
- const stdoutCheck = classifyPostCompactHookStdout(result.stdout || "");
1645
- if (stdoutCheck)
1646
- return stdoutCheck;
1647
- return {
1648
- name: "Native PostCompact hook",
1649
- status: "pass",
1650
- message: "verbose smoke validation confirmed the effective PostCompact hook exits successfully with no stdout",
1651
- };
2100
+ })();
2101
+ }
2102
+ catch (error) {
2103
+ primaryError = error instanceof Error ? error : new Error(String(error));
2104
+ throw primaryError;
1652
2105
  }
1653
2106
  finally {
1654
- await rm(smokeCwd, { recursive: true, force: true });
2107
+ const cleanupCheck = await cleanupSmokeDirectoryIfUnchanged(smokeCwd, smokeDirectory);
2108
+ if (cleanupCheck) {
2109
+ if (primaryResult) {
2110
+ if (primaryResult.status === "pass")
2111
+ primaryResult.status = cleanupCheck.status;
2112
+ primaryResult.message = `${primaryResult.message}; ${cleanupCheck.message}`;
2113
+ }
2114
+ else if (primaryError) {
2115
+ primaryError.message = `${primaryError.message}; ${cleanupCheck.message}`;
2116
+ }
2117
+ }
1655
2118
  }
2119
+ return primaryResult;
1656
2120
  }
1657
2121
  async function checkNativeHookRuntimeMirrors(cwd, hooksPath) {
1658
2122
  if (!existsSync(hooksPath))