oh-my-codex 0.11.11 → 0.11.13

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 (342) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/README.de.md +12 -6
  4. package/README.el.md +223 -0
  5. package/README.es.md +12 -6
  6. package/README.fr.md +11 -5
  7. package/README.it.md +12 -6
  8. package/README.ja.md +12 -6
  9. package/README.ko.md +12 -6
  10. package/README.md +56 -28
  11. package/README.pl.md +216 -0
  12. package/README.pt.md +12 -6
  13. package/README.ru.md +12 -6
  14. package/README.tr.md +12 -6
  15. package/README.vi.md +148 -183
  16. package/README.zh-TW.md +14 -17
  17. package/README.zh.md +12 -6
  18. package/crates/omx-runtime-core/src/engine.rs +122 -4
  19. package/crates/omx-runtime-core/src/lib.rs +17 -0
  20. package/dist/autoresearch/contracts.d.ts.map +1 -1
  21. package/dist/autoresearch/contracts.js +1 -0
  22. package/dist/autoresearch/contracts.js.map +1 -1
  23. package/dist/autoresearch/runtime.d.ts.map +1 -1
  24. package/dist/autoresearch/runtime.js +7 -1
  25. package/dist/autoresearch/runtime.js.map +1 -1
  26. package/dist/cli/__tests__/agents.test.js +24 -1
  27. package/dist/cli/__tests__/agents.test.js.map +1 -1
  28. package/dist/cli/__tests__/autoresearch.test.js +11 -0
  29. package/dist/cli/__tests__/autoresearch.test.js.map +1 -1
  30. package/dist/cli/__tests__/cleanup.test.js +117 -4
  31. package/dist/cli/__tests__/cleanup.test.js.map +1 -1
  32. package/dist/cli/__tests__/doctor-warning-copy.test.js +33 -3
  33. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  34. package/dist/cli/__tests__/error-handling-warnings.test.js +13 -0
  35. package/dist/cli/__tests__/error-handling-warnings.test.js.map +1 -1
  36. package/dist/cli/__tests__/exec.test.js +6 -0
  37. package/dist/cli/__tests__/exec.test.js.map +1 -1
  38. package/dist/cli/__tests__/index.test.js +101 -1
  39. package/dist/cli/__tests__/index.test.js.map +1 -1
  40. package/dist/cli/__tests__/launch-fallback.test.js +3 -0
  41. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  42. package/dist/cli/__tests__/package-bin-contract.test.js +10 -0
  43. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  44. package/dist/cli/__tests__/packaged-script-resolution.test.js +4 -3
  45. package/dist/cli/__tests__/packaged-script-resolution.test.js.map +1 -1
  46. package/dist/cli/__tests__/resume.test.js +6 -0
  47. package/dist/cli/__tests__/resume.test.js.map +1 -1
  48. package/dist/cli/__tests__/setup-refresh.test.js +29 -12
  49. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  50. package/dist/cli/__tests__/setup-scope.test.js +1 -1
  51. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  52. package/dist/cli/__tests__/star-prompt.test.js +16 -0
  53. package/dist/cli/__tests__/star-prompt.test.js.map +1 -1
  54. package/dist/cli/__tests__/uninstall.test.js +112 -1
  55. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  56. package/dist/cli/__tests__/windows-popup-loop-contract.test.d.ts +2 -0
  57. package/dist/cli/__tests__/windows-popup-loop-contract.test.d.ts.map +1 -0
  58. package/dist/cli/__tests__/windows-popup-loop-contract.test.js +30 -0
  59. package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -0
  60. package/dist/cli/agents.d.ts.map +1 -1
  61. package/dist/cli/agents.js +9 -3
  62. package/dist/cli/agents.js.map +1 -1
  63. package/dist/cli/autoresearch-guided.d.ts.map +1 -1
  64. package/dist/cli/autoresearch-guided.js +9 -3
  65. package/dist/cli/autoresearch-guided.js.map +1 -1
  66. package/dist/cli/autoresearch.d.ts.map +1 -1
  67. package/dist/cli/autoresearch.js +8 -2
  68. package/dist/cli/autoresearch.js.map +1 -1
  69. package/dist/cli/cleanup.d.ts +2 -0
  70. package/dist/cli/cleanup.d.ts.map +1 -1
  71. package/dist/cli/cleanup.js +27 -1
  72. package/dist/cli/cleanup.js.map +1 -1
  73. package/dist/cli/doctor.js +7 -0
  74. package/dist/cli/doctor.js.map +1 -1
  75. package/dist/cli/index.d.ts +9 -1
  76. package/dist/cli/index.d.ts.map +1 -1
  77. package/dist/cli/index.js +171 -55
  78. package/dist/cli/index.js.map +1 -1
  79. package/dist/cli/setup.d.ts.map +1 -1
  80. package/dist/cli/setup.js +18 -15
  81. package/dist/cli/setup.js.map +1 -1
  82. package/dist/cli/star-prompt.d.ts.map +1 -1
  83. package/dist/cli/star-prompt.js +2 -0
  84. package/dist/cli/star-prompt.js.map +1 -1
  85. package/dist/cli/team.d.ts.map +1 -1
  86. package/dist/cli/team.js +5 -1
  87. package/dist/cli/team.js.map +1 -1
  88. package/dist/cli/tmux-hook.d.ts.map +1 -1
  89. package/dist/cli/tmux-hook.js +4 -1
  90. package/dist/cli/tmux-hook.js.map +1 -1
  91. package/dist/cli/uninstall.d.ts.map +1 -1
  92. package/dist/cli/uninstall.js +26 -0
  93. package/dist/cli/uninstall.js.map +1 -1
  94. package/dist/cli/update.d.ts.map +1 -1
  95. package/dist/cli/update.js +1 -0
  96. package/dist/cli/update.js.map +1 -1
  97. package/dist/compat/__tests__/rust-runtime-compat.test.js +84 -1
  98. package/dist/compat/__tests__/rust-runtime-compat.test.js.map +1 -1
  99. package/dist/config/__tests__/generator-idempotent.test.js +4 -4
  100. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  101. package/dist/config/__tests__/mcp-registry.test.js +13 -16
  102. package/dist/config/__tests__/mcp-registry.test.js.map +1 -1
  103. package/dist/config/mcp-registry.d.ts +1 -0
  104. package/dist/config/mcp-registry.d.ts.map +1 -1
  105. package/dist/config/mcp-registry.js +4 -4
  106. package/dist/config/mcp-registry.js.map +1 -1
  107. package/dist/config/models.d.ts +1 -0
  108. package/dist/config/models.d.ts.map +1 -1
  109. package/dist/config/models.js +39 -1
  110. package/dist/config/models.js.map +1 -1
  111. package/dist/hooks/__tests__/keyword-detector.test.js +12 -1
  112. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  113. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +554 -18
  114. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  115. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +347 -16
  116. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  117. package/dist/hooks/__tests__/notify-hook-modules.test.js +5 -0
  118. package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
  119. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.d.ts +2 -0
  120. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.d.ts.map +1 -0
  121. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +597 -0
  122. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -0
  123. package/dist/hooks/__tests__/notify-hook-regression-205.test.js +19 -1
  124. package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
  125. package/dist/hooks/__tests__/notify-hook-session-scope.test.js +73 -53
  126. package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
  127. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +193 -2
  128. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  129. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +183 -0
  130. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  131. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +255 -97
  132. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  133. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +0 -0
  134. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
  135. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +46 -0
  136. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  137. package/dist/hooks/__tests__/prompt-team-routing.test.js +34 -0
  138. package/dist/hooks/__tests__/prompt-team-routing.test.js.map +1 -1
  139. package/dist/hooks/__tests__/tmux-hook-engine.test.js +32 -1
  140. package/dist/hooks/__tests__/tmux-hook-engine.test.js.map +1 -1
  141. package/dist/hooks/code-simplifier/index.d.ts.map +1 -1
  142. package/dist/hooks/code-simplifier/index.js +1 -0
  143. package/dist/hooks/code-simplifier/index.js.map +1 -1
  144. package/dist/hooks/codebase-map.d.ts.map +1 -1
  145. package/dist/hooks/codebase-map.js +1 -0
  146. package/dist/hooks/codebase-map.js.map +1 -1
  147. package/dist/hooks/extensibility/sdk/tmux.d.ts.map +1 -1
  148. package/dist/hooks/extensibility/sdk/tmux.js +3 -1
  149. package/dist/hooks/extensibility/sdk/tmux.js.map +1 -1
  150. package/dist/hooks/keyword-detector.d.ts +1 -0
  151. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  152. package/dist/hooks/keyword-detector.js +48 -0
  153. package/dist/hooks/keyword-detector.js.map +1 -1
  154. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  155. package/dist/hooks/prompt-guidance-contract.js +6 -0
  156. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  157. package/dist/hooks/session.d.ts.map +1 -1
  158. package/dist/hooks/session.js +1 -0
  159. package/dist/hooks/session.js.map +1 -1
  160. package/dist/hud/__tests__/state.test.js +70 -1
  161. package/dist/hud/__tests__/state.test.js.map +1 -1
  162. package/dist/hud/authority.d.ts.map +1 -1
  163. package/dist/hud/authority.js +1 -0
  164. package/dist/hud/authority.js.map +1 -1
  165. package/dist/hud/state.d.ts.map +1 -1
  166. package/dist/hud/state.js +52 -0
  167. package/dist/hud/state.js.map +1 -1
  168. package/dist/mcp/state-server.d.ts.map +1 -1
  169. package/dist/mcp/state-server.js +5 -0
  170. package/dist/mcp/state-server.js.map +1 -1
  171. package/dist/modes/__tests__/base-session-scope.test.js +46 -0
  172. package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
  173. package/dist/modes/base.d.ts.map +1 -1
  174. package/dist/modes/base.js +4 -0
  175. package/dist/modes/base.js.map +1 -1
  176. package/dist/notifications/__tests__/custom-alias-enablement.test.d.ts +2 -0
  177. package/dist/notifications/__tests__/custom-alias-enablement.test.d.ts.map +1 -0
  178. package/dist/notifications/__tests__/custom-alias-enablement.test.js +84 -0
  179. package/dist/notifications/__tests__/custom-alias-enablement.test.js.map +1 -0
  180. package/dist/notifications/__tests__/idle-cooldown.test.js +55 -0
  181. package/dist/notifications/__tests__/idle-cooldown.test.js.map +1 -1
  182. package/dist/notifications/idle-cooldown.d.ts +8 -6
  183. package/dist/notifications/idle-cooldown.d.ts.map +1 -1
  184. package/dist/notifications/idle-cooldown.js +53 -22
  185. package/dist/notifications/idle-cooldown.js.map +1 -1
  186. package/dist/notifications/notifier.js +1 -1
  187. package/dist/notifications/notifier.js.map +1 -1
  188. package/dist/notifications/reply-listener.d.ts.map +1 -1
  189. package/dist/notifications/reply-listener.js +1 -0
  190. package/dist/notifications/reply-listener.js.map +1 -1
  191. package/dist/notifications/tmux.d.ts.map +1 -1
  192. package/dist/notifications/tmux.js +4 -0
  193. package/dist/notifications/tmux.js.map +1 -1
  194. package/dist/openclaw/config.js +2 -2
  195. package/dist/openclaw/config.js.map +1 -1
  196. package/dist/runtime/bridge.d.ts +2 -0
  197. package/dist/runtime/bridge.d.ts.map +1 -1
  198. package/dist/runtime/bridge.js +8 -0
  199. package/dist/runtime/bridge.js.map +1 -1
  200. package/dist/scripts/notify-fallback-watcher.js +103 -53
  201. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  202. package/dist/scripts/notify-hook/auto-nudge.d.ts +2 -1
  203. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  204. package/dist/scripts/notify-hook/auto-nudge.js +90 -104
  205. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  206. package/dist/scripts/notify-hook/managed-tmux.d.ts +19 -0
  207. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -0
  208. package/dist/scripts/notify-hook/managed-tmux.js +320 -0
  209. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -0
  210. package/dist/scripts/notify-hook/operational-events.d.ts.map +1 -1
  211. package/dist/scripts/notify-hook/operational-events.js +2 -0
  212. package/dist/scripts/notify-hook/operational-events.js.map +1 -1
  213. package/dist/scripts/notify-hook/ralph-session-resume.d.ts +22 -0
  214. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -0
  215. package/dist/scripts/notify-hook/ralph-session-resume.js +277 -0
  216. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -0
  217. package/dist/scripts/notify-hook/state-io.d.ts +1 -1
  218. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  219. package/dist/scripts/notify-hook/state-io.js +2 -10
  220. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  221. package/dist/scripts/notify-hook/team-dispatch.d.ts +1 -1
  222. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  223. package/dist/scripts/notify-hook/team-dispatch.js +123 -72
  224. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  225. package/dist/scripts/notify-hook/team-leader-nudge.d.ts +2 -1
  226. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  227. package/dist/scripts/notify-hook/team-leader-nudge.js +13 -5
  228. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  229. package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
  230. package/dist/scripts/notify-hook/team-tmux-guard.js +1 -19
  231. package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
  232. package/dist/scripts/notify-hook/team-worker.js +4 -4
  233. package/dist/scripts/notify-hook/team-worker.js.map +1 -1
  234. package/dist/scripts/notify-hook/tmux-injection.d.ts +1 -1
  235. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  236. package/dist/scripts/notify-hook/tmux-injection.js +102 -35
  237. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  238. package/dist/scripts/notify-hook.js +144 -20
  239. package/dist/scripts/notify-hook.js.map +1 -1
  240. package/dist/scripts/run-provider-advisor.js +2 -0
  241. package/dist/scripts/run-provider-advisor.js.map +1 -1
  242. package/dist/scripts/run-test-files.d.ts +2 -0
  243. package/dist/scripts/run-test-files.d.ts.map +1 -0
  244. package/dist/scripts/run-test-files.js +41 -0
  245. package/dist/scripts/run-test-files.js.map +1 -0
  246. package/dist/scripts/tmux-hook-engine.d.ts +2 -0
  247. package/dist/scripts/tmux-hook-engine.d.ts.map +1 -1
  248. package/dist/scripts/tmux-hook-engine.js +15 -0
  249. package/dist/scripts/tmux-hook-engine.js.map +1 -1
  250. package/dist/team/__tests__/api-interop.test.js +136 -4
  251. package/dist/team/__tests__/api-interop.test.js.map +1 -1
  252. package/dist/team/__tests__/leader-activity.test.js +107 -2
  253. package/dist/team/__tests__/leader-activity.test.js.map +1 -1
  254. package/dist/team/__tests__/runtime-cli.test.js +32 -0
  255. package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
  256. package/dist/team/__tests__/runtime.test.js +148 -0
  257. package/dist/team/__tests__/runtime.test.js.map +1 -1
  258. package/dist/team/__tests__/shutdown-fallback.test.js +13 -0
  259. package/dist/team/__tests__/shutdown-fallback.test.js.map +1 -1
  260. package/dist/team/__tests__/state-root.test.js +11 -1
  261. package/dist/team/__tests__/state-root.test.js.map +1 -1
  262. package/dist/team/__tests__/state.test.js +237 -0
  263. package/dist/team/__tests__/state.test.js.map +1 -1
  264. package/dist/team/__tests__/tmux-session.test.js +521 -2
  265. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  266. package/dist/team/api-interop.d.ts.map +1 -1
  267. package/dist/team/api-interop.js +41 -31
  268. package/dist/team/api-interop.js.map +1 -1
  269. package/dist/team/commit-hygiene.d.ts +60 -0
  270. package/dist/team/commit-hygiene.d.ts.map +1 -0
  271. package/dist/team/commit-hygiene.js +232 -0
  272. package/dist/team/commit-hygiene.js.map +1 -0
  273. package/dist/team/leader-activity.d.ts.map +1 -1
  274. package/dist/team/leader-activity.js +56 -4
  275. package/dist/team/leader-activity.js.map +1 -1
  276. package/dist/team/runtime-cli.d.ts +9 -1
  277. package/dist/team/runtime-cli.d.ts.map +1 -1
  278. package/dist/team/runtime-cli.js +15 -6
  279. package/dist/team/runtime-cli.js.map +1 -1
  280. package/dist/team/runtime.d.ts +7 -2
  281. package/dist/team/runtime.d.ts.map +1 -1
  282. package/dist/team/runtime.js +392 -171
  283. package/dist/team/runtime.js.map +1 -1
  284. package/dist/team/scaling.d.ts.map +1 -1
  285. package/dist/team/scaling.js +6 -2
  286. package/dist/team/scaling.js.map +1 -1
  287. package/dist/team/state/dispatch.d.ts +2 -0
  288. package/dist/team/state/dispatch.d.ts.map +1 -1
  289. package/dist/team/state/dispatch.js +86 -40
  290. package/dist/team/state/dispatch.js.map +1 -1
  291. package/dist/team/state/mailbox.d.ts +3 -0
  292. package/dist/team/state/mailbox.d.ts.map +1 -1
  293. package/dist/team/state/mailbox.js +93 -19
  294. package/dist/team/state/mailbox.js.map +1 -1
  295. package/dist/team/state-root.d.ts +1 -1
  296. package/dist/team/state-root.d.ts.map +1 -1
  297. package/dist/team/state-root.js +8 -3
  298. package/dist/team/state-root.js.map +1 -1
  299. package/dist/team/state.d.ts.map +1 -1
  300. package/dist/team/state.js +96 -2
  301. package/dist/team/state.js.map +1 -1
  302. package/dist/team/tmux-session.d.ts.map +1 -1
  303. package/dist/team/tmux-session.js +81 -29
  304. package/dist/team/tmux-session.js.map +1 -1
  305. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  306. package/dist/team/worker-bootstrap.js +4 -0
  307. package/dist/team/worker-bootstrap.js.map +1 -1
  308. package/dist/team/worktree.d.ts.map +1 -1
  309. package/dist/team/worktree.js +9 -0
  310. package/dist/team/worktree.js.map +1 -1
  311. package/dist/utils/__tests__/paths.test.js +98 -11
  312. package/dist/utils/__tests__/paths.test.js.map +1 -1
  313. package/dist/utils/__tests__/platform-command.test.js +101 -2
  314. package/dist/utils/__tests__/platform-command.test.js.map +1 -1
  315. package/dist/utils/git-layout.d.ts +8 -0
  316. package/dist/utils/git-layout.d.ts.map +1 -0
  317. package/dist/utils/git-layout.js +58 -0
  318. package/dist/utils/git-layout.js.map +1 -0
  319. package/dist/utils/paths.d.ts +3 -0
  320. package/dist/utils/paths.d.ts.map +1 -1
  321. package/dist/utils/paths.js +14 -4
  322. package/dist/utils/paths.js.map +1 -1
  323. package/dist/utils/platform-command.d.ts.map +1 -1
  324. package/dist/utils/platform-command.js +35 -3
  325. package/dist/utils/platform-command.js.map +1 -1
  326. package/package.json +9 -5
  327. package/src/scripts/notify-fallback-watcher.ts +103 -53
  328. package/src/scripts/notify-hook/auto-nudge.ts +97 -103
  329. package/src/scripts/notify-hook/managed-tmux.ts +324 -0
  330. package/src/scripts/notify-hook/operational-events.ts +2 -0
  331. package/src/scripts/notify-hook/ralph-session-resume.ts +337 -0
  332. package/src/scripts/notify-hook/state-io.ts +2 -10
  333. package/src/scripts/notify-hook/team-dispatch.ts +131 -66
  334. package/src/scripts/notify-hook/team-leader-nudge.ts +19 -5
  335. package/src/scripts/notify-hook/team-tmux-guard.ts +0 -20
  336. package/src/scripts/notify-hook/team-worker.ts +4 -4
  337. package/src/scripts/notify-hook/tmux-injection.ts +103 -33
  338. package/src/scripts/notify-hook.ts +150 -21
  339. package/src/scripts/run-provider-advisor.ts +4 -2
  340. package/src/scripts/run-test-files.ts +48 -0
  341. package/src/scripts/tmux-hook-engine.ts +16 -0
  342. package/templates/AGENTS.md +51 -43
