oh-my-codex 0.20.0 → 0.20.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (661) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +10 -1
  4. package/dist/adapt/__tests__/hermes.test.js.map +1 -1
  5. package/dist/adapt/contracts.d.ts.map +1 -1
  6. package/dist/agents/native-config.js.map +1 -1
  7. package/dist/auth/__tests__/hotswap.test.d.ts +2 -0
  8. package/dist/auth/__tests__/hotswap.test.d.ts.map +1 -0
  9. package/dist/auth/__tests__/hotswap.test.js +183 -0
  10. package/dist/auth/__tests__/hotswap.test.js.map +1 -0
  11. package/dist/auth/hotswap.d.ts.map +1 -1
  12. package/dist/auth/hotswap.js +17 -4
  13. package/dist/auth/hotswap.js.map +1 -1
  14. package/dist/auth/paths.js.map +1 -1
  15. package/dist/auth/storage.js.map +1 -1
  16. package/dist/autopilot/__tests__/ralplan-gate.test.js +4 -4
  17. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
  18. package/dist/autopilot/completion-gate.js.map +1 -1
  19. package/dist/autopilot/fsm.d.ts +1 -1
  20. package/dist/autopilot/fsm.d.ts.map +1 -1
  21. package/dist/autoresearch/goal.js.map +1 -1
  22. package/dist/autoresearch/runtime.js.map +1 -1
  23. package/dist/capabilities/lockfile.js.map +1 -1
  24. package/dist/catalog/reader.js.map +1 -1
  25. package/dist/catalog/skill-mirror.js.map +1 -1
  26. package/dist/cli/__tests__/adapt-help.test.js.map +1 -1
  27. package/dist/cli/__tests__/agents-init.test.js.map +1 -1
  28. package/dist/cli/__tests__/agents.test.js.map +1 -1
  29. package/dist/cli/__tests__/api.test.js.map +1 -1
  30. package/dist/cli/__tests__/ask.test.js.map +1 -1
  31. package/dist/cli/__tests__/auth.test.js.map +1 -1
  32. package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
  33. package/dist/cli/__tests__/autoresearch.test.js.map +1 -1
  34. package/dist/cli/__tests__/capabilities.test.js.map +1 -1
  35. package/dist/cli/__tests__/codex-plugin-layout.test.js +27 -0
  36. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  37. package/dist/cli/__tests__/doctor-context-window-warning.test.js +83 -78
  38. package/dist/cli/__tests__/doctor-context-window-warning.test.js.map +1 -1
  39. package/dist/cli/__tests__/doctor-invalid-config.test.js +56 -4
  40. package/dist/cli/__tests__/doctor-invalid-config.test.js.map +1 -1
  41. package/dist/cli/__tests__/doctor-team.test.js.map +1 -1
  42. package/dist/cli/__tests__/doctor-warning-copy.test.js +735 -14
  43. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  44. package/dist/cli/__tests__/exec.test.js.map +1 -1
  45. package/dist/cli/__tests__/imagegen-continuation.test.js.map +1 -1
  46. package/dist/cli/__tests__/index.test.js +216 -11
  47. package/dist/cli/__tests__/index.test.js.map +1 -1
  48. package/dist/cli/__tests__/launch-fallback.test.js +49 -3
  49. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  50. package/dist/cli/__tests__/mcp-parity.test.js +84 -12
  51. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  52. package/dist/cli/__tests__/native-hook-claim-journal.test.d.ts +2 -0
  53. package/dist/cli/__tests__/native-hook-claim-journal.test.d.ts.map +1 -0
  54. package/dist/cli/__tests__/native-hook-claim-journal.test.js +128 -0
  55. package/dist/cli/__tests__/native-hook-claim-journal.test.js.map +1 -0
  56. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  57. package/dist/cli/__tests__/question.test.js.map +1 -1
  58. package/dist/cli/__tests__/ralph-deslop-contract.test.js.map +1 -1
  59. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +6 -0
  60. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  61. package/dist/cli/__tests__/ralph-prd-deep-interview.test.js.map +1 -1
  62. package/dist/cli/__tests__/ralph-prd-smoke.test.js.map +1 -1
  63. package/dist/cli/__tests__/ralph.test.js +8 -3
  64. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  65. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.d.ts +2 -0
  66. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.d.ts.map +1 -0
  67. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js +187 -0
  68. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js.map +1 -0
  69. package/dist/cli/__tests__/ralplan.test.d.ts +2 -0
  70. package/dist/cli/__tests__/ralplan.test.d.ts.map +1 -0
  71. package/dist/cli/__tests__/ralplan.test.js +129 -0
  72. package/dist/cli/__tests__/ralplan.test.js.map +1 -0
  73. package/dist/cli/__tests__/resume.test.js +6 -1
  74. package/dist/cli/__tests__/resume.test.js.map +1 -1
  75. package/dist/cli/__tests__/session-scoped-runtime.test.js +25 -0
  76. package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
  77. package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
  78. package/dist/cli/__tests__/session-search.test.js.map +1 -1
  79. package/dist/cli/__tests__/setup-agents-overwrite.test.js +79 -0
  80. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  81. package/dist/cli/__tests__/setup-gh-star.test.js.map +1 -1
  82. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +131 -5
  83. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +1 -1
  84. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.d.ts +2 -0
  85. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.d.ts.map +1 -0
  86. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.js +285 -0
  87. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.js.map +1 -0
  88. package/dist/cli/__tests__/setup-install-mode.test.js +2264 -17
  89. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  90. package/dist/cli/__tests__/setup-preferences.test.d.ts +2 -0
  91. package/dist/cli/__tests__/setup-preferences.test.d.ts.map +1 -0
  92. package/dist/cli/__tests__/setup-preferences.test.js +79 -0
  93. package/dist/cli/__tests__/setup-preferences.test.js.map +1 -0
  94. package/dist/cli/__tests__/setup-refresh.test.js +168 -5
  95. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  96. package/dist/cli/__tests__/setup-scope.test.js +73 -6
  97. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  98. package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
  99. package/dist/cli/__tests__/state.test.js +37 -0
  100. package/dist/cli/__tests__/state.test.js.map +1 -1
  101. package/dist/cli/__tests__/team.test.js.map +1 -1
  102. package/dist/cli/__tests__/ultragoal.test.js +61 -1
  103. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  104. package/dist/cli/__tests__/uninstall.test.js +2067 -26
  105. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  106. package/dist/cli/__tests__/update.test.js +58 -0
  107. package/dist/cli/__tests__/update.test.js.map +1 -1
  108. package/dist/cli/__tests__/url.test.js.map +1 -1
  109. package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
  110. package/dist/cli/adapt.js.map +1 -1
  111. package/dist/cli/agents-init.js.map +1 -1
  112. package/dist/cli/agents.js.map +1 -1
  113. package/dist/cli/api.js.map +1 -1
  114. package/dist/cli/ask.d.ts +1 -1
  115. package/dist/cli/ask.d.ts.map +1 -1
  116. package/dist/cli/ask.js.map +1 -1
  117. package/dist/cli/auth.js.map +1 -1
  118. package/dist/cli/autoresearch-guided.js.map +1 -1
  119. package/dist/cli/autoresearch-intake.js.map +1 -1
  120. package/dist/cli/capabilities.js.map +1 -1
  121. package/dist/cli/cleanup.js.map +1 -1
  122. package/dist/cli/codex-feature-probe.js.map +1 -1
  123. package/dist/cli/codex-home.js.map +1 -1
  124. package/dist/cli/doctor.d.ts +20 -0
  125. package/dist/cli/doctor.d.ts.map +1 -1
  126. package/dist/cli/doctor.js +636 -161
  127. package/dist/cli/doctor.js.map +1 -1
  128. package/dist/cli/explore.js.map +1 -1
  129. package/dist/cli/index.d.ts +11 -3
  130. package/dist/cli/index.d.ts.map +1 -1
  131. package/dist/cli/index.js +108 -20
  132. package/dist/cli/index.js.map +1 -1
  133. package/dist/cli/mcp-serve.js.map +1 -1
  134. package/dist/cli/mission.js.map +1 -1
  135. package/dist/cli/native-assets.js.map +1 -1
  136. package/dist/cli/native-hook-claim-journal.d.ts +18 -0
  137. package/dist/cli/native-hook-claim-journal.d.ts.map +1 -0
  138. package/dist/cli/native-hook-claim-journal.js +239 -0
  139. package/dist/cli/native-hook-claim-journal.js.map +1 -0
  140. package/dist/cli/omx.js.map +1 -1
  141. package/dist/cli/plugin-marketplace.js.map +1 -1
  142. package/dist/cli/project-runtime-codex-homes.js.map +1 -1
  143. package/dist/cli/question.js.map +1 -1
  144. package/dist/cli/ralph.d.ts.map +1 -1
  145. package/dist/cli/ralph.js +3 -2
  146. package/dist/cli/ralph.js.map +1 -1
  147. package/dist/cli/ralplan.d.ts +14 -0
  148. package/dist/cli/ralplan.d.ts.map +1 -0
  149. package/dist/cli/ralplan.js +203 -0
  150. package/dist/cli/ralplan.js.map +1 -0
  151. package/dist/cli/setup-preferences.d.ts +11 -0
  152. package/dist/cli/setup-preferences.d.ts.map +1 -1
  153. package/dist/cli/setup-preferences.js +32 -0
  154. package/dist/cli/setup-preferences.js.map +1 -1
  155. package/dist/cli/setup.d.ts +53 -0
  156. package/dist/cli/setup.d.ts.map +1 -1
  157. package/dist/cli/setup.js +1403 -400
  158. package/dist/cli/setup.js.map +1 -1
  159. package/dist/cli/sparkshell.js.map +1 -1
  160. package/dist/cli/star-prompt.js.map +1 -1
  161. package/dist/cli/state.d.ts.map +1 -1
  162. package/dist/cli/state.js +2 -1
  163. package/dist/cli/state.js.map +1 -1
  164. package/dist/cli/team.js.map +1 -1
  165. package/dist/cli/ultragoal.d.ts +5 -1
  166. package/dist/cli/ultragoal.d.ts.map +1 -1
  167. package/dist/cli/ultragoal.js +34 -2
  168. package/dist/cli/ultragoal.js.map +1 -1
  169. package/dist/cli/uninstall.d.ts +8 -0
  170. package/dist/cli/uninstall.d.ts.map +1 -1
  171. package/dist/cli/uninstall.js +947 -99
  172. package/dist/cli/uninstall.js.map +1 -1
  173. package/dist/cli/update.d.ts.map +1 -1
  174. package/dist/cli/update.js +8 -1
  175. package/dist/cli/update.js.map +1 -1
  176. package/dist/cli/url.js.map +1 -1
  177. package/dist/compat/__tests__/doctor-contract.test.js +5 -1
  178. package/dist/compat/__tests__/doctor-contract.test.js.map +1 -1
  179. package/dist/compat/__tests__/rust-runtime-compat.test.js.map +1 -1
  180. package/dist/config/__tests__/codex-hooks.test.js +1287 -11
  181. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  182. package/dist/config/__tests__/generator-idempotent.test.js +1049 -135
  183. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  184. package/dist/config/__tests__/generator-notify.test.js +28 -49
  185. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  186. package/dist/config/codex-feature-flags.d.ts.map +1 -1
  187. package/dist/config/codex-feature-flags.js.map +1 -1
  188. package/dist/config/codex-hooks.d.ts +124 -1
  189. package/dist/config/codex-hooks.d.ts.map +1 -1
  190. package/dist/config/codex-hooks.js +2142 -265
  191. package/dist/config/codex-hooks.js.map +1 -1
  192. package/dist/config/commit-lore-guard.js.map +1 -1
  193. package/dist/config/generator.d.ts +40 -26
  194. package/dist/config/generator.d.ts.map +1 -1
  195. package/dist/config/generator.js +1159 -288
  196. package/dist/config/generator.js.map +1 -1
  197. package/dist/config/mcp-registry.js.map +1 -1
  198. package/dist/config/models.d.ts +2 -2
  199. package/dist/config/models.d.ts.map +1 -1
  200. package/dist/config/models.js.map +1 -1
  201. package/dist/config/omx-first-party-mcp.js.map +1 -1
  202. package/dist/config/team-mode.d.ts.map +1 -1
  203. package/dist/document-refresh/__tests__/enforcer.test.js.map +1 -1
  204. package/dist/document-refresh/enforcer.js.map +1 -1
  205. package/dist/exec/followup.js.map +1 -1
  206. package/dist/goal-workflows/artifacts.js.map +1 -1
  207. package/dist/hooks/__tests__/agents-overlay.test.js +7 -4
  208. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  209. package/dist/hooks/__tests__/analyze-routing-contract.test.js.map +1 -1
  210. package/dist/hooks/__tests__/analyze-skill-contract.test.js.map +1 -1
  211. package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
  212. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +9 -3
  213. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  214. package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
  215. package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
  216. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  217. package/dist/hooks/__tests__/design-skill.test.js.map +1 -1
  218. package/dist/hooks/__tests__/keyword-detector.test.js +1372 -2
  219. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  220. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +14 -0
  221. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  222. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  223. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +669 -1
  224. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  225. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +2 -0
  226. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
  227. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +142 -1
  228. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  229. package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
  230. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js +48 -2
  231. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js.map +1 -1
  232. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +61 -44
  233. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
  234. package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
  235. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js +3 -2
  236. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js.map +1 -1
  237. package/dist/hooks/__tests__/notify-hook-session-scope.test.js +243 -4
  238. package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
  239. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  240. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +8 -1
  241. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  242. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  243. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  244. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
  245. package/dist/hooks/__tests__/notify-hook-visual-verdict.test.js.map +1 -1
  246. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  247. package/dist/hooks/__tests__/openclaw-setup-contract.test.js.map +1 -1
  248. package/dist/hooks/__tests__/pre-context-gate-skills.test.js.map +1 -1
  249. package/dist/hooks/__tests__/prometheus-strict-contract.test.js.map +1 -1
  250. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js.map +1 -1
  251. package/dist/hooks/__tests__/prompt-guidance-test-helpers.js.map +1 -1
  252. package/dist/hooks/__tests__/prompt-session-provenance.test.d.ts +2 -0
  253. package/dist/hooks/__tests__/prompt-session-provenance.test.d.ts.map +1 -0
  254. package/dist/hooks/__tests__/prompt-session-provenance.test.js +82 -0
  255. package/dist/hooks/__tests__/prompt-session-provenance.test.js.map +1 -0
  256. package/dist/hooks/__tests__/session.test.js +532 -11
  257. package/dist/hooks/__tests__/session.test.js.map +1 -1
  258. package/dist/hooks/__tests__/skill-catalog-hygiene.test.js.map +1 -1
  259. package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.js.map +1 -1
  260. package/dist/hooks/__tests__/visual-ralph-skill.test.js.map +1 -1
  261. package/dist/hooks/__tests__/visual-verdict-loop.test.js.map +1 -1
  262. package/dist/hooks/agents-overlay.js +5 -5
  263. package/dist/hooks/agents-overlay.js.map +1 -1
  264. package/dist/hooks/code-simplifier/index.js.map +1 -1
  265. package/dist/hooks/explore-routing.js.map +1 -1
  266. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  267. package/dist/hooks/extensibility/events.js.map +1 -1
  268. package/dist/hooks/extensibility/loader.js.map +1 -1
  269. package/dist/hooks/extensibility/plugin-runner-stdin.js.map +1 -1
  270. package/dist/hooks/extensibility/sdk/logging.js.map +1 -1
  271. package/dist/hooks/keyword-detector.d.ts +33 -7
  272. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  273. package/dist/hooks/keyword-detector.js +2228 -119
  274. package/dist/hooks/keyword-detector.js.map +1 -1
  275. package/dist/hooks/keyword-registry.d.ts +12 -0
  276. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  277. package/dist/hooks/keyword-registry.js +41 -0
  278. package/dist/hooks/keyword-registry.js.map +1 -1
  279. package/dist/hooks/prompt-session-provenance.d.ts +87 -0
  280. package/dist/hooks/prompt-session-provenance.d.ts.map +1 -0
  281. package/dist/hooks/prompt-session-provenance.js +180 -0
  282. package/dist/hooks/prompt-session-provenance.js.map +1 -0
  283. package/dist/hooks/session.d.ts +139 -34
  284. package/dist/hooks/session.d.ts.map +1 -1
  285. package/dist/hooks/session.js +1239 -317
  286. package/dist/hooks/session.js.map +1 -1
  287. package/dist/hooks/task-size-detector.js.map +1 -1
  288. package/dist/hud/__tests__/state.test.js.map +1 -1
  289. package/dist/hud/authority.js.map +1 -1
  290. package/dist/hud/constants.js.map +1 -1
  291. package/dist/hud/index.js.map +1 -1
  292. package/dist/hud/reconcile.js.map +1 -1
  293. package/dist/hud/render.js.map +1 -1
  294. package/dist/hud/state.js.map +1 -1
  295. package/dist/hud/tmux.js.map +1 -1
  296. package/dist/leader/__tests__/contract.test.js +146 -2
  297. package/dist/leader/__tests__/contract.test.js.map +1 -1
  298. package/dist/leader/contract.d.ts +26 -5
  299. package/dist/leader/contract.d.ts.map +1 -1
  300. package/dist/leader/contract.js +131 -7
  301. package/dist/leader/contract.js.map +1 -1
  302. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  303. package/dist/mcp/__tests__/server-lifecycle.test.js +15 -2
  304. package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
  305. package/dist/mcp/__tests__/state-paths.test.js +98 -1
  306. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  307. package/dist/mcp/bootstrap.js.map +1 -1
  308. package/dist/mcp/code-intel-server.d.ts +58 -58
  309. package/dist/mcp/code-intel-server.d.ts.map +1 -1
  310. package/dist/mcp/code-intel-server.js.map +1 -1
  311. package/dist/mcp/hermes-bridge.js.map +1 -1
  312. package/dist/mcp/hermes-server.d.ts +70 -70
  313. package/dist/mcp/hermes-server.d.ts.map +1 -1
  314. package/dist/mcp/lifecycle-telemetry.js.map +1 -1
  315. package/dist/mcp/memory-server.d.ts +34 -34
  316. package/dist/mcp/memory-server.d.ts.map +1 -1
  317. package/dist/mcp/memory-server.js.map +1 -1
  318. package/dist/mcp/state-paths.d.ts +14 -0
  319. package/dist/mcp/state-paths.d.ts.map +1 -1
  320. package/dist/mcp/state-paths.js +96 -20
  321. package/dist/mcp/state-paths.js.map +1 -1
  322. package/dist/mcp/state-server.d.ts +47 -47
  323. package/dist/mcp/state-server.d.ts.map +1 -1
  324. package/dist/mcp/trace-server.d.ts +4 -4
  325. package/dist/mcp/trace-server.d.ts.map +1 -1
  326. package/dist/mcp/trace-server.js.map +1 -1
  327. package/dist/mcp/wiki-server.d.ts +23 -23
  328. package/dist/mcp/wiki-server.d.ts.map +1 -1
  329. package/dist/mcp/wiki-server.js.map +1 -1
  330. package/dist/modes/__tests__/base-session-scope.test.js +53 -1
  331. package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
  332. package/dist/modes/base.d.ts.map +1 -1
  333. package/dist/modes/base.js +5 -5
  334. package/dist/modes/base.js.map +1 -1
  335. package/dist/notifications/__tests__/lifecycle-dedupe.test.js.map +1 -1
  336. package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
  337. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js +177 -85
  338. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js.map +1 -1
  339. package/dist/notifications/__tests__/session-registry.test.js.map +1 -1
  340. package/dist/notifications/__tests__/template-engine.test.js.map +1 -1
  341. package/dist/notifications/__tests__/verbosity.test.js.map +1 -1
  342. package/dist/notifications/http-client.js.map +1 -1
  343. package/dist/notifications/index.d.ts +4 -1
  344. package/dist/notifications/index.d.ts.map +1 -1
  345. package/dist/notifications/index.js +3 -9
  346. package/dist/notifications/index.js.map +1 -1
  347. package/dist/notifications/lifecycle-dedupe.js.map +1 -1
  348. package/dist/notifications/reply-listener.d.ts.map +1 -1
  349. package/dist/notifications/reply-listener.js.map +1 -1
  350. package/dist/notifications/session-registry.js.map +1 -1
  351. package/dist/notifications/session-status.js.map +1 -1
  352. package/dist/notifications/temp-contract.js.map +1 -1
  353. package/dist/notifications/tmux-detector.js.map +1 -1
  354. package/dist/notifications/tmux.js.map +1 -1
  355. package/dist/openclaw/config.js.map +1 -1
  356. package/dist/openclaw/dispatcher.js.map +1 -1
  357. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  358. package/dist/pipeline/__tests__/stages.test.js +8 -5
  359. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  360. package/dist/pipeline/stages/code-review.js.map +1 -1
  361. package/dist/pipeline/stages/ralph-verify.js.map +1 -1
  362. package/dist/pipeline/stages/ralplan.js.map +1 -1
  363. package/dist/pipeline/stages/team-exec.js.map +1 -1
  364. package/dist/pipeline/stages/ultragoal.js.map +1 -1
  365. package/dist/pipeline/stages/ultraqa.js.map +1 -1
  366. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js.map +1 -1
  367. package/dist/planning/artifact-names.js.map +1 -1
  368. package/dist/planning/artifacts.js.map +1 -1
  369. package/dist/question/__tests__/state.test.js.map +1 -1
  370. package/dist/question/__tests__/ui.test.js.map +1 -1
  371. package/dist/question/autopilot-wait.js.map +1 -1
  372. package/dist/question/client.d.ts.map +1 -1
  373. package/dist/question/client.js.map +1 -1
  374. package/dist/question/deep-interview.js.map +1 -1
  375. package/dist/question/events.js.map +1 -1
  376. package/dist/question/renderer.js.map +1 -1
  377. package/dist/question/state.d.ts.map +1 -1
  378. package/dist/question/state.js.map +1 -1
  379. package/dist/question/ui.js.map +1 -1
  380. package/dist/ralph/contract.d.ts +1 -1
  381. package/dist/ralph/contract.d.ts.map +1 -1
  382. package/dist/ralplan/__tests__/consensus-gate.test.js +403 -8
  383. package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
  384. package/dist/ralplan/__tests__/runtime.test.js +112 -14
  385. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  386. package/dist/ralplan/consensus-gate.d.ts +3 -3
  387. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  388. package/dist/ralplan/consensus-gate.js +155 -60
  389. package/dist/ralplan/consensus-gate.js.map +1 -1
  390. package/dist/ralplan/runtime.d.ts +2 -2
  391. package/dist/ralplan/runtime.d.ts.map +1 -1
  392. package/dist/ralplan/runtime.js +1 -2
  393. package/dist/ralplan/runtime.js.map +1 -1
  394. package/dist/runtime/bridge.d.ts.map +1 -1
  395. package/dist/runtime/bridge.js.map +1 -1
  396. package/dist/runtime/process-tree.js.map +1 -1
  397. package/dist/runtime/run-loop.js.map +1 -1
  398. package/dist/runtime/run-outcome.d.ts +3 -3
  399. package/dist/runtime/run-outcome.d.ts.map +1 -1
  400. package/dist/runtime/run-outcome.js.map +1 -1
  401. package/dist/runtime/run-state.js.map +1 -1
  402. package/dist/scripts/__tests__/codex-native-hook.test.js +5414 -801
  403. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  404. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.d.ts +2 -0
  405. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.d.ts.map +1 -0
  406. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.js +634 -0
  407. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.js.map +1 -0
  408. package/dist/scripts/__tests__/generate-release-body.test.js.map +1 -1
  409. package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -1
  410. package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -1
  411. package/dist/scripts/__tests__/notify-fallback-delivery.test.d.ts +2 -0
  412. package/dist/scripts/__tests__/notify-fallback-delivery.test.d.ts.map +1 -0
  413. package/dist/scripts/__tests__/notify-fallback-delivery.test.js +174 -0
  414. package/dist/scripts/__tests__/notify-fallback-delivery.test.js.map +1 -0
  415. package/dist/scripts/__tests__/notify-state-io.test.js +57 -1
  416. package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
  417. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.d.ts +2 -0
  418. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.d.ts.map +1 -0
  419. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js +205 -0
  420. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js.map +1 -0
  421. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.d.ts +2 -0
  422. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.d.ts.map +1 -0
  423. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js +175 -0
  424. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js.map +1 -0
  425. package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
  426. package/dist/scripts/__tests__/smoke-packed-install.test.js +848 -4
  427. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  428. package/dist/scripts/__tests__/test-reply-listener-live.test.js.map +1 -1
  429. package/dist/scripts/build-api.js.map +1 -1
  430. package/dist/scripts/build-explore-harness.js.map +1 -1
  431. package/dist/scripts/build-sparkshell.js.map +1 -1
  432. package/dist/scripts/cleanup-explore-harness.js.map +1 -1
  433. package/dist/scripts/codex-execution-surface.js.map +1 -1
  434. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  435. package/dist/scripts/codex-native-hook.js +825 -172
  436. package/dist/scripts/codex-native-hook.js.map +1 -1
  437. package/dist/scripts/generate-native-release-manifest.js.map +1 -1
  438. package/dist/scripts/generate-release-body.js.map +1 -1
  439. package/dist/scripts/hook-payload-guard.d.ts.map +1 -1
  440. package/dist/scripts/hook-payload-guard.js.map +1 -1
  441. package/dist/scripts/notify-fallback-delivery.d.ts +48 -0
  442. package/dist/scripts/notify-fallback-delivery.d.ts.map +1 -0
  443. package/dist/scripts/notify-fallback-delivery.js +419 -0
  444. package/dist/scripts/notify-fallback-delivery.js.map +1 -0
  445. package/dist/scripts/notify-fallback-watcher.js +136 -30
  446. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  447. package/dist/scripts/notify-hook/auto-nudge.d.ts +18 -9
  448. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  449. package/dist/scripts/notify-hook/auto-nudge.js +39 -13
  450. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  451. package/dist/scripts/notify-hook/managed-tmux.d.ts +23 -0
  452. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  453. package/dist/scripts/notify-hook/managed-tmux.js +122 -12
  454. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  455. package/dist/scripts/notify-hook/orchestration-intent.d.ts +7 -7
  456. package/dist/scripts/notify-hook/orchestration-intent.d.ts.map +1 -1
  457. package/dist/scripts/notify-hook/ralph-session-resume.d.ts +5 -2
  458. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
  459. package/dist/scripts/notify-hook/ralph-session-resume.js +33 -37
  460. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  461. package/dist/scripts/notify-hook/state-io.d.ts +17 -0
  462. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  463. package/dist/scripts/notify-hook/state-io.js +73 -11
  464. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  465. package/dist/scripts/notify-hook/team-dispatch.d.ts +1 -1
  466. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  467. package/dist/scripts/notify-hook/team-leader-nudge.d.ts +2 -2
  468. package/dist/scripts/notify-hook/team-tmux-guard.d.ts +4 -4
  469. package/dist/scripts/notify-hook/team-worker-posttooluse.js.map +1 -1
  470. package/dist/scripts/notify-hook/team-worker-stop.d.ts +1 -1
  471. package/dist/scripts/notify-hook/team-worker.d.ts +2 -2
  472. package/dist/scripts/notify-hook/tmux-injection.d.ts +8 -1
  473. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  474. package/dist/scripts/notify-hook/tmux-injection.js +25 -16
  475. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  476. package/dist/scripts/notify-hook/visual-verdict.d.ts +1 -1
  477. package/dist/scripts/notify-hook/visual-verdict.d.ts.map +1 -1
  478. package/dist/scripts/notify-hook/visual-verdict.js +15 -13
  479. package/dist/scripts/notify-hook/visual-verdict.js.map +1 -1
  480. package/dist/scripts/notify-hook.d.ts +24 -1
  481. package/dist/scripts/notify-hook.d.ts.map +1 -1
  482. package/dist/scripts/notify-hook.js +266 -90
  483. package/dist/scripts/notify-hook.js.map +1 -1
  484. package/dist/scripts/postinstall.js.map +1 -1
  485. package/dist/scripts/prompt-inventory.js.map +1 -1
  486. package/dist/scripts/smoke-packed-install.d.ts +879 -2
  487. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  488. package/dist/scripts/smoke-packed-install.js +1528 -7
  489. package/dist/scripts/smoke-packed-install.js.map +1 -1
  490. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  491. package/dist/scripts/test-reply-listener-live.js.map +1 -1
  492. package/dist/scripts/test-sparkshell.js.map +1 -1
  493. package/dist/scripts/verify-native-agents.js.map +1 -1
  494. package/dist/session-history/friction.js.map +1 -1
  495. package/dist/session-history/search.js.map +1 -1
  496. package/dist/sidecar/__tests__/boundary.test.js.map +1 -1
  497. package/dist/sidecar/collector.js.map +1 -1
  498. package/dist/sidecar/index.js.map +1 -1
  499. package/dist/sidecar/render.js.map +1 -1
  500. package/dist/sidecar/tmux.js.map +1 -1
  501. package/dist/state/__tests__/operations.test.js +64 -15
  502. package/dist/state/__tests__/operations.test.js.map +1 -1
  503. package/dist/state/__tests__/skill-active.test.js +19 -1
  504. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  505. package/dist/state/__tests__/workflow-transition.test.js +61 -0
  506. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  507. package/dist/state/mode-state-context.js.map +1 -1
  508. package/dist/state/operations.d.ts +1 -1
  509. package/dist/state/operations.d.ts.map +1 -1
  510. package/dist/state/operations.js +49 -55
  511. package/dist/state/operations.js.map +1 -1
  512. package/dist/state/paths.d.ts +1 -1
  513. package/dist/state/paths.d.ts.map +1 -1
  514. package/dist/state/paths.js +1 -1
  515. package/dist/state/paths.js.map +1 -1
  516. package/dist/state/skill-active.d.ts +13 -1
  517. package/dist/state/skill-active.d.ts.map +1 -1
  518. package/dist/state/skill-active.js +43 -4
  519. package/dist/state/skill-active.js.map +1 -1
  520. package/dist/state/terminal-normalization.js.map +1 -1
  521. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  522. package/dist/state/workflow-transition-reconcile.js +20 -4
  523. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  524. package/dist/state/workflow-transition.d.ts +1 -1
  525. package/dist/state/workflow-transition.d.ts.map +1 -1
  526. package/dist/state/workflow-transition.js.map +1 -1
  527. package/dist/subagents/__tests__/adapted-role-binding.test.d.ts +2 -0
  528. package/dist/subagents/__tests__/adapted-role-binding.test.d.ts.map +1 -0
  529. package/dist/subagents/__tests__/adapted-role-binding.test.js +988 -0
  530. package/dist/subagents/__tests__/adapted-role-binding.test.js.map +1 -0
  531. package/dist/subagents/__tests__/leader-bootstrap-3181.test.d.ts +2 -0
  532. package/dist/subagents/__tests__/leader-bootstrap-3181.test.d.ts.map +1 -0
  533. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js +273 -0
  534. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js.map +1 -0
  535. package/dist/subagents/__tests__/tracker.test.js +1550 -10
  536. package/dist/subagents/__tests__/tracker.test.js.map +1 -1
  537. package/dist/subagents/adapted-role-binding.d.ts +17 -0
  538. package/dist/subagents/adapted-role-binding.d.ts.map +1 -0
  539. package/dist/subagents/adapted-role-binding.js +75 -0
  540. package/dist/subagents/adapted-role-binding.js.map +1 -0
  541. package/dist/subagents/role-routing-marker.d.ts +10 -0
  542. package/dist/subagents/role-routing-marker.d.ts.map +1 -0
  543. package/dist/subagents/role-routing-marker.js +136 -0
  544. package/dist/subagents/role-routing-marker.js.map +1 -0
  545. package/dist/subagents/tracker.d.ts +172 -0
  546. package/dist/subagents/tracker.d.ts.map +1 -1
  547. package/dist/subagents/tracker.js +1077 -61
  548. package/dist/subagents/tracker.js.map +1 -1
  549. package/dist/team/__tests__/approved-execution.test.js.map +1 -1
  550. package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
  551. package/dist/team/__tests__/model-contract.test.js +231 -1
  552. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  553. package/dist/team/__tests__/role-router.test.js.map +1 -1
  554. package/dist/team/__tests__/runtime.test.js +401 -1
  555. package/dist/team/__tests__/runtime.test.js.map +1 -1
  556. package/dist/team/__tests__/scaling.test.js +181 -0
  557. package/dist/team/__tests__/scaling.test.js.map +1 -1
  558. package/dist/team/__tests__/state-root.test.js.map +1 -1
  559. package/dist/team/__tests__/tmux-session.test.js +434 -4
  560. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  561. package/dist/team/__tests__/tmux-test-fixture.js.map +1 -1
  562. package/dist/team/api-interop.d.ts +2 -2
  563. package/dist/team/api-interop.d.ts.map +1 -1
  564. package/dist/team/api-interop.js.map +1 -1
  565. package/dist/team/commit-hygiene.js.map +1 -1
  566. package/dist/team/contracts.d.ts +5 -5
  567. package/dist/team/contracts.d.ts.map +1 -1
  568. package/dist/team/delivery-log.js.map +1 -1
  569. package/dist/team/followup-planner.js.map +1 -1
  570. package/dist/team/goal-workflow.js.map +1 -1
  571. package/dist/team/idle-nudge.d.ts.map +1 -1
  572. package/dist/team/model-contract.d.ts +20 -1
  573. package/dist/team/model-contract.d.ts.map +1 -1
  574. package/dist/team/model-contract.js +267 -30
  575. package/dist/team/model-contract.js.map +1 -1
  576. package/dist/team/orchestrator.js.map +1 -1
  577. package/dist/team/pane-status.js.map +1 -1
  578. package/dist/team/phase-controller.js.map +1 -1
  579. package/dist/team/reminder-intents.d.ts +1 -1
  580. package/dist/team/reminder-intents.d.ts.map +1 -1
  581. package/dist/team/reminder-intents.js.map +1 -1
  582. package/dist/team/runtime-cli.js.map +1 -1
  583. package/dist/team/runtime.d.ts.map +1 -1
  584. package/dist/team/runtime.js +83 -52
  585. package/dist/team/runtime.js.map +1 -1
  586. package/dist/team/scaling.d.ts +9 -7
  587. package/dist/team/scaling.d.ts.map +1 -1
  588. package/dist/team/scaling.js +69 -28
  589. package/dist/team/scaling.js.map +1 -1
  590. package/dist/team/state/dispatch-lock.js.map +1 -1
  591. package/dist/team/state/dispatch.js.map +1 -1
  592. package/dist/team/state/events.js.map +1 -1
  593. package/dist/team/state/tasks.js.map +1 -1
  594. package/dist/team/state-root.js.map +1 -1
  595. package/dist/team/state.js.map +1 -1
  596. package/dist/team/team-identity.d.ts.map +1 -1
  597. package/dist/team/team-identity.js.map +1 -1
  598. package/dist/team/tmux-session.d.ts +2 -0
  599. package/dist/team/tmux-session.d.ts.map +1 -1
  600. package/dist/team/tmux-session.js +90 -42
  601. package/dist/team/tmux-session.js.map +1 -1
  602. package/dist/team/worker-bootstrap.js.map +1 -1
  603. package/dist/team/worktree.js.map +1 -1
  604. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  605. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
  606. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  607. package/dist/ultragoal/artifacts.js +4 -1
  608. package/dist/ultragoal/artifacts.js.map +1 -1
  609. package/dist/url-reader/index.js.map +1 -1
  610. package/dist/utils/__tests__/agents-md.test.js +16 -0
  611. package/dist/utils/__tests__/agents-md.test.js.map +1 -1
  612. package/dist/utils/agents-md.d.ts.map +1 -1
  613. package/dist/utils/agents-md.js +4 -3
  614. package/dist/utils/agents-md.js.map +1 -1
  615. package/dist/utils/agents-model-table.js.map +1 -1
  616. package/dist/utils/package.js.map +1 -1
  617. package/dist/utils/paths.js.map +1 -1
  618. package/dist/utils/platform-command.js.map +1 -1
  619. package/dist/utils/repo-deps.js.map +1 -1
  620. package/dist/utils/version.js.map +1 -1
  621. package/dist/verification/__tests__/ci-rust-gates.test.js +1 -1
  622. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
  623. package/dist/verification/verifier.js.map +1 -1
  624. package/dist/visual/constants.d.ts +1 -1
  625. package/dist/visual/constants.d.ts.map +1 -1
  626. package/dist/vscode/__tests__/index.test.js.map +1 -1
  627. package/dist/vscode/index.js.map +1 -1
  628. package/dist/wiki/__tests__/cjk-tokenize.test.js.map +1 -1
  629. package/dist/wiki/__tests__/lint.test.js.map +1 -1
  630. package/dist/wiki/__tests__/query.test.js.map +1 -1
  631. package/dist/wiki/__tests__/storage.test.js.map +1 -1
  632. package/dist/wiki/__tests__/test-helpers.d.ts.map +1 -1
  633. package/dist/wiki/lint.js.map +1 -1
  634. package/dist/wiki/query.js.map +1 -1
  635. package/dist/wiki/storage.js.map +1 -1
  636. package/package.json +2 -2
  637. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  638. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +10 -5
  639. package/plugins/oh-my-codex/skills/ralph/SKILL.md +4 -2
  640. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +5 -2
  641. package/skills/omx-setup/SKILL.md +10 -5
  642. package/skills/ralph/SKILL.md +4 -2
  643. package/skills/ralplan/SKILL.md +5 -2
  644. package/src/scripts/__tests__/codex-native-hook.test.ts +20497 -12377
  645. package/src/scripts/__tests__/fixtures/issue-3138-installed-smoke.ts +734 -0
  646. package/src/scripts/__tests__/notify-fallback-delivery.test.ts +177 -0
  647. package/src/scripts/__tests__/notify-state-io.test.ts +73 -0
  648. package/src/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.ts +215 -0
  649. package/src/scripts/__tests__/role-intent-durable-recovery-3181.test.ts +193 -0
  650. package/src/scripts/__tests__/smoke-packed-install.test.ts +991 -4
  651. package/src/scripts/codex-native-hook.ts +918 -172
  652. package/src/scripts/notify-fallback-delivery.ts +416 -0
  653. package/src/scripts/notify-fallback-watcher.ts +132 -40
  654. package/src/scripts/notify-hook/auto-nudge.ts +43 -12
  655. package/src/scripts/notify-hook/managed-tmux.ts +149 -12
  656. package/src/scripts/notify-hook/ralph-session-resume.ts +37 -36
  657. package/src/scripts/notify-hook/state-io.ts +100 -13
  658. package/src/scripts/notify-hook/tmux-injection.ts +38 -21
  659. package/src/scripts/notify-hook/visual-verdict.ts +22 -12
  660. package/src/scripts/notify-hook.ts +335 -90
  661. package/src/scripts/smoke-packed-install.ts +1829 -6
