oh-my-codex 0.12.4 → 0.12.6

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 (475) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/README.md +27 -3
  4. package/dist/cli/__tests__/ask.test.js +26 -0
  5. package/dist/cli/__tests__/ask.test.js.map +1 -1
  6. package/dist/cli/__tests__/doctor-warning-copy.test.js +28 -0
  7. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  8. package/dist/cli/__tests__/explore.test.js +95 -8
  9. package/dist/cli/__tests__/explore.test.js.map +1 -1
  10. package/dist/cli/__tests__/index.test.js +102 -4
  11. package/dist/cli/__tests__/index.test.js.map +1 -1
  12. package/dist/cli/__tests__/launch-fallback.test.js +169 -0
  13. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  14. package/dist/cli/__tests__/mcp-parity.test.js +31 -0
  15. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  16. package/dist/cli/__tests__/setup-agents-overwrite.test.js +66 -2
  17. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  18. package/dist/cli/__tests__/setup-refresh.test.js +51 -1
  19. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  20. package/dist/cli/__tests__/team.test.js +148 -3
  21. package/dist/cli/__tests__/team.test.js.map +1 -1
  22. package/dist/cli/__tests__/uninstall.test.js +14 -1
  23. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  24. package/dist/cli/cleanup.js +1 -1
  25. package/dist/cli/cleanup.js.map +1 -1
  26. package/dist/cli/constants.d.ts +1 -0
  27. package/dist/cli/constants.d.ts.map +1 -1
  28. package/dist/cli/constants.js +1 -0
  29. package/dist/cli/constants.js.map +1 -1
  30. package/dist/cli/doctor.d.ts.map +1 -1
  31. package/dist/cli/doctor.js +15 -0
  32. package/dist/cli/doctor.js.map +1 -1
  33. package/dist/cli/explore.d.ts +1 -0
  34. package/dist/cli/explore.d.ts.map +1 -1
  35. package/dist/cli/explore.js +49 -1
  36. package/dist/cli/explore.js.map +1 -1
  37. package/dist/cli/index.d.ts +2 -1
  38. package/dist/cli/index.d.ts.map +1 -1
  39. package/dist/cli/index.js +127 -14
  40. package/dist/cli/index.js.map +1 -1
  41. package/dist/cli/mcp-parity.d.ts +1 -1
  42. package/dist/cli/mcp-parity.d.ts.map +1 -1
  43. package/dist/cli/mcp-parity.js +24 -0
  44. package/dist/cli/mcp-parity.js.map +1 -1
  45. package/dist/cli/setup.d.ts.map +1 -1
  46. package/dist/cli/setup.js +17 -5
  47. package/dist/cli/setup.js.map +1 -1
  48. package/dist/cli/team.d.ts.map +1 -1
  49. package/dist/cli/team.js +80 -6
  50. package/dist/cli/team.js.map +1 -1
  51. package/dist/cli/uninstall.d.ts.map +1 -1
  52. package/dist/cli/uninstall.js +1 -0
  53. package/dist/cli/uninstall.js.map +1 -1
  54. package/dist/config/__tests__/generator-idempotent.test.js +60 -0
  55. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  56. package/dist/config/__tests__/mcp-registry.test.js +61 -0
  57. package/dist/config/__tests__/mcp-registry.test.js.map +1 -1
  58. package/dist/config/__tests__/wiki-config-contract.test.d.ts +2 -0
  59. package/dist/config/__tests__/wiki-config-contract.test.d.ts.map +1 -0
  60. package/dist/config/__tests__/wiki-config-contract.test.js +19 -0
  61. package/dist/config/__tests__/wiki-config-contract.test.js.map +1 -0
  62. package/dist/config/generator.d.ts +1 -0
  63. package/dist/config/generator.d.ts.map +1 -1
  64. package/dist/config/generator.js +88 -3
  65. package/dist/config/generator.js.map +1 -1
  66. package/dist/config/mcp-registry.d.ts +2 -0
  67. package/dist/config/mcp-registry.d.ts.map +1 -1
  68. package/dist/config/mcp-registry.js +12 -0
  69. package/dist/config/mcp-registry.js.map +1 -1
  70. package/dist/hooks/__tests__/agents-overlay.test.js +39 -0
  71. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  72. package/dist/hooks/__tests__/keyword-detector.test.js +297 -4
  73. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  74. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +392 -22
  75. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  76. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +166 -67
  77. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  78. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +112 -2
  79. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  80. package/dist/hooks/__tests__/notify-hook-modules.test.js +52 -12
  81. package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
  82. package/dist/hooks/__tests__/notify-hook-regression-205.test.d.ts +2 -3
  83. package/dist/hooks/__tests__/notify-hook-regression-205.test.d.ts.map +1 -1
  84. package/dist/hooks/__tests__/notify-hook-regression-205.test.js +18 -23
  85. package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
  86. package/dist/hooks/__tests__/notify-hook-session-scope.test.js +33 -0
  87. package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
  88. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +176 -1
  89. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  90. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +355 -7
  91. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  92. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +90 -2
  93. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  94. package/dist/hooks/__tests__/session.test.js +142 -2
  95. package/dist/hooks/__tests__/session.test.js.map +1 -1
  96. package/dist/hooks/__tests__/wiki-docs-contract.test.d.ts +2 -0
  97. package/dist/hooks/__tests__/wiki-docs-contract.test.d.ts.map +1 -0
  98. package/dist/hooks/__tests__/wiki-docs-contract.test.js +34 -0
  99. package/dist/hooks/__tests__/wiki-docs-contract.test.js.map +1 -0
  100. package/dist/hooks/agents-overlay.d.ts.map +1 -1
  101. package/dist/hooks/agents-overlay.js +0 -1
  102. package/dist/hooks/agents-overlay.js.map +1 -1
  103. package/dist/hooks/extensibility/__tests__/dispatcher.test.js +32 -0
  104. package/dist/hooks/extensibility/__tests__/dispatcher.test.js.map +1 -1
  105. package/dist/hooks/extensibility/__tests__/runtime.test.js +31 -0
  106. package/dist/hooks/extensibility/__tests__/runtime.test.js.map +1 -1
  107. package/dist/hooks/extensibility/__tests__/sdk.test.js +33 -3
  108. package/dist/hooks/extensibility/__tests__/sdk.test.js.map +1 -1
  109. package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
  110. package/dist/hooks/extensibility/dispatcher.js +41 -0
  111. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  112. package/dist/hooks/extensibility/sdk/runtime-state.d.ts.map +1 -1
  113. package/dist/hooks/extensibility/sdk/runtime-state.js +7 -1
  114. package/dist/hooks/extensibility/sdk/runtime-state.js.map +1 -1
  115. package/dist/hooks/extensibility/types.d.ts +1 -0
  116. package/dist/hooks/extensibility/types.d.ts.map +1 -1
  117. package/dist/hooks/keyword-detector.d.ts +6 -1
  118. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  119. package/dist/hooks/keyword-detector.js +207 -10
  120. package/dist/hooks/keyword-detector.js.map +1 -1
  121. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  122. package/dist/hooks/keyword-registry.js +3 -0
  123. package/dist/hooks/keyword-registry.js.map +1 -1
  124. package/dist/hooks/session.d.ts +14 -2
  125. package/dist/hooks/session.d.ts.map +1 -1
  126. package/dist/hooks/session.js +120 -16
  127. package/dist/hooks/session.js.map +1 -1
  128. package/dist/hud/__tests__/state.test.js +111 -2
  129. package/dist/hud/__tests__/state.test.js.map +1 -1
  130. package/dist/hud/state.d.ts.map +1 -1
  131. package/dist/hud/state.js +18 -21
  132. package/dist/hud/state.js.map +1 -1
  133. package/dist/mcp/__tests__/bootstrap.test.js +88 -1
  134. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  135. package/dist/mcp/__tests__/server-lifecycle.test.js +3 -0
  136. package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
  137. package/dist/mcp/__tests__/state-paths.test.js +30 -2
  138. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  139. package/dist/mcp/__tests__/state-server.test.js +415 -0
  140. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  141. package/dist/mcp/__tests__/wiki-server.test.d.ts +2 -0
  142. package/dist/mcp/__tests__/wiki-server.test.d.ts.map +1 -0
  143. package/dist/mcp/__tests__/wiki-server.test.js +30 -0
  144. package/dist/mcp/__tests__/wiki-server.test.js.map +1 -0
  145. package/dist/mcp/bootstrap.d.ts +19 -1
  146. package/dist/mcp/bootstrap.d.ts.map +1 -1
  147. package/dist/mcp/bootstrap.js +185 -0
  148. package/dist/mcp/bootstrap.js.map +1 -1
  149. package/dist/mcp/state-paths.d.ts +5 -0
  150. package/dist/mcp/state-paths.d.ts.map +1 -1
  151. package/dist/mcp/state-paths.js +41 -11
  152. package/dist/mcp/state-paths.js.map +1 -1
  153. package/dist/mcp/state-server.d.ts +4 -4
  154. package/dist/mcp/state-server.d.ts.map +1 -1
  155. package/dist/mcp/state-server.js +49 -2
  156. package/dist/mcp/state-server.js.map +1 -1
  157. package/dist/mcp/wiki-server.d.ts +181 -0
  158. package/dist/mcp/wiki-server.d.ts.map +1 -0
  159. package/dist/mcp/wiki-server.js +235 -0
  160. package/dist/mcp/wiki-server.js.map +1 -0
  161. package/dist/modes/__tests__/base-autoresearch-contract.test.js +74 -2
  162. package/dist/modes/__tests__/base-autoresearch-contract.test.js.map +1 -1
  163. package/dist/modes/__tests__/base-multi-state-compat.test.d.ts +2 -0
  164. package/dist/modes/__tests__/base-multi-state-compat.test.d.ts.map +1 -0
  165. package/dist/modes/__tests__/base-multi-state-compat.test.js +38 -0
  166. package/dist/modes/__tests__/base-multi-state-compat.test.js.map +1 -0
  167. package/dist/modes/__tests__/base-tmux-pane.test.js +1 -1
  168. package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
  169. package/dist/modes/base.d.ts +2 -1
  170. package/dist/modes/base.d.ts.map +1 -1
  171. package/dist/modes/base.js +55 -31
  172. package/dist/modes/base.js.map +1 -1
  173. package/dist/notifications/__tests__/formatter.test.js +11 -0
  174. package/dist/notifications/__tests__/formatter.test.js.map +1 -1
  175. package/dist/notifications/__tests__/idle-cooldown.test.js +32 -1
  176. package/dist/notifications/__tests__/idle-cooldown.test.js.map +1 -1
  177. package/dist/notifications/__tests__/index.test.d.ts +2 -0
  178. package/dist/notifications/__tests__/index.test.d.ts.map +1 -0
  179. package/dist/notifications/__tests__/index.test.js +113 -0
  180. package/dist/notifications/__tests__/index.test.js.map +1 -0
  181. package/dist/notifications/__tests__/lifecycle-dedupe.test.d.ts +2 -0
  182. package/dist/notifications/__tests__/lifecycle-dedupe.test.d.ts.map +1 -0
  183. package/dist/notifications/__tests__/lifecycle-dedupe.test.js +86 -0
  184. package/dist/notifications/__tests__/lifecycle-dedupe.test.js.map +1 -0
  185. package/dist/notifications/__tests__/reply-listener.test.js +174 -0
  186. package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
  187. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.d.ts +2 -0
  188. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.d.ts.map +1 -0
  189. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js +93 -0
  190. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js.map +1 -0
  191. package/dist/notifications/__tests__/session-registry.test.js +48 -1
  192. package/dist/notifications/__tests__/session-registry.test.js.map +1 -1
  193. package/dist/notifications/__tests__/session-status.test.d.ts +2 -0
  194. package/dist/notifications/__tests__/session-status.test.d.ts.map +1 -0
  195. package/dist/notifications/__tests__/session-status.test.js +159 -0
  196. package/dist/notifications/__tests__/session-status.test.js.map +1 -0
  197. package/dist/notifications/__tests__/tmux.test.js +58 -1
  198. package/dist/notifications/__tests__/tmux.test.js.map +1 -1
  199. package/dist/notifications/idle-cooldown.d.ts +11 -0
  200. package/dist/notifications/idle-cooldown.d.ts.map +1 -1
  201. package/dist/notifications/idle-cooldown.js +42 -8
  202. package/dist/notifications/idle-cooldown.js.map +1 -1
  203. package/dist/notifications/index.d.ts +1 -1
  204. package/dist/notifications/index.d.ts.map +1 -1
  205. package/dist/notifications/index.js +41 -8
  206. package/dist/notifications/index.js.map +1 -1
  207. package/dist/notifications/lifecycle-dedupe.d.ts +8 -0
  208. package/dist/notifications/lifecycle-dedupe.d.ts.map +1 -0
  209. package/dist/notifications/lifecycle-dedupe.js +112 -0
  210. package/dist/notifications/lifecycle-dedupe.js.map +1 -0
  211. package/dist/notifications/reply-listener.d.ts +10 -1
  212. package/dist/notifications/reply-listener.d.ts.map +1 -1
  213. package/dist/notifications/reply-listener.js +49 -11
  214. package/dist/notifications/reply-listener.js.map +1 -1
  215. package/dist/notifications/session-registry.d.ts.map +1 -1
  216. package/dist/notifications/session-registry.js +7 -1
  217. package/dist/notifications/session-registry.js.map +1 -1
  218. package/dist/notifications/session-status.d.ts +23 -0
  219. package/dist/notifications/session-status.d.ts.map +1 -0
  220. package/dist/notifications/session-status.js +187 -0
  221. package/dist/notifications/session-status.js.map +1 -0
  222. package/dist/notifications/tmux.d.ts +10 -0
  223. package/dist/notifications/tmux.d.ts.map +1 -1
  224. package/dist/notifications/tmux.js +59 -5
  225. package/dist/notifications/tmux.js.map +1 -1
  226. package/dist/notifications/types.d.ts +2 -0
  227. package/dist/notifications/types.d.ts.map +1 -1
  228. package/dist/openclaw/__tests__/index.test.js +84 -0
  229. package/dist/openclaw/__tests__/index.test.js.map +1 -1
  230. package/dist/openclaw/index.d.ts.map +1 -1
  231. package/dist/openclaw/index.js +7 -14
  232. package/dist/openclaw/index.js.map +1 -1
  233. package/dist/openclaw/types.d.ts +2 -2
  234. package/dist/openclaw/types.d.ts.map +1 -1
  235. package/dist/scripts/__tests__/codex-native-hook.test.js +692 -40
  236. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  237. package/dist/scripts/__tests__/hook-derived-watcher.test.d.ts +2 -0
  238. package/dist/scripts/__tests__/hook-derived-watcher.test.d.ts.map +1 -0
  239. package/dist/scripts/__tests__/hook-derived-watcher.test.js +87 -0
  240. package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -0
  241. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  242. package/dist/scripts/codex-native-hook.js +309 -77
  243. package/dist/scripts/codex-native-hook.js.map +1 -1
  244. package/dist/scripts/hook-derived-watcher.js +43 -1
  245. package/dist/scripts/hook-derived-watcher.js.map +1 -1
  246. package/dist/scripts/notify-fallback-watcher.js +95 -21
  247. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  248. package/dist/scripts/notify-hook/active-team.d.ts +9 -0
  249. package/dist/scripts/notify-hook/active-team.d.ts.map +1 -0
  250. package/dist/scripts/notify-hook/active-team.js +44 -0
  251. package/dist/scripts/notify-hook/active-team.js.map +1 -0
  252. package/dist/scripts/notify-hook/auto-nudge.d.ts +5 -3
  253. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  254. package/dist/scripts/notify-hook/auto-nudge.js +121 -78
  255. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  256. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  257. package/dist/scripts/notify-hook/managed-tmux.js +18 -4
  258. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  259. package/dist/scripts/notify-hook/operational-events.d.ts.map +1 -1
  260. package/dist/scripts/notify-hook/operational-events.js +21 -0
  261. package/dist/scripts/notify-hook/operational-events.js.map +1 -1
  262. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
  263. package/dist/scripts/notify-hook/ralph-session-resume.js +3 -2
  264. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  265. package/dist/scripts/notify-hook/state-io.d.ts +10 -1
  266. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  267. package/dist/scripts/notify-hook/state-io.js +56 -12
  268. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  269. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  270. package/dist/scripts/notify-hook/team-dispatch.js +305 -167
  271. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  272. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  273. package/dist/scripts/notify-hook/team-leader-nudge.js +87 -15
  274. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  275. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  276. package/dist/scripts/notify-hook/tmux-injection.js +11 -2
  277. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  278. package/dist/scripts/notify-hook.js +26 -16
  279. package/dist/scripts/notify-hook.js.map +1 -1
  280. package/dist/scripts/run-provider-advisor.js +20 -2
  281. package/dist/scripts/run-provider-advisor.js.map +1 -1
  282. package/dist/scripts/smoke-packed-install.d.ts +1 -8
  283. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  284. package/dist/scripts/smoke-packed-install.js +12 -68
  285. package/dist/scripts/smoke-packed-install.js.map +1 -1
  286. package/dist/state/__tests__/operations.test.js +113 -0
  287. package/dist/state/__tests__/operations.test.js.map +1 -1
  288. package/dist/state/__tests__/skill-active.test.js +35 -0
  289. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  290. package/dist/state/__tests__/workflow-transition.test.d.ts +2 -0
  291. package/dist/state/__tests__/workflow-transition.test.d.ts.map +1 -0
  292. package/dist/state/__tests__/workflow-transition.test.js +56 -0
  293. package/dist/state/__tests__/workflow-transition.test.js.map +1 -0
  294. package/dist/state/operations.d.ts +1 -1
  295. package/dist/state/operations.d.ts.map +1 -1
  296. package/dist/state/operations.js +88 -2
  297. package/dist/state/operations.js.map +1 -1
  298. package/dist/state/skill-active.d.ts +2 -2
  299. package/dist/state/skill-active.d.ts.map +1 -1
  300. package/dist/state/skill-active.js +119 -33
  301. package/dist/state/skill-active.js.map +1 -1
  302. package/dist/state/workflow-transition-reconcile.d.ts +15 -0
  303. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -0
  304. package/dist/state/workflow-transition-reconcile.js +100 -0
  305. package/dist/state/workflow-transition-reconcile.js.map +1 -0
  306. package/dist/state/workflow-transition.d.ts +22 -0
  307. package/dist/state/workflow-transition.d.ts.map +1 -0
  308. package/dist/state/workflow-transition.js +188 -0
  309. package/dist/state/workflow-transition.js.map +1 -0
  310. package/dist/team/__tests__/api-interop.test.js +90 -0
  311. package/dist/team/__tests__/api-interop.test.js.map +1 -1
  312. package/dist/team/__tests__/current-task-baseline.test.d.ts +2 -0
  313. package/dist/team/__tests__/current-task-baseline.test.d.ts.map +1 -0
  314. package/dist/team/__tests__/current-task-baseline.test.js +87 -0
  315. package/dist/team/__tests__/current-task-baseline.test.js.map +1 -0
  316. package/dist/team/__tests__/hardening-e2e.test.js +17 -0
  317. package/dist/team/__tests__/hardening-e2e.test.js.map +1 -1
  318. package/dist/team/__tests__/runtime.test.js +673 -65
  319. package/dist/team/__tests__/runtime.test.js.map +1 -1
  320. package/dist/team/__tests__/shutdown-fallback.test.js +11 -1
  321. package/dist/team/__tests__/shutdown-fallback.test.js.map +1 -1
  322. package/dist/team/__tests__/tmux-session.test.js +447 -4
  323. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  324. package/dist/team/api-interop.d.ts.map +1 -1
  325. package/dist/team/api-interop.js +10 -1
  326. package/dist/team/api-interop.js.map +1 -1
  327. package/dist/team/current-task-baseline.d.ts +32 -0
  328. package/dist/team/current-task-baseline.d.ts.map +1 -0
  329. package/dist/team/current-task-baseline.js +85 -0
  330. package/dist/team/current-task-baseline.js.map +1 -0
  331. package/dist/team/delivery-log.d.ts +1 -1
  332. package/dist/team/delivery-log.d.ts.map +1 -1
  333. package/dist/team/delivery-log.js.map +1 -1
  334. package/dist/team/leader-activity.d.ts +1 -0
  335. package/dist/team/leader-activity.d.ts.map +1 -1
  336. package/dist/team/leader-activity.js +4 -2
  337. package/dist/team/leader-activity.js.map +1 -1
  338. package/dist/team/progress-evidence.d.ts +2 -0
  339. package/dist/team/progress-evidence.d.ts.map +1 -0
  340. package/dist/team/progress-evidence.js +77 -0
  341. package/dist/team/progress-evidence.js.map +1 -0
  342. package/dist/team/runtime.d.ts.map +1 -1
  343. package/dist/team/runtime.js +269 -64
  344. package/dist/team/runtime.js.map +1 -1
  345. package/dist/team/scaling.d.ts.map +1 -1
  346. package/dist/team/scaling.js +1 -1
  347. package/dist/team/scaling.js.map +1 -1
  348. package/dist/team/state.d.ts.map +1 -1
  349. package/dist/team/state.js +2 -13
  350. package/dist/team/state.js.map +1 -1
  351. package/dist/team/tmux-session.d.ts +12 -3
  352. package/dist/team/tmux-session.d.ts.map +1 -1
  353. package/dist/team/tmux-session.js +174 -20
  354. package/dist/team/tmux-session.js.map +1 -1
  355. package/dist/team/worktree.d.ts +6 -1
  356. package/dist/team/worktree.d.ts.map +1 -1
  357. package/dist/team/worktree.js +28 -4
  358. package/dist/team/worktree.js.map +1 -1
  359. package/dist/utils/__tests__/agents-md.test.js +21 -1
  360. package/dist/utils/__tests__/agents-md.test.js.map +1 -1
  361. package/dist/utils/__tests__/repo-deps.test.d.ts +2 -0
  362. package/dist/utils/__tests__/repo-deps.test.d.ts.map +1 -0
  363. package/dist/utils/__tests__/repo-deps.test.js +71 -0
  364. package/dist/utils/__tests__/repo-deps.test.js.map +1 -0
  365. package/dist/utils/agents-md.d.ts +1 -0
  366. package/dist/utils/agents-md.d.ts.map +1 -1
  367. package/dist/utils/agents-md.js +7 -3
  368. package/dist/utils/agents-md.js.map +1 -1
  369. package/dist/utils/paths.d.ts +4 -0
  370. package/dist/utils/paths.d.ts.map +1 -1
  371. package/dist/utils/paths.js +20 -0
  372. package/dist/utils/paths.js.map +1 -1
  373. package/dist/utils/repo-deps.d.ts +20 -0
  374. package/dist/utils/repo-deps.d.ts.map +1 -0
  375. package/dist/utils/repo-deps.js +78 -0
  376. package/dist/utils/repo-deps.js.map +1 -0
  377. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.d.ts +2 -0
  378. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.d.ts.map +1 -0
  379. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +54 -0
  380. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -0
  381. package/dist/wiki/__tests__/cjk-tokenize.test.d.ts +12 -0
  382. package/dist/wiki/__tests__/cjk-tokenize.test.d.ts.map +1 -0
  383. package/dist/wiki/__tests__/cjk-tokenize.test.js +139 -0
  384. package/dist/wiki/__tests__/cjk-tokenize.test.js.map +1 -0
  385. package/dist/wiki/__tests__/crlf-parse.test.d.ts +2 -0
  386. package/dist/wiki/__tests__/crlf-parse.test.d.ts.map +1 -0
  387. package/dist/wiki/__tests__/crlf-parse.test.js +24 -0
  388. package/dist/wiki/__tests__/crlf-parse.test.js.map +1 -0
  389. package/dist/wiki/__tests__/escape-newline.test.d.ts +2 -0
  390. package/dist/wiki/__tests__/escape-newline.test.d.ts.map +1 -0
  391. package/dist/wiki/__tests__/escape-newline.test.js +45 -0
  392. package/dist/wiki/__tests__/escape-newline.test.js.map +1 -0
  393. package/dist/wiki/__tests__/ingest.test.d.ts +5 -0
  394. package/dist/wiki/__tests__/ingest.test.d.ts.map +1 -0
  395. package/dist/wiki/__tests__/ingest.test.js +181 -0
  396. package/dist/wiki/__tests__/ingest.test.js.map +1 -0
  397. package/dist/wiki/__tests__/lint.test.d.ts +5 -0
  398. package/dist/wiki/__tests__/lint.test.d.ts.map +1 -0
  399. package/dist/wiki/__tests__/lint.test.js +163 -0
  400. package/dist/wiki/__tests__/lint.test.js.map +1 -0
  401. package/dist/wiki/__tests__/query.test.d.ts +5 -0
  402. package/dist/wiki/__tests__/query.test.d.ts.map +1 -0
  403. package/dist/wiki/__tests__/query.test.js +141 -0
  404. package/dist/wiki/__tests__/query.test.js.map +1 -0
  405. package/dist/wiki/__tests__/reserved-file-guard.test.d.ts +2 -0
  406. package/dist/wiki/__tests__/reserved-file-guard.test.d.ts.map +1 -0
  407. package/dist/wiki/__tests__/reserved-file-guard.test.js +44 -0
  408. package/dist/wiki/__tests__/reserved-file-guard.test.js.map +1 -0
  409. package/dist/wiki/__tests__/session-hooks.test.d.ts +5 -0
  410. package/dist/wiki/__tests__/session-hooks.test.d.ts.map +1 -0
  411. package/dist/wiki/__tests__/session-hooks.test.js +36 -0
  412. package/dist/wiki/__tests__/session-hooks.test.js.map +1 -0
  413. package/dist/wiki/__tests__/slug-nonascii.test.d.ts +2 -0
  414. package/dist/wiki/__tests__/slug-nonascii.test.d.ts.map +1 -0
  415. package/dist/wiki/__tests__/slug-nonascii.test.js +24 -0
  416. package/dist/wiki/__tests__/slug-nonascii.test.js.map +1 -0
  417. package/dist/wiki/__tests__/storage.test.d.ts +5 -0
  418. package/dist/wiki/__tests__/storage.test.d.ts.map +1 -0
  419. package/dist/wiki/__tests__/storage.test.js +278 -0
  420. package/dist/wiki/__tests__/storage.test.js.map +1 -0
  421. package/dist/wiki/__tests__/test-helpers.d.ts +31 -0
  422. package/dist/wiki/__tests__/test-helpers.d.ts.map +1 -0
  423. package/dist/wiki/__tests__/test-helpers.js +108 -0
  424. package/dist/wiki/__tests__/test-helpers.js.map +1 -0
  425. package/dist/wiki/index.d.ts +14 -0
  426. package/dist/wiki/index.d.ts.map +1 -0
  427. package/dist/wiki/index.js +17 -0
  428. package/dist/wiki/index.js.map +1 -0
  429. package/dist/wiki/ingest.d.ts +20 -0
  430. package/dist/wiki/ingest.d.ts.map +1 -0
  431. package/dist/wiki/ingest.js +115 -0
  432. package/dist/wiki/ingest.js.map +1 -0
  433. package/dist/wiki/lifecycle.d.ts +20 -0
  434. package/dist/wiki/lifecycle.d.ts.map +1 -0
  435. package/dist/wiki/lifecycle.js +212 -0
  436. package/dist/wiki/lifecycle.js.map +1 -0
  437. package/dist/wiki/lint.d.ts +25 -0
  438. package/dist/wiki/lint.d.ts.map +1 -0
  439. package/dist/wiki/lint.js +166 -0
  440. package/dist/wiki/lint.js.map +1 -0
  441. package/dist/wiki/query.d.ts +36 -0
  442. package/dist/wiki/query.d.ts.map +1 -0
  443. package/dist/wiki/query.js +138 -0
  444. package/dist/wiki/query.js.map +1 -0
  445. package/dist/wiki/storage.d.ts +33 -0
  446. package/dist/wiki/storage.d.ts.map +1 -0
  447. package/dist/wiki/storage.js +321 -0
  448. package/dist/wiki/storage.js.map +1 -0
  449. package/dist/wiki/types.d.ts +83 -0
  450. package/dist/wiki/types.d.ts.map +1 -0
  451. package/dist/wiki/types.js +15 -0
  452. package/dist/wiki/types.js.map +1 -0
  453. package/package.json +3 -1
  454. package/skills/configure-notifications/SKILL.md +1 -0
  455. package/skills/doctor/SKILL.md +11 -0
  456. package/skills/omx-setup/SKILL.md +1 -1
  457. package/skills/wiki/SKILL.md +57 -0
  458. package/src/scripts/__tests__/codex-native-hook.test.ts +920 -56
  459. package/src/scripts/__tests__/hook-derived-watcher.test.ts +111 -0
  460. package/src/scripts/codex-native-hook.ts +377 -83
  461. package/src/scripts/hook-derived-watcher.ts +43 -1
  462. package/src/scripts/notify-fallback-watcher.ts +99 -20
  463. package/src/scripts/notify-hook/active-team.ts +54 -0
  464. package/src/scripts/notify-hook/auto-nudge.ts +132 -79
  465. package/src/scripts/notify-hook/managed-tmux.ts +22 -4
  466. package/src/scripts/notify-hook/operational-events.ts +21 -0
  467. package/src/scripts/notify-hook/ralph-session-resume.ts +3 -2
  468. package/src/scripts/notify-hook/state-io.ts +89 -12
  469. package/src/scripts/notify-hook/team-dispatch.ts +326 -168
  470. package/src/scripts/notify-hook/team-leader-nudge.ts +91 -14
  471. package/src/scripts/notify-hook/tmux-injection.ts +11 -2
  472. package/src/scripts/notify-hook.ts +36 -22
  473. package/src/scripts/run-provider-advisor.ts +20 -2
  474. package/src/scripts/smoke-packed-install.ts +16 -83
  475. package/templates/AGENTS.md +3 -4
