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
@@ -4,11 +4,18 @@
4
4
  * automatically send a continuation prompt so the agent keeps working.
5
5
  */
6
6
 
7
- import { readFile, writeFile } from 'fs/promises';
8
- import { join } from 'path';
7
+ import { mkdir, readFile, writeFile } from 'fs/promises';
8
+ import { dirname, join } from 'path';
9
9
  import { homedir } from 'os';
10
10
  import { asNumber, safeString } from './utils.js';
11
- import { readJsonIfExists, getScopedStateDirsForCurrentSession, readdir } from './state-io.js';
11
+ import {
12
+ getScopedStateDirsForCurrentSession,
13
+ getScopedStatePath,
14
+ readJsonIfExists,
15
+ readScopedJsonIfExists,
16
+ readdir,
17
+ writeScopedJson,
18
+ } from './state-io.js';
12
19
  import { runProcess } from './process-runner.js';
13
20
  import { logTmuxHookEvent } from './log.js';
14
21
  import { evaluatePaneInjectionReadiness, mapPaneInjectionReadinessReason, sendPaneInput } from './team-tmux-guard.js';
@@ -26,6 +33,7 @@ import {
26
33
  export const SKILL_ACTIVE_STATE_FILE = 'skill-active-state.json';
27
34
  export const DEEP_INTERVIEW_BLOCKED_APPROVAL_INPUTS = ['yes', 'y', 'proceed', 'continue', 'ok', 'sure', 'go ahead', 'next i should'];
28
35
  export const DEEP_INTERVIEW_INPUT_LOCK_MESSAGE = 'Deep interview is active; auto-approval shortcuts are blocked until the interview finishes.';
36
+ export const DEFAULT_AUTO_NUDGE_RESPONSE = 'continue with the current task only if it is already authorized';
29
37
  const DEEP_INTERVIEW_ERROR_PATTERNS = [' error', ' failed', ' failure', ' exception', 'unable to continue', 'cannot continue', 'could not continue'];
30
38
  const DEEP_INTERVIEW_ABORT_PATTERNS = ['aborted', 'cancelled', 'canceled'];
31
39
  const DEEP_INTERVIEW_ABORT_INPUTS = new Set(['abort', 'cancel', 'stop']);
@@ -171,29 +179,30 @@ export function inferSkillPhaseFromText(text, currentPhase = 'planning') {
171
179
  return normalizeSkillPhase(currentPhase);
172
180
  }
173
181
 
174
- async function loadSkillActiveState(stateDir) {
175
- const raw = await readJsonIfExists(join(stateDir, SKILL_ACTIVE_STATE_FILE), null);
182
+ async function loadSkillActiveState(stateDir, sessionId) {
183
+ const raw = await readScopedJsonIfExists(stateDir, SKILL_ACTIVE_STATE_FILE, sessionId, null);
176
184
  return normalizeSkillActiveState(raw);
177
185
  }
178
186
 
179
- async function persistSkillActiveState(stateDir, state) {
180
- await writeFile(join(stateDir, SKILL_ACTIVE_STATE_FILE), JSON.stringify(state, null, 2)).catch(() => {});
187
+ async function persistSkillActiveState(stateDir, sessionId, state) {
188
+ await writeScopedJson(stateDir, SKILL_ACTIVE_STATE_FILE, sessionId, state).catch(() => {});
181
189
  }
182
190
 
183
191
 
184
- export async function isDeepInterviewStateActive(stateDir) {
185
- const modeState = await readJsonIfExists(join(stateDir, 'deep-interview-state.json'), null);
192
+ export async function isDeepInterviewStateActive(stateDir, sessionId) {
193
+ const modeState = await readScopedJsonIfExists(stateDir, 'deep-interview-state.json', sessionId, null);
186
194
  return Boolean(modeState && modeState.active === true);
187
195
  }
188
196
 
189
- export async function isDeepInterviewInputLockActive(stateDir) {
190
- const skillState = await loadSkillActiveState(stateDir);
197
+ export async function isDeepInterviewInputLockActive(stateDir, sessionId) {
198
+ const skillState = await loadSkillActiveState(stateDir, sessionId);
191
199
  return isDeepInterviewAutoApprovalLocked(skillState);
192
200
  }
193
201
 
194
202
  export async function resolveAutoNudgeSignature(stateDir, payload, lastMessage = '') {
195
203
  const normalizedMessage = normalizeAutoNudgeSignatureText(lastMessage);
196
- const hudState = await readJsonIfExists(join(stateDir, 'hud-state.json'), null);
204
+ const invocationSessionId = resolveInvocationSessionId(payload);
205
+ const hudState = await readScopedJsonIfExists(stateDir, 'hud-state.json', invocationSessionId, null);
197
206
  const hudTurnAt = safeString(hudState?.last_turn_at).trim();
198
207
  const hudTurnCount = Number.isFinite(hudState?.turn_count) ? hudState.turn_count : null;
199
208
  const hudMessage = normalizeAutoNudgeSignatureText(hudState?.last_agent_output || hudState?.last_agent_message || '');
@@ -218,70 +227,65 @@ function latestUserInputFromPayload(payload) {
218
227
  }
219
228
 
220
229
  export const DEFAULT_STALL_PATTERNS = [
221
- 'if you want',
222
- 'would you like',
223
- 'shall i',
224
- 'next i can',
225
230
  'continue with',
226
231
  'continue on',
227
- 'do you want me to',
228
- 'let me know if',
229
- 'do you want',
230
- 'want me to',
231
- 'let me know',
232
- 'just let me know',
233
- 'i can also',
234
- 'i could also',
235
232
  'pick up with',
236
- 'next step',
237
- 'next steps',
238
- 'ready to proceed',
239
- 'i\'m ready to',
240
233
  'keep going',
241
- 'should i',
242
- 'whenever you',
243
- 'say go',
244
- 'say yes',
245
- 'type continue',
246
234
  'and i\'ll continue',
247
- 'and i\'ll proceed',
248
235
  'keep driving',
249
236
  'keep pushing',
250
237
  'move forward',
251
238
  'drive forward',
252
- 'proceed from here',
253
239
  'i\'ll continue from',
254
240
  ];
255
241
 
256
242
  const SEMANTIC_STALL_PROMPT_PATTERNS = [
257
- /\bif you want\b/g,
258
- /\bwould you like\b/g,
259
- /\bshall i\b/g,
260
- /\bshould i\b/g,
261
- /\bdo you want(?: me)? to\b/g,
262
- /\bwant me to\b/g,
263
- /\blet me know(?: if)?\b/g,
264
- /\bjust let me know\b/g,
265
- /\bi can also\b/g,
266
- /\bi could also\b/g,
267
- /\bnext i can\b/g,
268
243
  /\bcontinue (?:with|on)\b/g,
269
244
  /\bpick up with\b/g,
270
- /\bnext steps?\b/g,
271
- /\bready to proceed\b/g,
272
- /\bi'?m ready to\b/g,
273
245
  /\bkeep going\b/g,
274
- /\bwhenever you\b/g,
275
- /\bsay (?:go|yes)\b/g,
276
- /\btype continue\b/g,
277
- /\band i'?ll (?:continue|proceed)\b/g,
246
+ /\band i'?ll continue\b/g,
278
247
  /\bkeep (?:driving|pushing)\b/g,
279
248
  /\bmove forward\b/g,
280
249
  /\bdrive forward\b/g,
281
- /\bproceed from here\b/g,
282
250
  /\bi'?ll continue from\b/g,
283
251
  ];
284
252
 
253
+ const PLANNING_ONLY_STALL_PATTERNS = [
254
+ 'plan',
255
+ 'planning',
256
+ 'approach',
257
+ 'proposal',
258
+ 'options',
259
+ 'review',
260
+ 'feedback',
261
+ 'spec',
262
+ 'design',
263
+ 'next step',
264
+ 'next steps',
265
+ 'ready to proceed',
266
+ ];
267
+
268
+ const PERMISSION_SEEKING_STALL_PATTERNS = [
269
+ 'if you want',
270
+ 'would you like',
271
+ 'shall i',
272
+ 'should i',
273
+ 'do you want me to',
274
+ 'do you want',
275
+ 'want me to',
276
+ 'let me know if',
277
+ 'let me know',
278
+ 'just let me know',
279
+ 'i can also',
280
+ 'i could also',
281
+ 'next i can',
282
+ 'whenever you',
283
+ 'say go',
284
+ 'say yes',
285
+ 'type continue',
286
+ 'proceed from here',
287
+ ];
288
+
285
289
  function normalizeStallDetectionText(text) {
286
290
  return stripOrchestrationIntentTags(safeString(text))
287
291
  .replace(/\r\n?/g, '\n')
@@ -315,6 +319,52 @@ export function normalizeAutoNudgeSignatureText(text) {
315
319
  return normalized;
316
320
  }
317
321
 
322
+ function normalizePatternList(patterns) {
323
+ return patterns.map((pattern) => normalizeStallDetectionText(pattern)).filter(Boolean);
324
+ }
325
+
326
+ function usesDefaultStallPatterns(patterns) {
327
+ const normalizedPatterns = normalizePatternList(patterns);
328
+ const normalizedDefaults = normalizePatternList(DEFAULT_STALL_PATTERNS);
329
+ return normalizedPatterns.length === normalizedDefaults.length
330
+ && normalizedPatterns.every((pattern, index) => pattern === normalizedDefaults[index]);
331
+ }
332
+
333
+ function matchesNormalizedPatterns(normalizedText, normalizedPatterns) {
334
+ if (!normalizedText || normalizedPatterns.length === 0) return false;
335
+ const tail = normalizedText.slice(-800);
336
+ const lines = tail.split('\n').filter((line) => line.trim());
337
+ const hotZone = lines.slice(-3).join('\n');
338
+ if (normalizedPatterns.some((pattern) => hotZone.includes(pattern))) return true;
339
+ return normalizedPatterns.some((pattern) => tail.includes(pattern));
340
+ }
341
+
342
+ function looksLikePlanningOnlyContinuation(normalizedText) {
343
+ return matchesNormalizedPatterns(normalizedText, normalizePatternList(PLANNING_ONLY_STALL_PATTERNS));
344
+ }
345
+
346
+ function looksLikePermissionSeekingContinuation(normalizedText) {
347
+ return matchesNormalizedPatterns(normalizedText, normalizePatternList(PERMISSION_SEEKING_STALL_PATTERNS));
348
+ }
349
+
350
+ /**
351
+ * Pattern to identify test-runner output lines (status symbols, PASS/FAIL prefixes).
352
+ * These lines may incidentally contain stall-pattern words inside test names and
353
+ * should be excluded before running stall detection on pane captures.
354
+ */
355
+ const CAPTURE_TEST_LINE_RE = /^\s*(?:[✓✗✕×●✔✘▶◆○]|(?:PASS|FAIL|SKIP|ERROR)\s)/u;
356
+
357
+ /**
358
+ * Strip lines that look like test-runner output so stall patterns inside test
359
+ * names (e.g. "✓ should continue with the next step") do not trigger a nudge.
360
+ */
361
+ function filterCapturedTestLines(text) {
362
+ return safeString(text)
363
+ .split('\n')
364
+ .filter((line) => !CAPTURE_TEST_LINE_RE.test(line))
365
+ .join('\n');
366
+ }
367
+
318
368
  function summarizePaneCaptureForLog(captured, maxLines = 6) {
319
369
  const lines = safeString(captured)
320
370
  .replace(/\r\n?/g, '\n')
@@ -358,6 +408,12 @@ export function normalizeAutoNudgeConfig(raw) {
358
408
  };
359
409
  }
360
410
 
411
+ export function resolveEffectiveAutoNudgeResponse(response) {
412
+ const normalized = safeString(response).trim();
413
+ if (!normalized) return DEFAULT_AUTO_NUDGE_RESPONSE;
414
+ return isBlockedAutoApprovalInput(normalized) ? DEFAULT_AUTO_NUDGE_RESPONSE : normalized;
415
+ }
416
+
361
417
  export async function loadAutoNudgeConfig() {
362
418
  const codexHomePath = process.env.CODEX_HOME || join(homedir(), '.codex');
363
419
  const configPath = join(codexHomePath, '.omx-config.json');
@@ -373,16 +429,16 @@ async function localTmuxInjectionDisabled(cwd) {
373
429
  return tmuxHookExplicitlyDisablesInjection(raw);
374
430
  }
375
431
 
376
- export function detectStallPattern(text, patterns) {
432
+ export function detectStallPattern(text, patterns, currentPhase = '') {
377
433
  if (!text || typeof text !== 'string') return false;
378
434
  const normalized = normalizeStallDetectionText(text);
379
435
  if (!normalized) return false;
380
- const tail = normalized.slice(-800);
381
- const normalizedPatterns = patterns.map((pattern) => normalizeStallDetectionText(pattern)).filter(Boolean);
382
- const lines = tail.split('\n').filter((line) => line.trim());
383
- const hotZone = lines.slice(-3).join('\n');
384
- if (normalizedPatterns.some((pattern) => hotZone.includes(pattern))) return true;
385
- return normalizedPatterns.some((pattern) => tail.includes(pattern));
436
+ const normalizedPatterns = normalizePatternList(patterns);
437
+ if (!matchesNormalizedPatterns(normalized, normalizedPatterns)) return false;
438
+ if (!usesDefaultStallPatterns(patterns)) return true;
439
+ if (looksLikePermissionSeekingContinuation(normalized)) return false;
440
+ if (safeString(currentPhase).trim().toLowerCase() === 'planning') return false;
441
+ return !looksLikePlanningOnlyContinuation(normalized);
386
442
  }
387
443
 
388
444
  export async function capturePane(paneId, lines = 10) {
@@ -426,6 +482,7 @@ export async function resolveNudgePaneTarget(stateDir: any, cwd = '', payload: a
426
482
 
427
483
  export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
428
484
  const config = await loadAutoNudgeConfig();
485
+ const effectiveResponse = resolveEffectiveAutoNudgeResponse(config.response);
429
486
  if (!config.enabled) return;
430
487
  if (await localTmuxInjectionDisabled(cwd)) {
431
488
  await logTmuxHookEvent(logsDir, {
@@ -450,7 +507,8 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
450
507
 
451
508
  const lastMessage = safeString(payload['last-assistant-message'] || payload.last_assistant_message || '');
452
509
  const latestUserInput = latestUserInputFromPayload(payload);
453
- let skillState = await loadSkillActiveState(stateDir);
510
+ const invocationSessionId = resolveInvocationSessionId(payload);
511
+ let skillState = await loadSkillActiveState(stateDir, invocationSessionId);
454
512
  let releaseReason = null;
455
513
 
456
514
  try {
@@ -460,23 +518,23 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
460
518
  skillState.active = inferredPhase !== 'completing';
461
519
  skillState.updated_at = new Date().toISOString();
462
520
  releaseReason = inferDeepInterviewReleaseReason({ skillState, latestUserInput, lastMessage });
463
- await persistSkillActiveState(stateDir, skillState);
521
+ await persistSkillActiveState(stateDir, invocationSessionId, skillState);
464
522
  }
465
523
 
466
- const nudgeStatePath = join(stateDir, 'auto-nudge-state.json');
467
- let nudgeState = await readJsonIfExists(nudgeStatePath, null);
524
+ const nudgeStatePath = await getScopedStatePath(stateDir, 'auto-nudge-state.json', invocationSessionId);
525
+ let nudgeState = await readScopedJsonIfExists(stateDir, 'auto-nudge-state.json', invocationSessionId, null);
468
526
  if (!nudgeState || typeof nudgeState !== 'object') {
469
527
  nudgeState = { nudgeCount: 0, lastNudgeAt: '', lastSignature: '', lastSemanticSignature: '' };
470
528
  }
471
529
  const paneId = await resolveNudgePaneTarget(stateDir, cwd, payload);
472
530
 
473
- let detected = detectStallPattern(lastMessage, config.patterns);
531
+ let detected = detectStallPattern(lastMessage, config.patterns, skillState?.phase);
474
532
  let source = 'payload';
475
533
  let captured = '';
476
534
 
477
535
  if (!detected && paneId) {
478
536
  captured = await capturePane(paneId);
479
- detected = detectStallPattern(captured, config.patterns);
537
+ detected = detectStallPattern(filterCapturedTestLines(captured), config.patterns, skillState?.phase);
480
538
  source = 'capture-pane';
481
539
  }
482
540
 
@@ -523,6 +581,7 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
523
581
  if (!isFallbackWatcherSource && config.stallMs > 0) {
524
582
  nudgeState.pendingSignature = signature;
525
583
  nudgeState.pendingSince = new Date().toISOString();
584
+ await mkdir(dirname(nudgeStatePath), { recursive: true }).catch(() => {});
526
585
  await writeFile(nudgeStatePath, JSON.stringify(nudgeState, null, 2)).catch(() => {});
527
586
  await logTmuxHookEvent(logsDir, {
528
587
  timestamp: new Date().toISOString(),
@@ -556,10 +615,10 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
556
615
  timestamp: new Date().toISOString(),
557
616
  type: 'auto_nudge_blocked',
558
617
  pane_id: paneId,
559
- response: config.response,
618
+ response: effectiveResponse,
560
619
  source,
561
620
  blocked_by: 'deep-interview-lock',
562
- block_kind: isBlockedAutoApprovalInput(config.response, skillState.input_lock?.blocked_inputs)
621
+ block_kind: isBlockedAutoApprovalInput(effectiveResponse, skillState.input_lock?.blocked_inputs)
563
622
  ? 'blocked-auto-approval'
564
623
  : 'input-lock-active',
565
624
  message: blockedMessage,
@@ -576,7 +635,7 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
576
635
  try {
577
636
  const sendResult = await sendPaneInput({
578
637
  paneTarget: paneId,
579
- prompt: `${config.response} ${DEFAULT_MARKER}`,
638
+ prompt: `${effectiveResponse} ${DEFAULT_MARKER}`,
580
639
  submitKeyPresses: 2,
581
640
  submitDelayMs: 100,
582
641
  });
@@ -590,20 +649,14 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
590
649
  nudgeState.lastSemanticSignature = semanticSignature;
591
650
  nudgeState.pendingSignature = '';
592
651
  nudgeState.pendingSince = '';
652
+ await mkdir(dirname(nudgeStatePath), { recursive: true }).catch(() => {});
593
653
  await writeFile(nudgeStatePath, JSON.stringify(nudgeState, null, 2)).catch(() => {});
594
654
 
595
- if (skillState && skillState.phase === 'planning') {
596
- skillState.phase = 'executing';
597
- skillState.active = true;
598
- skillState.updated_at = nowIso;
599
- await persistSkillActiveState(stateDir, skillState);
600
- }
601
-
602
655
  await logTmuxHookEvent(logsDir, {
603
656
  timestamp: nowIso,
604
657
  type: 'auto_nudge',
605
658
  pane_id: paneId,
606
- response: config.response,
659
+ response: effectiveResponse,
607
660
  source,
608
661
  nudge_count: nudgeState.nudgeCount,
609
662
  });
@@ -618,7 +671,7 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
618
671
  } finally {
619
672
  if (releaseReason && skillState && isDeepInterviewAutoApprovalLocked(skillState)) {
620
673
  releaseDeepInterviewInputLock(skillState, releaseReason, new Date().toISOString());
621
- await persistSkillActiveState(stateDir, skillState).catch(() => {});
674
+ await persistSkillActiveState(stateDir, invocationSessionId, skillState).catch(() => {});
622
675
  }
623
676
  }
624
677
  }
@@ -1,9 +1,10 @@
1
1
  import { execFileSync } from 'child_process';
2
2
  import { readFileSync } from 'fs';
3
- import { basename, dirname, resolve as resolvePath } from 'path';
3
+ import { basename, dirname } from 'path';
4
4
  import { readSessionState, isSessionStale } from '../../hooks/session.js';
5
5
  import { runProcess } from './process-runner.js';
6
6
  import { safeString } from './utils.js';
7
+ import { sameFilePath } from '../../utils/paths.js';
7
8
 
8
9
  function sanitizeTmuxToken(value: string): string {
9
10
  const cleaned = safeString(value)
@@ -56,6 +57,10 @@ export function resolveInvocationSessionId(payload: any): string {
56
57
  ).trim();
57
58
  }
58
59
 
60
+ function readNativeSessionId(sessionState: { native_session_id?: unknown; codex_session_id?: unknown }): string {
61
+ return safeString(sessionState.native_session_id || sessionState.codex_session_id || '').trim();
62
+ }
63
+
59
64
  function readCurrentTmuxSessionName(): string {
60
65
  if (!process.env.TMUX) return '';
61
66
  try {
@@ -135,23 +140,32 @@ export async function resolveManagedSessionContext(cwd: string, payload: any, {
135
140
  if (!sessionState) {
136
141
  return { managed: false, reason: 'missing_session_state', invocationSessionId, sessionState: null, expectedTmuxSessionName: '', currentTmuxSessionName: '' };
137
142
  }
138
- if (resolvePath(safeString(sessionState.cwd || cwd)) !== resolvePath(cwd)) {
143
+ if (!sameFilePath(safeString(sessionState.cwd || cwd), cwd)) {
139
144
  return { managed: false, reason: 'cwd_mismatch', invocationSessionId, sessionState, expectedTmuxSessionName: '', currentTmuxSessionName: '' };
140
145
  }
141
- if (safeString(sessionState.session_id).trim() !== invocationSessionId) {
146
+ const canonicalSessionId = safeString(sessionState.session_id).trim();
147
+ const nativeSessionId = readNativeSessionId(sessionState);
148
+ const allowedInvocationIds = new Set([canonicalSessionId, nativeSessionId].filter(Boolean));
149
+ if (!allowedInvocationIds.has(invocationSessionId)) {
142
150
  return { managed: false, reason: 'session_id_mismatch', invocationSessionId, sessionState, expectedTmuxSessionName: '', currentTmuxSessionName: '' };
143
151
  }
144
152
  if (isSessionStale(sessionState)) {
145
153
  return { managed: false, reason: 'stale_session', invocationSessionId, sessionState, expectedTmuxSessionName: '', currentTmuxSessionName: '' };
146
154
  }
147
155
 
148
- const expectedTmuxSessionName = buildExpectedManagedTmuxSessionName(cwd, invocationSessionId);
156
+ const authoritativeSessionCwd = safeString(sessionState.cwd || cwd).trim() || cwd;
157
+ const expectedTmuxSessionName = buildExpectedManagedTmuxSessionName(
158
+ authoritativeSessionCwd,
159
+ canonicalSessionId || invocationSessionId,
160
+ );
149
161
  const currentTmuxSessionName = readCurrentTmuxSessionName();
150
162
  if (currentTmuxSessionName && currentTmuxSessionName === expectedTmuxSessionName) {
151
163
  return {
152
164
  managed: true,
153
165
  reason: 'tmux_session_match',
154
166
  invocationSessionId,
167
+ canonicalSessionId,
168
+ nativeSessionId,
155
169
  sessionState,
156
170
  expectedTmuxSessionName,
157
171
  currentTmuxSessionName,
@@ -163,6 +177,8 @@ export async function resolveManagedSessionContext(cwd: string, payload: any, {
163
177
  managed: true,
164
178
  reason: currentTmuxSessionName ? 'pid_ancestry_match_tmux_mismatch' : 'pid_ancestry_match',
165
179
  invocationSessionId,
180
+ canonicalSessionId,
181
+ nativeSessionId,
166
182
  sessionState,
167
183
  expectedTmuxSessionName,
168
184
  currentTmuxSessionName: '',
@@ -173,6 +189,8 @@ export async function resolveManagedSessionContext(cwd: string, payload: any, {
173
189
  managed: false,
174
190
  reason: currentTmuxSessionName ? 'tmux_session_mismatch' : 'pid_ancestry_mismatch',
175
191
  invocationSessionId,
192
+ canonicalSessionId,
193
+ nativeSessionId,
176
194
  sessionState,
177
195
  expectedTmuxSessionName,
178
196
  currentTmuxSessionName,
@@ -1,6 +1,7 @@
1
1
  import { execFileSync } from 'child_process';
2
2
  import { basename, dirname } from 'path';
3
3
  import { safeString } from './utils.js';
4
+ import { upsertCurrentTaskBaseline } from '../../team/current-task-baseline.js';
4
5
 
5
6
  const TEST_SEGMENT_PATTERNS = [
6
7
  /^npm\s+(?:run\s+)?test\b/i,
@@ -216,6 +217,26 @@ export function buildOperationalContext({
216
217
  };
217
218
  const resolvedSessionName = resolveOperationalSessionName(cwd, sessionId, sessionName);
218
219
 
220
+ if (repoMeta.repo_path && repoMeta.branch) {
221
+ try {
222
+ const lifecycleStatus = normalizedEvent === 'pr-merged'
223
+ ? 'merged'
224
+ : normalizedEvent === 'pr-closed'
225
+ ? 'closed'
226
+ : undefined;
227
+ upsertCurrentTaskBaseline(repoMeta.repo_path, {
228
+ branch_name: repoMeta.branch,
229
+ worktree_path: repoMeta.worktree_path,
230
+ issue_number: detectedIssue,
231
+ pr_number: detectedPrInfo.pr_number,
232
+ pr_url: detectedPrInfo.pr_url,
233
+ ...(lifecycleStatus ? { status: lifecycleStatus } : {}),
234
+ });
235
+ } catch {
236
+ // best effort only; operational context building must stay non-fatal
237
+ }
238
+ }
239
+
219
240
  return {
220
241
  normalized_event: normalizedEvent,
221
242
  ...(resolvedSessionName ? { session_name: resolvedSessionName } : {}),
@@ -1,7 +1,8 @@
1
1
  import { existsSync } from 'fs';
2
2
  import { mkdir, readFile, readdir, rename, rm, stat, writeFile } from 'fs/promises';
3
- import { dirname, join } from 'path';
3
+ import { dirname, join, resolve } from 'path';
4
4
  import { captureTmuxPaneFromEnv } from '../../state/mode-state-context.js';
5
+ import { readUsableSessionState } from '../../hooks/session.js';
5
6
  import { resolveCodexPane } from '../tmux-hook-engine.js';
6
7
  import { safeString } from './utils.js';
7
8
 
@@ -128,7 +129,7 @@ function isActiveRalphCandidate(state: Record<string, unknown> | null): state is
128
129
  }
129
130
 
130
131
  async function readCurrentOmxSessionId(stateDir: string): Promise<string> {
131
- const session = await readJson(join(stateDir, 'session.json'));
132
+ const session = await readUsableSessionState(resolve(stateDir, '..', '..'));
132
133
  const sessionId = safeString(session?.session_id).trim();
133
134
  return SESSION_ID_PATTERN.test(sessionId) ? sessionId : '';
134
135
  }
@@ -2,8 +2,10 @@
2
2
  * State file I/O helpers for notify-hook modules.
3
3
  */
4
4
 
5
- import { readFile, readdir } from 'fs/promises';
6
- import { join } from 'path';
5
+ import { mkdir, readFile, readdir, writeFile } from 'fs/promises';
6
+ import { dirname, join, resolve } from 'path';
7
+ import { existsSync } from 'fs';
8
+ import { readUsableSessionState } from '../../hooks/session.js';
7
9
  import { asNumber, safeString } from './utils.js';
8
10
 
9
11
  const SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
@@ -16,18 +18,93 @@ export function readJsonIfExists(path: string, fallback: any): Promise<any> {
16
18
  .catch(() => fallback);
17
19
  }
18
20
 
19
- export async function getScopedStateDirsForCurrentSession(baseStateDir: string): Promise<string[]> {
20
- const sessionPath = join(baseStateDir, 'session.json');
21
- try {
22
- const session = JSON.parse(await readFile(sessionPath, 'utf-8'));
23
- const sessionId = safeString(session && session.session_id ? session.session_id : '');
24
- if (SESSION_ID_PATTERN.test(sessionId)) {
25
- return [join(baseStateDir, 'sessions', sessionId)];
21
+ function isSafeStateFileName(fileName: string): boolean {
22
+ return fileName.length > 0
23
+ && !fileName.includes('..')
24
+ && !fileName.includes('/')
25
+ && !fileName.includes('\\');
26
+ }
27
+
28
+ export async function readCurrentSessionId(baseStateDir: string): Promise<string | undefined> {
29
+ const cwd = resolve(baseStateDir, '..', '..');
30
+ const session = await readUsableSessionState(cwd);
31
+ const sessionId = safeString(session?.session_id);
32
+ return SESSION_ID_PATTERN.test(sessionId) ? sessionId : undefined;
33
+ }
34
+
35
+ export async function resolveScopedStateDir(
36
+ baseStateDir: string,
37
+ explicitSessionId?: string,
38
+ ): Promise<string> {
39
+ const currentSessionId = await readCurrentSessionId(baseStateDir);
40
+ if (currentSessionId) {
41
+ return join(baseStateDir, 'sessions', currentSessionId);
42
+ }
43
+
44
+ const normalizedExplicit = safeString(explicitSessionId).trim();
45
+ if (SESSION_ID_PATTERN.test(normalizedExplicit)) {
46
+ const explicitDir = join(baseStateDir, 'sessions', normalizedExplicit);
47
+ if (existsSync(explicitDir)) {
48
+ return explicitDir;
26
49
  }
27
- } catch {
28
- // No session file or malformed - fall back to global only
29
50
  }
30
- return [baseStateDir];
51
+ return baseStateDir;
52
+ }
53
+
54
+ export async function getScopedStateDirsForCurrentSession(
55
+ baseStateDir: string,
56
+ explicitSessionId?: string,
57
+ options: { includeRootFallback?: boolean } = {},
58
+ ): Promise<string[]> {
59
+ const scopedDir = await resolveScopedStateDir(baseStateDir, explicitSessionId);
60
+ if (scopedDir === baseStateDir || options.includeRootFallback !== true) {
61
+ return [scopedDir];
62
+ }
63
+ return [scopedDir, baseStateDir];
64
+ }
65
+
66
+ export async function getScopedStatePath(
67
+ baseStateDir: string,
68
+ fileName: string,
69
+ explicitSessionId?: string,
70
+ ): Promise<string> {
71
+ if (!isSafeStateFileName(fileName)) {
72
+ throw new Error(`unsafe state file name: ${fileName}`);
73
+ }
74
+ return join(await resolveScopedStateDir(baseStateDir, explicitSessionId), fileName);
75
+ }
76
+
77
+ export async function readScopedJsonIfExists(
78
+ baseStateDir: string,
79
+ fileName: string,
80
+ explicitSessionId: string | undefined,
81
+ fallback: any,
82
+ options: { includeRootFallback?: boolean } = {},
83
+ ): Promise<any> {
84
+ if (!isSafeStateFileName(fileName)) {
85
+ throw new Error(`unsafe state file name: ${fileName}`);
86
+ }
87
+ const candidateDirs = await getScopedStateDirsForCurrentSession(
88
+ baseStateDir,
89
+ explicitSessionId,
90
+ options,
91
+ );
92
+ for (const dir of candidateDirs) {
93
+ const value = await readJsonIfExists(join(dir, fileName), fallback);
94
+ if (value !== fallback) return value;
95
+ }
96
+ return fallback;
97
+ }
98
+
99
+ export async function writeScopedJson(
100
+ baseStateDir: string,
101
+ fileName: string,
102
+ explicitSessionId: string | undefined,
103
+ value: unknown,
104
+ ): Promise<void> {
105
+ const targetPath = await getScopedStatePath(baseStateDir, fileName, explicitSessionId);
106
+ await mkdir(dirname(targetPath), { recursive: true });
107
+ await writeFile(targetPath, JSON.stringify(value, null, 2));
31
108
  }
32
109
 
33
110
  export function normalizeTmuxState(raw: any): any {