@@ -17,13 +17,13 @@ import {
17
17
  } from './state-io.js';
18
18
  import { runProcess } from './process-runner.js';
19
19
  import { logTmuxHookEvent } from './log.js';
20
+ import { resolveManagedCurrentPane, resolveManagedSessionContext, verifyManagedPaneTarget } from './managed-tmux.js';
20
21
  import { evaluatePaneInjectionReadiness, mapPaneInjectionReadinessReason, sendPaneInput } from './team-tmux-guard.js';
21
22
  import {
22
23
  normalizeTmuxHookConfig,
23
24
  pickActiveMode,
24
25
  evaluateInjectionGuards,
25
26
  buildSendKeysArgv,
26
- resolveCodexPane,
27
27
  } from '../tmux-hook-engine.js';
28
28
 
29
29
  function isHudPaneStartCommand(startCommand: any): boolean {
@@ -96,6 +96,25 @@ async function resolveCanonicalPaneFromPaneTarget(paneTarget: any, expectedCwd:
96
96
  return finalizeResolvedPane(healedPaneId, 'healed_hud_pane_target', expectedCwd);
97
97
  }
98
98
 
99
+ async function resolvePreferredModePane(stateDir: string, allowedModes: string[]): Promise<{ mode: string; state: any; pane: string } | null> {
100
+ const scopedDirs = await getScopedStateDirsForCurrentSession(stateDir).catch(() => [stateDir]);
101
+ const dirs = [...scopedDirs];
102
+ if (!dirs.map((dir) => resolvePath(dir)).includes(resolvePath(stateDir))) {
103
+ dirs.push(stateDir);
104
+ }
105
+ for (const dir of dirs) {
106
+ for (const mode of allowedModes || []) {
107
+ const path = join(dir, `${mode}-state.json`);
108
+ const parsed = await readJsonIfExists(path, null);
109
+ const pane = safeString(parsed?.tmux_pane_id || '').trim();
110
+ if (parsed?.active && pane) {
111
+ return { mode, state: parsed, pane };
112
+ }
113
+ }
114
+ }
115
+ return null;
116
+ }
117
+
99
118
  export async function resolveSessionToPane(sessionName: any): Promise<string | null> {
100
119
  const result = await runProcess('tmux', ['list-panes', '-t', sessionName, '-F', '#{pane_id}\t#{pane_active}\t#{pane_current_command}\t#{pane_start_command}']);
101
120
  const rows = result.stdout
@@ -128,27 +147,35 @@ export async function resolveSessionToPane(sessionName: any): Promise<string | n
128
147
  return nonHudRows[0]?.paneId || null;
129
148
  }
130
149
 
131
- export async function resolvePaneTarget(target: any, fallbackPane: any, expectedCwd: any, modePane: any): Promise<any> {
132
- const canonicalFallbackPane = safeString(fallbackPane).trim();
133
- if (canonicalFallbackPane) {
134
- try {
135
- return await finalizeResolvedPane(canonicalFallbackPane, 'fallback_current_pane', expectedCwd);
136
- } catch {
137
- // Fall through to mode/config probes
138
- }
150
+ export async function resolvePaneTarget(target: any, expectedCwd: any, modePane: any, cwd: string, payload: any): Promise<any> {
151
+ const requiresManagedOwnership = safeString(cwd).trim() !== '' && safeString(payload?.session_id || payload?.['session-id'] || process.env.OMX_SESSION_ID || '').trim() !== '';
152
+ const managedContext = requiresManagedOwnership
153
+ ? await resolveManagedSessionContext(cwd, payload, { allowTeamWorker: false })
154
+ : { managed: false, reason: 'not_required', invocationSessionId: '', sessionState: null, expectedTmuxSessionName: '', currentTmuxSessionName: '' };
155
+ if (requiresManagedOwnership && !managedContext.managed) {
156
+ return { paneTarget: null, reason: managedContext.reason || 'unmanaged_session' };
139
157
  }
140
158
 
141
- if (modePane) {
159
+ const canonicalModePane = safeString(modePane).trim();
160
+ if (canonicalModePane) {
142
161
  try {
143
- const resolved = await resolveCanonicalPaneFromPaneTarget(modePane, expectedCwd);
162
+ const resolved = await resolveCanonicalPaneFromPaneTarget(canonicalModePane, expectedCwd);
144
163
  if (resolved.paneTarget) {
145
- return {
146
- ...resolved,
147
- reason: resolved.reason === 'ok' ? 'fallback_mode_state_pane' : resolved.reason,
148
- };
164
+ const ownership = requiresManagedOwnership
165
+ ? await verifyManagedPaneTarget(resolved.paneTarget, cwd, payload, { allowTeamWorker: false })
166
+ : { ok: true };
167
+ if (ownership.ok) {
168
+ return {
169
+ ...resolved,
170
+ reason: resolved.reason === 'ok' ? 'fallback_mode_state_pane' : resolved.reason,
171
+ source: 'mode_state',
172
+ healTarget: true,
173
+ };
174
+ }
175
+ return { paneTarget: null, reason: ownership.reason || 'pane_not_managed_session' };
149
176
  }
150
177
  } catch {
151
- // Fall through to config probes
178
+ // Fall through to explicit config target
152
179
  }
153
180
  }
154
181
 
@@ -157,20 +184,52 @@ export async function resolvePaneTarget(target: any, fallbackPane: any, expected
157
184
  if (target.type === 'pane') {
158
185
  try {
159
186
  const resolved = await resolveCanonicalPaneFromPaneTarget(target.value, expectedCwd);
160
- if (resolved.paneTarget) return resolved;
161
- } catch {
162
- // Fall through
163
- }
164
- } else {
165
- try {
166
- const paneId = await resolveSessionToPane(target.value);
167
- if (paneId) return await finalizeResolvedPane(paneId, 'ok', expectedCwd);
187
+ if (resolved.paneTarget) {
188
+ const ownership = requiresManagedOwnership
189
+ ? await verifyManagedPaneTarget(resolved.paneTarget, cwd, payload, { allowTeamWorker: false })
190
+ : { ok: true };
191
+ if (ownership.ok) {
192
+ return {
193
+ ...resolved,
194
+ reason: resolved.reason === 'ok' ? 'explicit_pane_target' : resolved.reason,
195
+ source: 'explicit_target',
196
+ healTarget: true,
197
+ };
198
+ }
199
+ return { paneTarget: null, reason: ownership.reason || 'pane_not_managed_session' };
200
+ }
168
201
  } catch {
169
202
  // Fall through
170
203
  }
204
+ return { paneTarget: null, reason: 'target_not_found' };
171
205
  }
172
206
 
173
- return { paneTarget: null, reason: 'target_not_found' };
207
+ try {
208
+ if (!requiresManagedOwnership) return { paneTarget: null, reason: 'target_session_requires_managed_context' };
209
+ const explicitSessionTarget = safeString(target.value).trim();
210
+ const expectedSessionTarget = safeString(managedContext.expectedTmuxSessionName).trim();
211
+ const sessionIdTarget = safeString(managedContext.invocationSessionId).trim();
212
+ const stateSessionTarget = safeString(managedContext.sessionState?.session_id).trim();
213
+ const allowedSessionTargets = new Set([expectedSessionTarget, sessionIdTarget, stateSessionTarget].filter(Boolean));
214
+ if (!allowedSessionTargets.has(explicitSessionTarget)) {
215
+ return { paneTarget: null, reason: 'target_session_not_managed' };
216
+ }
217
+ const paneId = await resolveSessionToPane(expectedSessionTarget);
218
+ if (!paneId) return { paneTarget: null, reason: 'target_not_found' };
219
+ const resolved = await finalizeResolvedPane(paneId, 'managed_session_target', expectedCwd);
220
+ if (!resolved.paneTarget) return resolved;
221
+ const ownership = await verifyManagedPaneTarget(resolved.paneTarget, cwd, payload, { allowTeamWorker: false });
222
+ if (!ownership.ok) {
223
+ return { paneTarget: null, reason: ownership.reason || 'pane_not_managed_session' };
224
+ }
225
+ return {
226
+ ...resolved,
227
+ source: 'explicit_target',
228
+ healTarget: true,
229
+ };
230
+ } catch {
231
+ return { paneTarget: null, reason: 'target_not_found' };
232
+ }
174
233
  }
175
234
 
176
235
  export async function handleTmuxInjection({
@@ -202,7 +261,6 @@ export async function handleTmuxInjection({
202
261
  const activeModes: string[] = [];
203
262
  const activeModeStates: Record<string, any> = {};
204
263
  const scannedStateDirs = new Set<string>();
205
- const payloadSessionId = safeString(payload.session_id || payload['session-id'] || '');
206
264
  const scanActiveModeStateDirs = async (dirs: string[], preserveExisting = false) => {
207
265
  for (const scopedDir of dirs) {
208
266
  const resolvedScopedDir = resolvePath(scopedDir);
@@ -225,7 +283,7 @@ export async function handleTmuxInjection({
225
283
  }
226
284
  };
227
285
  try {
228
- const scopedDirs = await getScopedStateDirsForCurrentSession(stateDir, payloadSessionId);
286
+ const scopedDirs = await getScopedStateDirsForCurrentSession(stateDir);
229
287
  await scanActiveModeStateDirs(scopedDirs);
230
288
 
231
289
  if (!pickActiveMode(activeModes, config.allowed_modes) && !scannedStateDirs.has(resolvePath(stateDir))) {
@@ -235,9 +293,10 @@ export async function handleTmuxInjection({
235
293
  // Non-fatal
236
294
  }
237
295
 
238
- const mode = pickActiveMode(activeModes, config.allowed_modes);
239
- const modeState = mode ? (activeModeStates[mode] || {}) : {};
240
- const modePane = safeString(modeState.tmux_pane_id || '');
296
+ const preferredModePane = await resolvePreferredModePane(stateDir, config.allowed_modes).catch(() => null);
297
+ const mode = preferredModePane?.mode || pickActiveMode(activeModes, config.allowed_modes);
298
+ const modeState = preferredModePane?.state || (mode ? (activeModeStates[mode] || {}) : {});
299
+ const modePane = preferredModePane?.pane || safeString(modeState.tmux_pane_id || '');
241
300
  const preGuard = evaluateInjectionGuards({
242
301
  config,
243
302
  mode,
@@ -280,8 +339,19 @@ export async function handleTmuxInjection({
280
339
  turnId,
281
340
  timestamp: nowIso,
282
341
  }), sourceText);
283
- const fallbackPane = resolveCodexPane();
284
- const resolution = await resolvePaneTarget(config.target, fallbackPane, cwd, modePane);
342
+ const preferredPaneTarget = modePane || await resolveManagedCurrentPane(cwd, payload, { allowTeamWorker: false });
343
+ let resolution = preferredModePane
344
+ ? await resolveCanonicalPaneFromPaneTarget(preferredModePane.pane, cwd).then((resolved) => (
345
+ resolved.paneTarget
346
+ ? { ...resolved, reason: 'fallback_mode_state_pane', source: 'mode_state', healTarget: true }
347
+ : resolved
348
+ ))
349
+ : preferredPaneTarget
350
+ ? await resolvePaneTarget({ type: 'pane', value: preferredPaneTarget }, cwd, '', cwd, payload)
351
+ : await resolvePaneTarget(config.target, cwd, modePane, cwd, payload);
352
+ if (!resolution.paneTarget && preferredPaneTarget) {
353
+ resolution = await resolvePaneTarget(config.target, cwd, modePane, cwd, payload);
354
+ }
285
355
  if (!resolution.paneTarget) {
286
356
  state.last_reason = resolution.reason;
287
357
  state.last_event_at = nowIso;
@@ -319,7 +389,7 @@ export async function handleTmuxInjection({
319
389
  }
320
390
 
321
391
  // Pane-canonical healing: persist resolved pane target so routing stops depending on session names or stale pane ids.
322
- if (config.target && (config.target.type !== 'pane' || safeString(config.target.value).trim() !== paneTarget)) {
392
+ if (resolution.healTarget && config.target && (config.target.type !== 'pane' || safeString(config.target.value).trim() !== paneTarget)) {
323
393
  try {
324
394
  const healed = {
325
395
  ...(rawConfig && typeof rawConfig === 'object' ? rawConfig : {}),
@@ -39,6 +39,10 @@ import { isLeaderStale, resolveLeaderStalenessThresholdMs, maybeNudgeTeamLeader
39
39
  import { drainPendingTeamDispatch } from './notify-hook/team-dispatch.js';
40
40
  import { handleTmuxInjection } from './notify-hook/tmux-injection.js';
41
41
  import { maybeAutoNudge, resolveNudgePaneTarget, isDeepInterviewStateActive } from './notify-hook/auto-nudge.js';
42
+ import { isManagedOmxSession } from './notify-hook/managed-tmux.js';
43
+ import { logNotifyHookEvent } from './notify-hook/log.js';
44
+ import { reconcileRalphSessionResume } from './notify-hook/ralph-session-resume.js';
45
+ import { sendPaneInput } from './notify-hook/team-tmux-guard.js';
42
46
  import {
43
47
  buildOperationalContext,
44
48
  deriveAssistantSignalEvents,
@@ -68,6 +72,79 @@ const RALPH_ACTIVE_PROGRESS_PHASES = new Set([
68
72
  'fixing',
69
73
  ]);
70
74
 
75
+ const IDLE_NOTIFICATION_SUMMARY_MAX_LENGTH = 240;
76
+
77
+ function summarizeIdleNotificationMessage(message: unknown): string {
78
+ const source = safeString(message)
79
+ .split('\n')
80
+ .map((line) => line.trim())
81
+ .filter(Boolean);
82
+ const preferred = source.at(-1) || '';
83
+ const normalized = preferred.replace(/\s+/g, ' ').trim();
84
+ if (!normalized) return '';
85
+ return normalized.length > IDLE_NOTIFICATION_SUMMARY_MAX_LENGTH
86
+ ? `${normalized.slice(0, IDLE_NOTIFICATION_SUMMARY_MAX_LENGTH - 1)}…`
87
+ : normalized;
88
+ }
89
+
90
+ function classifyIdleNotificationPhase(message: unknown): 'idle' | 'progress' | 'finished' | 'failed' {
91
+ const lower = safeString(message).toLowerCase();
92
+ if (!lower) return 'idle';
93
+
94
+ if (/(error|failed|exception|invalid|timed out|timeout)/i.test(lower)) {
95
+ return 'failed';
96
+ }
97
+
98
+ if ([
99
+ 'all tests pass',
100
+ 'build succeeded',
101
+ 'completed',
102
+ 'complete',
103
+ 'done',
104
+ 'final summary',
105
+ 'summary',
106
+ ].some((pattern) => lower.includes(pattern))) {
107
+ return 'finished';
108
+ }
109
+
110
+ if ([
111
+ 'verify',
112
+ 'verified',
113
+ 'verification',
114
+ 'review',
115
+ 'reviewed',
116
+ 'diagnostic',
117
+ 'typecheck',
118
+ 'test',
119
+ 'implement',
120
+ 'implemented',
121
+ 'apply patch',
122
+ 'change',
123
+ 'fix',
124
+ 'update',
125
+ 'refactor',
126
+ 'resume',
127
+ 'resumed',
128
+ 'progress',
129
+ 'continue',
130
+ 'continued',
131
+ ].some((pattern) => lower.includes(pattern))) {
132
+ return 'progress';
133
+ }
134
+
135
+ return 'idle';
136
+ }
137
+
138
+ function buildIdleNotificationFingerprint(payload: Record<string, unknown>): string {
139
+ const lastAssistantMessage = safeString(payload['last-assistant-message'] || payload.last_assistant_message || '');
140
+ const summary = summarizeIdleNotificationMessage(lastAssistantMessage);
141
+ const phase = classifyIdleNotificationPhase(lastAssistantMessage);
142
+ return JSON.stringify({
143
+ phase,
144
+ ...(summary ? { summary } : {}),
145
+ });
146
+ }
147
+
71
148
  async function main() {
72
149
  const rawPayload = process.argv[process.argv.length - 1];
73
150
  if (!rawPayload || rawPayload.startsWith('-')) {
@@ -83,6 +160,9 @@ async function main() {
83
160
 
84
161
  const cwd = payload.cwd || payload['cwd'] || process.cwd();
85
162
  const payloadSessionId = safeString(payload.session_id || payload['session-id'] || '');
163
+ const payloadThreadId = safeString(payload['thread-id'] || payload.thread_id || '');
164
+ const inputMessages = normalizeInputMessages(payload);
165
+ const latestUserInput = safeString(inputMessages.length > 0 ? inputMessages[inputMessages.length - 1] : '');
86
166
 
87
167
  // Team worker detection via environment variable
88
168
  const teamWorkerEnv = process.env.OMX_TEAM_WORKER; // e.g., "fix-ts/worker-1"
@@ -94,6 +174,7 @@ async function main() {
94
174
  : join(cwd, '.omx', 'state');
95
175
  const logsDir = join(cwd, '.omx', 'logs');
96
176
  const omxDir = join(cwd, '.omx');
177
+ let currentOmxSessionId = '';
97
178
 
98
179
  // Ensure directories exist
99
180
  await mkdir(logsDir, { recursive: true }).catch(() => {});
@@ -158,11 +239,45 @@ async function main() {
158
239
  const logFile = join(logsDir, `turns-${new Date().toISOString().split('T')[0]}.jsonl`);
159
240
  await appendFile(logFile, JSON.stringify(logEntry) + '\n').catch(() => {});
160
241
 
242
+ // Reconcile Ralph ownership for same-Codex-session continuation before
243
+ // lifecycle counters or injection read the active scope.
244
+ if (!isTeamWorker) {
245
+ try {
246
+ const resumeResult = await reconcileRalphSessionResume({
247
+ stateDir,
248
+ payloadSessionId,
249
+ payloadThreadId,
250
+ });
251
+ currentOmxSessionId = resumeResult.currentOmxSessionId;
252
+ if (resumeResult.resumed || resumeResult.updatedCurrentOwner) {
253
+ await logNotifyHookEvent(logsDir, {
254
+ timestamp: new Date().toISOString(),
255
+ type: 'ralph_session_resume',
256
+ reason: resumeResult.reason,
257
+ current_omx_session_id: resumeResult.currentOmxSessionId || null,
258
+ payload_codex_session_id: payloadSessionId || null,
259
+ source_path: resumeResult.sourcePath || null,
260
+ target_path: resumeResult.targetPath || null,
261
+ owner_updated: resumeResult.updatedCurrentOwner,
262
+ resumed: resumeResult.resumed,
263
+ });
264
+ }
265
+ } catch (error) {
266
+ await logNotifyHookEvent(logsDir, {
267
+ timestamp: new Date().toISOString(),
268
+ level: 'warn',
269
+ type: 'ralph_session_resume_failure',
270
+ payload_codex_session_id: payloadSessionId || null,
271
+ error: error instanceof Error ? error.message : String(error),
272
+ });
273
+ }
274
+ }
275
+
161
276
  // 2. Update active mode state (increment iteration)
162
277
  // GUARD: Skip when running inside a team worker to prevent state corruption
163
278
  if (!isTeamWorker) {
164
279
  try {
165
- const scopedDirs = await getScopedStateDirsForCurrentSession(stateDir, payloadSessionId);
280
+ const scopedDirs = await getScopedStateDirsForCurrentSession(stateDir);
166
281
  for (const scopedDir of scopedDirs) {
167
282
  const stateFiles = await readdir(scopedDir).catch(() => []);
168
283
  for (const f of stateFiles) {
@@ -298,7 +413,9 @@ async function main() {
298
413
  if (existsSync(hudStatePath)) {
299
414
  hudState = JSON.parse(await readFile(hudStatePath, 'utf-8'));
300
415
  }
301
- hudState.last_turn_at = new Date().toISOString();
416
+ const nowIso = new Date().toISOString();
417
+ hudState.last_turn_at = nowIso;
418
+ (hudState as any).last_progress_at = nowIso;
302
419
  hudState.turn_count = (hudState.turn_count || 0) + 1;
303
420
  (hudState as any).last_agent_output = (payload['last-assistant-message'] || payload.last_assistant_message || '')
304
421
  .slice(0, 100);
@@ -323,14 +440,12 @@ async function main() {
323
440
  // 4.45. Skill activation tracking: update skill-active-state.json before any nudge logic.
324
441
  try {
325
442
  const { recordSkillActivation } = await import('../hooks/keyword-detector.js');
326
- const inputMessages = normalizeInputMessages(payload);
327
- const latestUserInput = safeString(inputMessages.length > 0 ? inputMessages[inputMessages.length - 1] : '');
328
443
  if (latestUserInput) {
329
444
  await recordSkillActivation({
330
445
  stateDir,
331
446
  text: latestUserInput,
332
447
  sessionId: payloadSessionId,
333
- threadId: safeString(payload['thread-id'] || payload.thread_id || ''),
448
+ threadId: payloadThreadId,
334
449
  turnId: safeString(payload['turn-id'] || payload.turn_id || ''),
335
450
  });
336
451
  }
@@ -443,19 +558,20 @@ async function main() {
443
558
  const { notifyLifecycle } = await import('../notifications/index.js');
444
559
  const { shouldSendIdleNotification, recordIdleNotificationSent } = await import('../notifications/idle-cooldown.js');
445
560
  const sessionJsonPath = join(stateDir, 'session.json');
561
+ const idleFingerprint = buildIdleNotificationFingerprint(payload);
446
562
  let notifySessionId = '';
447
563
  try {
448
564
  const sessionData = JSON.parse(await readFile(sessionJsonPath, 'utf-8'));
449
565
  notifySessionId = safeString(sessionData && sessionData.session_id ? sessionData.session_id : '');
450
566
  } catch { /* no session file */ }
451
567
 
452
- if (notifySessionId && shouldSendIdleNotification(stateDir, notifySessionId)) {
568
+ if (notifySessionId && shouldSendIdleNotification(stateDir, notifySessionId, idleFingerprint)) {
453
569
  const idleResult = await notifyLifecycle('session-idle', {
454
570
  sessionId: notifySessionId,
455
571
  projectPath: cwd,
456
572
  });
457
573
  if (idleResult && idleResult.anySuccess) {
458
- recordIdleNotificationSent(stateDir, notifySessionId);
574
+ recordIdleNotificationSent(stateDir, notifySessionId, idleFingerprint);
459
575
  }
460
576
  try {
461
577
  const { buildNativeHookEvent } = await import('../hooks/extensibility/events.js');
@@ -506,7 +622,7 @@ async function main() {
506
622
  payload,
507
623
  stateDir,
508
624
  logsDir,
509
- sessionId: payloadSessionId,
625
+ sessionId: currentOmxSessionId || payloadSessionId,
510
626
  turnId: safeString(payload['turn-id'] || payload.turn_id || ''),
511
627
  });
512
628
  } catch (err) {
@@ -531,23 +647,36 @@ async function main() {
531
647
  const { processCodeSimplifier } = await import('../hooks/code-simplifier/index.js');
532
648
  const csResult = processCodeSimplifier(cwd, stateDir);
533
649
  if (csResult.triggered) {
534
- const csPaneId = await resolveNudgePaneTarget(stateDir);
535
- if (csPaneId) {
536
- const csText = `${csResult.message} ${DEFAULT_MARKER}`;
537
- const { runProcess } = await import('./notify-hook/process-runner.js');
538
- await runProcess('tmux', ['send-keys', '-t', csPaneId, '-l', csText], 3000);
539
- await new Promise(r => setTimeout(r, 100));
540
- await runProcess('tmux', ['send-keys', '-t', csPaneId, 'C-m'], 3000);
541
- await new Promise(r => setTimeout(r, 100));
542
- await runProcess('tmux', ['send-keys', '-t', csPaneId, 'C-m'], 3000);
543
-
650
+ const managedSession = await isManagedOmxSession(cwd, payload, { allowTeamWorker: false });
651
+ if (!managedSession) {
544
652
  const { logTmuxHookEvent } = await import('./notify-hook/log.js');
545
653
  await logTmuxHookEvent(logsDir, {
546
654
  timestamp: new Date().toISOString(),
547
- type: 'code_simplifier_triggered',
548
- pane_id: csPaneId,
549
- file_count: csResult.message.split('\n').filter(l => l.trimStart().startsWith('- ')).length,
655
+ type: 'code_simplifier_skipped',
656
+ reason: 'unmanaged_session',
550
657
  });
658
+ } else {
659
+ const csPaneId = await resolveNudgePaneTarget(stateDir, cwd, payload);
660
+ if (csPaneId) {
661
+ const csText = `${csResult.message} ${DEFAULT_MARKER}`;
662
+ const sendResult = await sendPaneInput({
663
+ paneTarget: csPaneId,
664
+ prompt: csText,
665
+ submitKeyPresses: 2,
666
+ submitDelayMs: 100,
667
+ });
668
+ if (!sendResult.ok) {
669
+ throw new Error(sendResult.error || sendResult.reason || 'send_failed');
670
+ }
671
+
672
+ const { logTmuxHookEvent } = await import('./notify-hook/log.js');
673
+ await logTmuxHookEvent(logsDir, {
674
+ timestamp: new Date().toISOString(),
675
+ type: 'code_simplifier_triggered',
676
+ pane_id: csPaneId,
677
+ file_count: csResult.message.split('\n').filter(l => l.trimStart().startsWith('- ')).length,
678
+ });
679
+ }
551
680
  }
552
681
  }
553
682
  } catch {
@@ -59,7 +59,8 @@ function ensureBinary(binary: string): void {
59
59
  const probe = spawnSync(binary, ['--version'], {
60
60
  stdio: 'ignore',
61
61
  encoding: 'utf8',
62
- });
62
+ windowsHide: true,
63
+ });
63
64
 
64
65
  if (probe.error && (probe.error as NodeJS.ErrnoException).code === 'ENOENT') {
65
66
  const verify = `${binary} --version`;
@@ -153,7 +154,8 @@ async function main(): Promise<void> {
153
154
  const run = spawnSync(binary, ['-p', prompt], {
154
155
  encoding: 'utf8',
155
156
  maxBuffer: 10 * 1024 * 1024,
156
- });
157
+ windowsHide: true,
158
+ });
157
159
 
158
160
  const stdout = run.stdout || '';
159
161
  const stderr = run.stderr || '';
@@ -0,0 +1,48 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { readdirSync, statSync } from 'node:fs';
3
+ import { join, resolve } from 'node:path';
4
+
5
+ function collectTests(path: string, out: string[]): void {
6
+ let stats;
7
+ try {
8
+ stats = statSync(path);
9
+ } catch {
10
+ return;
11
+ }
12
+
13
+ if (stats.isDirectory()) {
14
+ for (const entry of readdirSync(path)) {
15
+ collectTests(join(path, entry), out);
16
+ }
17
+ return;
18
+ }
19
+
20
+ if (stats.isFile() && path.endsWith('.test.js')) {
21
+ out.push(path);
22
+ }
23
+ }
24
+
25
+ const roots = process.argv.slice(2);
26
+ const targets = roots.length > 0 ? roots : ['dist'];
27
+ const files: string[] = [];
28
+ for (const target of targets) {
29
+ collectTests(resolve(target), files);
30
+ }
31
+
32
+ files.sort();
33
+
34
+ if (files.length === 0) {
35
+ console.error(`No test files found under: ${targets.join(', ')}`);
36
+ process.exit(1);
37
+ }
38
+
39
+ const result = spawnSync(process.execPath, ['--test', ...files], {
40
+ stdio: 'inherit',
41
+ env: process.env,
42
+ });
43
+
44
+ if (typeof result.status === 'number') {
45
+ process.exit(result.status);
46
+ }
47
+
48
+ process.exit(1);
@@ -80,6 +80,10 @@ export function normalizeTmuxHookConfig(raw: any): any {
80
80
  };
81
81
  }
82
82
 
83
+ export function tmuxHookExplicitlyDisablesInjection(raw: any): boolean {
84
+ return Boolean(raw && typeof raw === 'object' && raw.enabled === false);
85
+ }
86
+
83
87
  export function pickActiveMode(activeModes: any, allowedModes: any): string | null {
84
88
  const activeSet = new Set((activeModes || []).filter((mode: any) => typeof mode === 'string'));
85
89
  for (const mode of allowedModes || []) {
@@ -223,6 +227,7 @@ export function resolveCodexPane(): string {
223
227
  try {
224
228
  const sessionName = execFileSync('tmux', ['display-message', '-t', envPane, '-p', '#S'], {
225
229
  encoding: 'utf-8', timeout: 2000,
230
+ windowsHide: true,
226
231
  }).trim();
227
232
  if (!sessionName) return '';
228
233
 
@@ -297,6 +302,17 @@ export function paneLooksReady(captured: any): boolean {
297
302
  return lines.some((line) => /^\s*(?:[›>❯]\s*)?[A-Z][A-Z0-9]+-\d+\s+only(?:\s*(?:…|\.{3}))?\s*$/iu.test(line));
298
303
  }
299
304
 
305
+ export function paneShowsCodexViewport(captured: any): boolean {
306
+ const lines = normalizePaneLines(captured);
307
+ if (lines.length === 0) return false;
308
+ if (paneIsBootstrapping(lines)) return false;
309
+
310
+ const hasCodexBanner = lines.some((line) => /\bOpenAI Codex\b/i.test(line));
311
+ if (!hasCodexBanner) return false;
312
+
313
+ return lines.some((line) => /(?:^|\s)(?:model|directory):/i.test(line));
314
+ }
315
+
300
316
  export function paneHasActiveTask(captured: any): boolean {
301
317
  const tail = normalizePaneLines(captured).map((line) => line.trim()).slice(-40);
302
318
  if (tail.some((line) => /\b\d+\s+background terminal running\b/i.test(line))) return true;