@@ -2,24 +2,33 @@ import { execFileSync } from "child_process";
2
2
  import { existsSync, readFileSync } from "fs";
3
3
  import { mkdir, readFile, readdir, writeFile } from "fs/promises";
4
4
  import { join, resolve } from "path";
5
- import { readModeState, updateModeState } from "../modes/base.js";
5
+ import { readModeState, readModeStateForSession, updateModeState } from "../modes/base.js";
6
6
  import { listActiveSkills, readVisibleSkillActiveState, } from "../state/skill-active.js";
7
7
  import { readSubagentSessionSummary } from "../subagents/tracker.js";
8
8
  import { resolveCanonicalTeamStateRoot } from "../team/state-root.js";
9
+ import { readUsableSessionState, reconcileNativeSessionStart } from "../hooks/session.js";
9
10
  import { appendTeamEvent, readTeamLeaderAttention, readTeamManifestV2, readTeamPhase, writeTeamLeaderAttention, writeTeamPhase, } from "../team/state.js";
10
- import { omxNotepadPath, omxProjectMemoryPath, omxStateDir } from "../utils/paths.js";
11
- import { detectPrimaryKeyword, recordSkillActivation, } from "../hooks/keyword-detector.js";
12
- import { detectStallPattern, loadAutoNudgeConfig, normalizeAutoNudgeSignatureText, } from "./notify-hook/auto-nudge.js";
11
+ import { omxNotepadPath, omxProjectMemoryPath } from "../utils/paths.js";
12
+ import { getStateFilePath } from "../mcp/state-paths.js";
13
+ import { detectKeywords, detectPrimaryKeyword, recordSkillActivation, } from "../hooks/keyword-detector.js";
14
+ import { detectStallPattern, loadAutoNudgeConfig, normalizeAutoNudgeSignatureText, resolveEffectiveAutoNudgeResponse, } from "./notify-hook/auto-nudge.js";
13
15
  import { buildNativePostToolUseOutput, buildNativePreToolUseOutput, detectMcpTransportFailure, } from "./codex-native-pre-post.js";
