oh-my-codex 0.18.0 → 0.18.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 (410) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +45 -19
  4. package/crates/omx-api/src/lib.rs +66 -9
  5. package/crates/omx-sparkshell/src/exec.rs +125 -3
  6. package/crates/omx-sparkshell/src/main.rs +126 -36
  7. package/crates/omx-sparkshell/tests/execution.rs +225 -1
  8. package/dist/agents/__tests__/definitions.test.js +14 -0
  9. package/dist/agents/__tests__/definitions.test.js.map +1 -1
  10. package/dist/agents/__tests__/native-config.test.js +19 -0
  11. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  12. package/dist/agents/definitions.d.ts.map +1 -1
  13. package/dist/agents/definitions.js +30 -0
  14. package/dist/agents/definitions.js.map +1 -1
  15. package/dist/agents/native-config.d.ts +1 -0
  16. package/dist/agents/native-config.d.ts.map +1 -1
  17. package/dist/agents/native-config.js +4 -0
  18. package/dist/agents/native-config.js.map +1 -1
  19. package/dist/catalog/__tests__/generator.test.js +4 -0
  20. package/dist/catalog/__tests__/generator.test.js.map +1 -1
  21. package/dist/cli/__tests__/codex-plugin-layout.test.js +15 -7
  22. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  23. package/dist/cli/__tests__/doctor-warning-copy.test.js +137 -8
  24. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  25. package/dist/cli/__tests__/index.test.js +203 -15
  26. package/dist/cli/__tests__/index.test.js.map +1 -1
  27. package/dist/cli/__tests__/install-docs-contract.test.d.ts +2 -0
  28. package/dist/cli/__tests__/install-docs-contract.test.d.ts.map +1 -0
  29. package/dist/cli/__tests__/install-docs-contract.test.js +55 -0
  30. package/dist/cli/__tests__/install-docs-contract.test.js.map +1 -0
  31. package/dist/cli/__tests__/launch-fallback.test.js +163 -0
  32. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  33. package/dist/cli/__tests__/question.test.js +29 -43
  34. package/dist/cli/__tests__/question.test.js.map +1 -1
  35. package/dist/cli/__tests__/setup-install-mode.test.js +94 -35
  36. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  37. package/dist/cli/__tests__/sparkshell-cli.test.js +20 -1
  38. package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
  39. package/dist/cli/__tests__/sparkshell-packaging.test.js +1 -0
  40. package/dist/cli/__tests__/sparkshell-packaging.test.js.map +1 -1
  41. package/dist/cli/__tests__/ultragoal.test.js +227 -4
  42. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  43. package/dist/cli/__tests__/update.test.js +72 -1
  44. package/dist/cli/__tests__/update.test.js.map +1 -1
  45. package/dist/cli/codex-feature-probe.d.ts +5 -0
  46. package/dist/cli/codex-feature-probe.d.ts.map +1 -1
  47. package/dist/cli/codex-feature-probe.js +13 -7
  48. package/dist/cli/codex-feature-probe.js.map +1 -1
  49. package/dist/cli/doctor.d.ts +7 -0
  50. package/dist/cli/doctor.d.ts.map +1 -1
  51. package/dist/cli/doctor.js +297 -17
  52. package/dist/cli/doctor.js.map +1 -1
  53. package/dist/cli/index.d.ts +9 -1
  54. package/dist/cli/index.d.ts.map +1 -1
  55. package/dist/cli/index.js +465 -110
  56. package/dist/cli/index.js.map +1 -1
  57. package/dist/cli/plugin-marketplace.d.ts +2 -0
  58. package/dist/cli/plugin-marketplace.d.ts.map +1 -1
  59. package/dist/cli/plugin-marketplace.js +15 -1
  60. package/dist/cli/plugin-marketplace.js.map +1 -1
  61. package/dist/cli/setup.d.ts.map +1 -1
  62. package/dist/cli/setup.js +71 -11
  63. package/dist/cli/setup.js.map +1 -1
  64. package/dist/cli/sparkshell.d.ts +7 -1
  65. package/dist/cli/sparkshell.d.ts.map +1 -1
  66. package/dist/cli/sparkshell.js +13 -3
  67. package/dist/cli/sparkshell.js.map +1 -1
  68. package/dist/cli/ultragoal.d.ts +1 -1
  69. package/dist/cli/ultragoal.d.ts.map +1 -1
  70. package/dist/cli/ultragoal.js +184 -10
  71. package/dist/cli/ultragoal.js.map +1 -1
  72. package/dist/cli/update.d.ts +2 -0
  73. package/dist/cli/update.d.ts.map +1 -1
  74. package/dist/cli/update.js +14 -3
  75. package/dist/cli/update.js.map +1 -1
  76. package/dist/compat/__tests__/doctor-contract.test.js +3 -0
  77. package/dist/compat/__tests__/doctor-contract.test.js.map +1 -1
  78. package/dist/config/__tests__/codex-feature-flags.test.js +11 -1
  79. package/dist/config/__tests__/codex-feature-flags.test.js.map +1 -1
  80. package/dist/config/__tests__/codex-hooks.test.js +22 -11
  81. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  82. package/dist/config/__tests__/commit-lore-guard.test.d.ts +2 -0
  83. package/dist/config/__tests__/commit-lore-guard.test.d.ts.map +1 -0
  84. package/dist/config/__tests__/commit-lore-guard.test.js +20 -0
  85. package/dist/config/__tests__/commit-lore-guard.test.js.map +1 -0
  86. package/dist/config/codex-feature-flags.d.ts +4 -0
  87. package/dist/config/codex-feature-flags.d.ts.map +1 -1
  88. package/dist/config/codex-feature-flags.js +4 -0
  89. package/dist/config/codex-feature-flags.js.map +1 -1
  90. package/dist/config/codex-hooks.d.ts +1 -0
  91. package/dist/config/codex-hooks.d.ts.map +1 -1
  92. package/dist/config/codex-hooks.js +8 -10
  93. package/dist/config/codex-hooks.js.map +1 -1
  94. package/dist/config/commit-lore-guard.d.ts +1 -0
  95. package/dist/config/commit-lore-guard.d.ts.map +1 -1
  96. package/dist/config/commit-lore-guard.js +29 -3
  97. package/dist/config/commit-lore-guard.js.map +1 -1
  98. package/dist/config/generator.d.ts +17 -1
  99. package/dist/config/generator.d.ts.map +1 -1
  100. package/dist/config/generator.js +124 -11
  101. package/dist/config/generator.js.map +1 -1
  102. package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js +21 -0
  103. package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js.map +1 -1
  104. package/dist/goal-workflows/codex-goal-snapshot.d.ts +4 -0
  105. package/dist/goal-workflows/codex-goal-snapshot.d.ts.map +1 -1
  106. package/dist/goal-workflows/codex-goal-snapshot.js +50 -3
  107. package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -1
  108. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +27 -6
  109. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  110. package/dist/hooks/__tests__/consensus-execution-handoff.test.d.ts +1 -1
  111. package/dist/hooks/__tests__/consensus-execution-handoff.test.js +13 -11
  112. package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
  113. package/dist/hooks/__tests__/deep-interview-contract.test.js +4 -3
  114. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  115. package/dist/hooks/__tests__/keyword-detector.test.js +173 -17
  116. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  117. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +33 -0
  118. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  119. package/dist/hooks/__tests__/prometheus-strict-contract.test.d.ts +2 -0
  120. package/dist/hooks/__tests__/prometheus-strict-contract.test.d.ts.map +1 -0
  121. package/dist/hooks/__tests__/prometheus-strict-contract.test.js +320 -0
  122. package/dist/hooks/__tests__/prometheus-strict-contract.test.js.map +1 -0
  123. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js +12 -0
  124. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js.map +1 -1
  125. package/dist/hooks/__tests__/research-workflow-boundaries.test.d.ts +2 -0
  126. package/dist/hooks/__tests__/research-workflow-boundaries.test.d.ts.map +1 -0
  127. package/dist/hooks/__tests__/research-workflow-boundaries.test.js +35 -0
  128. package/dist/hooks/__tests__/research-workflow-boundaries.test.js.map +1 -0
  129. package/dist/hooks/extensibility/__tests__/dispatcher.test.js +26 -3
  130. package/dist/hooks/extensibility/__tests__/dispatcher.test.js.map +1 -1
  131. package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
  132. package/dist/hooks/extensibility/dispatcher.js +29 -14
  133. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  134. package/dist/hooks/keyword-detector.d.ts +1 -1
  135. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  136. package/dist/hooks/keyword-detector.js +36 -9
  137. package/dist/hooks/keyword-detector.js.map +1 -1
  138. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  139. package/dist/hooks/keyword-registry.js +1 -0
  140. package/dist/hooks/keyword-registry.js.map +1 -1
  141. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  142. package/dist/hooks/prompt-guidance-contract.js +14 -2
  143. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  144. package/dist/hud/__tests__/hud-tmux-injection.test.js +36 -8
  145. package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
  146. package/dist/hud/__tests__/reconcile.test.js +122 -11
  147. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  148. package/dist/hud/__tests__/render.test.js +84 -0
  149. package/dist/hud/__tests__/render.test.js.map +1 -1
  150. package/dist/hud/__tests__/resource-leak-watch.test.d.ts +2 -0
  151. package/dist/hud/__tests__/resource-leak-watch.test.d.ts.map +1 -0
  152. package/dist/hud/__tests__/resource-leak-watch.test.js +28 -0
  153. package/dist/hud/__tests__/resource-leak-watch.test.js.map +1 -0
  154. package/dist/hud/__tests__/state.test.js +51 -1
  155. package/dist/hud/__tests__/state.test.js.map +1 -1
  156. package/dist/hud/__tests__/tmux.test.js +69 -23
  157. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  158. package/dist/hud/index.d.ts +2 -2
  159. package/dist/hud/index.d.ts.map +1 -1
  160. package/dist/hud/index.js +17 -6
  161. package/dist/hud/index.js.map +1 -1
  162. package/dist/hud/reconcile.d.ts.map +1 -1
  163. package/dist/hud/reconcile.js +6 -3
  164. package/dist/hud/reconcile.js.map +1 -1
  165. package/dist/hud/render.d.ts.map +1 -1
  166. package/dist/hud/render.js +26 -0
  167. package/dist/hud/render.js.map +1 -1
  168. package/dist/hud/state.d.ts +2 -1
  169. package/dist/hud/state.d.ts.map +1 -1
  170. package/dist/hud/state.js +62 -1
  171. package/dist/hud/state.js.map +1 -1
  172. package/dist/hud/tmux.d.ts +10 -3
  173. package/dist/hud/tmux.d.ts.map +1 -1
  174. package/dist/hud/tmux.js +60 -11
  175. package/dist/hud/tmux.js.map +1 -1
  176. package/dist/hud/types.d.ts +22 -0
  177. package/dist/hud/types.d.ts.map +1 -1
  178. package/dist/hud/types.js.map +1 -1
  179. package/dist/notifications/__tests__/http-client-resource.test.d.ts +2 -0
  180. package/dist/notifications/__tests__/http-client-resource.test.d.ts.map +1 -0
  181. package/dist/notifications/__tests__/http-client-resource.test.js +41 -0
  182. package/dist/notifications/__tests__/http-client-resource.test.js.map +1 -0
  183. package/dist/notifications/__tests__/verbosity.test.js +20 -0
  184. package/dist/notifications/__tests__/verbosity.test.js.map +1 -1
  185. package/dist/notifications/config.d.ts.map +1 -1
  186. package/dist/notifications/config.js +6 -3
  187. package/dist/notifications/config.js.map +1 -1
  188. package/dist/notifications/http-client.d.ts.map +1 -1
  189. package/dist/notifications/http-client.js +78 -27
  190. package/dist/notifications/http-client.js.map +1 -1
  191. package/dist/notifications/types.d.ts +2 -0
  192. package/dist/notifications/types.d.ts.map +1 -1
  193. package/dist/openclaw/__tests__/dispatcher.test.js +49 -1
  194. package/dist/openclaw/__tests__/dispatcher.test.js.map +1 -1
  195. package/dist/openclaw/dispatcher.d.ts +7 -4
  196. package/dist/openclaw/dispatcher.d.ts.map +1 -1
  197. package/dist/openclaw/dispatcher.js +32 -69
  198. package/dist/openclaw/dispatcher.js.map +1 -1
  199. package/dist/pipeline/__tests__/orchestrator.test.js +128 -4
  200. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  201. package/dist/pipeline/__tests__/stages.test.js +460 -9
  202. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  203. package/dist/pipeline/index.d.ts +8 -2
  204. package/dist/pipeline/index.d.ts.map +1 -1
  205. package/dist/pipeline/index.js +5 -2
  206. package/dist/pipeline/index.js.map +1 -1
  207. package/dist/pipeline/orchestrator.d.ts +5 -4
  208. package/dist/pipeline/orchestrator.d.ts.map +1 -1
  209. package/dist/pipeline/orchestrator.js +85 -17
  210. package/dist/pipeline/orchestrator.js.map +1 -1
  211. package/dist/pipeline/stages/code-review.d.ts +2 -2
  212. package/dist/pipeline/stages/code-review.d.ts.map +1 -1
  213. package/dist/pipeline/stages/code-review.js +5 -3
  214. package/dist/pipeline/stages/code-review.js.map +1 -1
  215. package/dist/pipeline/stages/deep-interview.d.ts +15 -0
  216. package/dist/pipeline/stages/deep-interview.d.ts.map +1 -0
  217. package/dist/pipeline/stages/deep-interview.js +32 -0
  218. package/dist/pipeline/stages/deep-interview.js.map +1 -0
  219. package/dist/pipeline/stages/ralph-verify.d.ts +5 -5
  220. package/dist/pipeline/stages/ralph-verify.d.ts.map +1 -1
  221. package/dist/pipeline/stages/ralph-verify.js +2 -2
  222. package/dist/pipeline/stages/ralph-verify.js.map +1 -1
  223. package/dist/pipeline/stages/ralplan.d.ts.map +1 -1
  224. package/dist/pipeline/stages/ralplan.js +41 -6
  225. package/dist/pipeline/stages/ralplan.js.map +1 -1
  226. package/dist/pipeline/stages/ultragoal.d.ts +19 -0
  227. package/dist/pipeline/stages/ultragoal.d.ts.map +1 -0
  228. package/dist/pipeline/stages/ultragoal.js +38 -0
  229. package/dist/pipeline/stages/ultragoal.js.map +1 -0
  230. package/dist/pipeline/stages/ultraqa.d.ts +30 -0
  231. package/dist/pipeline/stages/ultraqa.d.ts.map +1 -0
  232. package/dist/pipeline/stages/ultraqa.js +46 -0
  233. package/dist/pipeline/stages/ultraqa.js.map +1 -0
  234. package/dist/pipeline/types.d.ts +8 -6
  235. package/dist/pipeline/types.d.ts.map +1 -1
  236. package/dist/pipeline/types.js +2 -2
  237. package/dist/question/__tests__/ui.test.js +43 -10
  238. package/dist/question/__tests__/ui.test.js.map +1 -1
  239. package/dist/question/ui.d.ts +12 -0
  240. package/dist/question/ui.d.ts.map +1 -1
  241. package/dist/question/ui.js +83 -46
  242. package/dist/question/ui.js.map +1 -1
  243. package/dist/ralplan/__tests__/runtime.test.js +200 -10
  244. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  245. package/dist/ralplan/consensus-gate.d.ts +23 -0
  246. package/dist/ralplan/consensus-gate.d.ts.map +1 -0
  247. package/dist/ralplan/consensus-gate.js +212 -0
  248. package/dist/ralplan/consensus-gate.js.map +1 -0
  249. package/dist/ralplan/runtime.d.ts +25 -0
  250. package/dist/ralplan/runtime.d.ts.map +1 -1
  251. package/dist/ralplan/runtime.js +144 -8
  252. package/dist/ralplan/runtime.js.map +1 -1
  253. package/dist/scripts/__tests__/codex-native-hook.test.js +1358 -79
  254. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  255. package/dist/scripts/__tests__/docs-site-contract.test.d.ts +2 -0
  256. package/dist/scripts/__tests__/docs-site-contract.test.d.ts.map +1 -0
  257. package/dist/scripts/__tests__/docs-site-contract.test.js +42 -0
  258. package/dist/scripts/__tests__/docs-site-contract.test.js.map +1 -0
  259. package/dist/scripts/__tests__/notify-dispatcher.test.js +115 -2
  260. package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -1
  261. package/dist/scripts/__tests__/run-test-files.test.js +57 -0
  262. package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
  263. package/dist/scripts/__tests__/smoke-packed-install.test.js +23 -1
  264. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  265. package/dist/scripts/__tests__/verify-native-agents.test.js +18 -3
  266. package/dist/scripts/__tests__/verify-native-agents.test.js.map +1 -1
  267. package/dist/scripts/cleanup-explore-harness.js +1 -0
  268. package/dist/scripts/cleanup-explore-harness.js.map +1 -1
  269. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  270. package/dist/scripts/codex-native-hook.js +372 -44
  271. package/dist/scripts/codex-native-hook.js.map +1 -1
  272. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  273. package/dist/scripts/codex-native-pre-post.js +9 -1
  274. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  275. package/dist/scripts/notify-dispatcher.js +188 -4
  276. package/dist/scripts/notify-dispatcher.js.map +1 -1
  277. package/dist/scripts/notify-hook/process-runner.d.ts.map +1 -1
  278. package/dist/scripts/notify-hook/process-runner.js +39 -17
  279. package/dist/scripts/notify-hook/process-runner.js.map +1 -1
  280. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  281. package/dist/scripts/notify-hook/team-dispatch.js +9 -5
  282. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  283. package/dist/scripts/notify-hook/team-tmux-guard.d.ts +1 -1
  284. package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
  285. package/dist/scripts/notify-hook/team-tmux-guard.js +7 -1
  286. package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
  287. package/dist/scripts/run-test-files.js +13 -0
  288. package/dist/scripts/run-test-files.js.map +1 -1
  289. package/dist/scripts/smoke-packed-install.d.ts +3 -0
  290. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  291. package/dist/scripts/smoke-packed-install.js +99 -1
  292. package/dist/scripts/smoke-packed-install.js.map +1 -1
  293. package/dist/scripts/sync-plugin-mirror.js +2 -2
  294. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  295. package/dist/scripts/verify-native-agents.js +2 -2
  296. package/dist/scripts/verify-native-agents.js.map +1 -1
  297. package/dist/sidecar/__tests__/resource-leak-watch.test.d.ts +2 -0
  298. package/dist/sidecar/__tests__/resource-leak-watch.test.d.ts.map +1 -0
  299. package/dist/sidecar/__tests__/resource-leak-watch.test.js +38 -0
  300. package/dist/sidecar/__tests__/resource-leak-watch.test.js.map +1 -0
  301. package/dist/sidecar/index.d.ts +1 -1
  302. package/dist/sidecar/index.d.ts.map +1 -1
  303. package/dist/sidecar/index.js +29 -12
  304. package/dist/sidecar/index.js.map +1 -1
  305. package/dist/state/__tests__/operations-ralph-phase.test.js +88 -1
  306. package/dist/state/__tests__/operations-ralph-phase.test.js.map +1 -1
  307. package/dist/state/__tests__/workflow-transition.test.js +6 -0
  308. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  309. package/dist/state/operations.d.ts.map +1 -1
  310. package/dist/state/operations.js +11 -0
  311. package/dist/state/operations.js.map +1 -1
  312. package/dist/state/workflow-transition.d.ts +1 -1
  313. package/dist/state/workflow-transition.d.ts.map +1 -1
  314. package/dist/state/workflow-transition.js +7 -0
  315. package/dist/state/workflow-transition.js.map +1 -1
  316. package/dist/subagents/tracker.d.ts.map +1 -1
  317. package/dist/subagents/tracker.js +4 -3
  318. package/dist/subagents/tracker.js.map +1 -1
  319. package/dist/team/__tests__/runtime.test.js +36 -44
  320. package/dist/team/__tests__/runtime.test.js.map +1 -1
  321. package/dist/team/__tests__/tmux-session.test.js +163 -15
  322. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  323. package/dist/team/runtime.d.ts.map +1 -1
  324. package/dist/team/runtime.js +10 -20
  325. package/dist/team/runtime.js.map +1 -1
  326. package/dist/team/tmux-session.d.ts.map +1 -1
  327. package/dist/team/tmux-session.js +51 -21
  328. package/dist/team/tmux-session.js.map +1 -1
  329. package/dist/ultragoal/__tests__/artifacts.test.js +764 -10
  330. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  331. package/dist/ultragoal/__tests__/docs-contract.test.js +57 -1
  332. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
  333. package/dist/ultragoal/__tests__/steering-fixtures.d.ts +68 -0
  334. package/dist/ultragoal/__tests__/steering-fixtures.d.ts.map +1 -0
  335. package/dist/ultragoal/__tests__/steering-fixtures.js +259 -0
  336. package/dist/ultragoal/__tests__/steering-fixtures.js.map +1 -0
  337. package/dist/ultragoal/__tests__/steering-fixtures.test.d.ts +2 -0
  338. package/dist/ultragoal/__tests__/steering-fixtures.test.d.ts.map +1 -0
  339. package/dist/ultragoal/__tests__/steering-fixtures.test.js +65 -0
  340. package/dist/ultragoal/__tests__/steering-fixtures.test.js.map +1 -0
  341. package/dist/ultragoal/artifacts.d.ts +97 -2
  342. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  343. package/dist/ultragoal/artifacts.js +837 -256
  344. package/dist/ultragoal/artifacts.js.map +1 -1
  345. package/dist/utils/__tests__/sleep-resource.test.d.ts +2 -0
  346. package/dist/utils/__tests__/sleep-resource.test.d.ts.map +1 -0
  347. package/dist/utils/__tests__/sleep-resource.test.js +39 -0
  348. package/dist/utils/__tests__/sleep-resource.test.js.map +1 -0
  349. package/dist/utils/sleep.d.ts.map +1 -1
  350. package/dist/utils/sleep.js +17 -6
  351. package/dist/utils/sleep.js.map +1 -1
  352. package/package.json +2 -1
  353. package/plugins/oh-my-codex/.codex-plugin/plugin.json +4 -3
  354. package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +56 -0
  355. package/plugins/oh-my-codex/hooks/hooks.json +77 -0
  356. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +92 -50
  357. package/plugins/oh-my-codex/skills/autoresearch/SKILL.md +4 -0
  358. package/plugins/oh-my-codex/skills/autoresearch-goal/SKILL.md +1 -1
  359. package/plugins/oh-my-codex/skills/best-practice-research/SKILL.md +1 -1
  360. package/plugins/oh-my-codex/skills/cancel/SKILL.md +2 -2
  361. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +8 -8
  362. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +1 -1
  363. package/plugins/oh-my-codex/skills/pipeline/SKILL.md +23 -12
  364. package/plugins/oh-my-codex/skills/plan/SKILL.md +8 -8
  365. package/plugins/oh-my-codex/skills/prometheus-strict/README.md +35 -0
  366. package/plugins/oh-my-codex/skills/prometheus-strict/SKILL.md +219 -0
  367. package/plugins/oh-my-codex/skills/ralph/SKILL.md +7 -0
  368. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +22 -7
  369. package/plugins/oh-my-codex/skills/team/SKILL.md +1 -1
  370. package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +38 -4
  371. package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +1 -1
  372. package/prompts/planner.md +1 -1
  373. package/prompts/prometheus-strict-metis.md +274 -0
  374. package/prompts/prometheus-strict-momus.md +82 -0
  375. package/prompts/prometheus-strict-oracle.md +107 -0
  376. package/prompts/researcher.md +22 -3
  377. package/skills/autopilot/SKILL.md +92 -50
  378. package/skills/autoresearch/SKILL.md +4 -0
  379. package/skills/autoresearch-goal/SKILL.md +1 -1
  380. package/skills/best-practice-research/SKILL.md +1 -1
  381. package/skills/cancel/SKILL.md +2 -2
  382. package/skills/deep-interview/SKILL.md +8 -8
  383. package/skills/omx-setup/SKILL.md +1 -1
  384. package/skills/pipeline/SKILL.md +23 -12
  385. package/skills/plan/SKILL.md +8 -8
  386. package/skills/prometheus-strict/README.md +35 -0
  387. package/skills/prometheus-strict/SKILL.md +219 -0
  388. package/skills/ralph/SKILL.md +7 -0
  389. package/skills/ralplan/SKILL.md +22 -7
  390. package/skills/team/SKILL.md +1 -1
  391. package/skills/ultragoal/SKILL.md +38 -4
  392. package/skills/ultrawork/SKILL.md +1 -1
  393. package/src/scripts/__tests__/codex-native-hook.test.ts +1757 -210
  394. package/src/scripts/__tests__/docs-site-contract.test.ts +47 -0
  395. package/src/scripts/__tests__/notify-dispatcher.test.ts +132 -3
  396. package/src/scripts/__tests__/run-test-files.test.ts +67 -0
  397. package/src/scripts/__tests__/smoke-packed-install.test.ts +31 -0
  398. package/src/scripts/__tests__/verify-native-agents.test.ts +23 -3
  399. package/src/scripts/cleanup-explore-harness.ts +1 -0
  400. package/src/scripts/codex-native-hook.ts +393 -40
  401. package/src/scripts/codex-native-pre-post.ts +16 -1
  402. package/src/scripts/notify-dispatcher.ts +202 -4
  403. package/src/scripts/notify-hook/process-runner.ts +40 -16
  404. package/src/scripts/notify-hook/team-dispatch.ts +9 -5
  405. package/src/scripts/notify-hook/team-tmux-guard.ts +7 -0
  406. package/src/scripts/run-test-files.ts +13 -0
  407. package/src/scripts/smoke-packed-install.ts +105 -0
  408. package/src/scripts/sync-plugin-mirror.ts +3 -3
  409. package/src/scripts/verify-native-agents.ts +2 -2
  410. package/templates/catalog-manifest.json +22 -0
