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
@@ -6,18 +6,22 @@ import { pathToFileURL } from "url";
6
6
  import { readModeStateForActiveDecision, readModeStateForSession, updateModeState } from "../modes/base.js";
7
7
  import { redactAuthSecrets } from "../auth/redact.js";
8
8
  import { SKILL_ACTIVE_STATE_FILE, extractSessionIdFromInitializedStatePath, getSkillActiveStatePathsForStateDir, listActiveSkills, readSkillActiveState, readVisibleSkillActiveStateForStateDir, } from "../state/skill-active.js";
9
- import { isTrustedSubagentThread, readSubagentSessionSummary, readSubagentSessionLedger, readSubagentTrackingState, recordSubagentTurnForSession, } from "../subagents/tracker.js";
9
+ import { isTrustedSubagentThread, OMX_ADAPTED_PROVENANCE, attestLeaderThread, readSubagentSessionSummary, readSubagentSessionLedger, readSubagentTrackingState, readSubagentTrackingStateStrict, recordSubagentTurn, recordSubagentTurnForSession, resolveInstalledRoleName, } from "../subagents/tracker.js";
10
+ import { bindAndPublishAdaptedRole, NATIVE_SUBAGENT_ROLE_ROUTING_MARKER_TTL_MS, recoverAdaptedRoleBindings, } from "../subagents/adapted-role-binding.js";
11
+ import { readRoleRoutingMarker, writeRoleRoutingMarker } from "../subagents/role-routing-marker.js";
10
12
  import { resolveCanonicalTeamStateRoot, resolveWorkerNotifyTeamStateRootPath } from "../team/state-root.js";
11
13
  import { inferTerminalLifecycleOutcome } from "../runtime/run-outcome.js";
12
- import { appendToLog, isSessionStale, isSessionStateUsable, readSessionState, readUsableSessionState, reconcileNativeSessionStart, } from "../hooks/session.js";
14
+ import { appendPromptSessionProvenanceRejection, appendToLog, isSessionPointerLaunchAbort, isSessionStale, isSessionStateUsable, normalizeSessionId, readSessionPointer, readSessionState, readUsableSessionState, reconcileNativeSessionStart, resolveSessionPointerContext, } from "../hooks/session.js";
15
+ import { evaluateResolvedPromptTurn, extractSelectedTargetOwnerEvidence, preflightSelectedTargetOwner, } from "../hooks/prompt-session-provenance.js";
13
16
  import { appendTeamEvent, readTeamLeaderAttention, readTeamConfig, readTeamManifestV2, readTeamPhase, writeTeamLeaderAttention, writeTeamPhase, } from "../team/state.js";
14
- import { codexAgentsDir, omxNotepadPath, projectCodexAgentsDir, resolveProjectMemoryPath } from "../utils/paths.js";
17
+ import { omxNotepadPath, resolveProjectMemoryPath } from "../utils/paths.js";
15
18
  import { findGitLayout } from "../utils/git-layout.js";
16
- import { getBaseStateDir, getStateFilePath, getStatePath, getAuthoritativeActiveStatePaths } from "../mcp/state-paths.js";
17
- import { detectKeywords, detectPrimaryKeyword, recordSkillActivation, } from "../hooks/keyword-detector.js";
19
+ import { getAuthoritativeActiveStatePaths, getBaseStateDir, getStateFilePath, getStatePath, resolveWritableStateScope, WRITABLE_STATE_SCOPE_ERRORS, } from "../mcp/state-paths.js";
20
+ import { classifyKeywordInput, recordSkillActivation, } from "../hooks/keyword-detector.js";
18
21
  import { buildDeepInterviewConfigInstruction } from "../hooks/deep-interview-config-instruction.js";
19
22
  import { readTeamModeConfig } from "../config/team-mode.js";
20
23
  import { detectNativeStopStallPattern, loadAutoNudgeConfig, normalizeAutoNudgeSignatureText, resolveEffectiveAutoNudgeResponse, } from "./notify-hook/auto-nudge.js";
24
+ import { probeActualTmuxInstanceEvidence, tmuxEvidenceBindsCandidate } from "./notify-hook/managed-tmux.js";
21
25
  import { SLOPPY_FALLBACK_GROUNDING_PATTERNS, SLOPPY_FALLBACK_IMPLEMENTATION_CONTEXT_PATTERNS, SLOPPY_FALLBACK_PHRASE_PATTERNS, buildNativePostToolUseOutput, buildNativePreToolUseOutput, commandInvokesApplyPatch, detectMcpTransportFailure, hasAnyPattern, } from "./codex-native-pre-post.js";
22
26
  import { handleTeamWorkerPostToolUseSuccess } from "./notify-hook/team-worker-posttooluse.js";
23
27
  import { maybeNudgeLeaderForAllowedWorkerStop } from "./notify-hook/team-worker-stop.js";
@@ -29,9 +33,8 @@ import { getNotificationConfig, getVerbosity } from "../notifications/config.js"
29
33
  import { reconcileHudForPromptSubmit } from "../hud/reconcile.js";
30
34
  import { onPreCompact as buildWikiPreCompactContext, onSessionStart as buildWikiSessionStartContext, } from "../wiki/lifecycle.js";
31
35
  import { readAutoresearchCompletionStatus, readAutoresearchModeStateForActiveDecision } from "../autoresearch/skill-validation.js";
32
- import { AGENT_DEFINITIONS } from "../agents/definitions.js";
33
36
  import { deriveAutopilotChildPhase, normalizeAutopilotPhase } from "../autopilot/fsm.js";
34
- import { CONDUCTOR_ORCHESTRATION_METADATA_PREFIXES, LEADER_CONDUCTOR_BLOCK, LEADER_CONDUCTOR_REUSE_AND_LEDGER_GUIDANCE, NATIVE_SUBAGENT_SUPPORT_BLOCKER_FILE, actionKindForConductorArtifact, authorizeConductorAction, buildUnsupportedNativeSubagentGuidance, classifyConductorArtifactKind, isUnsupportedNativeSubagentEvidence, resolveNativeSubagentSupportStatus, } from "../leader/contract.js";
37
+ import { CONDUCTOR_ORCHESTRATION_METADATA_PREFIXES, LEADER_CONDUCTOR_BLOCK, LEADER_CONDUCTOR_REUSE_AND_LEDGER_GUIDANCE, NATIVE_SUBAGENT_SUPPORT_BLOCKER_FILE, actionKindForConductorArtifact, authorizeConductorAction, buildRoleRoutingUnavailableGuidance, buildUnsupportedNativeSubagentGuidance, classifyConductorArtifactKind, isNativeSubagentSpawnToolName, isRoleRoutingUnavailableEvidence, isUnsupportedNativeSubagentEvidence, parseRoleIntentCorrelationToken, resolveNativeSubagentSupportStatus, } from "../leader/contract.js";
35
38
  import { readRunState } from "../runtime/run-state.js";
36
39
  import { evaluateRalphCompletionAuditEvidence, isRalphCompletePhase } from "../ralph/completion-audit.js";
37
40
  import { buildCodexGoalTerminalCleanupNotice, } from "../goal-workflows/codex-goal-snapshot.js";
@@ -89,9 +92,6 @@ const RALPH_LIVE_RISK_PATTERNS = [
89
92
  /\b(?:database|db|terraform|kubectl|kubernetes|aws|gcp|azure|external|destructive)\b/i,
90
93
  /\b(?:telegram|vps|service|restart|send|notify|notification|notifications|cron)\b/i,
91
94
  ];
92
- const KNOWN_TYPED_AGENT_ROLES = new Set(Object.keys(AGENT_DEFINITIONS));
93
- let installedTypedAgentRoleNamesCacheKey = "";
94
- let installedTypedAgentRoleNamesCache = new Set();
95
95
  const RALPH_TASK_TEXT_FIELDS = [
96
96
  "task_description",
97
97
  "taskDescription",
@@ -115,6 +115,18 @@ const MAX_SESSION_META_LINE_BYTES = 256 * 1024;
115
115
  function safeString(value) {
116
116
  return typeof value === "string" ? value : "";
117
117
  }
118
+ async function resolveVerifiedOwnerOmxSessionId() {
119
+ const candidate = normalizeSessionId(process.env.OMX_SESSION_ID);
120
+ if (!candidate)
121
+ return undefined;
122
+ const evidence = await probeActualTmuxInstanceEvidence(process.env.TMUX_PANE);
123
+ return tmuxEvidenceBindsCandidate(evidence, candidate) ? candidate : undefined;
124
+ }
125
+ function isImplicitWritableScopeFailure(error) {
126
+ return error instanceof Error
127
+ && (error.message === WRITABLE_STATE_SCOPE_ERRORS.unboundEnvironment
128
+ || error.message === WRITABLE_STATE_SCOPE_ERRORS.unusableSession);
129
+ }
118
130
  function safeObject(value) {
119
131
  return value && typeof value === "object" ? value : {};
120
132
  }
@@ -219,6 +231,14 @@ function readBoundedFirstLineSync(path) {
219
231
  closeSync(fd);
220
232
  }
221
233
  }