14
16
  import { buildNativeHookEvent, } from "../hooks/extensibility/events.js";
15
17
  import { dispatchHookEvent } from "../hooks/extensibility/dispatcher.js";
16
- import { writeSessionStart } from "../hooks/session.js";
17
18
  import { reconcileHudForPromptSubmit } from "../hud/reconcile.js";
19
+ import { onSessionStart as buildWikiSessionStartContext } from "../wiki/lifecycle.js";
18
20
  const TERMINAL_RALPH_PHASES = new Set(["complete", "failed", "cancelled"]);
19
21
  const TERMINAL_MODE_PHASES = new Set(["complete", "failed", "cancelled"]);
20
- const SKILL_STOP_BLOCKERS = new Set(["ralplan", "deep-interview"]);
22
+ const SKILL_STOP_BLOCKERS = new Set(["ralplan"]);
21
23
  const TEAM_TERMINAL_TASK_STATUSES = new Set(["completed", "failed"]);
22
24
  const NATIVE_STOP_STATE_FILE = "native-stop-state.json";
25
+ const STABLE_FINAL_RECOMMENDATION_PATTERNS = [
26
+ /^\s*(?:launch|release|ship)-?ready\s*:\s*(?:yes|no)\b[^\n\r]*/im,
27
+ /^\s*ready to release\s*:\s*(?:yes|no)\b[^\n\r]*/im,
28
+ /^\s*(?:final\s+)?recommendation\s*:\s*(?:yes|no|ship|hold|release|do not release|proceed|do not proceed)\b[^\n\r]*/im,
29
+ /^\s*decision\s*:\s*(?:yes|no|ship|hold|release|do not release|proceed|do not proceed)\b[^\n\r]*/im,
30
+ ];
31
+ const RELEASE_READINESS_FINALIZE_SYSTEM_MESSAGE = "OMX release-readiness detected a stable final recommendation with no active worker tasks; emit one concise final decision summary and finalize.";
23
32
  function safeString(value) {
24
33
  return typeof value === "string" ? value : "";
25
34
  }