@@ -3,8 +3,9 @@ import { closeSync, existsSync, openSync, readFileSync, readSync } from "fs";
3
3
  import { appendFile, mkdir, readFile, readdir, stat, writeFile } from "fs/promises";
4
4
  import { extname, join, relative, resolve } from "path";
5
5
  import { pathToFileURL } from "url";
6
- import { readModeState, readModeStateForActiveDecision, readModeStateForSession, updateModeState } from "../modes/base.js";
6
+ import { readModeStateForActiveDecision, readModeStateForSession, updateModeState } from "../modes/base.js";
7
7
  import {
8
+ SKILL_ACTIVE_STATE_FILE,
8
9
  extractSessionIdFromInitializedStatePath,
9
10
  getSkillActiveStatePathsForStateDir,
10
11
  listActiveSkills,
@@ -14,6 +15,7 @@ import {
14
15
  } from "../state/skill-active.js";
15
16
  import {
16
17
  readSubagentSessionSummary,
18
+ readSubagentTrackingState,
17
19
  recordSubagentTurnForSession,
18
20
  } from "../subagents/tracker.js";
19
21
  import { resolveCanonicalTeamStateRoot, resolveWorkerNotifyTeamStateRootPath } from "../team/state-root.js";
@@ -69,6 +71,7 @@ import {
69
71
  } from "../hooks/extensibility/events.js";
70
72
  import type { HookEventEnvelope } from "../hooks/extensibility/types.js";
71
73
  import { dispatchHookEventRuntime } from "../hooks/extensibility/runtime.js";
74
+ import { getNotificationConfig, getVerbosity } from "../notifications/config.js";
72
75
  import { reconcileHudForPromptSubmit } from "../hud/reconcile.js";
73
76
  import {
74
77
  onPreCompact as buildWikiPreCompactContext,
@@ -78,6 +81,11 @@ import { readAutoresearchCompletionStatus, readAutoresearchModeStateForActiveDec
78
81
  import { readRunState } from "../runtime/run-state.js";
79
82
  import { evaluateRalphCompletionAuditEvidence, isRalphCompletePhase } from "../ralph/completion-audit.js";
80
83
  import { getRunContinuationSnapshot, shouldContinueRun } from "../runtime/run-loop.js";
84
+ import {
85
+ parseUltragoalSteeringDirective,
86
+ steerUltragoal,
87
+ type UltragoalSteeringProposal,
88
+ } from "../ultragoal/artifacts.js";
81
89
  import { triagePrompt } from "../hooks/triage-heuristic.js";
82
90
  import { readTriageConfig } from "../hooks/triage-config.js";
83
91
  import {
@@ -291,18 +299,39 @@ async function isNativeSubagentHook(
291
299
  nativeSessionId: string,
292
300
  threadId: string,
293
301
  ): Promise<boolean> {
294
- const sessionId = canonicalSessionId.trim();
295
- if (!sessionId) return false;
296
-
297
- const summary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
298
- if (!summary) return false;
299
-
300
302
  const candidateIds = [nativeSessionId, threadId]
301
303
  .map((value) => value.trim())
302
304
  .filter(Boolean);
303
305
  if (candidateIds.length === 0) return false;
304
306
 
305
- return candidateIds.some((id) => summary.allSubagentThreadIds.includes(id));
307
+ const sessionId = canonicalSessionId.trim();
308
+ if (sessionId) {
309
+ const summary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
310
+ if (summary && candidateIds.some((id) => summary.allSubagentThreadIds.includes(id))) {
311
+ return true;
312
+ }
313
+ }
314
+
315
+ // Native Codex resume can report the child native session as the canonical
316
+ // session id before OMX reconciles it back to the owning session. In that
317
+ // window the per-session summary lookup above misses the child and a
318
+ // subagent UserPromptSubmit can accidentally activate workflow keywords from
319
+ // quoted review context. Fall back to the global tracking index so any known
320
+ // subagent thread is treated as subagent-scoped, regardless of the current
321
+ // hook payload's session-id mapping.
322
+ const trackingState = await readSubagentTrackingState(cwd).catch(() => null);
323
+ if (!trackingState) return false;
324
+
325
+ return Object.values(trackingState.sessions).some((session) => (
326
+ candidateIds.some((id) => session.threads[id]?.kind === "subagent")
327
+ ));
328
+ }
329
+
330
+ function shouldSuppressSubagentLifecycleHookDispatch(): boolean {
331
+ const config = getNotificationConfig();
332
+ if (config?.includeChildAgents === true) return false;
333
+ const verbosity = getVerbosity(config);
334
+ return verbosity !== "agent" && verbosity !== "verbose";
306
335
  }
307
336
 
308
337
  async function recordIgnoredNativeSubagentSessionStart(
@@ -419,6 +448,104 @@ function readPromptText(payload: CodexHookPayload): string {
419
448
  return "";
420
449
  }
421
450
 
451
+
452
+ function extractBalancedJsonObject(text: string, startIndex: number): string | null {
453
+ let depth = 0;
454
+ let inString = false;
455
+ let escaped = false;
456
+ for (let index = startIndex; index < text.length; index++) {
457
+ const char = text[index];
458
+ if (inString) {
459
+ if (escaped) escaped = false;
460
+ else if (char === "\\") escaped = true;
461
+ else if (char === '"') inString = false;
462
+ continue;
463
+ }
464
+ if (char === '"') {
465
+ inString = true;
466
+ continue;
467
+ }
468
+ if (char === "{") depth += 1;
469
+ else if (char === "}") {
470
+ depth -= 1;
471
+ if (depth === 0) return text.slice(startIndex, index + 1);
472
+ }
473
+ }
474
+ return null;
475
+ }
476
+
477
+ function normalizePromptSteeringProposal(raw: unknown, prompt: string): UltragoalSteeringProposal | null {
478
+ const candidate = safeObject(raw);
479
+ const nested = candidate.omx_ultragoal_steer ?? candidate.ultragoal_steer ?? candidate.steering ?? candidate;
480
+ const proposal = parseUltragoalSteeringDirective(JSON.stringify(nested));
481
+ if (!proposal) return null;
482
+ if (proposal.source !== "user_prompt_submit") return null;
483
+ const normalized = prompt.trim().toLowerCase();
484
+ return {
485
+ ...proposal,
486
+ directiveText: proposal.directiveText ?? safeContextSnippet(prompt, 600),
487
+ promptSignature: proposal.promptSignature ?? promptSignature(normalized),
488
+ idempotencyKey: proposal.idempotencyKey ?? `user_prompt_submit:${promptSignature(normalized)}`,
489
+ };
490
+ }
491
+
492
+ function parseUserPromptUltragoalSteeringDirective(prompt: string): UltragoalSteeringProposal | null {
493
+ const trimmed = prompt.trim();
494
+ if (!trimmed) return null;
495
+ const fenced = trimmed.match(/```(?:omx-ultragoal-steer|ultragoal-steer)\s*([\s\S]*?)```/i);
496
+ if (fenced?.[1]) {
497
+ try {
498
+ return normalizePromptSteeringProposal(JSON.parse(fenced[1]), prompt);
499
+ } catch {
500
+ return null;
501
+ }
502
+ }
503
+
504
+ const label = trimmed.match(/(?:^|\n)\s*(?:OMX_ULTRAGOAL_STEER|omx\.ultragoal\.steer|omx ultragoal steer)\s*:\s*{/i);
505
+ if (label?.index !== undefined) {
506
+ const brace = trimmed.indexOf("{", label.index);
507
+ const json = brace >= 0 ? extractBalancedJsonObject(trimmed, brace) : null;
508
+ if (json) {
509
+ try {
510
+ return normalizePromptSteeringProposal(JSON.parse(json), prompt);
511
+ } catch {
512
+ return null;
513
+ }
514
+ }
515
+ }
516
+
517
+ if (trimmed.startsWith("{")) {
518
+ try {
519
+ const parsed = JSON.parse(trimmed);
520
+ const object = safeObject(parsed);
521
+ if ("omx_ultragoal_steer" in object || "ultragoal_steer" in object) {
522
+ return normalizePromptSteeringProposal(parsed, prompt);
523
+ }
524
+ } catch {
525
+ return null;
526
+ }
527
+ }
528
+ return null;
529
+ }
530
+
531
+ async function applyUserPromptUltragoalSteering(cwd: string, prompt: string): Promise<string | null> {
532
+ const proposal = parseUserPromptUltragoalSteeringDirective(prompt);
533
+ if (!proposal) return null;
534
+ try {
535
+ const result = await steerUltragoal(cwd, proposal);
536
+ const status = result.deduped ? "deduped" : result.accepted ? "accepted" : "rejected";
537
+ const reasons = result.rejectedReasons.length > 0 ? ` rejectedReasons=${result.rejectedReasons.join("; ")}` : "";
538
+ return [
539
+ `OMX native UserPromptSubmit applied bounded .omx/ultragoal steering for G002-cli-and-prompt-submit-bridge: ${status}.`,
540
+ `mutation=${result.audit.kind}; source=${result.audit.source}; targets=${result.audit.targetGoalIds.join(",") || "none"}; idempotencyKey=${result.audit.idempotencyKey ?? "none"}.${reasons}`,
541
+ "Only explicit structured steering directives are parsed; normal prose is ignored and cannot mutate .omx/ultragoal.",
542
+ ].join(" ");
543
+ } catch (error) {
544
+ const message = error instanceof Error ? error.message : String(error);
545
+ return `OMX native UserPromptSubmit rejected bounded .omx/ultragoal steering for G002-cli-and-prompt-submit-bridge: ${message}`;
546
+ }
547
+ }
548
+
422
549
  function sanitizePayloadForHookContext(
423
550
  payload: CodexHookPayload,
424
551
  hookEventName: CodexHookEventName,
@@ -793,13 +920,12 @@ async function reopenRalphCompletionAuditBlock(block: RalphCompletionAuditBlockS
793
920
  const nowIso = new Date().toISOString();
794
921
  const next: Record<string, unknown> = {
795
922
  ...block.state,
796
- active: true,
797
- current_phase: "verifying",
923
+ active: false,
924
+ current_phase: "complete",
798
925
  completion_audit_gate: "blocked",
799
926
  completion_audit_missing_reason: block.reason,
800
927
  completion_audit_blocked_at: nowIso,
801
928
  };
802
- delete next.completed_at;
803
929
  await writeFile(block.path, JSON.stringify(next, null, 2));
804
930
  }
805
931
 
@@ -1573,6 +1699,17 @@ function buildSkillStateCliInstruction(mode: string, statePath: string): string
1573
1699
  return `skill: ${mode} activated and initial state initialized at ${statePath}; use CLI-first state updates via \`omx state write/read/clear --input '<json>' --json\`; use omx_state MCP only when explicit MCP compatibility is enabled.`;
1574
1700
  }
1575
1701
 
1702
+ function buildAutopilotPromptActivationNote(skillState?: SkillActiveState | null): string | null {
1703
+ if (skillState?.initialized_mode !== "autopilot") return null;
1704
+ return [
1705
+ "Autopilot protocol: the durable default chain is $deep-interview -> $ralplan -> $ultragoal (+ $team if needed) -> $code-review -> $ultraqa (deep-interview -> ralplan -> ultragoal -> code-review -> ultraqa).",
1706
+ "Start/resume at current_phase=deep-interview unless the task is clear and bounded; if deep-interview is intentionally skipped, persist and state an explicit deep_interview_gate.skip_reason before moving to ralplan.",
1707
+ "The ralplan phase is not complete until Planner output has been reviewed sequentially by Architect and then Critic; do not hand off to Ultragoal or implementation until the ralplan state/artifact records both ralplan_architect_review and ralplan_critic_review with approval or an explicit blocker.",
1708
+ "Do not silently fall back to ordinary $plan/ralplan-only handling; keep autopilot-state.json, skill-active-state.json, HUD/statusline, and Codex goal-mode handoff guidance visible while the workflow is active.",
1709
+ "When Codex goal tools are available, call get_goal/create_goal only from the active thread handoff and treat the active goal as the completion contract until code-review and ultraqa are clean.",
1710
+ ].join(" ");
1711
+ }
1712
+
1576
1713
  function buildAdditionalContextMessage(
1577
1714
  prompt: string,
1578
1715
  skillState?: SkillActiveState | null,
@@ -1604,8 +1741,9 @@ function buildAdditionalContextMessage(
1604
1741
  ? "Ultrawork protocol: ground the task before editing, define pass/fail acceptance criteria, keep shared-file work local, and use direct-tool plus background evidence lanes only for truly independent work. Direct ultrawork provides lightweight verification only; Ralph owns persistence and the full verified-completion promise."
1605
1742
  : null;
1606
1743
  const ultragoalPromptActivationNote = match.skill === "ultragoal"
1607
- ? "Ultragoal protocol: use `omx ultragoal create-goals` / `complete-goals` / `checkpoint` for `.omx/ultragoal` artifacts, then use Codex goal model tools only from the active agent handoff (`get_goal`, `create_goal`, `update_goal`) and never overwrite a different active Codex goal."
1744
+ ? "Ultragoal protocol: use `omx ultragoal create-goals` / `complete-goals` / `checkpoint` for `.omx/ultragoal` artifacts, then use Codex goal model tools only from the active agent handoff (`get_goal`, `create_goal`, `update_goal`) and never overwrite a different active Codex goal. Ultragoal does not call `/goal clear`; for multiple sequential ultragoal runs in one Codex session/thread, manually clear the completed Codex goal in the UI before creating the next aggregate goal."
1608
1745
  : null;
1746
+ const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState);
1609
1747
  const combinedTransitionMessage = (() => {
1610
1748
  if (!skillState?.transition_message) return null;
1611
1749
  if (matches.length <= 1 || activeSkills.length <= 1) return skillState.transition_message;
@@ -1633,6 +1771,7 @@ function buildAdditionalContextMessage(
1633
1771
  : null,
1634
1772
  promptPriorityMessage,
1635
1773
  ultragoalPromptActivationNote,
1774
+ autopilotPromptActivationNote,
1636
1775
  skillState.initialized_mode && skillState.initialized_state_path
1637
1776
  ? buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path)
1638
1777
  : null,
@@ -1659,6 +1798,7 @@ function buildAdditionalContextMessage(
1659
1798
  deepInterviewPromptActivationNote,
1660
1799
  ultraworkPromptActivationNote,
1661
1800
  ultragoalPromptActivationNote,
1801
+ autopilotPromptActivationNote,
1662
1802
  buildTeamRuntimeInstruction(cwd, payload),
1663
1803
  buildTeamHelpInstruction(cwd, payload),
1664
1804
  "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
@@ -1677,12 +1817,13 @@ function buildAdditionalContextMessage(
1677
1817
  deepInterviewPromptActivationNote,
1678
1818
  ultraworkPromptActivationNote,
1679
1819
  ultragoalPromptActivationNote,
1820
+ autopilotPromptActivationNote,
1680
1821
  ralphPromptActivationNote,
1681
1822
  "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
1682
1823
  ].join(" ");
1683
1824
  }
1684
1825
 
1685
- return [detectedKeywordMessage, promptPriorityMessage, ultragoalPromptActivationNote, "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules."].filter(Boolean).join(" ");
1826
+ return [detectedKeywordMessage, promptPriorityMessage, ultragoalPromptActivationNote, autopilotPromptActivationNote, "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules."].filter(Boolean).join(" ");
1686
1827
  }
1687
1828
 
1688
1829
  function parseTeamWorkerEnv(rawValue: string): { teamName: string; workerName: string } | null {
@@ -1867,6 +2008,9 @@ async function buildModeBasedStopOutput(
1867
2008
  cwd: string,
1868
2009
  sessionId?: string,
1869
2010
  ): Promise<Record<string, unknown> | null> {
2011
+ if (await readCanonicalTerminalRunStateForStop(cwd, sessionId, mode)) {
2012
+ return null;
2013
+ }
1870
2014
  const state = await readModeStateForActiveDecision(mode, sessionId?.trim() || undefined, cwd);
1871
2015
  if (!state || !shouldContinueRun(state)) return null;
1872
2016
  const phase = formatPhase(state.current_phase);
@@ -1895,6 +2039,22 @@ function reportsAutoresearchGoalObjectiveMismatch(text: string): boolean {
1895
2039
  && /objective mismatch/i.test(text);
1896
2040
  }
1897
2041
 
2042
+ function reportsBlockedPerformanceGoalObjectiveMismatch(state: unknown): boolean {
2043
+ const performanceState = safeObject(state);
2044
+ const lastValidation = safeObject(performanceState.lastValidation);
2045
+ if (safeString(performanceState.workflow) !== "performance-goal") return false;
2046
+ if (safeString(performanceState.status) !== "blocked") return false;
2047
+ if (safeString(lastValidation.status) !== "blocked") return false;
2048
+
2049
+ const evidence = [
2050
+ safeString(lastValidation.evidence),
2051
+ safeString(lastValidation.message),
2052
+ safeString(performanceState.evidence),
2053
+ safeString(performanceState.message),
2054
+ ].join(" ");
2055
+ return /objective mismatch/i.test(evidence);
2056
+ }
2057
+
1898
2058
  async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Promise<{ workflow: string; command: string; remediation?: string } | null> {
1899
2059
  const ultragoal = await readJsonIfExists(join(cwd, ".omx", "ultragoal", "goals.json"));
1900
2060
  const aggregateCompletion = safeObject(ultragoal?.aggregateCompletion);
@@ -1912,7 +2072,8 @@ async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Pro
1912
2072
  `If get_goal returns a completed task-scoped objective for the same aggregate ultragoal plan, checkpoint ${goalId} with evidence naming ${goalId} plus .omx/ultragoal/goals.json or ledger.jsonl and pass final quality-gate JSON; OMX will reconcile the completed planned scope without mutating Codex goal state.`,
1913
2073
  `If get_goal instead returns a different completed legacy objective and complete checkpointing fails, do not repeat --status complete in this thread.`,
1914
2074
  `Record the non-terminal blocker with: omx ultragoal checkpoint --goal-id ${goalId} --status blocked --codex-goal-json '<different completed get_goal JSON or path>' --evidence '<completed legacy Codex goal blocks create_goal in this thread>'.`,
1915
- "Then continue this ultragoal from a fresh Codex thread in the same repo/worktree and create the intended goal there.",
2075
+ `If get_goal itself is unavailable with a Codex DB/schema/context error such as "no such table: thread_goals", record an auditable safe-recovery blocker instead: omx ultragoal checkpoint --goal-id ${goalId} --status blocked --codex-goal-json '<unavailable get_goal error JSON or path>' --evidence '<get_goal unavailable due to Codex DB/schema/context error; safe recovery requires a working Codex goal context>'.`,
2076
+ "Then continue only from a Codex goal context with no active/completed conflicting goal in the same repo/worktree and create the intended goal there.",
1916
2077
  ].join(" "),
1917
2078
  };
1918
2079
  }
@@ -1922,6 +2083,9 @@ async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Pro
1922
2083
  if (!entry.isDirectory()) continue;
1923
2084
  const state = await readJsonIfExists(join(performanceRoot, entry.name, "state.json"));
1924
2085
  const status = safeString(state?.status);
2086
+ if (reportsBlockedPerformanceGoalObjectiveMismatch(state)) {
2087
+ continue;
2088
+ }
1925
2089
  if (state?.workflow === "performance-goal" && status && status !== "complete") {
1926
2090
  return {
1927
2091
  workflow: "performance-goal",
@@ -1948,8 +2112,8 @@ async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Pro
1948
2112
  workflow: "autoresearch-goal",
1949
2113
  command: `omx autoresearch-goal complete --slug ${safeString(mission.slug) || entry.name} --codex-goal-json '<get_goal JSON or path>'`,
1950
2114
  remediation: [
1951
- "If that command fails with a Codex goal objective mismatch after a fresh get_goal snapshot, do not repeat the same complete command blindly in this thread.",
1952
- "Either retry with a correct fresh snapshot or record an explicit blocked verdict for this autoresearch-goal and continue it from a fresh Codex thread.",
2115
+ "If that command fails with a Codex goal objective mismatch after a refreshed get_goal snapshot, do not repeat the same complete command blindly in this thread.",
2116
+ "Either retry with a correct refreshed snapshot or record an explicit blocked verdict for this autoresearch-goal and continue from the explicit blocker path.",
1953
2117
  ].join(" "),
1954
2118
  };
1955
2119
  }
@@ -1995,36 +2159,60 @@ async function buildGoalWorkflowReconciliationStopOutput(
1995
2159
  };
1996
2160
  }
1997
2161
 
2162
+ interface TeamModeStateForStop {
2163
+ state: Record<string, unknown>;
2164
+ scope: "session" | "root";
2165
+ }
2166
+
2167
+ function teamStateMatchesThreadForStop(
2168
+ state: Record<string, unknown>,
2169
+ threadId?: string,
2170
+ options: { requireOwnerThread?: boolean } = {},
2171
+ ): boolean {
2172
+ const normalizedThreadId = safeString(threadId).trim();
2173
+ if (!normalizedThreadId) return true;
2174
+
2175
+ const ownerThreadId = safeString(state.owner_codex_thread_id ?? state.thread_id).trim();
2176
+ if (!ownerThreadId) return options.requireOwnerThread !== true;
2177
+ return ownerThreadId === normalizedThreadId;
2178
+ }
2179
+
1998
2180
  async function readTeamModeStateForStop(
1999
2181
  cwd: string,
2000
2182
  stateDir: string,
2001
2183
  sessionId?: string,
2002
- ): Promise<Record<string, unknown> | null> {
2184
+ threadId?: string,
2185
+ ): Promise<TeamModeStateForStop | null> {
2003
2186
  const normalizedSessionId = safeString(sessionId).trim();
2004
- if (!normalizedSessionId) {
2005
- return await readModeState("team", cwd);
2006
- }
2187
+ if (!normalizedSessionId) return null;
2007
2188
 
2008
2189
  const scopedState = await readStopSessionPinnedState("team-state.json", cwd, normalizedSessionId, stateDir);
2009
- if (scopedState) return scopedState;
2190
+ if (scopedState) {
2191
+ return teamStateMatchesThreadForStop(scopedState, threadId)
2192
+ ? { state: scopedState, scope: "session" }
2193
+ : null;
2194
+ }
2010
2195
 
2011
2196
  const rootState = await readJsonIfExists(join(stateDir, "team-state.json"));
2012
2197
  if (rootState?.active !== true) return null;
2013
2198
 
2199
+ const teamName = safeString(rootState.team_name).trim();
2200
+ if (!teamName) return null;
2201
+
2014
2202
  const ownerSessionId = safeString(rootState.session_id).trim();
2015
- if (ownerSessionId && ownerSessionId !== normalizedSessionId) {
2016
- return null;
2017
- }
2203
+ if (!ownerSessionId || ownerSessionId !== normalizedSessionId) return null;
2204
+ if (!teamStateMatchesThreadForStop(rootState, threadId, { requireOwnerThread: true })) return null;
2018
2205
 
2019
- return rootState;
2206
+ return { state: rootState, scope: "root" };
2020
2207
  }
2021
2208
 
2022
- async function buildTeamStopOutput(cwd: string, sessionId?: string): Promise<Record<string, unknown> | null> {
2209
+ async function buildTeamStopOutput(cwd: string, sessionId?: string, threadId?: string): Promise<Record<string, unknown> | null> {
2023
2210
  if (await readCanonicalTerminalRunStateForStop(cwd, sessionId, "team")) {
2024
2211
  return null;
2025
2212
  }
2026
- const teamState = await readTeamModeStateForStop(cwd, getBaseStateDir(cwd), sessionId);
2027
- if (teamState?.active !== true) return null;
2213
+ const teamStateForStop = await readTeamModeStateForStop(cwd, getBaseStateDir(cwd), sessionId, threadId);
2214
+ if (!teamStateForStop || teamStateForStop.state.active !== true) return null;
2215
+ const teamState = teamStateForStop.state;
2028
2216
  const teamName = safeString(teamState.team_name).trim();
2029
2217
  if (teamName) {
2030
2218
  const canonicalTeamDir = join(resolveCanonicalTeamStateRoot(cwd), "team", teamName);
@@ -2033,7 +2221,9 @@ async function buildTeamStopOutput(cwd: string, sessionId?: string): Promise<Rec
2033
2221
  }
2034
2222
  }
2035
2223
  const coarsePhase = teamState.current_phase;
2036
- const canonicalPhase = teamName ? (await readTeamPhase(teamName, cwd))?.current_phase ?? coarsePhase : coarsePhase;
2224
+ const canonicalPhaseState = teamName ? await readTeamPhase(teamName, cwd) : null;
2225
+ if (teamStateForStop.scope === "root" && !canonicalPhaseState) return null;
2226
+ const canonicalPhase = canonicalPhaseState?.current_phase ?? coarsePhase;
2037
2227
  if (!isNonTerminalPhase(canonicalPhase)) return null;
2038
2228
  return buildTeamStopOutputForPhase(teamName, formatPhase(canonicalPhase));
2039
2229
  }
@@ -2140,6 +2330,151 @@ async function readStopSessionPinnedState(
2140
2330
  return readJsonIfExists(statePath);
2141
2331
  }
2142
2332
 
2333
+ const DEEP_INTERVIEW_ALLOWED_WRITE_PREFIXES = [
2334
+ ".omx/context",
2335
+ ".omx/interviews",
2336
+ ".omx/specs",
2337
+ ".omx/state",
2338
+ ] as const;
2339
+
2340
+ const DEEP_INTERVIEW_IMPLEMENTATION_TOOL_NAMES = new Set([
2341
+ "Write",
2342
+ "Edit",
2343
+ "MultiEdit",
2344
+ "apply_patch",
2345
+ "ApplyPatch",
2346
+ ]);
2347
+
2348
+ function isActiveDeepInterviewPhase(state: Record<string, unknown> | null): boolean {
2349
+ if (!state || state.active !== true) return false;
2350
+ const mode = safeString(state.mode).trim();
2351
+ if (mode && mode !== "deep-interview") return false;
2352
+ const phase = safeString(state.current_phase ?? state.currentPhase).trim().toLowerCase();
2353
+ if (phase && (TERMINAL_MODE_PHASES.has(phase) || phase === "completing")) return false;
2354
+ return true;
2355
+ }
2356
+
2357
+ function isAllowedDeepInterviewArtifactPath(cwd: string, rawPath: string): boolean {
2358
+ const trimmed = rawPath.trim().replace(/^['"]|['"]$/g, "");
2359
+ if (!trimmed || trimmed.includes("\0")) return false;
2360
+ let relativePath: string;
2361
+ try {
2362
+ const absolute = resolve(cwd, trimmed);
2363
+ relativePath = relative(cwd, absolute).replace(/\\/g, "/");
2364
+ } catch {
2365
+ return false;
2366
+ }
2367
+ if (!relativePath || relativePath.startsWith("..") || relativePath.startsWith("/")) return false;
2368
+ return DEEP_INTERVIEW_ALLOWED_WRITE_PREFIXES.some((prefix) => (
2369
+ relativePath === prefix || relativePath.startsWith(`${prefix}/`)
2370
+ ));
2371
+ }
2372
+
2373
+ function readPreToolUseCommand(payload: CodexHookPayload): string {
2374
+ const toolInput = safeObject(payload.tool_input);
2375
+ return safeString(toolInput.command).trim();
2376
+ }
2377
+
2378
+ function readPreToolUsePathCandidates(payload: CodexHookPayload): string[] {
2379
+ const input = safeObject(payload.tool_input);
2380
+ const candidates = [
2381
+ input.file_path,
2382
+ input.filePath,
2383
+ input.path,
2384
+ input.target_path,
2385
+ input.targetPath,
2386
+ ];
2387
+ return candidates.map((candidate) => safeString(candidate).trim()).filter(Boolean);
2388
+ }
2389
+
2390
+ function commandHasDeepInterviewWriteIntent(command: string): boolean {
2391
+ return /\bapply_patch\b/.test(command)
2392
+ || /(?:^|[;&|]\s*)(?:cat|printf|echo)\b[\s\S]{0,240}>\s*[^\s&|;]+/.test(command)
2393
+ || /\btee\s+(?:-a\s+)?[^\s&|;]+/.test(command)
2394
+ || /\bsed\s+(?:[^\n;&|]*\s)?-i(?:\b|['"])/.test(command)
2395
+ || /\b(?:python3?|node|perl|ruby)\b[\s\S]{0,260}\b(?:writeFileSync|writeFile|write_text|open\([^)]*["']w|File\.write|Path\()/.test(command)
2396
+ || /\b(?:git\s+(?:checkout|switch|restore|reset|apply|am|merge|rebase)|npm\s+(?:install|i|ci)|pnpm\s+(?:install|i)|yarn\s+(?:install|add))\b/.test(command);
2397
+ }
2398
+
2399
+ function extractDeepInterviewCommandWriteTargets(command: string): string[] {
2400
+ const targets: string[] = [];
2401
+ for (const match of command.matchAll(/(?:^|[^>])>{1,2}\s*(["']?)([^\s&|;<>]+)\1/g)) {
2402
+ const candidate = safeString(match[2]).trim();
2403
+ if (candidate) targets.push(candidate);
2404
+ }
2405
+ for (const match of command.matchAll(/\btee\s+(?:-a\s+)?(["']?)([^\s&|;<>]+)\1/g)) {
2406
+ const candidate = safeString(match[2]).trim();
2407
+ if (candidate) targets.push(candidate);
2408
+ }
2409
+ return targets;
2410
+ }
2411
+
2412
+ function isAllowedDeepInterviewBashWrite(cwd: string, command: string): boolean {
2413
+ if (!commandHasDeepInterviewWriteIntent(command)) return true;
2414
+ if (/\bomx\s+(?:state\s+(?:write|read|clear)|question)\b/.test(command)) return true;
2415
+ const targets = extractDeepInterviewCommandWriteTargets(command);
2416
+ return targets.length > 0 && targets.every((target) => isAllowedDeepInterviewArtifactPath(cwd, target));
2417
+ }
2418
+
2419
+ async function readActiveDeepInterviewStateForPreToolUse(
2420
+ cwd: string,
2421
+ stateDir: string,
2422
+ sessionId: string,
2423
+ threadId: string,
2424
+ ): Promise<Record<string, unknown> | null> {
2425
+ const modeState = sessionId
2426
+ ? await readStopSessionPinnedState("deep-interview-state.json", cwd, sessionId, stateDir)
2427
+ : await readJsonIfExists(join(stateDir, "deep-interview-state.json"));
2428
+ if (!isActiveDeepInterviewPhase(modeState) || !modeState) return null;
2429
+ if (!modeStateMatchesSkillStopContext(modeState, cwd, sessionId)) return null;
2430
+
2431
+ const canonicalState = sessionId
2432
+ ? await readVisibleSkillActiveStateForStateDir(stateDir, sessionId)
2433
+ : await readSkillActiveState(join(stateDir, SKILL_ACTIVE_STATE_FILE));
2434
+ if (!canonicalState) return modeState;
2435
+ const hasActiveDeepInterviewSkill = listActiveSkills(canonicalState).some((entry) => (
2436
+ entry.skill === "deep-interview"
2437
+ && matchesSkillStopContext(entry, canonicalState, sessionId, threadId)
2438
+ ));
2439
+ return hasActiveDeepInterviewSkill ? modeState : null;
2440
+ }
2441
+
2442
+ async function buildDeepInterviewPreToolUseBoundaryOutput(
2443
+ payload: CodexHookPayload,
2444
+ cwd: string,
2445
+ stateDir: string,
2446
+ ): Promise<Record<string, unknown> | null> {
2447
+ const sessionId = readPayloadSessionId(payload);
2448
+ const threadId = readPayloadThreadId(payload);
2449
+ const activeState = await readActiveDeepInterviewStateForPreToolUse(cwd, stateDir, sessionId, threadId);
2450
+ if (!activeState) return null;
2451
+
2452
+ const toolName = safeString(payload.tool_name).trim();
2453
+ const command = readPreToolUseCommand(payload);
2454
+ const pathCandidates = readPreToolUsePathCandidates(payload);
2455
+ let blocked = false;
2456
+
2457
+ if (toolName === "Bash") {
2458
+ blocked = !isAllowedDeepInterviewBashWrite(cwd, command);
2459
+ } else if (DEEP_INTERVIEW_IMPLEMENTATION_TOOL_NAMES.has(toolName)) {
2460
+ blocked = pathCandidates.length === 0
2461
+ || !pathCandidates.every((candidate) => isAllowedDeepInterviewArtifactPath(cwd, candidate));
2462
+ }
2463
+
2464
+ if (!blocked) return null;
2465
+
2466
+ const phase = formatPhase(activeState.current_phase ?? activeState.currentPhase, "planning");
2467
+ return {
2468
+ decision: "block",
2469
+ reason: `Deep-interview is active (phase: ${phase}); implementation/write tools are blocked until an explicit handoff workflow is activated.`,
2470
+ hookSpecificOutput: {
2471
+ hookEventName: "PreToolUse",
2472
+ additionalContext:
2473
+ "Deep-interview is requirements/spec mode. Treat detailed user answers as interview/spec material, not implicit implementation authorization. You may write only deep-interview artifacts under `.omx/context/`, `.omx/interviews/`, `.omx/specs/`, or required `.omx/state/` files. To implement, first ask for or process an explicit transition such as `$ralplan`, `$autopilot`, `$ralph`, `$team`, or `$ultragoal`.",
2474
+ },
2475
+ };
2476
+ }
2477
+
2143
2478
  function matchesSkillStopContext(
2144
2479
  entry: { session_id?: string; thread_id?: string },
2145
2480
  state: { session_id?: string; thread_id?: string },
@@ -2777,6 +3112,7 @@ async function returnPersistentStopBlock(
2777
3112
  async function findCanonicalActiveTeamForSession(
2778
3113
  cwd: string,
2779
3114
  sessionId: string,
3115
+ threadId?: string,
2780
3116
  ): Promise<{ teamName: string; phase: string } | null> {
2781
3117
  if (!sessionId.trim()) return null;
2782
3118
  const teamsRoot = join(resolveCanonicalTeamStateRoot(cwd), "team");
@@ -2795,6 +3131,7 @@ async function findCanonicalActiveTeamForSession(
2795
3131
  if (!manifest || !phaseState) continue;
2796
3132
  const ownerSessionId = (manifest.leader?.session_id ?? "").trim();
2797
3133
  if (ownerSessionId && ownerSessionId !== sessionId.trim()) continue;
3134
+ if (!teamStateMatchesThreadForStop(manifest.leader as unknown as Record<string, unknown>, threadId)) continue;
2798
3135
  if (!isNonTerminalPhase(phaseState.current_phase)) continue;
2799
3136
 
2800
3137
  return {
@@ -2810,12 +3147,13 @@ async function resolveActiveTeamNameForStop(
2810
3147
  cwd: string,
2811
3148
  stateDir: string,
2812
3149
  sessionId: string,
3150
+ threadId?: string,
2813
3151
  ): Promise<string> {
2814
- const directState = await readTeamModeStateForStop(cwd, stateDir, sessionId);
2815
- const directTeamName = safeString(directState?.team_name).trim();
2816
- if (directState?.active === true && directTeamName) return directTeamName;
3152
+ const directState = await readTeamModeStateForStop(cwd, stateDir, sessionId, threadId);
3153
+ const directTeamName = safeString(directState?.state.team_name).trim();
3154
+ if (directState?.state.active === true && directTeamName) return directTeamName;
2817
3155
 
2818
- const canonicalTeam = await findCanonicalActiveTeamForSession(cwd, sessionId);
3156
+ const canonicalTeam = await findCanonicalActiveTeamForSession(cwd, sessionId, threadId);
2819
3157
  return canonicalTeam?.teamName ?? "";
2820
3158
  }
2821
3159
 
@@ -2827,7 +3165,7 @@ async function maybeBuildReleaseReadinessFinalizeStopOutput(
2827
3165
  ): Promise<{ matched: boolean; output: Record<string, unknown> | null }> {
2828
3166
  if (!sessionId) return { matched: false, output: null };
2829
3167
 
2830
- const teamName = await resolveActiveTeamNameForStop(cwd, stateDir, sessionId);
3168
+ const teamName = await resolveActiveTeamNameForStop(cwd, stateDir, sessionId, readPayloadThreadId(payload));
2831
3169
  if (!teamName) return { matched: false, output: null };
2832
3170
 
2833
3171
  const explicitReleaseReadinessContext =
@@ -3040,7 +3378,7 @@ async function buildStopHookOutput(
3040
3378
  `OMX Ralph completion audit is missing required evidence (${ralphCompletionAuditBlock.reason}; state: ${blockingPath}).`,
3041
3379
  "Continue verification and do not report complete yet.",
3042
3380
  "Record machine-readable completion evidence before stopping:",
3043
- "- either set state.completion_audit = { passed: true, prompt_to_artifact_checklist: [...], verification_evidence: [...] }",
3381
+ '- either set "completion_audit" on the Ralph state object, for example: omx state write --input \'{"mode":"ralph","active":false,"current_phase":"complete","completion_audit":{"passed":true,"prompt_to_artifact_checklist":["..."],"verification_evidence":["..."]}}\' --json',
3044
3382
  "- or set completion_audit_path / completion_audit_evidence_path to a repo-relative JSON file with those same fields.",
3045
3383
  "Markdown artifacts and flat top-level checklist/evidence fields are not accepted by the Ralph Stop gate.",
3046
3384
  ].join(" ");
@@ -3156,7 +3494,7 @@ async function buildStopHookOutput(
3156
3494
  );
3157
3495
  if (releaseReadinessFinalizeResult.matched) return releaseReadinessFinalizeResult.output;
3158
3496
 
3159
- const teamOutput = await buildTeamStopOutput(cwd, canonicalSessionId);
3497
+ const teamOutput = await buildTeamStopOutput(cwd, canonicalSessionId, threadId);
3160
3498
  if (teamOutput) {
3161
3499
  return await returnPersistentStopBlock(
3162
3500
  payload,
@@ -3188,7 +3526,7 @@ async function buildStopHookOutput(
3188
3526
 
3189
3527
  const canonicalTeam = await readCanonicalTerminalRunStateForStop(cwd, canonicalSessionId, "team")
3190
3528
  ? null
3191
- : await findCanonicalActiveTeamForSession(cwd, canonicalSessionId);
3529
+ : await findCanonicalActiveTeamForSession(cwd, canonicalSessionId, threadId);
3192
3530
  if (canonicalTeam) {
3193
3531
  const canonicalTeamOutput = buildTeamStopOutputForPhase(
3194
3532
  canonicalTeam.teamName,
@@ -3337,6 +3675,7 @@ export async function dispatchCodexNativeHook(
3337
3675
  let skillState: SkillActiveState | null = null;
3338
3676
  let triageAdditionalContext: string | null = null;
3339
3677
  let goalWorkflowAdditionalContext: string | null = null;
3678
+ let ultragoalSteeringAdditionalContext: string | null = null;
3340
3679
 
3341
3680
  const nativeSessionId = safeString(payload.session_id ?? payload.sessionId).trim();
3342
3681
  const threadId = safeString(payload.thread_id ?? payload.threadId).trim();
@@ -3345,11 +3684,13 @@ export async function dispatchCodexNativeHook(
3345
3684
  let canonicalSessionId = safeString(currentSessionState?.session_id).trim();
3346
3685
  let resolvedNativeSessionId = nativeSessionId;
3347
3686
  let skipCanonicalSessionStartContext = false;
3687
+ let isSubagentSessionStart = false;
3348
3688
 
3349
3689
  if (hookEventName === "SessionStart" && nativeSessionId) {
3350
3690
  const transcriptPath = safeString(payload.transcript_path ?? payload.transcriptPath).trim();
3351
3691
  const subagentSessionStart = readNativeSubagentSessionStartMetadata(transcriptPath);
3352
3692
  if (subagentSessionStart && canonicalSessionId) {
3693
+ isSubagentSessionStart = true;
3353
3694
  const belongsToCanonicalSession = await nativeSubagentSessionStartBelongsToCanonicalSession(
3354
3695
  cwd,
3355
3696
  canonicalSessionId,
@@ -3418,10 +3759,16 @@ export async function dispatchCodexNativeHook(
3418
3759
  .map((candidateSessionId) => isNativeSubagentHook(cwd, candidateSessionId, nativeSessionId, threadId)),
3419
3760
  )).some(Boolean)
3420
3761
  : false;
3762
+ const suppressNoisySubagentLifecycleDispatch =
3763
+ (isSubagentSessionStart || isSubagentStop)
3764
+ && shouldSuppressSubagentLifecycleHookDispatch();
3421
3765
 
3422
3766
  if (hookEventName === "UserPromptSubmit") {
3423
3767
  const prompt = readPromptText(payload);
3424
3768
  goalWorkflowAdditionalContext = await buildGoalWorkflowReconciliationPromptWarning(cwd, prompt).catch(() => null);
3769
+ ultragoalSteeringAdditionalContext = prompt && !isSubagentPromptSubmit
3770
+ ? await applyUserPromptUltragoalSteering(cwd, prompt).catch((error) => `OMX native UserPromptSubmit rejected bounded .omx/ultragoal steering for G002-cli-and-prompt-submit-bridge: ${error instanceof Error ? error.message : String(error)}`)
3771
+ : null;
3425
3772
  if (prompt && !isSubagentPromptSubmit) {
3426
3773
  skillState = buildNativeOutsideTmuxTeamPromptBlockState(
3427
3774
  prompt,
@@ -3513,7 +3860,7 @@ export async function dispatchCodexNativeHook(
3513
3860
  await reconcileHudForPromptSubmitFn(cwd, { sessionId: canonicalSessionId || sessionIdForState || undefined }).catch(() => {});
3514
3861
  }
3515
3862
 
3516
- if (omxEventName && !skipCanonicalSessionStartContext) {
3863
+ if (omxEventName && !skipCanonicalSessionStartContext && !suppressNoisySubagentLifecycleDispatch) {
3517
3864
  const baseContext = buildBaseContext(cwd, payload, hookEventName!, canonicalSessionId);
3518
3865
  if (resolvedNativeSessionId) {
3519
3866
  baseContext.native_session_id = resolvedNativeSessionId;
@@ -3554,7 +3901,12 @@ export async function dispatchCodexNativeHook(
3554
3901
  })
3555
3902
  : isSubagentPromptSubmit
3556
3903
  ? null
3557
- : (buildAdditionalContextMessage(readPromptText(payload), skillState, cwd, payload) ?? goalWorkflowAdditionalContext ?? triageAdditionalContext);
3904
+ : [
3905
+ buildAdditionalContextMessage(readPromptText(payload), skillState, cwd, payload),
3906
+ ultragoalSteeringAdditionalContext,
3907
+ goalWorkflowAdditionalContext,
3908
+ triageAdditionalContext,
3909
+ ].filter((entry): entry is string => Boolean(entry)).join("\n\n") || null;
3558
3910
  if (additionalContext) {
3559
3911
  outputJson = {
3560
3912
  hookSpecificOutput: {
@@ -3564,7 +3916,8 @@ export async function dispatchCodexNativeHook(
3564
3916
  };
3565
3917
  }
3566
3918
  } else if (hookEventName === "PreToolUse") {
3567
- outputJson = buildNativePreToolUseOutput(payload);
3919
+ outputJson = await buildDeepInterviewPreToolUseBoundaryOutput(payload, cwd, stateDir)
3920
+ ?? buildNativePreToolUseOutput(payload);
3568
3921
  } else if (hookEventName === "PostToolUse") {
3569
3922
  if (detectMcpTransportFailure(payload)) {
3570
3923
  await markTeamTransportFailure(cwd, payload);