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
@@ -5,7 +5,6 @@
5
5
  */
6
6
 
7
7
  import { readFile, writeFile } from 'fs/promises';
8
- import { execFileSync } from 'child_process';
9
8
  import { join } from 'path';
10
9
  import { homedir } from 'os';
11
10
  import { asNumber, safeString } from './utils.js';
@@ -13,7 +12,15 @@ import { readJsonIfExists, getScopedStateDirsForCurrentSession, readdir } from '
13
12
  import { runProcess } from './process-runner.js';
14
13
  import { logTmuxHookEvent } from './log.js';
15
14
  import { evaluatePaneInjectionReadiness, mapPaneInjectionReadinessReason, sendPaneInput } from './team-tmux-guard.js';
16
- import { buildCapturePaneArgv, DEFAULT_MARKER } from '../tmux-hook-engine.js';
15
+ import { buildCapturePaneArgv, DEFAULT_MARKER, tmuxHookExplicitlyDisablesInjection } from '../tmux-hook-engine.js';
16
+ import {
17
+ isManagedOmxSession,
18
+ resolveManagedCurrentPane,
19
+ resolveManagedPaneFromAnchor,
20
+ resolveManagedSessionPane,
21
+ resolveInvocationSessionId,
22
+ verifyManagedPaneTarget,
23
+ } from './managed-tmux.js';
17
24
 
18
25
  export const SKILL_ACTIVE_STATE_FILE = 'skill-active-state.json';
19
26
  export const DEEP_INTERVIEW_BLOCKED_APPROVAL_INPUTS = ['yes', 'y', 'proceed', 'continue', 'ok', 'sure', 'go ahead', 'next i should'];
@@ -53,22 +60,29 @@ export function normalizeBlockedAutoApprovalInput(text) {
53
60
  .trim();
54
61
  }
55
62
 
63
+ function buildBlockedAutoApprovalMatcher(blockedInputs) {
64
+ const normalizedBlockedInputs = blockedInputs.map((entry) => normalizeBlockedAutoApprovalInput(entry)).filter(Boolean);
65
+ return {
66
+ exactMatches: new Set(normalizedBlockedInputs),
67
+ prefixedMatches: normalizedBlockedInputs.filter((entry) => DEEP_INTERVIEW_BLOCKED_APPROVAL_PREFIXES.has(entry)),
68
+ blockedTokenSet: new Set(normalizedBlockedInputs.flatMap((entry) => entry.split(/\s+/).filter(Boolean))),
69
+ };
70
+ }
71
+
56
72
  export function isBlockedAutoApprovalInput(text, blockedInputs = DEEP_INTERVIEW_BLOCKED_APPROVAL_INPUTS) {
57
73
  const normalized = normalizeBlockedAutoApprovalInput(text);
58
74
  if (!normalized) return false;
59
- if (blockedInputs.some((entry) => normalizeBlockedAutoApprovalInput(entry) === normalized)) return true;
60
- if (
61
- blockedInputs
62
- .map((entry) => normalizeBlockedAutoApprovalInput(entry))
63
- .filter((entry) => DEEP_INTERVIEW_BLOCKED_APPROVAL_PREFIXES.has(entry))
64
- .some((prefix) => normalized.startsWith(`${prefix} `))
65
- ) return true;
75
+ const normalizedBlockedInputs = blockedInputs.map((entry) => normalizeBlockedAutoApprovalInput(entry)).filter(Boolean);
76
+ if (normalizedBlockedInputs.includes(normalized)) return true;
77
+
78
+ const blockedPrefixes = normalizedBlockedInputs.filter((entry) => DEEP_INTERVIEW_BLOCKED_APPROVAL_PREFIXES.has(entry));
79
+ if (blockedPrefixes.some((prefix) => normalized.startsWith(`${prefix} `))) return true;
66
80
 
67
81
  const tokens = normalized.split(/\s+/).filter(Boolean);
68
82
  if (tokens.length === 0) return false;
69
83
 
70
84
  const blockedTokenSet = new Set(
71
- blockedInputs.flatMap((entry) => normalizeBlockedAutoApprovalInput(entry).split(/\s+/).filter(Boolean)),
85
+ normalizedBlockedInputs.flatMap((entry) => entry.split(/\s+/).filter(Boolean)),
72
86
  );
73
87
  return tokens.every((token) => blockedTokenSet.has(token));
74
88
  }