@@ -6,6 +6,9 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
6
6
  import { tmpdir } from 'node:os';
7
7
  import { join } from 'node:path';
8
8
  import { buildTmuxSessionName } from '../../cli/index.js';
9
+ import { classifyKeywordInput, recordSkillActivation } from '../keyword-detector.js';
10
+ import { recordNotifySkillActivation, recordNotifySkillActivationNonFatal } from '../../scripts/notify-hook.js';
11
+ import { normalizeSkillActiveState } from '../../scripts/notify-hook/auto-nudge.js';
9
12
  const NOTIFY_HOOK_SCRIPT = new URL('../../../dist/scripts/notify-hook.js', import.meta.url);
10
13
  const DEEP_INTERVIEW_BLOCKED_APPROVAL_INPUTS = ['yes', 'y', 'proceed', 'continue', 'ok', 'sure', 'go ahead', 'next i should'];
11
14
  const NEXT_I_SHOULD_RESPONSE = 'Next I should update the focused tests.';
@@ -18,6 +21,17 @@ const INHERITED_OMX_ENV_KEYS = [
18
21
  'OMX_STARTUP_CWD',
19
22
  'OMX_ENTRY_PATH',
20
23
  ];
24
+ describe('notify lifecycle owner normalization', () => {
25
+ it('preserves the stored Codex owner for lifecycle authorization checks', () => {
26
+ const state = normalizeSkillActiveState({
27
+ skill: 'ralph',
28
+ active: true,
29
+ phase: 'executing',
30
+ owner_codex_session_id: 'codex-owner-a',
31
+ });
32
+ assert.equal(state?.owner_codex_session_id, 'codex-owner-a');
33
+ });
34
+ });
21
35
  async function withTempWorkingDir(run) {
22
36
  const cwd = await mkdtemp(join(tmpdir(), 'omx-auto-nudge-'));
23
37
  try {
@@ -2048,6 +2062,656 @@ exit 0
2048
2062
  assert.equal(autopilotState.turn_id, 'turn-later-autopilot-start');
2049
2063
  });