234
+ function selectAuthoritativeTaskName(threadSpawn, subagent, payload) {
235
+ for (const obj of [threadSpawn, subagent, payload]) {
236
+ if (obj && typeof obj === "object" && "task_name" in obj) {
237
+ return { present: true, value: obj.task_name };
238
+ }
239
+ }
240
+ return { present: false, value: undefined };
241
+ }
222
242
  function readNativeSubagentSessionStartMetadata(transcriptPath) {
223
243
  const normalizedPath = transcriptPath.trim();
224
244
  if (!normalizedPath)
@@ -237,40 +257,100 @@ function readNativeSubagentSessionStartMetadata(transcriptPath) {
237
257
  const parentThreadId = safeString(threadSpawn.parent_thread_id).trim();
238
258
  if (!parentThreadId)
239
259
  return null;
240
- const agentNickname = safeString(threadSpawn.agent_nickname ?? payload.agent_nickname).trim();
241
- const agentRole = safeString(threadSpawn.agent_role ?? payload.agent_role).trim();
260
+ const agentNicknameCarrierValues = [
261
+ threadSpawn.agent_nickname ?? threadSpawn.agentNickname,
262
+ subagent.agent_nickname ?? subagent.agentNickname,
263
+ payload.agent_nickname ?? payload.agentNickname,
264
+ ];
265
+ const agentNickname = safeString(agentNicknameCarrierValues[0]).trim();
266
+ const authoritativeTaskName = selectAuthoritativeTaskName(threadSpawn, subagent, payload);
267
+ const correlationToken = authoritativeTaskName.present
268
+ ? parseRoleIntentCorrelationToken(authoritativeTaskName.value)
269
+ : undefined;
270
+ const agentRole = safeString(threadSpawn.agent_role
271
+ ?? threadSpawn.agentRole
272
+ ?? threadSpawn.agent_type
273
+ ?? threadSpawn.agentType
274
+ ?? payload.agent_role
275
+ ?? payload.agentRole
276
+ ?? payload.agent_type
277
+ ?? payload.agentType).trim();
242
278
  return {
243
279
  parentThreadId,
244
280
  ...(agentNickname ? { agentNickname } : {}),
245
281
  ...(agentRole ? { agentRole } : {}),
282
+ ...(correlationToken ? { correlationToken } : {}),
246
283
  };
247
284
  }
248
285
  catch {
249
286
  return null;
250
287
  }
251
288
  }