@@ -177,6 +191,11 @@ export async function isDeepInterviewStateActive(stateDir) {
177
191
  return Boolean(modeState && modeState.active === true);
178
192
  }
179
193
 
194
+ export async function isDeepInterviewInputLockActive(stateDir) {
195
+ const skillState = await loadSkillActiveState(stateDir);
196
+ return isDeepInterviewAutoApprovalLocked(skillState);
197
+ }
198
+
180
199
  export async function resolveAutoNudgeSignature(stateDir, payload, lastMessage = '') {
181
200
  const normalizedMessage = normalizeAutoNudgeSignatureText(lastMessage);
182
201
  const hudState = await readJsonIfExists(join(stateDir, 'hud-state.json'), null);
@@ -352,6 +371,13 @@ export async function loadAutoNudgeConfig() {
352
371
  return normalizeAutoNudgeConfig(raw.autoNudge);
353
372
  }
354
373
 
374
+ async function localTmuxInjectionDisabled(cwd) {
375
+ const normalizedCwd = safeString(cwd).trim();
376
+ if (!normalizedCwd) return false;
377
+ const raw = await readJsonIfExists(join(normalizedCwd, '.omx', 'tmux-hook.json'), null);
378
+ return tmuxHookExplicitlyDisablesInjection(raw);
379
+ }
380
+
355
381
  export function detectStallPattern(text, patterns) {
356
382
  if (!text || typeof text !== 'string') return false;
357
383
  const normalized = normalizeStallDetectionText(text);
@@ -373,105 +399,59 @@ export async function capturePane(paneId, lines = 10) {
373
399
  }
374
400
  }
375
401
 
376
- function resolveCodexPaneByCwdFallback(cwd) {
377
- const normalizedCwd = safeString(cwd).trim();
378
- if (!normalizedCwd) return '';
379
-
380
- try {
381
- const panes = execFileSync('tmux', [
382
- 'list-panes', '-a', '-F', '#{pane_id} #{pane_current_path} #{pane_current_command} #{pane_start_command}',
383
- ], { encoding: 'utf-8', timeout: 2000 })
384
- .trim()
385
- .split('\n')
386
- .filter(Boolean);
387
-
388
- for (const line of panes) {
389
- const [paneId, panePath = '', paneCommand = '', startCommand = ''] = line.split('\t');
390
- const normalizedPanePath = safeString(panePath).trim();
391
- const normalizedStart = safeString(startCommand).toLowerCase();
392
- const normalizedCommand = safeString(paneCommand).trim().toLowerCase();
393
- if (!paneId || normalizedPanePath !== normalizedCwd) continue;
394
- if (/\bomx\b.*\bhud\b.*--watch/i.test(normalizedStart)) continue;
395
- if (normalizedStart.includes('codex')) return paneId;
396
- if (normalizedCommand === 'codex' || normalizedCommand === 'node' || normalizedCommand === 'npx') return paneId;
397
- }
398
- } catch {
399
- // Fall back to empty when tmux scan is unavailable.
400
- }
401
-
402
- return '';
403
- }
404
-
405
- async function resolveCodexPaneFromAnchor(anchorPane) {
406
- const paneId = safeString(anchorPane).trim();
407
- if (!paneId) return '';
408
-
409
- try {
410
- const sessionResult = await runProcess('tmux', ['display-message', '-t', paneId, '-p', '#S'], 2000);
411
- const sessionName = safeString(sessionResult.stdout).trim();
412
- if (!sessionName) return '';
413
-
414
- const panesResult = await runProcess(
415
- 'tmux',
416
- ['list-panes', '-s', '-t', sessionName, '-F', '#{pane_id}\t#{pane_current_command}\t#{pane_start_command}'],
417
- 2000,
418
- );
419
- const panes = safeString(panesResult.stdout).trim().split('\n').filter(Boolean);
420
- for (const line of panes) {
421
- const [candidatePaneId, , rawStartCommand = ''] = line.split('\t');
422
- const startCommand = safeString(rawStartCommand).toLowerCase();
423
- if (!candidatePaneId) continue;
424
- if (/\bomx\b.*\bhud\b.*--watch/i.test(startCommand)) continue;
425
- if (startCommand.includes('codex')) return candidatePaneId;
426
- }
427
- } catch {
428
- // Fall back to the anchored pane when session scanning is unavailable.
429
- }
430
-
431
- return '';
432
- }
433
-
434
- export async function resolveNudgePaneTarget(stateDir: any, cwd = '') {
435
- // Use canonical codex pane resolver — validates pane is running an agent, not a shell
436
- const { resolveCodexPane } = await import('../tmux-hook-engine.js');
437
- const codexPane = resolveCodexPane();
438
- if (codexPane) return codexPane;
439
-
440
- let fallbackPane = '';
441
-
442
- try {
443
- const scopedDirs = await getScopedStateDirsForCurrentSession(stateDir);
444
- for (const dir of scopedDirs) {
445
- const files = await readdir(dir).catch(() => []);
446
- for (const f of files) {
447
- if (!f.endsWith('-state.json')) continue;
448
- const path = join(dir, f);
449
- try {
450
- const state = JSON.parse(await readFile(path, 'utf-8'));
451
- if (state && state.active && state.tmux_pane_id) {
452
- const anchoredPane = safeString(state.tmux_pane_id).trim();
453
- if (!anchoredPane) continue;
454
- const upgradedPane = await resolveCodexPaneFromAnchor(anchoredPane);
455
- if (upgradedPane) return upgradedPane;
456
- if (!fallbackPane) fallbackPane = anchoredPane;
457
- }
458
- } catch {
459
- // skip malformed state
460
- }
402
+ export async function resolveNudgePaneTarget(stateDir: any, cwd = '', payload: any = undefined) {
403
+ const allowTeamWorker = safeString(process.env.OMX_TEAM_WORKER || '').trim() !== '';
404
+ const managedCurrentPane = await resolveManagedCurrentPane(cwd, payload, { allowTeamWorker });
405
+ if (managedCurrentPane) return managedCurrentPane;
406
+
407
+ const invocationSessionId = resolveInvocationSessionId(payload);
408
+ const scopedDirs = await getScopedStateDirsForCurrentSession(stateDir, invocationSessionId).catch(() => []);
409
+ for (const dir of scopedDirs) {
410
+ const files = await readdir(dir).catch(() => []);
411
+ for (const f of files) {
412
+ if (!f.endsWith('-state.json')) continue;
413
+ const path = join(dir, f);
414
+ try {
415
+ const state = JSON.parse(await readFile(path, 'utf-8'));
416
+ if (!state || !state.active || !state.tmux_pane_id) continue;
417
+ const anchoredPane = safeString(state.tmux_pane_id).trim();
418
+ if (!anchoredPane) continue;
419
+ const managedPane = await resolveManagedPaneFromAnchor(anchoredPane, cwd, payload, { allowTeamWorker });
420
+ if (managedPane) return managedPane;
421
+ const verdict = await verifyManagedPaneTarget(anchoredPane, cwd, payload, { allowTeamWorker });
422
+ if (verdict.ok) return anchoredPane;
423
+ } catch {
424
+ // skip malformed state
461
425
  }
462
426
  }
463
- } catch {
464
- // Non-critical
465
427
  }
466
428
 
467
- if (fallbackPane) return fallbackPane;
468
-
469
- return resolveCodexPaneByCwdFallback(cwd);
429
+ return await resolveManagedSessionPane(cwd, payload);
470
430
  }
471
431
 
472
432
  export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
473
433
  const config = await loadAutoNudgeConfig();
474
434
  if (!config.enabled) return;
435
+ if (await localTmuxInjectionDisabled(cwd)) {
436
+ await logTmuxHookEvent(logsDir, {
437
+ timestamp: new Date().toISOString(),
438
+ type: 'auto_nudge_skipped',
439
+ reason: 'tmux_hook_disabled',
440
+ }).catch(() => {});
441
+ return;
442
+ }
443
+
444
+ const sourceName = safeString(payload?.source || '');
445
+ const managedSession = await isManagedOmxSession(cwd, payload, { allowTeamWorker: true });
446
+ if (!managedSession) {
447
+ if (sourceName === 'notify-fallback-watcher-stall') return;
448
+ await logTmuxHookEvent(logsDir, {
449
+ timestamp: new Date().toISOString(),
450
+ type: 'auto_nudge_skipped',
451
+ reason: 'unmanaged_session',
452
+ }).catch(() => {});
453
+ return;
454
+ }
475
455
 
476
456
  const lastMessage = safeString(payload['last-assistant-message'] || payload.last_assistant_message || '');
477
457
  const latestUserInput = latestUserInputFromPayload(payload);
@@ -493,7 +473,7 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
493
473
  if (!nudgeState || typeof nudgeState !== 'object') {
494
474
  nudgeState = { nudgeCount: 0, lastNudgeAt: '', lastSignature: '', lastSemanticSignature: '' };
495
475
  }
496
- const paneId = await resolveNudgePaneTarget(stateDir, cwd);
476
+ const paneId = await resolveNudgePaneTarget(stateDir, cwd, payload);
497
477
 
498
478
  let detected = detectStallPattern(lastMessage, config.patterns);
499
479
  let source = 'payload';
@@ -512,6 +492,18 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
512
492
  const signature = await resolveAutoNudgeSignature(stateDir, payload, signatureSourceText);
513
493
  const semanticSignature = normalizeAutoNudgeSignatureText(signatureSourceText);
514
494
 
495
+ if (signature && safeString(nudgeState.lastSignature) === signature) {
496
+ await logTmuxHookEvent(logsDir, {
497
+ timestamp: new Date().toISOString(),
498
+ type: 'auto_nudge_skipped',
499
+ reason: 'already_nudged_for_signature',
500
+ source,
501
+ signature,
502
+ semantic_signature: semanticSignature,
503
+ }).catch(() => {});
504
+ return;
505
+ }
506
+
515
507
  const lastNudgeAtMs = Date.parse(safeString(nudgeState.lastNudgeAt));
516
508
  if (
517
509
  semanticSignature
@@ -532,7 +524,6 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
532
524
  return;
533
525
  }
534
526
 
535
- const sourceName = safeString(payload?.source || '');
536
527
  const isFallbackWatcherSource = sourceName === 'notify-fallback-watcher-stall';
537
528
  if (!isFallbackWatcherSource && config.stallMs > 0) {
538
529
  nudgeState.pendingSignature = signature;
@@ -564,7 +555,7 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
564
555
  }
565
556
 
566
557
  const deepInterviewLockActive = isDeepInterviewAutoApprovalLocked(skillState) && !releaseReason;
567
- if (deepInterviewLockActive && isBlockedAutoApprovalInput(config.response, skillState.input_lock?.blocked_inputs)) {
558
+ if (deepInterviewLockActive) {
568
559
  const blockedMessage = skillState.input_lock?.message || DEEP_INTERVIEW_INPUT_LOCK_MESSAGE;
569
560
  await logTmuxHookEvent(logsDir, {
570
561
  timestamp: new Date().toISOString(),
@@ -573,6 +564,9 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
573
564
  response: config.response,
574
565
  source,
575
566
  blocked_by: 'deep-interview-lock',
567
+ block_kind: isBlockedAutoApprovalInput(config.response, skillState.input_lock?.blocked_inputs)
568
+ ? 'blocked-auto-approval'
569
+ : 'input-lock-active',
576
570
  message: blockedMessage,
577
571
  suppressed: true,
578
572
  }).catch(() => {});
@@ -0,0 +1,324 @@
1
+ import { execFileSync } from 'child_process';
2
+ import { readFileSync } from 'fs';
3
+ import { basename, dirname, resolve as resolvePath } from 'path';
4
+ import { readSessionState, isSessionStale } from '../../hooks/session.js';
5
+ import { runProcess } from './process-runner.js';
6
+ import { safeString } from './utils.js';
7
+
8
+ function sanitizeTmuxToken(value: string): string {
9
+ const cleaned = safeString(value)
10
+ .toLowerCase()
11
+ .replace(/[^a-z0-9]+/g, '-')
12
+ .replace(/^-+|-+$/g, '');
13
+ return cleaned || 'unknown';
14
+ }
15
+
16
+ export function buildExpectedManagedTmuxSessionName(cwd: string, sessionId: string): string {
17
+ const parentPath = dirname(cwd);
18
+ const parentDir = basename(parentPath);
19
+ const dirName = basename(cwd);
20
+ const grandparentPath = dirname(parentPath);
21
+ const grandparentDir = basename(grandparentPath);
22
+ const repoDir = parentDir.endsWith('.omx-worktrees')
23
+ ? parentDir.slice(0, -'.omx-worktrees'.length)
24
+ : parentDir === 'worktrees' && grandparentDir === '.omx'
25
+ ? basename(dirname(grandparentPath))
26
+ : null;
27
+ const dirToken = repoDir
28
+ ? sanitizeTmuxToken(`${repoDir}-${dirName}`)
29
+ : sanitizeTmuxToken(dirName);
30
+ let branchToken = 'detached';
31
+ try {
32
+ const branch = execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
33
+ cwd,
34
+ encoding: 'utf-8',
35
+ stdio: ['ignore', 'pipe', 'ignore'],
36
+ timeout: 2000,
37
+ windowsHide: true,
38
+ }).trim();
39
+ if (branch) branchToken = sanitizeTmuxToken(branch);
40
+ } catch {
41
+ // best effort only
42
+ }
43
+ const sessionToken = sanitizeTmuxToken(sessionId.replace(/^omx-/, ''));
44
+ const name = `omx-${dirToken}-${branchToken}-${sessionToken}`;
45
+ return name.length > 120 ? name.slice(0, 120) : name;
46
+ }
47
+
48
+ export function resolveInvocationSessionId(payload: any): string {
49
+ return safeString(
50
+ payload?.session_id
51
+ || payload?.['session-id']
52
+ || process.env.OMX_SESSION_ID
53
+ || process.env.CODEX_SESSION_ID
54
+ || process.env.SESSION_ID
55
+ || '',
56
+ ).trim();
57
+ }
58
+
59
+ function readCurrentTmuxSessionName(): string {
60
+ if (!process.env.TMUX) return '';
61
+ try {
62
+ return execFileSync('tmux', ['display-message', '-p', '#S'], {
63
+ encoding: 'utf-8',
64
+ stdio: ['ignore', 'pipe', 'ignore'],
65
+ timeout: 2000,
66
+ }).trim();
67
+ } catch {
68
+ return '';
69
+ }
70
+ }
71
+
72
+ function readParentPid(pid: number): number | null {
73
+ if (!Number.isInteger(pid) || pid <= 1) return null;
74
+ try {
75
+ if (process.platform === 'linux') {
76
+ const stat = readFileSync(`/proc/${pid}/stat`, 'utf-8');
77
+ const commandEnd = stat.lastIndexOf(')');
78
+ if (commandEnd === -1) return null;
79
+ const remainder = stat.slice(commandEnd + 1).trim();
80
+ const fields = remainder.split(/\s+/);
81
+ if (fields.length === 0) return null;
82
+ const ppid = Number(fields[1]);
83
+ return Number.isFinite(ppid) && ppid > 0 ? ppid : null;
84
+ }
85
+ const raw = execFileSync('ps', ['-o', 'ppid=', '-p', String(pid)], {
86
+ encoding: 'utf-8',
87
+ timeout: 2000,
88
+ }).trim();
89
+ const ppid = Number(raw);
90
+ return Number.isFinite(ppid) && ppid > 0 ? ppid : null;
91
+ } catch {
92
+ return null;
93
+ }
94
+ }
95
+
96
+ function processHasAncestorPid(targetPid: number, currentPid = process.pid): boolean {
97
+ if (!Number.isInteger(targetPid) || targetPid <= 1) return false;
98
+ let pid = Number.isInteger(currentPid) && currentPid > 1 ? currentPid : process.pid;
99
+ for (let depth = 0; depth < 64 && pid > 1; depth += 1) {
100
+ if (pid === targetPid) return true;
101
+ const parent = readParentPid(pid);
102
+ if (!parent || parent === pid) break;
103
+ pid = parent;
104
+ }
105
+ return false;
106
+ }
107
+
108
+ export async function resolveManagedSessionContext(cwd: string, payload: any, { allowTeamWorker = true } = {}): Promise<any> {
109
+ if (allowTeamWorker && safeString(process.env.OMX_TEAM_WORKER || '').trim() !== '') {
110
+ return {
111
+ managed: true,
112
+ reason: 'team_worker',
113
+ invocationSessionId: '',
114
+ sessionState: null,
115
+ expectedTmuxSessionName: '',
116
+ currentTmuxSessionName: '',
117
+ };
118
+ }
119
+
120
+ const invocationSessionId = resolveInvocationSessionId(payload);
121
+ if (!invocationSessionId) {
122
+ return {
123
+ managed: false,
124
+ reason: 'missing_session_id',
125
+ invocationSessionId: '',
126
+ sessionState: null,
127
+ expectedTmuxSessionName: '',
128
+ currentTmuxSessionName: '',
129
+ };
130
+ }
131
+
132
+ try {
133
+ const sessionState = await readSessionState(cwd);
134
+ if (!sessionState) {
135
+ return { managed: false, reason: 'missing_session_state', invocationSessionId, sessionState: null, expectedTmuxSessionName: '', currentTmuxSessionName: '' };
136
+ }
137
+ if (resolvePath(safeString(sessionState.cwd || cwd)) !== resolvePath(cwd)) {
138
+ return { managed: false, reason: 'cwd_mismatch', invocationSessionId, sessionState, expectedTmuxSessionName: '', currentTmuxSessionName: '' };
139
+ }
140
+ if (safeString(sessionState.session_id).trim() !== invocationSessionId) {
141
+ return { managed: false, reason: 'session_id_mismatch', invocationSessionId, sessionState, expectedTmuxSessionName: '', currentTmuxSessionName: '' };
142
+ }
143
+ if (isSessionStale(sessionState)) {
144
+ return { managed: false, reason: 'stale_session', invocationSessionId, sessionState, expectedTmuxSessionName: '', currentTmuxSessionName: '' };
145
+ }
146
+
147
+ const expectedTmuxSessionName = buildExpectedManagedTmuxSessionName(cwd, invocationSessionId);
148
+ const currentTmuxSessionName = readCurrentTmuxSessionName();
149
+ if (currentTmuxSessionName && currentTmuxSessionName === expectedTmuxSessionName) {
150
+ return {
151
+ managed: true,
152
+ reason: 'tmux_session_match',
153
+ invocationSessionId,
154
+ sessionState,
155
+ expectedTmuxSessionName,
156
+ currentTmuxSessionName,
157
+ };
158
+ }
159
+
160
+ if (processHasAncestorPid(sessionState.pid)) {
161
+ return {
162
+ managed: true,
163
+ reason: currentTmuxSessionName ? 'pid_ancestry_match_tmux_mismatch' : 'pid_ancestry_match',
164
+ invocationSessionId,
165
+ sessionState,
166
+ expectedTmuxSessionName,
167
+ currentTmuxSessionName: '',
168
+ };
169
+ }
170
+
171
+ return {
172
+ managed: false,
173
+ reason: currentTmuxSessionName ? 'tmux_session_mismatch' : 'pid_ancestry_mismatch',
174
+ invocationSessionId,
175
+ sessionState,
176
+ expectedTmuxSessionName,
177
+ currentTmuxSessionName,
178
+ };
179
+ } catch {
180
+ return {
181
+ managed: false,
182
+ reason: 'session_check_failed',
183
+ invocationSessionId,
184
+ sessionState: null,
185
+ expectedTmuxSessionName: '',
186
+ currentTmuxSessionName: '',
187
+ };
188
+ }
189
+ }
190
+
191
+ export async function isManagedOmxSession(cwd: string, payload: any, options: { allowTeamWorker?: boolean } = {}): Promise<boolean> {
192
+ const context = await resolveManagedSessionContext(cwd, payload, options);
193
+ return context.managed === true;
194
+ }
195
+
196
+ export async function verifyManagedPaneTarget(paneId: string, cwd: string, payload: any, { allowTeamWorker = true } = {}): Promise<any> {
197
+ const paneTarget = safeString(paneId).trim();
198
+ if (!paneTarget) {
199
+ return { ok: false, reason: 'missing_pane_target', paneTarget: '' };
200
+ }
201
+
202
+ const managedContext = await resolveManagedSessionContext(cwd, payload, { allowTeamWorker });
203
+ if (!managedContext.managed) {
204
+ return { ok: false, reason: managedContext.reason || 'unmanaged_session', paneTarget, managedContext };
205
+ }
206
+
207
+ if (managedContext.reason === 'team_worker') {
208
+ return { ok: true, reason: 'ok', paneTarget, managedContext };
209
+ }
210
+
211
+ const expectedSession = safeString(managedContext.expectedTmuxSessionName).trim();
212
+ if (!expectedSession) {
213
+ return { ok: false, reason: 'missing_expected_tmux_session', paneTarget, managedContext };
214
+ }
215
+
216
+ try {
217
+ const sessionResult = await runProcess('tmux', ['display-message', '-p', '-t', paneTarget, '#S'], 2000);
218
+ const paneSessionName = safeString(sessionResult.stdout).trim();
219
+ if (!paneSessionName) {
220
+ return { ok: false, reason: 'pane_session_missing', paneTarget, managedContext };
221
+ }
222
+ if (paneSessionName !== expectedSession) {
223
+ return { ok: false, reason: 'pane_not_managed_session', paneTarget, paneSessionName, managedContext };
224
+ }
225
+ return { ok: true, reason: 'ok', paneTarget, paneSessionName, managedContext };
226
+ } catch {
227
+ return { ok: false, reason: 'pane_session_lookup_failed', paneTarget, managedContext };
228
+ }
229
+ }
230
+
231
+
232
+ async function readManagedPaneCommandState(paneTarget: string): Promise<{ currentCommand: string; startCommand: string }> {
233
+ try {
234
+ const [currentResult, startResult] = await Promise.all([
235
+ runProcess('tmux', ['display-message', '-p', '-t', paneTarget, '#{pane_current_command}'], 2000),
236
+ runProcess('tmux', ['display-message', '-p', '-t', paneTarget, '#{pane_start_command}'], 2000),
237
+ ]);
238
+ return {
239
+ currentCommand: safeString(currentResult.stdout).trim().toLowerCase(),
240
+ startCommand: safeString(startResult.stdout).trim().toLowerCase(),
241
+ };
242
+ } catch {
243
+ return { currentCommand: '', startCommand: '' };
244
+ }
245
+ }
246
+
247
+ function paneLooksLikeManagedAgent({ currentCommand, startCommand }: { currentCommand: string; startCommand: string }): boolean {
248
+ if (/\bomx\b.*\bhud\b.*--watch/i.test(startCommand)) return false;
249
+ if (startCommand.includes('codex')) return true;
250
+ return currentCommand === 'codex' || currentCommand === 'node' || currentCommand === 'npx';
251
+ }
252
+ export async function resolveManagedCurrentPane(cwd: string, payload: any, { allowTeamWorker = false } = {}): Promise<string> {
253
+ const paneTarget = safeString(process.env.TMUX_PANE || '').trim();
254
+ if (!paneTarget) return '';
255
+ const verdict = await verifyManagedPaneTarget(paneTarget, cwd, payload, { allowTeamWorker });
256
+ if (!verdict.ok) return '';
257
+ const commandState = await readManagedPaneCommandState(paneTarget);
258
+ return paneLooksLikeManagedAgent(commandState) ? paneTarget : '';
259
+ }
260
+
261
+ export async function resolveManagedSessionPane(cwd: string, payload: any): Promise<string> {
262
+ const managedContext = await resolveManagedSessionContext(cwd, payload, { allowTeamWorker: false });
263
+ if (!managedContext.managed) return '';
264
+ const expectedSession = safeString(managedContext.expectedTmuxSessionName).trim();
265
+ if (!expectedSession) return '';
266
+
267
+ try {
268
+ const panesResult = await runProcess(
269
+ 'tmux',
270
+ ['list-panes', '-s', '-t', expectedSession, '-F', '#{pane_id}\t#{pane_current_command}\t#{pane_start_command}'],
271
+ 2000,
272
+ );
273
+ const panes = safeString(panesResult.stdout)
274
+ .trim()
275
+ .split('\n')
276
+ .filter(Boolean);
277
+ for (const line of panes) {
278
+ const [candidatePaneId, rawCurrentCommand = '', rawStartCommand = ''] = line.split('\t');
279
+ const startCommand = safeString(rawStartCommand).toLowerCase();
280
+ const currentCommand = safeString(rawCurrentCommand).trim().toLowerCase();
281
+ if (!candidatePaneId) continue;
282
+ if (/\bomx\b.*\bhud\b.*--watch/i.test(startCommand)) continue;
283
+ if (startCommand.includes('codex')) return candidatePaneId;
284
+ if (currentCommand === 'codex') return candidatePaneId;
285
+ }
286
+ } catch {
287
+ // best effort only
288
+ }
289
+
290
+ return '';
291
+ }
292
+
293
+ export async function resolveManagedPaneFromAnchor(anchorPane: string, cwd: string, payload: any, { allowTeamWorker = false } = {}): Promise<string> {
294
+ const paneTarget = safeString(anchorPane).trim();
295
+ if (!paneTarget) return '';
296
+ const verdict = await verifyManagedPaneTarget(paneTarget, cwd, payload, { allowTeamWorker });
297
+ if (!verdict.ok) return '';
298
+
299
+ try {
300
+ const sessionResult = await runProcess('tmux', ['display-message', '-p', '-t', paneTarget, '#S'], 2000);
301
+ const sessionName = safeString(sessionResult.stdout).trim();
302
+ if (!sessionName) return paneTarget;
303
+
304
+ const panesResult = await runProcess(
305
+ 'tmux',
306
+ ['list-panes', '-s', '-t', sessionName, '-F', '#{pane_id}\t#{pane_current_command}\t#{pane_start_command}'],
307
+ 2000,
308
+ );
309
+ const panes = safeString(panesResult.stdout).trim().split('\n').filter(Boolean);
310
+ for (const line of panes) {
311
+ const [candidatePaneId, rawCurrentCommand = '', rawStartCommand = ''] = line.split('\t');
312
+ const startCommand = safeString(rawStartCommand).toLowerCase();
313
+ const currentCommand = safeString(rawCurrentCommand).trim().toLowerCase();
314
+ if (!candidatePaneId) continue;
315
+ if (/\bomx\b.*\bhud\b.*--watch/i.test(startCommand)) continue;
316
+ if (startCommand.includes('codex')) return candidatePaneId;
317
+ if (currentCommand === 'codex') return candidatePaneId;
318
+ }
319
+ } catch {
320
+ // best effort only
321
+ }
322
+
323
+ return paneTarget;
324
+ }
@@ -39,6 +39,7 @@ function gitValue(cwd: any, args: string[]): string {
39
39
  encoding: 'utf-8',
40
40
  stdio: ['ignore', 'pipe', 'ignore'],
41
41
  timeout: 2000,
42
+ windowsHide: true,
42
43
  }).trim();
43
44
  } catch {
44
45
  return '';
@@ -83,6 +84,7 @@ export function resolveOperationalSessionName(cwd: any, sessionId = '', sessionN
83
84
  encoding: 'utf-8',
84
85
  stdio: ['ignore', 'pipe', 'ignore'],
85
86
  timeout: 2000,
87
+ windowsHide: true,
86
88
  }).trim();
87
89
  if (tmuxSession) return tmuxSession;
88
90
  } catch {