2050
2064
  });
2065
+ it('shares immutable classification across the terminal replay activation matrix', async () => {
2066
+ await withTempWorkingDir(async (cwd) => {
2067
+ const stateDir = join(cwd, '.omx', 'state');
2068
+ const sessionId = 'sess-notify-replay-matrix';
2069
+ const sessionStateDir = join(stateDir, 'sessions', sessionId);
2070
+ const terminalThreadId = 'thread-autopilot-complete';
2071
+ const terminalTurnId = 'turn-autopilot-complete';
2072
+ const terminalPayload = {
2073
+ type: 'agent-turn-complete',
2074
+ 'thread-id': terminalThreadId,
2075
+ 'turn-id': terminalTurnId,
2076
+ 'last-assistant-message': 'Autopilot complete.',
2077
+ };
2078
+ await mkdir(sessionStateDir, { recursive: true });
2079
+ await writeJson(join(sessionStateDir, 'autopilot-state.json'), {
2080
+ mode: 'autopilot',
2081
+ active: false,
2082
+ current_phase: 'complete',
2083
+ completed_at: '2026-05-31T20:24:39.005Z',
2084
+ session_id: sessionId,
2085
+ thread_id: terminalThreadId,
2086
+ turn_id: terminalTurnId,
2087
+ });
2088
+ const terminalAutopilotStatePath = join(sessionStateDir, 'autopilot-state.json');
2089
+ const terminalSessionSkillStatePath = join(sessionStateDir, 'skill-active-state.json');
2090
+ const terminalRootSkillStatePath = join(stateDir, 'skill-active-state.json');
2091
+ for (const text of ['$autopilot replay task café\r\n', 'autopilot mode — replay café']) {
2092
+ const bytesBefore = Buffer.from(text, 'utf8');
2093
+ const terminalStateBefore = await readFile(terminalAutopilotStatePath);
2094
+ let classifierCalls = 0;
2095
+ let classifierText = '';
2096
+ let writerCalls = 0;
2097
+ const result = await recordNotifySkillActivation({
2098
+ stateDir,
2099
+ sourceCwd: cwd,
2100
+ text,
2101
+ sessionId,
2102
+ threadId: terminalThreadId,
2103
+ turnId: terminalTurnId,
2104
+ payload: terminalPayload,
2105
+ }, {
2106
+ classifyKeywordInput: (input) => {
2107
+ classifierCalls += 1;
2108
+ classifierText = input;
2109
+ return classifyKeywordInput(input);
2110
+ },
2111
+ recordSkillActivation: async () => {
2112
+ writerCalls += 1;
2113
+ return null;
2114
+ },
2115
+ });
2116
+ assert.equal(classifierCalls, 1, 'notify should classify each latest input once');
2117
+ assert.equal(writerCalls, 0, 'same-turn terminal replay must not reach the writer');
2118
+ assert.equal(result, null);
2119
+ assert.deepEqual(Buffer.from(classifierText, 'utf8'), bytesBefore, 'classifier must receive the original input bytes');
2120
+ assert.deepEqual(await readFile(terminalAutopilotStatePath), terminalStateBefore, 'same-turn replay must preserve terminal detail bytes');
2121
+ assert.equal(existsSync(terminalSessionSkillStatePath), false, 'same-turn replay must not create session canonical state');
2122
+ assert.equal(existsSync(terminalRootSkillStatePath), false, 'same-turn replay must not create root canonical state');
2123
+ }
2124
+ const markedSessionId = 'sess-notify-terminal-marked-answer';
2125
+ const markedThreadId = 'thread-notify-terminal-marked-answer';
2126
+ const markedTurnId = 'turn-notify-terminal-marked-answer';
2127
+ const markedSessionDir = join(stateDir, 'sessions', markedSessionId);
2128
+ const markedSkillPath = join(markedSessionDir, 'skill-active-state.json');
2129
+ const markedDetailPath = join(markedSessionDir, 'autopilot-state.json');
2130
+ await mkdir(markedSessionDir, { recursive: true });
2131
+ await writeJson(markedSkillPath, {
2132
+ version: 1,
2133
+ active: true,
2134
+ skill: 'autopilot',
2135
+ keyword: '$autopilot',
2136
+ phase: 'completing',
2137
+ activated_at: '2026-05-31T20:24:39.005Z',
2138
+ updated_at: '2026-05-31T20:24:39.005Z',
2139
+ session_id: markedSessionId,
2140
+ active_skills: [{ skill: 'autopilot', phase: 'completing', active: true, session_id: markedSessionId }],
2141
+ });
2142
+ await writeJson(markedDetailPath, {
2143
+ mode: 'autopilot',
2144
+ active: false,
2145
+ current_phase: 'complete',
2146
+ completed_at: '2026-05-31T20:24:39.005Z',
2147
+ session_id: markedSessionId,
2148
+ });
2149
+ const markedSkillBefore = await readFile(markedSkillPath);
2150
+ const markedDetailBefore = await readFile(markedDetailPath);
2151
+ let markedWriterCalls = 0;
2152
+ const markedResult = await recordNotifySkillActivation({
2153
+ stateDir,
2154
+ sourceCwd: cwd,
2155
+ text: '[omx question answered] yes',
2156
+ sessionId: markedSessionId,
2157
+ threadId: markedThreadId,
2158
+ turnId: markedTurnId,
2159
+ payload: {
2160
+ type: 'agent-turn-complete',
2161
+ 'thread-id': markedThreadId,
2162
+ 'turn-id': markedTurnId,
2163
+ 'last-assistant-message': 'Autopilot complete.',
2164
+ },
2165
+ }, {
2166
+ recordSkillActivation: async (input) => {
2167
+ markedWriterCalls += 1;
2168
+ return recordSkillActivation(input);
2169
+ },
2170
+ });
2171
+ assert.equal(markedWriterCalls, 1);
2172
+ assert.equal(markedResult, null);
2173
+ assert.deepEqual(await readFile(markedSkillPath), markedSkillBefore);
2174
+ assert.deepEqual(await readFile(markedDetailPath), markedDetailBefore);
2175
+ for (const orderedCase of [
2176
+ { text: '$ralplan $autopilot plan this change', skill: 'ralplan' },
2177
+ { text: '$ralph $autopilot ship this change', skill: 'ralph' },
2178
+ ]) {
2179
+ const orderedSessionId = `sess-notify-primary-${orderedCase.skill}`;
2180
+ const orderedThreadId = `thread-notify-primary-${orderedCase.skill}`;
2181
+ const orderedTurnId = `turn-notify-primary-${orderedCase.skill}`;
2182
+ const orderedSessionDir = join(stateDir, 'sessions', orderedSessionId);
2183
+ await mkdir(orderedSessionDir, { recursive: true });
2184
+ await writeJson(join(orderedSessionDir, 'autopilot-state.json'), {
2185
+ mode: 'autopilot',
2186
+ active: false,
2187
+ current_phase: 'complete',
2188
+ completed_at: '2026-05-31T20:24:39.005Z',
2189
+ session_id: orderedSessionId,
2190
+ thread_id: orderedThreadId,
2191
+ turn_id: orderedTurnId,
2192
+ });
2193
+ const orderedAutopilotPath = join(orderedSessionDir, 'autopilot-state.json');
2194
+ const orderedAutopilotBefore = await readFile(orderedAutopilotPath);
2195
+ await writeJson(join(orderedSessionDir, 'skill-active-state.json'), {
2196
+ version: 1,
2197
+ active: true,
2198
+ skill: 'autopilot',
2199
+ keyword: '$autopilot',
2200
+ phase: 'completing',
2201
+ activated_at: '2026-05-31T20:24:39.005Z',
2202
+ updated_at: '2026-05-31T20:24:39.005Z',
2203
+ session_id: orderedSessionId,
2204
+ thread_id: orderedThreadId,
2205
+ turn_id: orderedTurnId,
2206
+ active_skills: [{
2207
+ skill: 'autopilot',
2208
+ phase: 'completing',
2209
+ active: true,
2210
+ session_id: orderedSessionId,
2211
+ thread_id: orderedThreadId,
2212
+ turn_id: orderedTurnId,
2213
+ }],
2214
+ });
2215
+ let orderedWriterCalls = 0;
2216
+ const orderedResult = await recordNotifySkillActivation({
2217
+ stateDir,
2218
+ sourceCwd: cwd,
2219
+ text: orderedCase.text,
2220
+ sessionId: orderedSessionId,
2221
+ threadId: orderedThreadId,
2222
+ turnId: orderedTurnId,
2223
+ payload: {
2224
+ type: 'agent-turn-complete',
2225
+ 'thread-id': orderedThreadId,
2226
+ 'turn-id': orderedTurnId,
2227
+ 'last-assistant-message': 'Autopilot complete.',
2228
+ },
2229
+ }, {
2230
+ recordSkillActivation: async (input) => {
2231
+ orderedWriterCalls += 1;
2232
+ return recordSkillActivation(input);
2233
+ },
2234
+ });
2235
+ assert.equal(orderedWriterCalls, 1, `leading ${orderedCase.skill} must reach the writer`);
2236
+ assert.equal(orderedResult?.skill, orderedCase.skill, orderedCase.text);
2237
+ assert.equal(orderedResult?.active, true, orderedCase.text);
2238
+ assert.equal(existsSync(join(orderedSessionDir, 'skill-active-state.json')), true, orderedCase.text);
2239
+ assert.equal(orderedResult?.active_skills?.some((entry) => entry.skill === 'autopilot'), false, orderedCase.text);
2240
+ assert.deepEqual(await readFile(orderedAutopilotPath), orderedAutopilotBefore, orderedCase.text);
2241
+ }
2242
+ const filteredSessionId = 'sess-notify-disabled-team-terminal-autopilot';
2243
+ const filteredThreadId = 'thread-notify-disabled-team-terminal-autopilot';
2244
+ const filteredTurnId = 'turn-notify-disabled-team-terminal-autopilot';
2245
+ const filteredSessionDir = join(stateDir, 'sessions', filteredSessionId);
2246
+ const filteredAutopilotPath = join(filteredSessionDir, 'autopilot-state.json');
2247
+ await mkdir(filteredSessionDir, { recursive: true });
2248
+ await writeJson(filteredAutopilotPath, {
2249
+ mode: 'autopilot',
2250
+ active: false,
2251
+ current_phase: 'complete',
2252
+ completed_at: '2026-05-31T20:24:39.005Z',
2253
+ session_id: filteredSessionId,
2254
+ thread_id: filteredThreadId,
2255
+ turn_id: filteredTurnId,
2256
+ });
2257
+ const filteredAutopilotBefore = await readFile(filteredAutopilotPath);
2258
+ const previousTeamMode = process.env.OMX_TEAM_MODE;
2259
+ process.env.OMX_TEAM_MODE = 'disabled';
2260
+ try {
2261
+ const filteredResult = await recordNotifySkillActivation({
2262
+ stateDir,
2263
+ sourceCwd: cwd,
2264
+ text: '$team $autopilot retry',
2265
+ sessionId: filteredSessionId,
2266
+ threadId: filteredThreadId,
2267
+ turnId: filteredTurnId,
2268
+ payload: {
2269
+ type: 'agent-turn-complete',
2270
+ 'thread-id': filteredThreadId,
2271
+ 'turn-id': filteredTurnId,
2272
+ 'last-assistant-message': 'Autopilot complete.',
2273
+ },
2274
+ });
2275
+ assert.equal(filteredResult, null);
2276
+ assert.deepEqual(await readFile(filteredAutopilotPath), filteredAutopilotBefore);
2277
+ assert.equal(existsSync(join(filteredSessionDir, 'skill-active-state.json')), false);
2278
+ assert.equal(existsSync(join(stateDir, 'skill-active-state.json')), false);
2279
+ }
2280
+ finally {
2281
+ if (previousTeamMode === undefined)
2282
+ delete process.env.OMX_TEAM_MODE;
2283
+ else
2284
+ process.env.OMX_TEAM_MODE = previousTeamMode;
2285
+ }
2286
+ const restartText = '$autopilot new task — café';
2287
+ const restartBytes = Buffer.from(restartText, 'utf8');
2288
+ let restartClassification;
2289
+ let writerClassification;
2290
+ let writerResult;
2291
+ let writerCalls = 0;
2292
+ let writerText = '';
2293
+ const restartResult = await recordNotifySkillActivation({
2294
+ stateDir,
2295
+ sourceCwd: cwd,
2296
+ text: restartText,
2297
+ sessionId,
2298
+ threadId: terminalThreadId,
2299
+ turnId: 'turn-autopilot-restart',
2300
+ payload: {
2301
+ ...terminalPayload,
2302
+ 'turn-id': 'turn-autopilot-restart',
2303
+ 'last-assistant-message': 'Autopilot complete text from a prior turn.',
2304
+ },
2305
+ }, {
2306
+ classifyKeywordInput: (input) => {
2307
+ restartClassification = classifyKeywordInput(input);
2308
+ return restartClassification;
2309
+ },
2310
+ recordSkillActivation: async (input) => {
2311
+ writerCalls += 1;
2312
+ writerText = input.text;
2313
+ writerClassification = input.classification;
2314
+ writerResult = await recordSkillActivation(input);
2315
+ return writerResult;
2316
+ },
2317
+ });
2318
+ assert.equal(writerCalls, 1, 'a different turn must reach the writer');
2319
+ assert.strictEqual(writerClassification, restartClassification, 'writer must receive the classifier object by identity');
2320
+ assert.strictEqual(restartResult, writerResult, 'notify must return the writer result unchanged');
2321
+ assert.deepEqual(Buffer.from(writerText, 'utf8'), restartBytes, 'writer must receive the original input bytes');
2322
+ const reactivatedAutopilot = JSON.parse(await readFile(join(sessionStateDir, 'autopilot-state.json'), 'utf-8'));
2323
+ assert.equal(reactivatedAutopilot.active, true);
2324
+ assert.equal(reactivatedAutopilot.current_phase, 'deep-interview');
2325
+ assert.equal(reactivatedAutopilot.turn_id, 'turn-autopilot-restart');
2326
+ const sessionSkillStatePath = join(sessionStateDir, 'skill-active-state.json');
2327
+ const autopilotStatePath = join(sessionStateDir, 'autopilot-state.json');
2328
+ const rootSkillStatePath = join(stateDir, 'skill-active-state.json');
2329
+ for (const [index, text] of ['do not start $autopilot — café', '"$autopilot" — quoted only'].entries()) {
2330
+ const bytesBefore = Buffer.from(text, 'utf8');
2331
+ const skillStateBefore = await readFile(sessionSkillStatePath);
2332
+ const autopilotStateBefore = await readFile(autopilotStatePath);
2333
+ let rejectedClassification;
2334
+ let rejectedWriterClassification;
2335
+ let rejectedWriterResult;
2336
+ let rejectedWriterCalls = 0;
2337
+ let rejectedWriterText = '';
2338
+ const rejectedResult = await recordNotifySkillActivation({
2339
+ stateDir,
2340
+ sourceCwd: cwd,
2341
+ text,
2342
+ sessionId,
2343
+ threadId: terminalThreadId,
2344
+ turnId: `turn-rejected-${index}`,
2345
+ payload: {
2346
+ type: 'agent-turn-complete',
2347
+ 'thread-id': terminalThreadId,
2348
+ 'turn-id': `turn-rejected-${index}`,
2349
+ 'last-assistant-message': 'Normal nonterminal reply.',
2350
+ },
2351
+ }, {
2352
+ classifyKeywordInput: (input) => {
2353
+ rejectedClassification = classifyKeywordInput(input);
2354
+ return rejectedClassification;
2355
+ },
2356
+ recordSkillActivation: async (input) => {
2357
+ rejectedWriterCalls += 1;
2358
+ rejectedWriterText = input.text;
2359
+ rejectedWriterClassification = input.classification;
2360
+ rejectedWriterResult = await recordSkillActivation(input);
2361
+ return rejectedWriterResult;
2362
+ },
2363
+ });
2364
+ assert.equal(rejectedWriterCalls, 1, 'rejected nonterminal text must still reach the real writer once');
2365
+ assert.strictEqual(rejectedResult, rejectedWriterResult, 'notify must return the real writer result unchanged');
2366
+ assert.equal(rejectedResult, null);
2367
+ assert.strictEqual(rejectedWriterClassification, rejectedClassification, 'rejected input must retain classification identity');
2368
+ assert.deepEqual(rejectedClassification?.matches, [], 'negated and quoted text must not activate a workflow');
2369
+ assert.deepEqual(Buffer.from(rejectedWriterText, 'utf8'), bytesBefore, 'writer must receive rejected input bytes unchanged');
2370
+ assert.deepEqual(await readFile(sessionSkillStatePath), skillStateBefore, 'rejected input must preserve canonical state bytes');
2371
+ assert.deepEqual(await readFile(autopilotStatePath), autopilotStateBefore, 'rejected input must preserve detail state bytes');
2372
+ assert.equal(existsSync(rootSkillStatePath), false, 'rejected session input must not create root canonical state');
2373
+ }
2374
+ });
2375
+ });
2376
+ it('G1a-N preserves ordered multi-skill classification and seeds only the ralplan primary when Team is enabled', async () => {
2377
+ await withTempWorkingDir(async (cwd) => {
2378
+ const stateDir = join(cwd, '.omx', 'state');
2379
+ const sessionId = 'sess-g1a-notify-ordered';
2380
+ const threadId = 'thread-g1a-notify-ordered';
2381
+ const turnId = 'turn-g1a-notify-ordered';
2382
+ const sessionDir = join(stateDir, 'sessions', sessionId);
2383
+ const text = '$ralplan, $autopilot; $team';
2384
+ let classification;
2385
+ let writerClassification;
2386
+ let writerCalls = 0;
2387
+ const previousTeamMode = process.env.OMX_TEAM_MODE;
2388
+ process.env.OMX_TEAM_MODE = 'enabled';
2389
+ try {
2390
+ const result = await recordNotifySkillActivation({
2391
+ stateDir,
2392
+ sourceCwd: cwd,
2393
+ text,
2394
+ sessionId,
2395
+ threadId,
2396
+ turnId,
2397
+ payload: { type: 'agent-turn-complete', 'thread-id': threadId, 'turn-id': turnId },
2398
+ }, {
2399
+ classifyKeywordInput: (input) => {
2400
+ classification = classifyKeywordInput(input);
2401
+ return classification;
2402
+ },
2403
+ recordSkillActivation: async (input) => {
2404
+ writerCalls += 1;
2405
+ writerClassification = input.classification;
2406
+ return recordSkillActivation(input);
2407
+ },
2408
+ });
2409
+ assert.equal(writerCalls, 1);
2410
+ assert.strictEqual(writerClassification, classification, 'notify must pass the ordered classifier output to the writer');
2411
+ assert.deepEqual(classification?.matches.map((match) => match.skill), ['ralplan', 'autopilot', 'team']);
2412
+ assert.equal(result?.skill, 'ralplan');
2413
+ assert.deepEqual(result?.deferred_skills, ['autopilot', 'team']);
2414
+ assert.deepEqual(result?.active_skills?.map((entry) => entry.skill), ['ralplan']);
2415
+ assert.equal(existsSync(join(sessionDir, 'ralplan-state.json')), true);
2416
+ assert.equal(existsSync(join(sessionDir, 'autopilot-state.json')), false);
2417
+ assert.equal(existsSync(join(stateDir, 'team-state.json')), false);
2418
+ }
2419
+ finally {
2420
+ if (previousTeamMode === undefined)
2421
+ delete process.env.OMX_TEAM_MODE;
2422
+ else
2423
+ process.env.OMX_TEAM_MODE = previousTeamMode;
2424
+ }
2425
+ });
2426
+ });
2427
+ it('G1c-N deduplicates exact canonical and alias Autopilot invocations across classifier, writer, canonical state, and detail state', async () => {
2428
+ await withTempWorkingDir(async (cwd) => {
2429
+ const stateDir = join(cwd, '.omx', 'state');
2430
+ const sessionId = 'sess-g1c-notify-duplicate';
2431
+ const threadId = 'thread-g1c-notify-duplicate';
2432
+ const turnId = 'turn-g1c-notify-duplicate';
2433
+ const sessionDir = join(stateDir, 'sessions', sessionId);
2434
+ const text = '$autopilot $oh-my-codex:autopilot build it';
2435
+ let classification;
2436
+ let writerClassification;
2437
+ let writerCalls = 0;
2438
+ const result = await recordNotifySkillActivation({
2439
+ stateDir,
2440
+ sourceCwd: cwd,
2441
+ text,
2442
+ sessionId,
2443
+ threadId,
2444
+ turnId,
2445
+ payload: { type: 'agent-turn-complete', 'thread-id': threadId, 'turn-id': turnId },
2446
+ }, {
2447
+ classifyKeywordInput: (input) => {
2448
+ classification = classifyKeywordInput(input);
2449
+ return classification;
2450
+ },
2451
+ recordSkillActivation: async (input) => {
2452
+ writerCalls += 1;
2453
+ writerClassification = input.classification;
2454
+ return recordSkillActivation(input);
2455
+ },
2456
+ });
2457
+ assert.equal(writerCalls, 1);
2458
+ assert.strictEqual(writerClassification, classification, 'writer must receive the deduplicated classifier output');
2459
+ assert.deepEqual(classification?.matches.map((match) => match.skill), ['autopilot']);
2460
+ assert.equal(result?.skill, 'autopilot');
2461
+ assert.deepEqual(result?.deferred_skills ?? [], []);
2462
+ assert.deepEqual(result?.active_skills?.map((entry) => entry.skill), ['autopilot']);
2463
+ const canonical = JSON.parse(await readFile(join(sessionDir, 'skill-active-state.json'), 'utf8'));
2464
+ assert.deepEqual(canonical.active_skills?.map((entry) => entry.skill), ['autopilot']);
2465
+ assert.equal(existsSync(join(sessionDir, 'autopilot-state.json')), true);
2466
+ });
2467
+ });
2468
+ // Notify Stop is N/A: it has no user-prompt activation transport to replay.
2469
+ it('G1b-N filters disabled Team before suppressing an exact same-turn terminal Autopilot replay without changing state bytes', async () => {
2470
+ await withTempWorkingDir(async (cwd) => {
2471
+ const stateDir = join(cwd, '.omx', 'state');
2472
+ const sessionId = 'sess-g1b-notify-terminal';
2473
+ const threadId = 'thread-g1b-notify-terminal';
2474
+ const turnId = 'turn-g1b-notify-terminal';
2475
+ const sessionDir = join(stateDir, 'sessions', sessionId);
2476
+ const sessionSkillPath = join(sessionDir, 'skill-active-state.json');
2477
+ const sessionAutopilotPath = join(sessionDir, 'autopilot-state.json');
2478
+ const rootSkillPath = join(stateDir, 'skill-active-state.json');
2479
+ const rootAutopilotPath = join(stateDir, 'autopilot-state.json');
2480
+ const seededPaths = [sessionSkillPath, sessionAutopilotPath, rootSkillPath, rootAutopilotPath];
2481
+ await mkdir(sessionDir, { recursive: true });
2482
+ await writeJson(sessionSkillPath, {
2483
+ version: 1,
2484
+ active: true,
2485
+ skill: 'autopilot',
2486
+ keyword: '$autopilot',
2487
+ phase: 'completing',
2488
+ session_id: sessionId,
2489
+ thread_id: threadId,
2490
+ turn_id: turnId,
2491
+ active_skills: [{ skill: 'autopilot', phase: 'completing', active: true, session_id: sessionId, thread_id: threadId, turn_id: turnId }],
2492
+ });
2493
+ await writeJson(sessionAutopilotPath, {
2494
+ mode: 'autopilot',
2495
+ active: false,
2496
+ current_phase: 'complete',
2497
+ completed_at: '2026-05-31T20:24:39.005Z',
2498
+ session_id: sessionId,
2499
+ thread_id: threadId,
2500
+ turn_id: turnId,
2501
+ });
2502
+ await writeJson(rootSkillPath, {
2503
+ version: 1,
2504
+ active: true,
2505
+ skill: 'autopilot',
2506
+ keyword: '$autopilot',
2507
+ phase: 'completing',
2508
+ scope: 'root-copy',
2509
+ session_id: sessionId,
2510
+ thread_id: threadId,
2511
+ turn_id: turnId,
2512
+ });
2513
+ await writeJson(rootAutopilotPath, {
2514
+ mode: 'autopilot',
2515
+ active: false,
2516
+ current_phase: 'complete',
2517
+ completed_at: '2026-05-31T20:24:39.005Z',
2518
+ scope: 'root-copy',
2519
+ session_id: sessionId,
2520
+ thread_id: threadId,
2521
+ turn_id: turnId,
2522
+ });
2523
+ const bytesBefore = new Map(await Promise.all(seededPaths.map(async (path) => [path, await readFile(path)])));
2524
+ const previousTeamMode = process.env.OMX_TEAM_MODE;
2525
+ process.env.OMX_TEAM_MODE = 'disabled';
2526
+ try {
2527
+ let classification;
2528
+ let writerCalls = 0;
2529
+ const result = await recordNotifySkillActivation({
2530
+ stateDir,
2531
+ sourceCwd: cwd,
2532
+ text: '$team $autopilot retry',
2533
+ sessionId,
2534
+ threadId,
2535
+ turnId,
2536
+ payload: {
2537
+ type: 'agent-turn-complete',
2538
+ 'thread-id': threadId,
2539
+ 'turn-id': turnId,
2540
+ 'last-assistant-message': 'Autopilot complete.',
2541
+ },
2542
+ }, {
2543
+ classifyKeywordInput: (text) => {
2544
+ classification = classifyKeywordInput(text);
2545
+ return classification;
2546
+ },
2547
+ recordSkillActivation: async () => {
2548
+ writerCalls += 1;
2549
+ return null;
2550
+ },
2551
+ });
2552
+ assert.deepEqual(classification?.matches.map((match) => match.skill), ['team', 'autopilot'], 'the source classification must retain Team before runtime filtering');
2553
+ assert.equal(writerCalls, 0, 'disabled Team filtering must expose the same-turn terminal Autopilot replay for suppression');
2554
+ assert.equal(result, null);
2555
+ for (const path of seededPaths) {
2556
+ assert.deepEqual(await readFile(path), bytesBefore.get(path), `same-turn notify replay must preserve ${path} byte-for-byte`);
2557
+ }
2558
+ }
2559
+ finally {
2560
+ if (previousTeamMode === undefined)
2561
+ delete process.env.OMX_TEAM_MODE;
2562
+ else
2563
+ process.env.OMX_TEAM_MODE = previousTeamMode;
2564
+ }
2565
+ });
2566
+ });
2567
+ it('G2a sends the exact stale predecessor prompt through the shared classification and writer boundary without state', async () => {
2568
+ await withTempWorkingDir(async (cwd) => {
2569
+ const stateDir = join(cwd, 'isolated-g2a-state-root');
2570
+ const sessionId = 'sess-g2a-stale-predecessor';
2571
+ const sessionDir = join(stateDir, 'sessions', sessionId);
2572
+ const rootSkillPath = join(stateDir, 'skill-active-state.json');
2573
+ const sessionSkillPath = join(sessionDir, 'skill-active-state.json');
2574
+ const rootRalplanPath = join(stateDir, 'ralplan-state.json');
2575
+ const sessionRalplanPath = join(sessionDir, 'ralplan-state.json');
2576
+ const text = 'use $ralplan is the consensus-planning command';
2577
+ const textBytes = Buffer.from(text, 'utf8');
2578
+ let classification;
2579
+ let writerClassification;
2580
+ let writerText = '';
2581
+ let writerCalls = 0;
2582
+ const result = await recordNotifySkillActivation({
2583
+ stateDir,
2584
+ sourceCwd: cwd,
2585
+ text,
2586
+ sessionId,
2587
+ threadId: 'thread-g2a-stale-predecessor',
2588
+ turnId: 'turn-g2a-stale-predecessor',
2589
+ payload: { type: 'agent-turn-complete' },
2590
+ }, {
2591
+ classifyKeywordInput: (input) => {
2592
+ classification = classifyKeywordInput(input);
2593
+ return classification;
2594
+ },
2595
+ recordSkillActivation: async (input) => {
2596
+ writerCalls += 1;
2597
+ writerText = input.text;
2598
+ writerClassification = input.classification;
2599
+ return recordSkillActivation(input);
2600
+ },
2601
+ });
2602
+ assert.equal(writerCalls, 1, 'notify must invoke the shared writer boundary once');
2603
+ assert.strictEqual(writerClassification, classification, 'notify must pass the original classification object to the writer');
2604
+ assert.deepEqual(Buffer.from(writerText, 'utf8'), textBytes, 'notify must pass the stale predecessor bytes unchanged');
2605
+ assert.deepEqual(classification?.matches, [], 'the stale predecessor wording must not activate ralplan');
2606
+ assert.equal(result, null);
2607
+ assert.equal(existsSync(stateDir), false, 'the stale predecessor wording must not create isolated state');
2608
+ assert.equal(existsSync(rootSkillPath), false, 'the stale predecessor wording must not create root canonical state');
2609
+ assert.equal(existsSync(sessionSkillPath), false, 'the stale predecessor wording must not create session canonical state');
2610
+ assert.equal(existsSync(rootRalplanPath), false, 'the stale predecessor wording must not create root ralplan detail state');
2611
+ assert.equal(existsSync(sessionRalplanPath), false, 'the stale predecessor wording must not create session ralplan detail state');
2612
+ });
2613
+ });
2614
+ it('G2b-N preserves five distinct terminal state files for the exact negated notify prompt without a fabricated Stop signal', async () => {
2615
+ await withTempWorkingDir(async (cwd) => {
2616
+ const stateDir = join(cwd, '.omx', 'state');
2617
+ const sessionId = 'sess-g2b-negated-terminal';
2618
+ const threadId = 'thread-g2b-negated-terminal';
2619
+ const turnId = 'turn-g2b-negated-terminal';
2620
+ const sessionDir = join(stateDir, 'sessions', sessionId);
2621
+ const sessionJsonPath = join(stateDir, 'session.json');
2622
+ const rootSkillPath = join(stateDir, 'skill-active-state.json');
2623
+ const rootAutopilotPath = join(stateDir, 'autopilot-state.json');
2624
+ const sessionSkillPath = join(sessionDir, 'skill-active-state.json');
2625
+ const sessionAutopilotPath = join(sessionDir, 'autopilot-state.json');
2626
+ const seededPaths = [sessionJsonPath, rootSkillPath, rootAutopilotPath, sessionSkillPath, sessionAutopilotPath];
2627
+ const terminalBytes = [
2628
+ Buffer.from(`{\n "session_id": "${sessionId}",\n "started_at": "2026-06-01T00:00:01.001Z",\n "turn_id": "turn-g2b-session-pointer"\n}\n`),
2629
+ Buffer.from(`{\n "version": 1,\n "active": true,\n "skill": "autopilot",\n "phase": "completing",\n "updated_at": "2026-06-01T00:00:02.002Z",\n "turn_id": "turn-g2b-root-skill"\n}\n`),
2630
+ Buffer.from(`{\n "mode": "autopilot",\n "active": false,\n "current_phase": "complete",\n "completed_at": "2026-06-01T00:00:03.003Z",\n "turn_id": "turn-g2b-root-detail"\n}\n`),
2631
+ Buffer.from(`{\n "version": 1,\n "active": true,\n "skill": "autopilot",\n "phase": "completing",\n "updated_at": "2026-06-01T00:00:04.004Z",\n "turn_id": "turn-g2b-session-skill"\n}\n`),
2632
+ Buffer.from(`{\n "mode": "autopilot",\n "active": false,\n "current_phase": "complete",\n "completed_at": "2026-06-01T00:00:05.005Z",\n "turn_id": "turn-g2b-session-detail"\n}\n`),
2633
+ ];
2634
+ await mkdir(sessionDir, { recursive: true });
2635
+ await Promise.all(seededPaths.map((path, index) => writeFile(path, terminalBytes[index])));
2636
+ const text = 'do not start $autopilot — café';
2637
+ const textBytes = Buffer.from(text, 'utf8');
2638
+ let classification;
2639
+ let writerClassification;
2640
+ let writerText = '';
2641
+ let writerCalls = 0;
2642
+ const result = await recordNotifySkillActivation({
2643
+ stateDir,
2644
+ sourceCwd: cwd,
2645
+ text,
2646
+ sessionId,
2647
+ threadId,
2648
+ turnId,
2649
+ payload: {
2650
+ type: 'agent-turn-complete',
2651
+ 'thread-id': threadId,
2652
+ 'turn-id': turnId,
2653
+ },
2654
+ }, {
2655
+ classifyKeywordInput: (input) => {
2656
+ classification = classifyKeywordInput(input);
2657
+ return classification;
2658
+ },
2659
+ recordSkillActivation: async (input) => {
2660
+ writerCalls += 1;
2661
+ writerText = input.text;
2662
+ writerClassification = input.classification;
2663
+ return recordSkillActivation(input);
2664
+ },
2665
+ });
2666
+ assert.equal(writerCalls, 1, 'the negated notify input must reach the shared writer boundary once');
2667
+ assert.strictEqual(writerClassification, classification, 'notify must retain classification identity for negated input');
2668
+ assert.deepEqual(Buffer.from(writerText, 'utf8'), textBytes, 'notify must retain the exact negated input bytes');
2669
+ assert.deepEqual(classification?.matches, [], 'the exact negated input must not restart Autopilot');
2670
+ assert.equal(result, null);
2671
+ for (const [index, path] of seededPaths.entries()) {
2672
+ assert.deepEqual(await readFile(path), terminalBytes[index], `negated notify input must preserve ${path} byte-for-byte`);
2673
+ }
2674
+ });
2675
+ });
2676
+ it('logs injected skill activation writer failures without failing the notify hook boundary', async () => {
2677
+ await withTempWorkingDir(async (cwd) => {
2678
+ const stateDir = join(cwd, '.omx', 'state');
2679
+ const logsDir = join(cwd, '.omx', 'logs');
2680
+ const sessionId = 'sess-notify-writer-failure';
2681
+ const threadId = 'thread-notify-writer-failure';
2682
+ const turnId = 'turn-notify-writer-failure';
2683
+ let writerCalls = 0;
2684
+ await mkdir(logsDir, { recursive: true });
2685
+ const result = await recordNotifySkillActivationNonFatal({
2686
+ stateDir,
2687
+ sourceCwd: cwd,
2688
+ text: '$ralplan plan this change',
2689
+ sessionId,
2690
+ threadId,
2691
+ turnId,
2692
+ payload: { type: 'agent-turn-complete' },
2693
+ }, logsDir, {
2694
+ recordSkillActivation: async () => {
2695
+ writerCalls += 1;
2696
+ throw new Error(`injected writer failure ${'x'.repeat(1_000)}`);
2697
+ },
2698
+ });
2699
+ assert.equal(result, null);
2700
+ assert.equal(writerCalls, 1);
2701
+ const logPath = join(logsDir, `notify-hook-${new Date().toISOString().split('T')[0]}.jsonl`);
2702
+ const events = (await readFile(logPath, 'utf8')).trim().split('\n')
2703
+ .map((line) => JSON.parse(line));
2704
+ const failure = events.find((event) => event.type === 'notify_skill_activation_failure');
2705
+ assert.ok(failure);
2706
+ assert.equal(failure.level, 'warn');
2707
+ assert.equal(failure.session_id, sessionId);
2708
+ assert.equal(failure.thread_id, threadId);
2709
+ assert.equal(failure.turn_id, turnId);
2710
+ assert.match(String(failure.error), /^injected writer failure /);
2711
+ assert.ok(String(failure.error).length <= 512);
2712
+ assert.equal(Number.isNaN(Date.parse(String(failure.timestamp))), false);
2713
+ });
2714
+ });
2051
2715
  it('writes skill-active-state.json when keyword activation is detected', async () => {
2052
2716
  await withTempWorkingDir(async (cwd) => {
2053
2717
  const omxDir = join(cwd, '.omx');
@@ -2067,7 +2731,7 @@ exit 0
2067
2731
  await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(join(cwd, 'tmux.log')));
2068
2732
  await chmod(join(fakeBinDir, 'tmux'), 0o755);
2069
2733
  const result = runNotifyHook(cwd, fakeBinDir, codexHome, {
2070
- 'input-messages': ['please use $autopilot for this task'],
2734
+ 'input-messages': ['$autopilot handle this task'],
2071
2735
  'last-assistant-message': 'Here is the plan I will follow.',
2072
2736
  });
2073
2737
  assert.equal(result.status, 0, `hook failed: ${result.stderr || result.stdout}`);
@@ -2439,12 +3103,14 @@ exit 0
2439
3103
  phase: 'executing',
2440
3104
  source: 'keyword-detector',
2441
3105
  session_id: 'sess-managed',
3106
+ owner_codex_session_id: 'sess-managed',
2442
3107
  });
2443
3108
  await writeJson(join(sessionStateDir, 'autoresearch-state.json'), {
2444
3109
  active: true,
2445
3110
  mode: 'autoresearch',
2446
3111
  current_phase: 'executing',
2447
3112
  session_id: 'sess-managed',
3113
+ owner_codex_session_id: 'sess-managed',
2448
3114
  validation_mode: 'mission-validator-script',
2449
3115
  mission_validator_command: 'node scripts/validate.js',
2450
3116
  completion_artifact_path: '.omx/specs/autoresearch-demo/completion.json',
@@ -2488,12 +3154,14 @@ exit 0
2488
3154
  phase: 'reviewing',
2489
3155
  source: 'keyword-detector',
2490
3156
  session_id: 'sess-managed',
3157
+ owner_codex_session_id: 'sess-managed',
2491
3158
  });
2492
3159
  await writeJson(join(sessionStateDir, 'autoresearch-state.json'), {
2493
3160
  active: true,
2494
3161
  mode: 'autoresearch',
2495
3162
  current_phase: 'reviewing',
2496
3163
  session_id: 'sess-managed',
3164
+ owner_codex_session_id: 'sess-managed',
2497
3165
  validation_mode: 'mission-validator-script',
2498
3166
  mission_validator_command: 'node scripts/validate.js',
2499
3167
  completion_artifact_path: '.omx/specs/autoresearch-demo/completion.json',