@@ -81,16 +90,24 @@ function readPromptText(payload) {
81
90
  }
82
91
  return "";
83
92
  }
93
+ function sanitizePayloadForHookContext(payload, hookEventName) {
94
+ if (hookEventName !== "UserPromptSubmit")
95
+ return payload;
96
+ const sanitized = { ...payload };
97
+ delete sanitized.prompt;
98
+ delete sanitized.input;
99
+ delete sanitized.user_prompt;
100
+ delete sanitized.userPrompt;
101
+ delete sanitized.text;
102
+ return sanitized;
103
+ }
84
104
  function buildBaseContext(cwd, payload, hookEventName) {
85
105
  return {
86
106
  cwd,
87
107
  project_path: cwd,
88
108
  transcript_path: safeString(payload.transcript_path ?? payload.transcriptPath) || null,
89
109
  source: safeString(payload.source),
90
- payload,
91
- ...(hookEventName === "UserPromptSubmit"
92
- ? { prompt: readPromptText(payload) }
93
- : {}),
110
+ payload: sanitizePayloadForHookContext(payload, hookEventName),
94
111
  };
95
112
  }
96
113
  async function readJsonIfExists(path) {
@@ -111,22 +128,26 @@ function formatPhase(value, fallback = "active") {
111
128
  const phase = safeString(value).trim();
112
129
  return phase || fallback;
113
130
  }
114
- async function readActiveRalphState(stateDir) {
115
- const sessionInfo = await readJsonIfExists(join(stateDir, "session.json"));
131
+ async function readActiveRalphState(stateDir, preferredSessionId) {
132
+ const sessionInfo = await readUsableSessionState(resolve(stateDir, "..", ".."));
116
133
  const currentOmxSessionId = safeString(sessionInfo?.session_id).trim();
117
- if (currentOmxSessionId) {
118
- const sessionScoped = await readJsonIfExists(join(stateDir, "sessions", currentOmxSessionId, "ralph-state.json"));
134
+ const sessionCandidates = [...new Set([
135
+ safeString(preferredSessionId).trim(),
136
+ currentOmxSessionId,
137
+ ].filter(Boolean))];
138
+ for (const sessionId of sessionCandidates) {
139
+ const sessionScoped = await readJsonIfExists(join(stateDir, "sessions", sessionId, "ralph-state.json"));
119
140
  if (sessionScoped?.active === true
120
141
  && !TERMINAL_RALPH_PHASES.has(safeString(sessionScoped.current_phase).trim().toLowerCase())) {
121
142
  return sessionScoped;
122
143
  }
123
144
  }
145
+ if (sessionCandidates.length > 0)
146
+ return null;
124
147
  const direct = await readJsonIfExists(join(stateDir, "ralph-state.json"));
125
148
  if (direct?.active === true && !TERMINAL_RALPH_PHASES.has(safeString(direct.current_phase).trim().toLowerCase())) {
126
149
  return direct;
127
150
  }
128
- if (currentOmxSessionId)
129
- return null;
130
151
  const sessionsRoot = join(stateDir, "sessions");
131
152
  if (!existsSync(sessionsRoot))
132
153
  return null;
@@ -328,6 +349,10 @@ async function buildSessionStartContext(cwd, sessionId) {
328
349
  // best effort only
329
350
  }
330
351
  }
352
+ const wikiContext = buildWikiSessionStartContext({ cwd });
353
+ if (wikiContext.additionalContext) {
354
+ sections.push(wikiContext.additionalContext);
355
+ }
331
356
  const subagentSummary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
332
357
  if (subagentSummary && subagentSummary.activeSubagentThreadIds.length > 0) {
333
358
  sections.push(`[Subagents]\n- active subagent threads: ${subagentSummary.activeSubagentThreadIds.length}`);
@@ -337,29 +362,83 @@ async function buildSessionStartContext(cwd, sessionId) {
337
362
  function buildAdditionalContextMessage(prompt, skillState) {
338
363
  if (!prompt)
339
364
  return null;
365
+ const matches = detectKeywords(prompt);
340
366
  const match = detectPrimaryKeyword(prompt);
341
367
  if (!match)
342
368
  return null;
343
- if (match.skill === "team") {
369
+ const detectedKeywordMessage = matches.length > 1
370
+ ? `OMX native UserPromptSubmit detected workflow keywords ${matches.map((entry) => `"${entry.keyword}" -> ${entry.skill}`).join(", ")}.`
371
+ : `OMX native UserPromptSubmit detected workflow keyword "${match.keyword}" -> ${match.skill}.`;
372
+ const activeSkills = Array.isArray(skillState?.active_skills)
373
+ ? skillState.active_skills.map((entry) => entry.skill)
374
+ : [];
375
+ const deferredSkills = Array.isArray(skillState?.deferred_skills)
376
+ ? skillState.deferred_skills
377
+ : [];
378
+ const teamDetected = activeSkills.includes("team");
379
+ const combinedTransitionMessage = (() => {
380
+ if (!skillState?.transition_message)
381
+ return null;
382
+ if (matches.length <= 1 || activeSkills.length <= 1)
383
+ return skillState.transition_message;
384
+ const source = skillState.transition_message.match(/^mode transiting: (.+?) -> /)?.[1];
385
+ if (!source)
386
+ return skillState.transition_message;
387
+ return `mode transiting: ${source} -> ${activeSkills.join(" + ")}`;
388
+ })();
389
+ if (skillState?.transition_error) {
390
+ return [
391
+ `OMX native UserPromptSubmit denied workflow keyword "${match.keyword}" -> ${match.skill}.`,
392
+ skillState.transition_error,
393
+ 'Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.',
394
+ ].join(' ');
395
+ }
396
+ if (skillState?.transition_message) {
397
+ return [
398
+ detectedKeywordMessage,
399
+ combinedTransitionMessage,
400
+ activeSkills.length > 1 ? `active skills: ${activeSkills.join(", ")}.` : null,
401
+ deferredSkills.length > 0
402
+ ? `planning preserved over simultaneous execution follow-up; deferred skills: ${deferredSkills.join(", ")}.`
403
+ : null,
404
+ skillState.initialized_mode && skillState.initialized_state_path
405
+ ? `skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`
406
+ : null,
407
+ teamDetected
408
+ ? "Use the durable OMX team runtime via `omx team ...` for coordinated execution; do not replace it with in-process fanout."
409
+ : null,
410
+ teamDetected ? "If you need help, run `omx team --help`." : null,
411
+ 'Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.',
412
+ ].filter(Boolean).join(' ');
413
+ }
414
+ if (teamDetected) {
344
415
  const initializedStateMessage = skillState?.initialized_mode && skillState.initialized_state_path
345
416
  ? `skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`
346
417
  : null;
347
418
  return [
348
- `OMX native UserPromptSubmit detected workflow keyword "${match.keyword}" -> ${match.skill}.`,
419
+ detectedKeywordMessage,
420
+ activeSkills.length > 1 ? `active skills: ${activeSkills.join(", ")}.` : null,
421
+ deferredSkills.length > 0
422
+ ? `planning preserved over simultaneous execution follow-up; deferred skills: ${deferredSkills.join(", ")}.`
423
+ : null,
349
424
  initializedStateMessage,
350
425
  "Use the durable OMX team runtime via `omx team ...` for coordinated execution; do not replace it with in-process fanout.",
351
426
  "If you need help, run `omx team --help`.",
352
- "Follow AGENTS.md routing and preserve ralplan/ralph execution gates.",
427
+ "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
353
428
  ].filter(Boolean).join(" ");
354
429
  }
355
430
  if (skillState?.initialized_mode && skillState.initialized_state_path) {
356
431
  return [
357
- `OMX native UserPromptSubmit detected workflow keyword "${match.keyword}" -> ${match.skill}.`,
432
+ detectedKeywordMessage,
433
+ activeSkills.length > 1 ? `active skills: ${activeSkills.join(", ")}.` : null,
434
+ deferredSkills.length > 0
435
+ ? `planning preserved over simultaneous execution follow-up; deferred skills: ${deferredSkills.join(", ")}.`
436
+ : null,
358
437
  `skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`,
359
- "Follow AGENTS.md routing and preserve ralplan/ralph execution gates.",
438
+ "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
360
439
  ].join(" ");
361
440
  }
362
- return `OMX native UserPromptSubmit detected workflow keyword "${match.keyword}" -> ${match.skill}. Follow AGENTS.md routing and preserve ralplan/ralph execution gates.`;
441
+ return `${detectedKeywordMessage} Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.`;
363
442
  }
364
443
  function parseTeamWorkerEnv(rawValue) {
365
444
  const match = /^([a-z0-9][a-z0-9-]{0,29})\/(worker-\d+)$/.exec(rawValue.trim());
@@ -455,8 +534,10 @@ function isStopExempt(payload) {
455
534
  || value.includes("compact")
456
535
  || value.includes("limit"));
457
536
  }
458
- async function buildModeBasedStopOutput(mode, cwd) {
459
- const state = await readModeState(mode, cwd);
537
+ async function buildModeBasedStopOutput(mode, cwd, sessionId) {
538
+ const state = sessionId
539
+ ? await readModeStateForSession(mode, sessionId, cwd)
540
+ : await readModeState(mode, cwd);
460
541
  if (state?.active !== true || !isNonTerminalPhase(state.current_phase))
461
542
  return null;
462
543
  const phase = formatPhase(state.current_phase);
@@ -467,11 +548,34 @@ async function buildModeBasedStopOutput(mode, cwd) {
467
548
  systemMessage: `OMX ${mode} is still active (phase: ${phase}).`,
468
549
  };
469
550
  }
470
- async function buildTeamStopOutput(cwd) {
471
- const teamState = await readModeState("team", cwd);
551
+ async function readTeamModeStateForStop(cwd, sessionId) {
552
+ const normalizedSessionId = safeString(sessionId).trim();
553
+ if (!normalizedSessionId) {
554
+ return await readModeState("team", cwd);
555
+ }
556
+ const scopedState = await readStopSessionPinnedState("team-state.json", cwd, normalizedSessionId);
557
+ if (scopedState)
558
+ return scopedState;
559
+ const rootState = await readJsonIfExists(join(cwd, ".omx", "state", "team-state.json"));
560
+ if (rootState?.active !== true)
561
+ return null;
562
+ const ownerSessionId = safeString(rootState.session_id).trim();
563
+ if (ownerSessionId && ownerSessionId !== normalizedSessionId) {
564
+ return null;
565
+ }
566
+ return rootState;
567
+ }
568
+ async function buildTeamStopOutput(cwd, sessionId) {
569
+ const teamState = await readTeamModeStateForStop(cwd, sessionId);
472
570
  if (teamState?.active !== true)
473
571
  return null;
474
572
  const teamName = safeString(teamState.team_name).trim();
573
+ if (teamName) {
574
+ const canonicalTeamDir = join(resolveCanonicalTeamStateRoot(cwd), "team", teamName);
575
+ if (!existsSync(canonicalTeamDir)) {
576
+ return null;
577
+ }
578
+ }
475
579
  const coarsePhase = teamState.current_phase;
476
580
  const canonicalPhase = teamName ? (await readTeamPhase(teamName, cwd))?.current_phase ?? coarsePhase : coarsePhase;
477
581
  if (!isNonTerminalPhase(canonicalPhase))
@@ -490,6 +594,43 @@ function buildTeamStopOutputForPhase(teamName, phase) {
490
594
  systemMessage: `OMX team pipeline is still active at phase ${phase}.`,
491
595
  };
492
596
  }
597
+ function extractStableFinalRecommendationSummary(message) {
598
+ for (const pattern of STABLE_FINAL_RECOMMENDATION_PATTERNS) {
599
+ const match = pattern.exec(message);
600
+ if (!match)
601
+ continue;
602
+ const summary = match[0]?.trim().replace(/\s+/g, " ");
603
+ if (!summary)
604
+ continue;
605
+ return /[.!?]$/.test(summary) ? summary : `${summary}.`;
606
+ }
607
+ return "";
608
+ }
609
+ function buildStableFinalRecommendationStopSignature(payload, teamName, summary) {
610
+ const sessionId = readPayloadSessionId(payload) || "no-session";
611
+ const threadId = readPayloadThreadId(payload) || "no-thread";
612
+ const normalizedSummary = normalizeAutoNudgeSignatureText(summary) || summary.toLowerCase();
613
+ return ["release-readiness-finalize", sessionId, threadId, teamName, normalizedSummary].join("|");
614
+ }
615
+ function hasReleaseReadinessMode(payload) {
616
+ const mode = safeString(payload.mode).trim().toLowerCase();
617
+ return mode === "release-readiness";
618
+ }
619
+ async function hasReleaseReadinessStopMarker(cwd, sessionId, teamName) {
620
+ if (!sessionId)
621
+ return false;
622
+ const markerState = await readStopSessionPinnedState("release-readiness-state.json", cwd, sessionId);
623
+ if (markerState?.active !== true || markerState.stable_final_recommendation_emitted !== true) {
624
+ return false;
625
+ }
626
+ const markerTeamName = safeString(markerState.team_name).trim();
627
+ if (markerTeamName && markerTeamName !== teamName)
628
+ return false;
629
+ const markerSessionId = safeString(markerState.session_id).trim();
630
+ if (markerSessionId && markerSessionId !== sessionId)
631
+ return false;
632
+ return true;
633
+ }
493
634
  function readPayloadSessionId(payload) {
494
635
  return safeString(payload.session_id ?? payload.sessionId).trim();
495
636
  }
@@ -499,26 +640,22 @@ function readPayloadThreadId(payload) {
499
640
  function readPayloadTurnId(payload) {
500
641
  return safeString(payload.turn_id ?? payload.turnId).trim();
501
642
  }
502
- async function isDeepInterviewSuppressedForStop(cwd, sessionId, threadId) {
503
- const scopedModeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, sessionId);
504
- if (scopedModeState?.active === true)
505
- return true;
506
- const canonicalState = await readVisibleSkillActiveState(cwd, sessionId);
507
- const deepInterviewEntry = canonicalState
508
- ? listActiveSkills(canonicalState).find((entry) => (entry.skill === "deep-interview"
509
- && matchesSkillStopContext(entry, canonicalState, sessionId, threadId)))
510
- : null;
511
- if (deepInterviewEntry
512
- && safeObject(canonicalState?.input_lock).active === true) {
513
- return true;
514
- }
515
- return (await readBlockingSkillForStop(cwd, sessionId, threadId, "deep-interview")) !== null;
643
+ async function resolveInternalSessionIdForPayload(cwd, payloadSessionId) {
644
+ const currentSession = await readUsableSessionState(cwd);
645
+ const canonicalSessionId = safeString(currentSession?.session_id).trim();
646
+ if (!canonicalSessionId)
647
+ return payloadSessionId;
648
+ const nativeSessionId = safeString(currentSession?.native_session_id).trim();
649
+ if (!payloadSessionId)
650
+ return canonicalSessionId;
651
+ if (payloadSessionId === canonicalSessionId)
652
+ return canonicalSessionId;
653
+ if (nativeSessionId && payloadSessionId === nativeSessionId)
654
+ return canonicalSessionId;
655
+ return payloadSessionId;
516
656
  }
517
657
  async function readStopSessionPinnedState(fileName, cwd, sessionId) {
518
- const stateDir = omxStateDir(cwd);
519
- const statePath = sessionId
520
- ? join(stateDir, "sessions", sessionId, fileName)
521
- : join(stateDir, fileName);
658
+ const statePath = getStateFilePath(fileName, cwd, sessionId || undefined);
522
659
  return readJsonIfExists(statePath);
523
660
  }
524
661
  function matchesSkillStopContext(entry, state, sessionId, threadId) {
@@ -559,6 +696,21 @@ async function readBlockingSkillForStop(cwd, sessionId, threadId, requiredSkill)
559
696
  }
560
697
  return null;
561
698
  }
699
+ async function readStopAutoNudgePhase(cwd, sessionId, threadId) {
700
+ if (!sessionId.trim())
701
+ return "";
702
+ const canonicalState = await readVisibleSkillActiveState(cwd, sessionId);
703
+ const visibleEntries = canonicalState ? listActiveSkills(canonicalState) : [];
704
+ const deepInterview = visibleEntries.find((entry) => (entry.skill === "deep-interview"
705
+ && matchesSkillStopContext(entry, canonicalState ?? {}, sessionId, threadId)));
706
+ if (!deepInterview)
707
+ return "";
708
+ const modeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, sessionId);
709
+ if (!modeState || modeState.active !== true)
710
+ return "";
711
+ const modePhase = safeString(modeState.current_phase).trim().toLowerCase();
712
+ return modePhase === "intent-first" ? "planning" : "";
713
+ }
562
714
  function buildRepeatableStopSignature(payload, kind, detail = "") {
563
715
  const sessionId = readPayloadSessionId(payload) || "no-session";
564
716
  const threadId = readPayloadThreadId(payload) || "no-thread";
@@ -586,9 +738,6 @@ function buildRepeatableStopSignature(payload, kind, detail = "") {
586
738
  normalizedDetail || "no-detail",
587
739
  ].join("|");
588
740
  }
589
- async function readNativeStopState(stateDir) {
590
- return await readJsonIfExists(join(stateDir, NATIVE_STOP_STATE_FILE)) ?? {};
591
- }
592
741
  function readNativeStopSessionKey(payload) {
593
742
  return readPayloadSessionId(payload) || readPayloadThreadId(payload) || "global";
594
743
  }
@@ -600,7 +749,8 @@ function readPreviousNativeStopSignature(state, sessionKey) {
600
749
  async function persistNativeStopSignature(stateDir, payload, signature) {
601
750
  if (!signature)
602
751
  return;
603
- const state = await readNativeStopState(stateDir);
752
+ const statePath = join(stateDir, NATIVE_STOP_STATE_FILE);
753
+ const state = await readJsonIfExists(statePath) ?? {};
604
754
  const sessions = safeObject(state.sessions);
605
755
  const sessionKey = readNativeStopSessionKey(payload);
606
756
  sessions[sessionKey] = {
@@ -608,7 +758,8 @@ async function persistNativeStopSignature(stateDir, payload, signature) {
608
758
  last_signature: signature,
609
759
  updated_at: new Date().toISOString(),
610
760
  };
611
- await writeFile(join(stateDir, NATIVE_STOP_STATE_FILE), JSON.stringify({
761
+ await mkdir(stateDir, { recursive: true });
762
+ await writeFile(statePath, JSON.stringify({
612
763
  ...state,
613
764
  sessions,
614
765
  }, null, 2));
@@ -618,7 +769,7 @@ async function maybeReturnRepeatableStopOutput(payload, stateDir, signature, out
618
769
  return null;
619
770
  const stopHookActive = payload.stop_hook_active === true || payload.stopHookActive === true;
620
771
  if (stopHookActive) {
621
- const state = await readNativeStopState(stateDir);
772
+ const state = await readJsonIfExists(join(stateDir, NATIVE_STOP_STATE_FILE)) ?? {};
622
773
  const previousSignature = readPreviousNativeStopSignature(state, readNativeStopSessionKey(payload));
623
774
  if (!signature || previousSignature === signature) {
624
775
  return null;
@@ -658,6 +809,43 @@ async function findCanonicalActiveTeamForSession(cwd, sessionId) {
658
809
  }
659
810
  return null;
660
811
  }
812
+ async function resolveActiveTeamNameForStop(cwd, sessionId) {
813
+ const directState = await readTeamModeStateForStop(cwd, sessionId);
814
+ const directTeamName = safeString(directState?.team_name).trim();
815
+ if (directState?.active === true && directTeamName)
816
+ return directTeamName;
817
+ const canonicalTeam = await findCanonicalActiveTeamForSession(cwd, sessionId);
818
+ return canonicalTeam?.teamName ?? "";
819
+ }
820
+ async function maybeBuildReleaseReadinessFinalizeStopOutput(payload, cwd, stateDir, sessionId) {
821
+ if (!sessionId)
822
+ return { matched: false, output: null };
823
+ const teamName = await resolveActiveTeamNameForStop(cwd, sessionId);
824
+ if (!teamName)
825
+ return { matched: false, output: null };
826
+ const explicitReleaseReadinessContext = hasReleaseReadinessMode(payload)
827
+ || await hasReleaseReadinessStopMarker(cwd, sessionId, teamName);
828
+ if (!explicitReleaseReadinessContext) {
829
+ return { matched: false, output: null };
830
+ }
831
+ const summary = extractStableFinalRecommendationSummary(safeString(payload.last_assistant_message ?? payload.lastAssistantMessage));
832
+ if (!summary)
833
+ return { matched: false, output: null };
834
+ const leaderAttention = await readTeamLeaderAttention(teamName, cwd);
835
+ if (!leaderAttention
836
+ || leaderAttention.leader_decision_state !== "done_waiting_on_leader"
837
+ || leaderAttention.work_remaining !== false) {
838
+ return { matched: false, output: null };
839
+ }
840
+ const signature = buildStableFinalRecommendationStopSignature(payload, teamName, summary);
841
+ const output = await maybeReturnRepeatableStopOutput(payload, stateDir, signature, {
842
+ decision: "block",
843
+ reason: `Stable final recommendation already reached with no active worker tasks. Emit exactly one concise final decision summary aligned to "${summary}" with no filler or residual acknowledgements (for example "yes"), then stop.`,
844
+ stopReason: "release_readiness_auto_finalize",
845
+ systemMessage: RELEASE_READINESS_FINALIZE_SYSTEM_MESSAGE,
846
+ });
847
+ return { matched: true, output };
848
+ }
661
849
  async function buildSkillStopOutput(cwd, sessionId, threadId) {
662
850
  const blocker = await readBlockingSkillForStop(cwd, sessionId, threadId);
663
851
  if (!blocker)
@@ -674,7 +862,7 @@ async function buildSkillStopOutput(cwd, sessionId, threadId) {
674
862
  };
675
863
  }
676
864
  async function findActiveTeamForTransportFailure(cwd, sessionId) {
677
- const teamState = await readModeState("team", cwd);
865
+ const teamState = await readModeStateForSession("team", sessionId, cwd);
678
866
  if (teamState?.active === true) {
679
867
  const teamName = safeString(teamState.team_name).trim();
680
868
  const coarsePhase = formatPhase(teamState.current_phase);
@@ -688,8 +876,8 @@ async function findActiveTeamForTransportFailure(cwd, sessionId) {
688
876
  return await findCanonicalActiveTeamForSession(cwd, sessionId);
689
877
  }
690
878
  async function markTeamTransportFailure(cwd, payload) {
691
- const sessionId = readPayloadSessionId(payload);
692
- const activeTeam = await findActiveTeamForTransportFailure(cwd, sessionId);
879
+ const canonicalSessionId = await resolveInternalSessionIdForPayload(cwd, readPayloadSessionId(payload));
880
+ const activeTeam = await findActiveTeamForTransportFailure(cwd, canonicalSessionId);
693
881
  if (!activeTeam)
694
882
  return;
695
883
  const nowIso = new Date().toISOString();
@@ -725,7 +913,7 @@ async function markTeamTransportFailure(cwd, payload) {
725
913
  ],
726
914
  leader_stale: existingAttention?.leader_stale ?? false,
727
915
  leader_session_active: existingAttention?.leader_session_active ?? true,
728
- leader_session_id: existingAttention?.leader_session_id ?? (sessionId || null),
916
+ leader_session_id: existingAttention?.leader_session_id ?? (canonicalSessionId || null),
729
917
  leader_session_stopped_at: existingAttention?.leader_session_stopped_at ?? null,
730
918
  unread_leader_message_count: existingAttention?.unread_leader_message_count ?? 0,
731
919
  work_remaining: existingAttention?.work_remaining ?? true,
@@ -744,7 +932,7 @@ async function markTeamTransportFailure(cwd, payload) {
744
932
  current_phase: "failed",
745
933
  error: "mcp_transport_dead",
746
934
  last_turn_at: nowIso,
747
- }, cwd);
935
+ }, cwd, canonicalSessionId || undefined);
748
936
  }
749
937
  catch {
750
938
  // Canonical team state already carries the preserved failure for coarse-state-missing sessions.
@@ -755,29 +943,33 @@ async function buildStopHookOutput(payload, cwd, stateDir) {
755
943
  return null;
756
944
  }
757
945
  const sessionId = readPayloadSessionId(payload);
946
+ const canonicalSessionId = await resolveInternalSessionIdForPayload(cwd, sessionId);
758
947
  const threadId = readPayloadThreadId(payload);
759
- const ralphState = await readActiveRalphState(stateDir);
948
+ const ralphState = await readActiveRalphState(stateDir, canonicalSessionId);
760
949
  const stopHookActive = payload.stop_hook_active === true || payload.stopHookActive === true;
761
950
  if (!ralphState) {
762
951
  const teamWorkerOutput = await buildTeamWorkerStopOutput(cwd);
763
952
  if (!stopHookActive && hasTeamWorkerContext())
764
953
  return teamWorkerOutput;
765
- const autopilotOutput = await buildModeBasedStopOutput("autopilot", cwd);
954
+ const autopilotOutput = await buildModeBasedStopOutput("autopilot", cwd, canonicalSessionId);
766
955
  if (!stopHookActive && autopilotOutput)
767
956
  return autopilotOutput;
768
- const ultraworkOutput = await buildModeBasedStopOutput("ultrawork", cwd);
957
+ const ultraworkOutput = await buildModeBasedStopOutput("ultrawork", cwd, canonicalSessionId);
769
958
  if (!stopHookActive && ultraworkOutput)
770
959
  return ultraworkOutput;
771
- const ultraqaOutput = await buildModeBasedStopOutput("ultraqa", cwd);
960
+ const ultraqaOutput = await buildModeBasedStopOutput("ultraqa", cwd, canonicalSessionId);
772
961
  if (!stopHookActive && ultraqaOutput)
773
962
  return ultraqaOutput;
774
- const teamOutput = await buildTeamStopOutput(cwd);
963
+ const releaseReadinessFinalizeResult = await maybeBuildReleaseReadinessFinalizeStopOutput(payload, cwd, stateDir, canonicalSessionId);
964
+ if (releaseReadinessFinalizeResult.matched)
965
+ return releaseReadinessFinalizeResult.output;
966
+ const teamOutput = await buildTeamStopOutput(cwd, canonicalSessionId);
775
967
  if (teamOutput) {
776
968
  const teamSignature = buildRepeatableStopSignature(payload, "team-stop", safeString(teamOutput.stopReason));
777
969
  return await maybeReturnRepeatableStopOutput(payload, stateDir, teamSignature, teamOutput);
778
970
  }
779
- if (sessionId) {
780
- const canonicalTeam = await findCanonicalActiveTeamForSession(cwd, sessionId);
971
+ if (canonicalSessionId) {
972
+ const canonicalTeam = await findCanonicalActiveTeamForSession(cwd, canonicalSessionId);
781
973
  if (canonicalTeam) {
782
974
  const canonicalTeamOutput = buildTeamStopOutputForPhase(canonicalTeam.teamName, canonicalTeam.phase);
783
975
  const canonicalTeamSignature = buildRepeatableStopSignature(payload, "team-stop", `${canonicalTeam.teamName}|${canonicalTeam.phase}`);
@@ -785,19 +977,19 @@ async function buildStopHookOutput(payload, cwd, stateDir) {
785
977
  if (repeatedCanonicalTeamOutput)
786
978
  return repeatedCanonicalTeamOutput;
787
979
  }
788
- const skillOutput = await buildSkillStopOutput(cwd, sessionId, threadId);
980
+ const skillOutput = await buildSkillStopOutput(cwd, canonicalSessionId, threadId);
789
981
  if (!stopHookActive && skillOutput)
790
982
  return skillOutput;
791
983
  }
792
- const deepInterviewActive = await isDeepInterviewSuppressedForStop(cwd, sessionId, threadId);
793
984
  const lastAssistantMessage = safeString(payload.last_assistant_message ?? payload.lastAssistantMessage);
794
985
  const autoNudgeConfig = await loadAutoNudgeConfig();
795
- if (!deepInterviewActive
796
- && autoNudgeConfig.enabled
797
- && detectStallPattern(lastAssistantMessage, autoNudgeConfig.patterns)) {
986
+ const autoNudgePhase = await readStopAutoNudgePhase(cwd, canonicalSessionId, threadId);
987
+ if (autoNudgeConfig.enabled
988
+ && detectStallPattern(lastAssistantMessage, autoNudgeConfig.patterns, autoNudgePhase)) {
989
+ const effectiveResponse = resolveEffectiveAutoNudgeResponse(autoNudgeConfig.response);
798
990
  return await maybeReturnRepeatableStopOutput(payload, stateDir, buildRepeatableStopSignature(payload, "auto-nudge", lastAssistantMessage), {
799
991
  decision: "block",
800
- reason: autoNudgeConfig.response,
992
+ reason: effectiveResponse,
801
993
  stopReason: "auto_nudge",
802
994
  systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.",
803
995
  });
@@ -824,21 +1016,28 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
824
1016
  await mkdir(stateDir, { recursive: true });
825
1017
  const omxEventName = mapCodexHookEventToOmxEvent(hookEventName);
826
1018
  let skillState = null;
827
- const sessionId = safeString(payload.session_id ?? payload.sessionId).trim();
1019
+ const nativeSessionId = safeString(payload.session_id ?? payload.sessionId).trim();
828
1020
  const threadId = safeString(payload.thread_id ?? payload.threadId).trim();
829
1021
  const turnId = safeString(payload.turn_id ?? payload.turnId).trim();
830
- if (hookEventName === "SessionStart" && sessionId) {
831
- await writeSessionStart(cwd, sessionId, {
1022
+ let canonicalSessionId = safeString((await readUsableSessionState(cwd))?.session_id).trim();
1023
+ if (hookEventName === "SessionStart" && nativeSessionId) {
1024
+ const sessionState = await reconcileNativeSessionStart(cwd, nativeSessionId, {
832
1025
  pid: options.sessionOwnerPid ?? resolveSessionOwnerPid(payload),
833
1026
  });
1027
+ canonicalSessionId = safeString(sessionState.session_id).trim();
1028
+ }
1029
+ else if (!canonicalSessionId) {
1030
+ canonicalSessionId = safeString((await readUsableSessionState(cwd))?.session_id).trim();
834
1031
  }
1032
+ const eventSessionId = canonicalSessionId || nativeSessionId || undefined;
1033
+ const sessionIdForState = canonicalSessionId || nativeSessionId;
835
1034
  if (hookEventName === "UserPromptSubmit") {
836
1035
  const prompt = readPromptText(payload);
837
1036
  if (prompt) {
838
1037
  skillState = await recordSkillActivation({
839
1038
  stateDir,
840
1039
  text: prompt,
841
- sessionId,
1040
+ sessionId: sessionIdForState,
842
1041
  threadId,
843
1042
  turnId,
844
1043
  });
@@ -846,8 +1045,16 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
846
1045
  await reconcileHudForPromptSubmit(cwd).catch(() => { });
847
1046
  }
848
1047
  if (omxEventName) {
849
- const event = buildNativeHookEvent(omxEventName, buildBaseContext(cwd, payload, hookEventName), {
850
- session_id: sessionId || undefined,
1048
+ const baseContext = buildBaseContext(cwd, payload, hookEventName);
1049
+ if (nativeSessionId) {
1050
+ baseContext.native_session_id = nativeSessionId;
1051
+ baseContext.codex_session_id = nativeSessionId;
1052
+ }
1053
+ if (canonicalSessionId) {
1054
+ baseContext.omx_session_id = canonicalSessionId;
1055
+ }
1056
+ const event = buildNativeHookEvent(omxEventName, baseContext, {
1057
+ session_id: eventSessionId,
851
1058
  thread_id: threadId || undefined,
852
1059
  turn_id: turnId || undefined,
853
1060
  mode: safeString(payload.mode).trim() || undefined,
@@ -857,7 +1064,7 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
857
1064
  let outputJson = null;
858
1065
  if (hookEventName === "SessionStart" || hookEventName === "UserPromptSubmit") {
859
1066
  const additionalContext = hookEventName === "SessionStart"
860
- ? await buildSessionStartContext(cwd, sessionId)
1067
+ ? await buildSessionStartContext(cwd, canonicalSessionId || nativeSessionId)
861
1068
  : buildAdditionalContextMessage(readPromptText(payload), skillState);
862
1069
  if (additionalContext) {
863
1070
  outputJson = {
@@ -893,10 +1100,35 @@ async function readStdinJson() {
893
1100
  chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
894
1101
  }
895
1102
  const raw = Buffer.concat(chunks).toString("utf-8").trim();
896
- return raw ? safeObject(JSON.parse(raw)) : {};
1103
+ if (!raw) {
1104
+ return { payload: {}, parseError: null };
1105
+ }
1106
+ try {
1107
+ return {
1108
+ payload: safeObject(JSON.parse(raw)),
1109
+ parseError: null,
1110
+ };
1111
+ }
1112
+ catch (error) {
1113
+ return {
1114
+ payload: {},
1115
+ parseError: error instanceof Error ? error : new Error(String(error)),
1116
+ };
1117
+ }
897
1118
  }
898
1119
  export async function runCodexNativeHookCli() {
899
- const payload = await readStdinJson();
1120
+ const { payload, parseError } = await readStdinJson();
1121
+ if (parseError) {
1122
+ process.stdout.write(`${JSON.stringify({
1123
+ decision: "block",
1124
+ reason: "OMX native hook received malformed JSON input. Preserve runtime state and inspect the emitting hook payload before retrying.",
1125
+ hookSpecificOutput: {
1126
+ hookEventName: "Unknown",
1127
+ additionalContext: `stdin JSON parsing failed inside codex-native-hook: ${parseError.message}. Emit valid JSON from the native hook caller before retrying.`,
1128
+ },
1129
+ })}\n`);
1130
+ return;
1131
+ }
900
1132
  const result = await dispatchCodexNativeHook(payload);
901
1133
  if (result.outputJson) {
902
1134
  process.stdout.write(`${JSON.stringify(result.outputJson)}\n`);