289
+ function reportRoleRoutingBindingFailure(error) {
290
+ console.error(`[omx] SECURITY: native adapted role binding did not complete; retained binding will be recovered: ${error instanceof Error ? error.message : String(error)}`);
291
+ }
252
292
  async function recordNativeSubagentSessionStart(cwd, canonicalSessionId, childSessionId, metadata, transcriptPath) {
253
293
  const parentThreadId = metadata.parentThreadId.trim();
254
294
  const childThreadId = childSessionId.trim();
295
+ const correlationSessionId = canonicalSessionId.trim() || parentThreadId;
255
296
  const trackingSessionIds = [...new Set([
256
297
  canonicalSessionId.trim(),
257
298
  parentThreadId,
258
299
  ].filter(Boolean))];
259
- for (const sessionId of trackingSessionIds) {
260
- if (parentThreadId && parentThreadId !== childThreadId) {
300
+ let adaptedRoleIntent = null;
301
+ if (!metadata.agentRole && correlationSessionId && parentThreadId) {
302
+ try {
303
+ const bound = bindAndPublishAdaptedRole(cwd, getBaseStateDir(cwd), {
304
+ correlationSessionId,
305
+ parentThreadId,
306
+ correlationToken: metadata.correlationToken,
307
+ }, (state, intent) => {
308
+ let next = state;
309
+ for (const sessionId of trackingSessionIds) {
310
+ if (parentThreadId && parentThreadId !== childThreadId) {
311
+ next = recordSubagentTurn(next, {
312
+ sessionId,
313
+ threadId: parentThreadId,
314
+ kind: "leader",
315
+ });
316
+ }
317
+ next = recordSubagentTurn(next, {
318
+ sessionId,
319
+ threadId: childThreadId,
320
+ kind: "subagent",
321
+ ...(parentThreadId && parentThreadId !== childThreadId ? { leaderThreadId: parentThreadId } : {}),
322
+ mode: intent.role,
323
+ role: intent.role,
324
+ provenanceKind: intent.provenanceKind,
325
+ });
326
+ }
327
+ return next;
328
+ });
329
+ adaptedRoleIntent = bound ? { role: bound.role, provenanceKind: OMX_ADAPTED_PROVENANCE } : null;
330
+ }
331
+ catch (error) {
332
+ reportRoleRoutingBindingFailure(error);
333
+ throw error;
334
+ }
335
+ }
336
+ if (!adaptedRoleIntent) {
337
+ for (const sessionId of trackingSessionIds) {
338
+ if (parentThreadId && parentThreadId !== childThreadId) {
339
+ await recordSubagentTurnForSession(cwd, {
340
+ sessionId,
341
+ threadId: parentThreadId,
342
+ kind: "leader",
343
+ }).catch(() => { });
344
+ }
261
345
  await recordSubagentTurnForSession(cwd, {
262
346
  sessionId,
263
- threadId: parentThreadId,
264
- kind: 'leader',
347
+ threadId: childThreadId,
348
+ kind: "subagent",
349
+ ...(parentThreadId && parentThreadId !== childThreadId ? { leaderThreadId: parentThreadId } : {}),
350
+ mode: metadata.agentRole,
265
351
  }).catch(() => { });
266
352
  }
267
- await recordSubagentTurnForSession(cwd, {
268
- sessionId,
269
- threadId: childThreadId,
270
- kind: 'subagent',
271
- ...(parentThreadId && parentThreadId !== childThreadId ? { leaderThreadId: parentThreadId } : {}),
272
- mode: metadata.agentRole,
273
- }).catch(() => { });
353
+ refreshNativeSubagentRoleRoutingMarker(cwd, getBaseStateDir(cwd), correlationSessionId, parentThreadId);
274
354
  }
275
355
  await appendToLog(cwd, {
276
356
  event: "subagent_session_start",
@@ -349,6 +429,26 @@ async function isNativeSubagentHook(cwd, canonicalSessionId, nativeSessionId, th
349
429
  return false;
350
430
  return Object.values(trackingState.sessions).some((session) => (candidateIds.some((id) => isTrustedSubagentThread(session, id))));
351
431
  }
432
+ async function readNativePromptThreadFacts(cwd, nativeSessionId, threadId, currentSessionState) {
433
+ const candidateIds = [nativeSessionId, threadId].map((value) => value.trim()).filter(Boolean);
434
+ if (candidateIds.length === 0)
435
+ return undefined;
436
+ const currentNativeIds = new Set([
437
+ safeString(currentSessionState?.native_session_id).trim(),
438
+ safeString(currentSessionState?.codex_session_id).trim(),
439
+ safeString(currentSessionState?.owner_codex_session_id).trim(),
440
+ ].filter(Boolean));
441
+ const currentTargetAnchored = Boolean(nativeSessionId && currentNativeIds.has(nativeSessionId));
442
+ const trackingState = await readSubagentTrackingState(cwd).catch(() => null);
443
+ if (!trackingState)
444
+ return currentTargetAnchored ? { currentTargetAnchored: true, rootOrDrift: true } : undefined;
445
+ const roots = Object.values(trackingState.sessions).flatMap((session) => (candidateIds.some((candidate) => isTrustedSubagentThread(session, candidate))
446
+ ? [session.session_id]
447
+ : []));
448
+ return roots.length > 0 || currentTargetAnchored
449
+ ? { trackerRootOwnerSessionIds: roots, currentTargetAnchored, rootOrDrift: currentTargetAnchored }
450
+ : undefined;
451
+ }
352
452
  function shouldSuppressSubagentLifecycleHookDispatch() {
353
453
  const config = getNotificationConfig();
354
454
  if (config?.includeChildAgents === true)
@@ -494,8 +594,8 @@ function readPromptText(payload) {
494
594
  payload.userPrompt,
495
595
  ];
496
596
  for (const candidate of candidates) {
497
- const value = safeString(candidate).trim();
498
- if (value)
597
+ const value = safeString(candidate);
598
+ if (value.trim())
499
599
  return value;
500
600
  }
501
601
  return "";
@@ -1759,19 +1859,21 @@ function buildTeamHelpInstruction(cwd, payload) {
1759
1859
  nativeSessionId: safeString(payload?.session_id ?? payload?.sessionId).trim(),
1760
1860
  }).teamHelpInstruction;
1761
1861
  }
1762
- function buildNativeOutsideTmuxTeamPromptBlockState(prompt, cwd, payload, sessionId, threadId, turnId) {
1763
- if (!readTeamModeConfig(cwd).enabled)
1764
- return null;
1765
- const match = detectPrimaryKeyword(prompt);
1766
- if (match?.skill !== "team")
1767
- return null;
1862
+ function isNativeOutsideTmuxUserPrompt(cwd, payload, sessionId) {
1768
1863
  const environment = resolveExecutionEnvironment(cwd, {
1769
1864
  hookEventName: "UserPromptSubmit",
1770
1865
  payload,
1771
1866
  canonicalSessionId: sessionId ?? "",
1772
1867
  nativeSessionId: safeString(payload.session_id ?? payload.sessionId).trim(),
1773
1868
  });
1774
- if (!(environment.launcher === "native" && environment.transport === "outside-tmux"))
1869
+ return environment.launcher === "native" && environment.transport === "outside-tmux";
1870
+ }
1871
+ function buildNativeOutsideTmuxTeamPromptBlockState(classification, cwd, payload, sessionId, threadId, turnId) {
1872
+ const teamMode = readTeamModeConfig(cwd);
1873
+ const match = classification.matches.filter((entry) => teamMode.enabled || entry.skill !== "team")[0] ?? null;
1874
+ if (match?.skill !== "team")
1875
+ return null;
1876
+ if (!isNativeOutsideTmuxUserPrompt(cwd, payload, sessionId))
1775
1877
  return null;
1776
1878
  const nowIso = new Date().toISOString();
1777
1879
  return {
@@ -1803,13 +1905,19 @@ function buildAutopilotPromptActivationNote(skillState, options = {}) {
1803
1905
  const nativeSubagentSupport = resolveNativeSubagentSupportStatus({
1804
1906
  payload: options.payload,
1805
1907
  persistedSupportBlocker: readJsonSyncIfExists(nativeSubagentSupportBlockerPath(stateDir)),
1908
+ persistedRoleRoutingMarker: readRoleRoutingMarker(stateDir, {
1909
+ cwd: options.cwd,
1910
+ sessionId: options.sessionId ?? "",
1911
+ }),
1806
1912
  persistedCapacityBlocker: readJsonSyncIfExists(nativeSubagentCapacityBlockerPath(stateDir)),
1807
1913
  cwd: options.cwd,
1808
1914
  sessionId: options.sessionId,
1809
1915
  });
1810
1916
  const conductorGuidance = nativeSubagentSupport.status === "unsupported"
1811
1917
  ? buildUnsupportedNativeSubagentGuidance(nativeSubagentSupport)
1812
- : `${LEADER_CONDUCTOR_BLOCK} ${LEADER_CONDUCTOR_REUSE_AND_LEDGER_GUIDANCE}`;
1918
+ : nativeSubagentSupport.status === "role_routing_unavailable"
1919
+ ? buildRoleRoutingUnavailableGuidance(nativeSubagentSupport)
1920
+ : `${LEADER_CONDUCTOR_BLOCK} ${LEADER_CONDUCTOR_REUSE_AND_LEDGER_GUIDANCE}`;
1813
1921
  return [
1814
1922
  `Autopilot protocol: the durable default chain is $deep-interview -> $ralplan -> $ultragoal${teamHandoff} -> $code-review -> $ultraqa (deep-interview -> ralplan -> ultragoal -> code-review -> ultraqa).`,
1815
1923
  "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.",
@@ -1829,7 +1937,8 @@ function formatExecutionHandoffList(cwd) {
1829
1937
  ? "`$ultragoal`, `$team`, or `$ralph`"
1830
1938
  : "`$ultragoal` or `$ralph`";
1831
1939
  }
1832
- function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(), payload) {
1940
+ function buildAdditionalContextMessage(classification, skillState, cwd = process.cwd(), payload) {
1941
+ const prompt = classification.originalText;
1833
1942
  if (!prompt)
1834
1943
  return null;
1835
1944
  const promptPriorityMessage = buildPromptPriorityMessage(prompt);
@@ -1837,20 +1946,29 @@ function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(),
1837
1946
  return promptPriorityMessage;
1838
1947
  }
1839
1948
  const teamMode = readTeamModeConfig(cwd);
1840
- const matches = detectKeywords(prompt).filter((entry) => teamMode.enabled || entry.skill !== "team");
1949
+ const matches = classification.matches.filter((entry) => teamMode.enabled || entry.skill !== "team");
1841
1950
  const match = matches[0] ?? null;
1842
1951
  if (!match) {
1952
+ const markedQuestionAnswer = classification.reservedInput === "omx-question-answered";
1843
1953
  const continuedSkill = safeString(skillState?.skill).trim();
1844
- if (!continuedSkill)
1954
+ const eligibleMarkedContinuation = markedQuestionAnswer
1955
+ && skillState?.active === true
1956
+ && (continuedSkill === "autopilot" || continuedSkill === "deep-interview");
1957
+ const eligibleOrdinaryContinuation = classification.reservedInput === null
1958
+ && !classification.hasExplicitLikeInvocation
1959
+ && skillState?.active === true
1960
+ && Boolean(continuedSkill);
1961
+ if (!eligibleMarkedContinuation && !eligibleOrdinaryContinuation)
1845
1962
  return promptPriorityMessage;
1846
1963
  const deepInterviewPromptActivationNote = skillState?.initialized_mode === "deep-interview"
1847
1964
  ? buildDeepInterviewQuestionBridgeInstruction(cwd, payload)
1848
1965
  : null;
1849
1966
  const deepInterviewConfigPromptActivationNote = buildDeepInterviewConfigInstruction(cwd, skillState);
1850
- const markedQuestionAnswer = /^\s*\[omx question answered\]/i.test(prompt);
1851
1967
  const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState, { markedQuestionAnswer, cwd, payload, sessionId: safeString(skillState?.session_id).trim() });
1852
1968
  return [
1853
- `OMX native UserPromptSubmit continued active workflow skill "${continuedSkill}".`,
1969
+ markedQuestionAnswer
1970
+ ? `OMX native UserPromptSubmit continued active workflow skill "${continuedSkill}"; workflow-like tokens inside the marked omx question answer are treated as answer text, not a new workflow activation.`
1971
+ : `OMX native UserPromptSubmit continued active workflow skill "${continuedSkill}".`,
1854
1972
  promptPriorityMessage,
1855
1973
  skillState?.initialized_mode && skillState.initialized_state_path
1856
1974
  ? buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path)
@@ -1864,27 +1982,6 @@ function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(),
1864
1982
  const detectedKeywordMessage = matches.length > 1
1865
1983
  ? `OMX native UserPromptSubmit detected workflow keywords ${matches.map((entry) => `"${entry.keyword}" -> ${entry.skill}`).join(", ")}.`
1866
1984
  : `OMX native UserPromptSubmit detected workflow keyword "${match.keyword}" -> ${match.skill}.`;
1867
- const continuedSkill = safeString(skillState?.skill).trim();
1868
- if (continuedSkill
1869
- && continuedSkill !== match.skill
1870
- && /^\s*\[omx question answered\]/i.test(prompt)) {
1871
- const deepInterviewPromptActivationNote = skillState?.initialized_mode === "deep-interview"
1872
- ? buildDeepInterviewQuestionBridgeInstruction(cwd, payload)
1873
- : null;
1874
- const deepInterviewConfigPromptActivationNote = buildDeepInterviewConfigInstruction(cwd, skillState);
1875
- const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState, { markedQuestionAnswer: true, cwd, payload, sessionId: safeString(skillState?.session_id).trim() });
1876
- return [
1877
- `OMX native UserPromptSubmit continued active workflow skill "${continuedSkill}"; workflow-like tokens inside the marked omx question answer are treated as answer text, not a new workflow activation.`,
1878
- promptPriorityMessage,
1879
- skillState?.initialized_mode && skillState.initialized_state_path
1880
- ? buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path)
1881
- : null,
1882
- deepInterviewPromptActivationNote,
1883
- deepInterviewConfigPromptActivationNote,
1884
- autopilotPromptActivationNote,
1885
- "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
1886
- ].filter(Boolean).join(" ");
1887
- }
1888
1985
  const activeSkills = Array.isArray(skillState?.active_skills)
1889
1986
  ? skillState.active_skills.map((entry) => entry.skill)
1890
1987
  : [];
@@ -2627,31 +2724,66 @@ function readPayloadAgentRole(payload) {
2627
2724
  ?? threadSpawn?.agent_type
2628
2725
  ?? threadSpawn?.agentType).trim().toLowerCase();
2629
2726
  }
2630
- function readInstalledTypedAgentRoleNames() {
2631
- const cacheKey = [codexAgentsDir(), projectCodexAgentsDir()].join("|");
2632
- if (installedTypedAgentRoleNamesCacheKey === cacheKey)
2633
- return installedTypedAgentRoleNamesCache;
2634
- const installedRoleNames = new Set();
2635
- for (const agentsDir of [codexAgentsDir(), projectCodexAgentsDir()]) {
2636
- try {
2637
- for (const entry of readdirSync(agentsDir, { withFileTypes: true })) {
2638
- if (!entry.isFile() || !entry.name.endsWith(".toml"))
2639
- continue;
2640
- installedRoleNames.add(entry.name.slice(0, -5).trim().toLowerCase());
2641
- }
2642
- }
2643
- catch {
2644
- // Ignore missing or unreadable agent directories; built-in definitions remain authoritative.
2645
- }
2646
- }
2647
- installedTypedAgentRoleNamesCacheKey = cacheKey;
2648
- installedTypedAgentRoleNamesCache = installedRoleNames;
2649
- return installedTypedAgentRoleNamesCache;
2727
+ function readRequestedSpawnRole(payload) {
2728
+ const toolName = safeString(payload.tool_name).trim();
2729
+ if (!isNativeSubagentSpawnToolName(toolName))
2730
+ return "";
2731
+ const toolInput = safeObject(payload.tool_input);
2732
+ return safeString(toolInput.agent_role
2733
+ ?? toolInput.agentRole
2734
+ ?? toolInput.agent_type
2735
+ ?? toolInput.agentType).trim().toLowerCase();
2650
2736
  }
2651
2737
  function isTypedAgentRolePayload(payload) {
2652
2738
  const agentRole = readPayloadAgentRole(payload);
2653
- return agentRole !== "" && (KNOWN_TYPED_AGENT_ROLES.has(agentRole)
2654
- || readInstalledTypedAgentRoleNames().has(agentRole));
2739
+ return agentRole !== "" && resolveInstalledRoleName(agentRole) !== null;
2740
+ }
2741
+ // #3181: detect a runtime-set native subagent PreToolUse. The runtime sets
2742
+ // source.subagent.thread_spawn (not agent-controlled tool arguments), so the mere
2743
+ // STRUCTURAL PRESENCE of that carrier — even if its parent id is blank, malformed, or
2744
+ // null — marks a child/subagent turn that must never be attested as the session leader.
2745
+ // This is a negative security decision, so it fails closed on any present carrier rather
2746
+ // than requiring a well-formed parent id.
2747
+ function hasSubagentThreadSpawnProvenance(payload) {
2748
+ const source = payload.source;
2749
+ if (!source || typeof source !== "object")
2750
+ return false;
2751
+ const subagent = source.subagent;
2752
+ if (!subagent || typeof subagent !== "object")
2753
+ return false;
2754
+ return Object.prototype.hasOwnProperty.call(subagent, "thread_spawn");
2755
+ }
2756
+ // #3181: positive counter-evidence guard. A thread durably tracked as a subagent in ANY
2757
+ // session must never be attested as a leader, even if the current payload lacks typed-role
2758
+ // or thread_spawn provenance. This closes the source-less/untyped child escalation where a
2759
+ // child recorded at its SessionStart later self-promotes via a source-less PreToolUse.
2760
+ async function isThreadTrackedAsSubagent(cwd, threadId) {
2761
+ const id = threadId.trim();
2762
+ if (!id)
2763
+ return false;
2764
+ // Strict read: an unreadable/corrupt tracker fails closed (treated as "is a subagent")
2765
+ // so corrupt evidence cannot let a source-less child reconcile/attest as leader.
2766
+ const read = await readSubagentTrackingStateStrict(cwd);
2767
+ if (!read.ok)
2768
+ return true;
2769
+ for (const session of Object.values(read.state.sessions)) {
2770
+ if (session.threads?.[id]?.kind === "subagent")
2771
+ return true;
2772
+ }
2773
+ return false;
2774
+ }
2775
+ function buildNativeUnknownRolePreToolUseOutput(payload) {
2776
+ const requestedRole = readRequestedSpawnRole(payload);
2777
+ if (!requestedRole || resolveInstalledRoleName(requestedRole) !== null)
2778
+ return null;
2779
+ return {
2780
+ decision: "block",
2781
+ reason: "Native typed-subagent dispatch denied: supplied agent_type/agent_role is unknown or not installed.",
2782
+ hookSpecificOutput: {
2783
+ hookEventName: "PreToolUse",
2784
+ additionalContext: "Use an installed OMX role for native agent_type/agent_role dispatch. On a role-routing-unavailable surface, record a validated OMX adapted role intent before spawning without fabricating agent_type.",
2785
+ },
2786
+ };
2655
2787
  }
2656
2788
  function readPayloadTurnId(payload) {
2657
2789
  return safeString(payload.turn_id ?? payload.turnId).trim();
@@ -2736,6 +2868,21 @@ async function recordNativeSubagentSupportBlocker(cwd, stateDir, payload) {
2736
2868
  cwd,
2737
2869
  }, null, 2));
2738
2870
  }
2871
+ function refreshNativeSubagentRoleRoutingMarker(cwd, stateDir, sessionId, parentThreadId) {
2872
+ const marker = readRoleRoutingMarker(stateDir, {
2873
+ cwd,
2874
+ sessionId,
2875
+ ...(parentThreadId ? { parentThreadId } : {}),
2876
+ });
2877
+ if (!marker)
2878
+ return;
2879
+ const nowMs = Date.now();
2880
+ writeRoleRoutingMarker(stateDir, {
2881
+ ...marker,
2882
+ observed_at: new Date(nowMs).toISOString(),
2883
+ expires_at: new Date(nowMs + NATIVE_SUBAGENT_ROLE_ROUTING_MARKER_TTL_MS).toISOString(),
2884
+ });
2885
+ }
2739
2886
  function summarizeCapacityFailure(text) {
2740
2887
  const normalized = text.replace(/\s+/g, " ").trim();
2741
2888
  if (!normalized)
@@ -2824,13 +2971,13 @@ async function buildNativeSubagentCapacityCloseGuardOutput(payload, cwd, stateDi
2824
2971
  },
2825
2972
  };
2826
2973
  }
2827
- async function resolveInternalSessionIdForPayload(cwd, payloadSessionId, stateDir) {
2828
- const currentSession = stateDir
2974
+ async function resolveInternalSessionIdForPayload(cwd, payloadSessionId, stateDir, pointerSessionState, allowUnboundPayloadFallback = true) {
2975
+ const currentSession = pointerSessionState ?? (stateDir
2829
2976
  ? await readUsableSessionStateFromStateDir(cwd, stateDir)
2830
- : await readUsableSessionState(cwd);
2977
+ : await readUsableSessionState(cwd));
2831
2978
  const canonicalSessionId = safeString(currentSession?.session_id).trim();
2832
2979
  if (!canonicalSessionId)
2833
- return payloadSessionId;
2980
+ return allowUnboundPayloadFallback ? payloadSessionId : "";
2834
2981
  const nativeSessionId = safeString(currentSession?.native_session_id).trim();
2835
2982
  const ownerOmxSessionId = safeString(currentSession?.owner_omx_session_id).trim();
2836
2983
  if (!payloadSessionId)
@@ -2841,7 +2988,7 @@ async function resolveInternalSessionIdForPayload(cwd, payloadSessionId, stateDi
2841
2988
  return canonicalSessionId;
2842
2989
  if (ownerOmxSessionId && payloadSessionId === ownerOmxSessionId)
2843
2990
  return canonicalSessionId;
2844
- return payloadSessionId;
2991
+ return allowUnboundPayloadFallback ? payloadSessionId : "";
2845
2992
  }
2846
2993
  async function readRootSessionStateFromStateDir(stateDir) {
2847
2994
  const sessionPath = join(stateDir, "session.json");
@@ -2945,17 +3092,6 @@ const RALPLAN_EXECUTION_HANDOFF_SKILLS = new Set([
2945
3092
  "ultrawork",
2946
3093
  "ultraqa",
2947
3094
  ]);
2948
- function isActiveDeepInterviewPhase(state) {
2949
- if (!state || state.active !== true)
2950
- return false;
2951
- const mode = safeString(state.mode).trim();
2952
- if (mode && mode !== "deep-interview")
2953
- return false;
2954
- const phase = safeString(state.current_phase ?? state.currentPhase).trim().toLowerCase();
2955
- if (phase && (TERMINAL_MODE_PHASES.has(phase) || phase === "completing"))
2956
- return false;
2957
- return true;
2958
- }
2959
3095
  function isInactiveCompletedDeepInterviewPhase(state) {
2960
3096
  if (!state || state.active !== false)
2961
3097
  return false;
@@ -6250,6 +6386,121 @@ function isCompleteRalplanTerminalWritePayload(payload, activeState, sessionId)
6250
6386
  return false;
6251
6387
  return true;
6252
6388
  }
6389
+ function hasUnquotedShellControlOrRedirection(command) {
6390
+ let quote = null;
6391
+ for (let index = 0; index < command.length; index += 1) {
6392
+ const char = command[index] ?? "";
6393
+ if (char === "\\" && quote !== "'") {
6394
+ index += 1;
6395
+ continue;
6396
+ }
6397
+ if (char === "'" || char === '"') {
6398
+ quote = quote === char ? null : quote ?? char;
6399
+ continue;
6400
+ }
6401
+ if (!quote && ";&|()<>\n\r".includes(char))
6402
+ return true;
6403
+ }
6404
+ return false;
6405
+ }
6406
+ function suppliedSessionAliasesMatch(payload, sessionId) {
6407
+ return [
6408
+ payload.session_id,
6409
+ payload.owner_omx_session_id,
6410
+ payload.codex_session_id,
6411
+ payload.owner_codex_session_id,
6412
+ ].filter((value) => value !== undefined)
6413
+ .every((value) => safeString(value).trim() === sessionId);
6414
+ }
6415
+ function hasOnlyFinishedExplicitOutcomes(payload) {
6416
+ const values = [
6417
+ payload.lifecycle_outcome,
6418
+ payload.lifecycleOutcome,
6419
+ payload.terminal_outcome,
6420
+ payload.terminalOutcome,
6421
+ payload.run_outcome,
6422
+ payload.runOutcome,
6423
+ ].filter((value) => value !== undefined);
6424
+ return values.every((value) => inferTerminalLifecycleOutcome({ lifecycle_outcome: value }, { includeQuestionEnforcement: false }) === "finished");
6425
+ }
6426
+ function isCompleteDeepInterviewTerminalWritePayload(payload, activeState, sessionId) {
6427
+ if (!sessionId)
6428
+ return false;
6429
+ const activeMode = safeString(activeState.mode).trim().toLowerCase();
6430
+ if (activeMode && activeMode !== "deep-interview")
6431
+ return false;
6432
+ const activeSessionId = safeString(activeState.session_id
6433
+ ?? activeState.owner_omx_session_id
6434
+ ?? activeState.codex_session_id
6435
+ ?? activeState.owner_codex_session_id).trim();
6436
+ if (activeSessionId && activeSessionId !== sessionId)
6437
+ return false;
6438
+ const mode = safeString(payload.mode).trim().toLowerCase();
6439
+ const phase = safeString(payload.current_phase ?? payload.currentPhase).trim().toLowerCase();
6440
+ const payloadSessionId = safeString(payload.session_id).trim();
6441
+ if (!hasOnlyFinishedExplicitOutcomes(payload))
6442
+ return false;
6443
+ return mode === "deep-interview"
6444
+ && payload.active === false
6445
+ && phase === "complete"
6446
+ && payloadSessionId === sessionId
6447
+ && inferTerminalLifecycleOutcome(payload, { includeQuestionEnforcement: false }) === "finished";
6448
+ }
6449
+ function isAllowedDeepInterviewTerminalStateWriteCommand(cwd, command, activeState, sessionId) {
6450
+ const rawWords = tokenizeShellWords(normalizeShellLineContinuations(command).trim());
6451
+ if (rawWords[0] !== "omx")
6452
+ return false;
6453
+ const canonicalCommand = canonicalizeOmxStateTransportCommand(command);
6454
+ if (hasUnquotedShellControlOrRedirection(command))
6455
+ return false;
6456
+ if (hasUnsafeUnquotedHeredocExpansion(canonicalCommand))
6457
+ return false;
6458
+ if (hasUnquotedShellSubstitution(canonicalCommand))
6459
+ return false;
6460
+ if (splitStateScanSegments(canonicalCommand).length !== 1)
6461
+ return false;
6462
+ if (sourcesFileWrittenEarlierInSameCommand(cwd, canonicalCommand))
6463
+ return false;
6464
+ if (findUnquotedOmxStateCommandIndexes(canonicalCommand, "clear").length > 0)
6465
+ return false;
6466
+ if (hasDynamicNestedShellExecution(canonicalCommand))
6467
+ return false;
6468
+ if (extractDeepInterviewCommandWriteTargets(command).length > 0)
6469
+ return false;
6470
+ const operations = collectOmxStateCommandOperations(command, "write");
6471
+ if (operations.length !== 1)
6472
+ return false;
6473
+ const operation = operations[0];
6474
+ if (!operation || operation.nested || operation.commandPrefix.trim() || hasPriorExecutableCommand(operation.prefix))
6475
+ return false;
6476
+ const inlineInputCount = operation.args.filter((arg) => arg === "--input" || arg.startsWith("--input=")).length;
6477
+ if (inlineInputCount !== 1 || operation.args.some((arg) => arg === "--input-file" || arg.startsWith("--input-file=")))
6478
+ return false;
6479
+ const inlineInput = readStateWriteFlagValue(operation.args, "--input");
6480
+ let rawPayload = null;
6481
+ try {
6482
+ rawPayload = inlineInput ? safeObject(JSON.parse(inlineInput)) : null;
6483
+ }
6484
+ catch {
6485
+ rawPayload = null;
6486
+ }
6487
+ const nestedState = safeObject(rawPayload?.state);
6488
+ if (!rawPayload
6489
+ || "workingDirectory" in rawPayload
6490
+ || !suppliedSessionAliasesMatch(rawPayload, sessionId)
6491
+ || !hasOnlyFinishedExplicitOutcomes(rawPayload)
6492
+ || (nestedState && ("mode" in nestedState
6493
+ || "session_id" in nestedState
6494
+ || "workingDirectory" in nestedState
6495
+ || "active" in nestedState
6496
+ || "current_phase" in nestedState
6497
+ || "currentPhase" in nestedState
6498
+ || !suppliedSessionAliasesMatch(nestedState, sessionId)
6499
+ || !hasOnlyFinishedExplicitOutcomes(nestedState))))
6500
+ return false;
6501
+ const payload = readStateWriteInputPayload(cwd, canonicalCommand, command);
6502
+ return payload ? isCompleteDeepInterviewTerminalWritePayload(payload, activeState, sessionId) : false;
6503
+ }
6253
6504
  function isAllowedRalplanTerminalStateWriteCommand(cwd, command, activeState, sessionId) {
6254
6505
  const canonicalCommand = canonicalizeOmxStateTransportCommand(command);
6255
6506
  if (hasUnsafeUnquotedHeredocExpansion(canonicalCommand))
@@ -6295,13 +6546,56 @@ function commandEndsPlanningPhase(cwd, command) {
6295
6546
  const payload = readStateWriteInputPayload(cwd, canonicalCommand, command);
6296
6547
  return payload ? isPlanningPhaseDeactivationPayload(payload) : true;
6297
6548
  }
6298
- function isAllowedDeepInterviewBashWrite(cwd, command) {
6549
+ function isAllowedDeepInterviewBashWrite(cwd, command, activeState, sessionId = "") {
6550
+ const stateWriteOperations = collectOmxStateCommandOperations(command, "write");
6551
+ const hasUnsafeRuntimeStateWrite = (words) => {
6552
+ const stateWordIndex = words.indexOf("state");
6553
+ const writeWordIndex = stateWordIndex >= 0 ? words.indexOf("write", stateWordIndex + 1) : -1;
6554
+ if (writeWordIndex <= stateWordIndex)
6555
+ return false;
6556
+ const hasRuntimeBeforeState = words.slice(0, stateWordIndex).some((word) => {
6557
+ const base = shellWordBaseName(word);
6558
+ return isNodeInterpreterCommandWord(base) || base === "bun" || base === "tsx";
6559
+ });
6560
+ if (!hasRuntimeBeforeState)
6561
+ return false;
6562
+ const inlineInput = readStateWriteFlagValue(words, "--input");
6563
+ let payload = null;
6564
+ try {
6565
+ const parsed = inlineInput ? safeObject(JSON.parse(inlineInput)) : null;
6566
+ const modeOverride = readStateWriteFlagValue(words, "--mode");
6567
+ payload = parsed
6568
+ ? normalizeStateWriteClassificationPayload(modeOverride ? { ...parsed, mode: modeOverride } : parsed)
6569
+ : null;
6570
+ }
6571
+ catch {
6572
+ payload = null;
6573
+ }
6574
+ return (!payload && stateWriteOperations.length === 0)
6575
+ || Boolean(payload && (safeString(payload.mode).trim().toLowerCase() !== "deep-interview"
6576
+ || isPlanningPhaseDeactivationPayload(payload)));
6577
+ };
6578
+ const rawCommand = normalizeShellLineContinuations(command).trim();
6579
+ if ([rawCommand, canonicalizeOmxStateTransportCommand(rawCommand)]
6580
+ .some((candidate) => hasUnsafeRuntimeStateWrite(tokenizeShellWords(candidate))))
6581
+ return false;
6582
+ if (stateWriteOperations.some((operation) => operation.nested))
6583
+ return false;
6299
6584
  if (isAllowedDeepInterviewRalplanHandoffCommand(cwd, command))
6300
6585
  return true;
6301
6586
  if (hasDeepInterviewRalplanHandoffStateMutation(cwd, command))
6302
6587
  return false;
6303
- if (commandEndsPlanningPhase(cwd, command))
6304
- return false;
6588
+ if (commandEndsPlanningPhase(cwd, command)) {
6589
+ return activeState
6590
+ ? isAllowedDeepInterviewTerminalStateWriteCommand(cwd, command, activeState, sessionId)
6591
+ : false;
6592
+ }
6593
+ if (stateWriteOperations.length > 0) {
6594
+ const canonicalCommand = canonicalizeOmxStateTransportCommand(command);
6595
+ const payload = readStateWriteInputPayload(cwd, canonicalCommand, command);
6596
+ if (!payload || safeString(payload.mode).trim().toLowerCase() !== "deep-interview")
6597
+ return false;
6598
+ }
6305
6599
  if (commandHasUntargetedPlanningForbiddenIntent(command))
6306
6600
  return false;
6307
6601
  if (firstPlanningTmpScriptExecutionTarget(cwd, command))
@@ -6324,12 +6618,10 @@ async function readActiveDeepInterviewStateForPreToolUse(cwd, stateDir, sessionI
6324
6618
  const modeState = sessionId
6325
6619
  ? await readStopSessionPinnedState("deep-interview-state.json", cwd, sessionId, stateDir)
6326
6620
  : await readJsonIfExists(join(stateDir, "deep-interview-state.json"));
6327
- if (isActiveDeepInterviewPhase(modeState) && modeState && modeStateMatchesSkillStopContext(modeState, cwd, sessionId)) {
6328
- const hasActiveDeepInterviewSkill = listActiveSkills(canonicalState).some((entry) => (entry.skill === "deep-interview"
6329
- && matchesSkillStopContext(entry, canonicalState, sessionId, threadId)));
6330
- if (hasActiveDeepInterviewSkill)
6331
- return modeState;
6332
- }
6621
+ const hasActiveDeepInterviewSkill = listActiveSkills(canonicalState).some((entry) => (entry.skill === "deep-interview"
6622
+ && matchesSkillStopContext(entry, canonicalState, sessionId, threadId)));
6623
+ if (hasActiveDeepInterviewSkill && modeState?.active === true)
6624
+ return modeState;
6333
6625
  if (isInactiveCompletedDeepInterviewPhase(modeState))
6334
6626
  return null;
6335
6627
  const autopilotState = sessionId
@@ -6547,7 +6839,7 @@ async function buildDeepInterviewPreToolUseBoundaryOutput(payload, cwd, stateDir
6547
6839
  let blocked = false;
6548
6840
  let blockedDetail = "implementation/write tools are blocked until an explicit handoff workflow is activated";
6549
6841
  if (toolName === "Bash") {
6550
- blocked = !isAllowedDeepInterviewBashWrite(cwd, command);
6842
+ blocked = !isAllowedDeepInterviewBashWrite(cwd, command, activeState, sessionId);
6551
6843
  if (blocked) {
6552
6844
  blockedDetail = buildDeepInterviewBashBlockedDetail(cwd, command);
6553
6845
  }
@@ -6661,6 +6953,11 @@ async function hasTrustedTypedSubagentProvenanceForPreToolUse(payload, cwd, stat
6661
6953
  if (!session)
6662
6954
  return false;
6663
6955
  const payloadThreadId = readPayloadThreadId(payload);
6956
+ const trackedPayloadThread = payloadThreadId ? session.threads[payloadThreadId] : undefined;
6957
+ const hasTrackedAdaptedTypedRole = payloadThreadId !== ""
6958
+ && isTrustedSubagentThread(session, payloadThreadId)
6959
+ && trackedPayloadThread?.provenance_kind === OMX_ADAPTED_PROVENANCE
6960
+ && resolveInstalledRoleName(trackedPayloadThread.role ?? "") !== null;
6664
6961
  // Resolve the Main-root leader THREAD identity from the tracker's leader_thread_id
6665
6962
  // plus the canonical session's native_session_id (the leader's native thread). Only
6666
6963
  // genuine leader-thread identifiers may anchor the leader: owner_*_session_id are
@@ -6693,9 +6990,11 @@ async function hasTrustedTypedSubagentProvenanceForPreToolUse(payload, cwd, stat
6693
6990
  return false;
6694
6991
  // Planning boundary guards (ralplan, deep-interview) still require a recognized typed
6695
6992
  // agent role, so an untyped collaboration.spawn_agent child cannot write before an
6696
- // execution handoff/approval. Only the Main-root Conductor/Ralph executing guard opts
6697
- // into untyped tracker/runtime provenance (#3116, #3117).
6698
- if (options.allowUntypedProvenance !== true && !isTypedAgentRolePayload(payload)) {
6993
+ // execution handoff/approval. A tracker-recorded OMX-adapted child is the equivalent
6994
+ // of a typed role only when its own persisted role is installed and validated.
6995
+ if (options.allowUntypedProvenance !== true
6996
+ && !isTypedAgentRolePayload(payload)
6997
+ && !hasTrackedAdaptedTypedRole) {
6699
6998
  return false;
6700
6999
  }
6701
7000
  // Tracker-backed provenance: the payload's own thread is a recorded, non-leader
@@ -7565,6 +7864,10 @@ export async function buildConductorPreToolUseWriteGuardOutput(payload, cwd, sta
7565
7864
  const nativeSubagentSupport = resolveNativeSubagentSupportStatus({
7566
7865
  payload,
7567
7866
  persistedSupportBlocker: await readJsonIfExists(nativeSubagentSupportBlockerPath(stateDir)),
7867
+ persistedRoleRoutingMarker: readRoleRoutingMarker(stateDir, {
7868
+ cwd,
7869
+ sessionId: resolvedSessionId || readPayloadSessionId(payload),
7870
+ }),
7568
7871
  persistedCapacityBlocker: await readJsonIfExists(nativeSubagentCapacityBlockerPath(stateDir)),
7569
7872
  cwd,
7570
7873
  sessionId: resolvedSessionId || readPayloadSessionId(payload),
@@ -7598,6 +7901,9 @@ export async function buildConductorPreToolUseWriteGuardOutput(payload, cwd, sta
7598
7901
  const unsupportedNativeGuidance = isUnsupportedNativeSubagentEvidence(nativeSubagentSupport)
7599
7902
  ? ` ${buildUnsupportedNativeSubagentGuidance(nativeSubagentSupport)} Treat the active conductor workflow as blocked/cancelled for native delegation recovery; do not call multi_agent_v1.close_agent.`
7600
7903
  : "";
7904
+ const roleRoutingUnavailableGuidance = isRoleRoutingUnavailableEvidence(nativeSubagentSupport)
7905
+ ? ` ${buildRoleRoutingUnavailableGuidance(nativeSubagentSupport)}`
7906
+ : "";
7601
7907
  return {
7602
7908
  decision: "block",
7603
7909
  reason: `Main-root Conductor mode is active (${activeState.mode} phase: ${formatPhase(activeState.phase, "active")}); direct plan/code writes are blocked and must be delegated; ${blockedDetail}.`,
@@ -7607,6 +7913,7 @@ export async function buildConductorPreToolUseWriteGuardOutput(payload, cwd, sta
7607
7913
  + "Use specialized agents for source edits and plan/spec authorship. "
7608
7914
  + `Main-root Conductor may write only orchestration metadata/transport/ledger artifacts under ${CONDUCTOR_ORCHESTRATION_METADATA_PREFIXES.join(", ")}; path location alone is not authorization for substantive deliverables. `
7609
7915
  + unsupportedNativeGuidance
7916
+ + roleRoutingUnavailableGuidance
7610
7917
  + " Autopilot rework and typed subagent/worker lanes are exempt from this guard.",
7611
7918
  },
7612
7919
  };
@@ -8424,7 +8731,8 @@ async function buildStopHookOutput(payload, cwd, stateDir, options = {}) {
8424
8731
  return null;
8425
8732
  }
8426
8733
  const sessionId = readPayloadSessionId(payload);
8427
- const canonicalSessionId = await resolveInternalSessionIdForPayload(cwd, sessionId);
8734
+ const canonicalSessionId = options.canonicalSessionId
8735
+ ?? await resolveInternalSessionIdForPayload(cwd, sessionId);
8428
8736
  const threadId = readPayloadThreadId(payload);
8429
8737
  const suppressParentWorkflowStop = shouldSuppressParentWorkflowStopForSideConversation(payload);
8430
8738
  if (canonicalSessionId) {
@@ -8468,7 +8776,7 @@ async function buildStopHookOutput(payload, cwd, stateDir, options = {}) {
8468
8776
  }
8469
8777
  const ralphState = options.skipRalphStopBlock === true
8470
8778
  ? null
8471
- : await readActiveRalphState(cwd, stateDir, canonicalSessionId, ralphOwnerContext);
8779
+ : await readActiveRalphState(cwd, stateDir, sessionId || canonicalSessionId, ralphOwnerContext);
8472
8780
  if (!ralphState) {
8473
8781
  const autoresearchState = await readActiveAutoresearchState(cwd, canonicalSessionId);
8474
8782
  if (autoresearchState) {
@@ -8549,7 +8857,8 @@ async function buildStopHookOutput(payload, cwd, stateDir, options = {}) {
8549
8857
  return ordinaryNoProgressOutput;
8550
8858
  const autoNudgeConfig = await loadAutoNudgeConfig();
8551
8859
  const autoNudgePhase = await readStopAutoNudgePhase(cwd, stateDir, canonicalSessionId, threadId);
8552
- if (autoNudgeConfig.enabled
8860
+ if (options.skipAutoNudge !== true
8861
+ && autoNudgeConfig.enabled
8553
8862
  && detectNativeStopStallPattern(lastAssistantMessage, autoNudgeConfig.patterns, autoNudgePhase)) {
8554
8863
  const effectiveResponse = resolveEffectiveAutoNudgeResponse(autoNudgeConfig.response);
8555
8864
  return await returnPersistentStopBlock(payload, stateDir, "auto-nudge", lastAssistantMessage, {
@@ -8583,10 +8892,37 @@ async function buildStopHookOutput(payload, cwd, stateDir, options = {}) {
8583
8892
  systemMessage,
8584
8893
  }, canonicalSessionId);
8585
8894
  }
8895
+ async function preflightNativePromptTarget(stateDir, context) {
8896
+ if (context.status !== "authorized")
8897
+ return context;
8898
+ const targetDir = join(stateDir, "sessions", context.authorization.targetSessionId);
8899
+ const evidence = [];
8900
+ let filenames;
8901
+ try {
8902
+ filenames = await readdir(targetDir);
8903
+ }
8904
+ catch (error) {
8905
+ if (error.code === "ENOENT")
8906
+ return context;
8907
+ return preflightSelectedTargetOwner(context, [{ ownerCodexSessionId: {} }], "native", new Date().toISOString());
8908
+ }
8909
+ for (const filename of filenames) {
8910
+ if (!filename.endsWith("-state.json") && filename !== SKILL_ACTIVE_STATE_FILE)
8911
+ continue;
8912
+ try {
8913
+ const value = JSON.parse(await readFile(join(targetDir, filename), "utf8"));
8914
+ evidence.push(...extractSelectedTargetOwnerEvidence(value));
8915
+ }
8916
+ catch {
8917
+ evidence.push({ ownerCodexSessionId: {} });
8918
+ }
8919
+ }
8920
+ return preflightSelectedTargetOwner(context, evidence, "native", new Date().toISOString());
8921
+ }
8586
8922
  export async function dispatchCodexNativeHook(payload, options = {}) {
8587
8923
  const hookEventName = readHookEventName(payload);
8588
8924
  const cwd = options.cwd ?? (safeString(payload.cwd).trim() || process.cwd());
8589
- if (hookEventName === "Stop" && !hasNativeStopRuntimeSurface(cwd)) {
8925
+ if (hookEventName === "PostCompact" && process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE === "1") {
8590
8926
  return {
8591
8927
  hookEventName,
8592
8928
  omxEventName: mapCodexHookEventToOmxEvent(hookEventName),
@@ -8594,22 +8930,70 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
8594
8930
  outputJson: null,
8595
8931
  };
8596
8932
  }
8597
- // Native hooks must use the same authoritative runtime state root as HUD/MCP
8598
- // when boxed/team roots are active; do not bypass it with cwd/.omx/state.
8599
- const stateDir = getBaseStateDir(cwd);
8600
- if (hookEventName !== "Stop") {
8601
- await mkdir(stateDir, { recursive: true });
8933
+ try {
8934
+ recoverAdaptedRoleBindings(cwd, getBaseStateDir(cwd));
8602
8935
  }
8936
+ catch {
8937
+ // Recovery is best-effort for unrelated hook events.
8938
+ }
8939
+ if (hookEventName === "Stop" && !hasNativeStopRuntimeSurface(cwd)) {
8940
+ return {
8941
+ hookEventName,
8942
+ omxEventName: mapCodexHookEventToOmxEvent(hookEventName),
8943
+ skillState: null,
8944
+ outputJson: null,
8945
+ };
8946
+ }
8947
+ // Native hooks must use the exact pointer root selected for this dispatch.
8948
+ const pointerContext = resolveSessionPointerContext(cwd);
8949
+ const stateDir = pointerContext.baseStateDir;
8603
8950
  const omxEventName = mapCodexHookEventToOmxEvent(hookEventName);
8604
8951
  let skillState = null;
8605
8952
  let triageAdditionalContext = null;
8606
8953
  let goalWorkflowAdditionalContext = null;
8607
8954
  let ultragoalSteeringAdditionalContext = null;
8955
+ let promptClassification = null;
8608
8956
  const nativeSessionId = safeString(payload.session_id ?? payload.sessionId).trim();
8609
8957
  const threadId = safeString(payload.thread_id ?? payload.threadId).trim();
8610
8958
  const turnId = safeString(payload.turn_id ?? payload.turnId).trim();
8611
- const currentSessionState = await readUsableSessionState(cwd);
8959
+ const pointer = await readSessionPointer(pointerContext);
8960
+ const currentSessionState = pointer.status === "usable" ? pointer.state ?? null : null;
8961
+ let promptTurnContext = hookEventName === "UserPromptSubmit"
8962
+ ? evaluateResolvedPromptTurn({
8963
+ producer: "native",
8964
+ payloadSessionId: payload.session_id ?? payload.sessionId,
8965
+ ownerEnvSessionId: undefined,
8966
+ selectedPointer: pointer,
8967
+ threadFacts: await readNativePromptThreadFacts(cwd, nativeSessionId, threadId, currentSessionState),
8968
+ nowIso: new Date().toISOString(),
8969
+ })
8970
+ : null;
8971
+ if (promptTurnContext) {
8972
+ promptTurnContext = await preflightNativePromptTarget(stateDir, promptTurnContext);
8973
+ }
8974
+ if (promptTurnContext?.status === "rejected") {
8975
+ await appendPromptSessionProvenanceRejection(pointerContext, promptTurnContext.diagnostic).catch(() => { });
8976
+ return { hookEventName, omxEventName, skillState: null, outputJson: null };
8977
+ }
8978
+ if (promptTurnContext?.status === "suppressed-target-child") {
8979
+ return { hookEventName, omxEventName, skillState: null, outputJson: null };
8980
+ }
8981
+ if (hookEventName !== "Stop") {
8982
+ await mkdir(stateDir, { recursive: true });
8983
+ }
8984
+ let allowImplicitSessionSideEffects = pointer.status === "usable" || pointer.status === "absent" || promptTurnContext?.status === "authorized";
8985
+ let stopAuthorizationFailure = allowImplicitSessionSideEffects
8986
+ ? null
8987
+ : {
8988
+ stopReason: "session_pointer_unusable",
8989
+ reason: `OMX cannot authorize Stop while the selected session pointer is ${pointer.status}; repair the pointer evidence before continuing.`,
8990
+ };
8612
8991
  let canonicalSessionId = safeString(currentSessionState?.session_id).trim();
8992
+ if (promptTurnContext?.status === "authorized") {
8993
+ canonicalSessionId = promptTurnContext.authorization.targetSessionId;
8994
+ }
8995
+ const allowPromptGlobalSideEffects = promptTurnContext?.status !== "authorized"
8996
+ || promptTurnContext.authorization.globalSideEffects === "allow";
8613
8997
  let resolvedNativeSessionId = nativeSessionId;
8614
8998
  let skipCanonicalSessionStartContext = false;
8615
8999
  let isSubagentSessionStart = false;
@@ -8648,20 +9032,58 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
8648
9032
  }
8649
9033
  }
8650
9034
  else {
8651
- const sessionState = await reconcileNativeSessionStart(cwd, nativeSessionId, {
8652
- pid: options.sessionOwnerPid ?? resolveSessionOwnerPid(payload),
8653
- });
8654
- canonicalSessionId = safeString(sessionState.session_id).trim();
8655
- resolvedNativeSessionId = safeString(sessionState.native_session_id).trim() || nativeSessionId;
9035
+ const ownerOmxSessionId = await resolveVerifiedOwnerOmxSessionId();
9036
+ try {
9037
+ const sessionState = await reconcileNativeSessionStart(cwd, nativeSessionId, {
9038
+ context: pointerContext,
9039
+ pid: options.sessionOwnerPid ?? resolveSessionOwnerPid(payload),
9040
+ ...(ownerOmxSessionId
9041
+ ? { ownerOmxSessionId, ownerAliasVerified: true }
9042
+ : {}),
9043
+ });
9044
+ canonicalSessionId = safeString(sessionState.session_id).trim();
9045
+ resolvedNativeSessionId = safeString(sessionState.native_session_id).trim() || nativeSessionId;
9046
+ allowImplicitSessionSideEffects = true;
9047
+ stopAuthorizationFailure = null;
9048
+ // #3181: leader attestation is intentionally NOT performed here. This branch is
9049
+ // reached whenever readNativeSubagentSessionStartMetadata() returns null, which
9050
+ // conflates a genuine root start with an unreadable/malformed child transcript, so
9051
+ // it cannot positively classify a leader (a legitimate leader may also carry no
9052
+ // transcript). Leader attestation is performed only on the strictly-gated fresh
9053
+ // leader PreToolUse path below, which fires before the first in-turn role-intent
9054
+ // write. Fail closed here rather than risk a false-leader adoption.
9055
+ }
9056
+ catch (error) {
9057
+ if (!isSessionPointerLaunchAbort(error))
9058
+ throw error;
9059
+ canonicalSessionId = "";
9060
+ resolvedNativeSessionId = nativeSessionId;
9061
+ skipCanonicalSessionStartContext = true;
9062
+ allowImplicitSessionSideEffects = false;
9063
+ stopAuthorizationFailure = {
9064
+ stopReason: "session_pointer_unusable",
9065
+ reason: `OMX cannot authorize Stop while the selected session pointer is ${pointer.status}; repair the pointer evidence before continuing.`,
9066
+ };
9067
+ }
8656
9068
  }
8657
9069
  }
8658
9070
  else if (!canonicalSessionId) {
8659
9071
  canonicalSessionId = safeString(currentSessionState?.session_id).trim();
8660
9072
  }
8661
9073
  if (hookEventName === "Stop") {
8662
- const inheritedSessionId = safeString(process.env.OMX_SESSION_ID || process.env.CODEX_SESSION_ID).trim();
8663
- const stopCanonicalSessionId = await resolveInternalSessionIdForPayload(cwd, readPayloadSessionId(payload) || inheritedSessionId);
8664
- if (stopCanonicalSessionId) {
9074
+ const stopPayloadSessionId = readPayloadSessionId(payload);
9075
+ const stopCanonicalSessionId = await resolveInternalSessionIdForPayload(cwd, stopPayloadSessionId, undefined, currentSessionState, pointer.status === "absent");
9076
+ if (stopPayloadSessionId && !stopCanonicalSessionId) {
9077
+ canonicalSessionId = "";
9078
+ allowImplicitSessionSideEffects = false;
9079
+ if (!stopAuthorizationFailure) {
9080
+ stopAuthorizationFailure = {
9081
+ stopReason: "session_scope_unmatched",
9082
+ reason: `OMX cannot authorize Stop for unmatched session id ${stopPayloadSessionId}; the selected session pointer remains authoritative.`,
9083
+ };
9084
+ }
9085
+ }
9086
+ else if (stopCanonicalSessionId) {
8665
9087
  canonicalSessionId = stopCanonicalSessionId;
8666
9088
  }
8667
9089
  if (canonicalSessionId && safeString(currentSessionState?.session_id).trim() === canonicalSessionId) {
@@ -8669,8 +9091,8 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
8669
9091
  safeString(currentSessionState?.native_session_id).trim() || resolvedNativeSessionId;
8670
9092
  }
8671
9093
  }
8672
- const eventSessionId = canonicalSessionId || nativeSessionId || undefined;
8673
- const sessionIdForState = canonicalSessionId || nativeSessionId;
9094
+ let eventSessionId = canonicalSessionId || nativeSessionId || undefined;
9095
+ let sessionIdForState = canonicalSessionId || null;
8674
9096
  let outputJson = null;
8675
9097
  const typedAgentRolePayload = isTypedAgentRolePayload(payload);
8676
9098
  const isSubagentPromptSubmit = hookEventName === "UserPromptSubmit"
@@ -8685,27 +9107,74 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
8685
9107
  ? safeString(currentSessionState?.native_session_id).trim()
8686
9108
  : "")))).some(Boolean)
8687
9109
  : false;
9110
+ if (isSubagentStop && stopAuthorizationFailure?.stopReason === "session_scope_unmatched") {
9111
+ canonicalSessionId = normalizeSessionId(readPayloadSessionId(payload)) ?? "";
9112
+ allowImplicitSessionSideEffects = true;
9113
+ stopAuthorizationFailure = null;
9114
+ eventSessionId = canonicalSessionId || nativeSessionId || undefined;
9115
+ sessionIdForState = canonicalSessionId || null;
9116
+ }
8688
9117
  const suppressNoisySubagentLifecycleDispatch = (isSubagentSessionStart || isSubagentStop)
8689
9118
  && shouldSuppressSubagentLifecycleHookDispatch();
8690
9119
  if (hookEventName === "UserPromptSubmit") {
8691
9120
  const prompt = readPromptText(payload);
8692
- goalWorkflowAdditionalContext = await buildCompletedGoalCleanupPromptWarning(cwd, prompt).catch(() => null)
8693
- ?? await buildGoalWorkflowReconciliationPromptWarning(cwd, prompt).catch(() => null);
8694
- ultragoalSteeringAdditionalContext = prompt && !isSubagentPromptSubmit
9121
+ if (!isSubagentPromptSubmit) {
9122
+ promptClassification = classifyKeywordInput(prompt);
9123
+ }
9124
+ goalWorkflowAdditionalContext = allowPromptGlobalSideEffects
9125
+ ? await buildCompletedGoalCleanupPromptWarning(cwd, prompt).catch(() => null)
9126
+ ?? await buildGoalWorkflowReconciliationPromptWarning(cwd, prompt).catch(() => null)
9127
+ : null;
9128
+ ultragoalSteeringAdditionalContext = prompt && !isSubagentPromptSubmit && allowImplicitSessionSideEffects && allowPromptGlobalSideEffects
8695
9129
  ? 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)}`)
8696
9130
  : null;
8697
- if (prompt && !isSubagentPromptSubmit) {
8698
- skillState = buildNativeOutsideTmuxTeamPromptBlockState(prompt, cwd, payload, sessionIdForState, threadId || undefined, turnId || undefined) ?? await recordSkillActivation({
9131
+ let suppressActivationSeeding = !allowImplicitSessionSideEffects;
9132
+ if (promptTurnContext?.status === "authorized") {
9133
+ sessionIdForState = promptTurnContext.authorization.targetSessionId;
9134
+ }
9135
+ else if (prompt && !isSubagentPromptSubmit && allowImplicitSessionSideEffects) {
9136
+ const rawHookSessionId = canonicalSessionId || nativeSessionId;
9137
+ const normalizedHookSessionId = normalizeSessionId(rawHookSessionId);
9138
+ const explicitHookSessionId = currentSessionState ? undefined : normalizedHookSessionId;
9139
+ if (rawHookSessionId && !normalizedHookSessionId) {
9140
+ suppressActivationSeeding = true;
9141
+ }
9142
+ else {
9143
+ try {
9144
+ const writableScope = await resolveWritableStateScope(cwd, explicitHookSessionId);
9145
+ sessionIdForState = writableScope.sessionId ?? null;
9146
+ }
9147
+ catch (error) {
9148
+ if (!isImplicitWritableScopeFailure(error))
9149
+ throw error;
9150
+ suppressActivationSeeding = true;
9151
+ }
9152
+ }
9153
+ }
9154
+ if (prompt && promptClassification && !isSubagentPromptSubmit && !suppressActivationSeeding) {
9155
+ skillState = buildNativeOutsideTmuxTeamPromptBlockState(promptClassification, cwd, payload, sessionIdForState || undefined, threadId || undefined, turnId || undefined) ?? await recordSkillActivation({
8699
9156
  stateDir,
8700
9157
  sourceCwd: cwd,
8701
9158
  text: prompt,
8702
- sessionId: sessionIdForState,
9159
+ classification: promptClassification,
9160
+ allowSecondaryTeam: !isNativeOutsideTmuxUserPrompt(cwd, payload, sessionIdForState || undefined),
9161
+ sessionId: sessionIdForState || undefined,
8703
9162
  threadId,
8704
9163
  turnId,
9164
+ resolvedPromptTurnContext: promptTurnContext ?? undefined,
9165
+ onProvenanceRejected: async (diagnostic) => {
9166
+ await appendPromptSessionProvenanceRejection(pointerContext, diagnostic).catch(() => { });
9167
+ },
8705
9168
  });
8706
9169
  }
8707
9170
  // --- Triage classifier (advisory-only, non-keyword prompts) ---
8708
- if (prompt && skillState === null && !isSubagentPromptSubmit) {
9171
+ if (prompt
9172
+ && skillState === null
9173
+ && !isSubagentPromptSubmit
9174
+ && allowPromptGlobalSideEffects
9175
+ && promptClassification?.reservedInput === null
9176
+ && promptClassification.hasExplicitLikeInvocation === false
9177
+ && promptClassification.matches.length === 0) {
8709
9178
  try {
8710
9179
  if (readTriageConfig().enabled) {
8711
9180
  const normalized = prompt.trim().toLowerCase();
@@ -8734,7 +9203,9 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
8734
9203
  },
8735
9204
  suppress_followup: true,
8736
9205
  };
8737
- writeTriageState({ cwd, sessionId: sessionIdForState || null, state: newState });
9206
+ if (!suppressActivationSeeding) {
9207
+ writeTriageState({ cwd, sessionId: sessionIdForState || null, state: newState });
9208
+ }
8738
9209
  }
8739
9210
  else if (decision.lane === "LIGHT") {
8740
9211
  if (decision.destination === "explore") {
@@ -8767,7 +9238,9 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
8767
9238
  },
8768
9239
  suppress_followup: true,
8769
9240
  };
8770
- writeTriageState({ cwd, sessionId: sessionIdForState || null, state: newState });
9241
+ if (!suppressActivationSeeding) {
9242
+ writeTriageState({ cwd, sessionId: sessionIdForState || null, state: newState });
9243
+ }
8771
9244
  }
8772
9245
  }
8773
9246
  // lane === "PASS": no context, no state write
@@ -8782,14 +9255,14 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
8782
9255
  const skipHudReconcileForDoctorSmoke = process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE === "1";
8783
9256
  const skipHudReconcileForTeamWorkerPane = !isSubagentPromptSubmit
8784
9257
  && await isConfirmedTeamWorkerPromptSubmitPane(cwd).catch(() => false);
8785
- if (!skipHudReconcileForDoctorSmoke && !skipHudReconcileForTeamWorkerPane) {
9258
+ if (allowImplicitSessionSideEffects && allowPromptGlobalSideEffects && !skipHudReconcileForDoctorSmoke && !skipHudReconcileForTeamWorkerPane) {
8786
9259
  const reconcileHudForPromptSubmitFn = options.reconcileHudForPromptSubmitFn ?? reconcileHudForPromptSubmit;
8787
9260
  const hudSessionId = resolveHudReconcileSessionId(currentSessionState, canonicalSessionId, sessionIdForState);
8788
9261
  const hudSessionIds = resolveHudReconcileSessionIds(currentSessionState, canonicalSessionId, sessionIdForState, nativeSessionId);
8789
9262
  await reconcileHudForPromptSubmitFn(cwd, { sessionId: hudSessionId, sessionIds: hudSessionIds }).catch(() => { });
8790
9263
  }
8791
9264
  }
8792
- if (omxEventName && !skipCanonicalSessionStartContext && !suppressNoisySubagentLifecycleDispatch) {
9265
+ if (omxEventName && allowImplicitSessionSideEffects && allowPromptGlobalSideEffects && !skipCanonicalSessionStartContext && !suppressNoisySubagentLifecycleDispatch) {
8793
9266
  const baseContext = buildBaseContext(cwd, payload, hookEventName, canonicalSessionId);
8794
9267
  if (resolvedNativeSessionId) {
8795
9268
  baseContext.native_session_id = resolvedNativeSessionId;
@@ -8826,12 +9299,14 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
8826
9299
  })
8827
9300
  : isSubagentPromptSubmit
8828
9301
  ? null
8829
- : [
8830
- buildAdditionalContextMessage(readPromptText(payload), skillState, cwd, payload),
8831
- ultragoalSteeringAdditionalContext,
8832
- goalWorkflowAdditionalContext,
8833
- triageAdditionalContext,
8834
- ].filter((entry) => Boolean(entry)).join("\n\n") || null;
9302
+ : promptClassification
9303
+ ? [
9304
+ buildAdditionalContextMessage(promptClassification, skillState, cwd, payload),
9305
+ ultragoalSteeringAdditionalContext,
9306
+ goalWorkflowAdditionalContext,
9307
+ triageAdditionalContext,
9308
+ ].filter((entry) => Boolean(entry)).join("\n\n") || null
9309
+ : null;
8835
9310
  if (additionalContext) {
8836
9311
  outputJson = {
8837
9312
  hookSpecificOutput: {
@@ -8842,12 +9317,52 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
8842
9317
  }
8843
9318
  }
8844
9319
  else if (hookEventName === "PreToolUse") {
9320
+ // #3181 Phase-1 (PreToolUse): this hook fires before the shell tool call that runs
9321
+ // the first in-turn `omx ralplan role-intent write`. On a fresh App/outside-tmux
9322
+ // turn where SessionStart did not establish the pointer, reconcile the canonical
9323
+ // pointer and attest the leader here so the first command can bootstrap. Strictly
9324
+ // gated to a fresh (absent-pointer) LEADER turn: no canonical session yet, a present
9325
+ // native session id, a leader-shaped thread (absent or equal to the native session
9326
+ // id), and no subagent/typed-role provenance. Best-effort; never blocks PreToolUse.
9327
+ if (allowImplicitSessionSideEffects
9328
+ && !canonicalSessionId
9329
+ && nativeSessionId
9330
+ && readPayloadAgentRole(payload) === ""
9331
+ && !hasSubagentThreadSpawnProvenance(payload)) {
9332
+ const preToolUseLeaderThreadId = readPayloadThreadId(payload);
9333
+ if (preToolUseLeaderThreadId
9334
+ && preToolUseLeaderThreadId === nativeSessionId
9335
+ && !(await isThreadTrackedAsSubagent(cwd, nativeSessionId))) {
9336
+ try {
9337
+ const ownerOmxSessionId = await resolveVerifiedOwnerOmxSessionId();
9338
+ const sessionState = await reconcileNativeSessionStart(cwd, nativeSessionId, {
9339
+ context: pointerContext,
9340
+ pid: options.sessionOwnerPid ?? resolveSessionOwnerPid(payload),
9341
+ ...(ownerOmxSessionId ? { ownerOmxSessionId, ownerAliasVerified: true } : {}),
9342
+ });
9343
+ const bootstrapSessionId = safeString(sessionState.session_id).trim();
9344
+ const bootstrapLeaderThreadId = safeString(sessionState.native_session_id).trim() || nativeSessionId;
9345
+ if (bootstrapSessionId && bootstrapLeaderThreadId) {
9346
+ canonicalSessionId = bootstrapSessionId;
9347
+ attestLeaderThread(cwd, {
9348
+ sessionId: bootstrapSessionId,
9349
+ leaderThreadId: bootstrapLeaderThreadId,
9350
+ source: 'native-pretooluse',
9351
+ });
9352
+ }
9353
+ }
9354
+ catch {
9355
+ // Best-effort bootstrap; PreToolUse must never fail on this.
9356
+ }
9357
+ }
9358
+ }
8845
9359
  const payloadSessionId = readPayloadSessionId(payload);
8846
9360
  const rootPointerConflict = await readLiveRootSessionPointerConflict(stateDir, payloadSessionId);
8847
9361
  const preToolUseSessionId = payloadSessionId
8848
9362
  ? await resolveInternalSessionIdForPayload(cwd, payloadSessionId, stateDir)
8849
9363
  : "";
8850
- outputJson = await buildDeepInterviewPreToolUseBoundaryOutput(payload, cwd, stateDir, preToolUseSessionId)
9364
+ outputJson = buildNativeUnknownRolePreToolUseOutput(payload)
9365
+ ?? await buildDeepInterviewPreToolUseBoundaryOutput(payload, cwd, stateDir, preToolUseSessionId)
8851
9366
  ?? await buildRalplanPreToolUseBoundaryOutput(payload, cwd, stateDir, preToolUseSessionId)
8852
9367
  ?? await buildPlanningRootPointerConflictPreToolUseOutput(payload, cwd, stateDir, rootPointerConflict)
8853
9368
  ?? await buildConductorPreToolUseWriteGuardOutput(payload, cwd, stateDir, preToolUseSessionId)
@@ -8856,18 +9371,36 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
8856
9371
  ?? buildNativePreToolUseOutput(payload);
8857
9372
  }
8858
9373
  else if (hookEventName === "PostToolUse") {
8859
- await recordNativeSubagentCapacityBlocker(cwd, stateDir, payload).catch(() => { });
8860
- await recordNativeSubagentSupportBlocker(cwd, stateDir, payload).catch(() => { });
8861
- if (detectMcpTransportFailure(payload)) {
8862
- await markTeamTransportFailure(cwd, payload);
9374
+ if (allowImplicitSessionSideEffects) {
9375
+ await recordNativeSubagentCapacityBlocker(cwd, stateDir, payload).catch(() => { });
9376
+ await recordNativeSubagentSupportBlocker(cwd, stateDir, payload).catch(() => { });
9377
+ if (detectMcpTransportFailure(payload)) {
9378
+ await markTeamTransportFailure(cwd, payload);
9379
+ }
9380
+ await handleTeamWorkerPostToolUseSuccess(payload, cwd);
8863
9381
  }
8864
9382
  outputJson = buildNativePostToolUseOutput(payload);
8865
- await handleTeamWorkerPostToolUseSuccess(payload, cwd);
8866
9383
  }
8867
9384
  else if (hookEventName === "Stop") {
8868
- outputJson = await buildStopHookOutput(payload, cwd, stateDir, {
8869
- skipRalphStopBlock: isSubagentStop,
8870
- }) ?? await buildCompletedGoalCleanupStopOutput(payload, cwd);
9385
+ if (allowImplicitSessionSideEffects) {
9386
+ outputJson = await buildStopHookOutput(payload, cwd, stateDir, {
9387
+ canonicalSessionId: canonicalSessionId || undefined,
9388
+ skipRalphStopBlock: isSubagentStop,
9389
+ skipAutoNudge: isSubagentStop,
9390
+ }) ?? await buildCompletedGoalCleanupStopOutput(payload, cwd);
9391
+ }
9392
+ else {
9393
+ const failure = stopAuthorizationFailure ?? {
9394
+ stopReason: "session_pointer_unusable",
9395
+ reason: "OMX cannot authorize Stop without a writable session authority.",
9396
+ };
9397
+ outputJson = {
9398
+ decision: "block",
9399
+ stopReason: failure.stopReason,
9400
+ reason: failure.reason,
9401
+ systemMessage: failure.reason,
9402
+ };
9403
+ }
8871
9404
  }
8872
9405
  return {
8873
9406
  hookEventName,