oh-my-codex 0.20.2 → 0.20.3

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 (367) hide show
  1. package/Cargo.lock +7 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +26 -0
  4. package/crates/omx-runtime/Cargo.toml +1 -0
  5. package/crates/omx-runtime/src/main.rs +25 -2
  6. package/crates/omx-runtime/tests/execution.rs +219 -0
  7. package/crates/omx-runtime-core/src/dispatch.rs +96 -10
  8. package/crates/omx-runtime-core/src/engine.rs +839 -29
  9. package/crates/omx-runtime-core/src/lib.rs +8 -0
  10. package/dist/agents/__tests__/definitions.test.js +81 -0
  11. package/dist/agents/__tests__/definitions.test.js.map +1 -1
  12. package/dist/agents/__tests__/native-config.test.js +25 -0
  13. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  14. package/dist/agents/native-config.d.ts +2 -1
  15. package/dist/agents/native-config.d.ts.map +1 -1
  16. package/dist/agents/native-config.js.map +1 -1
  17. package/dist/auth/hotswap.d.ts +6 -0
  18. package/dist/auth/hotswap.d.ts.map +1 -1
  19. package/dist/auth/hotswap.js +85 -15
  20. package/dist/auth/hotswap.js.map +1 -1
  21. package/dist/autopilot/__tests__/ralplan-gate.test.js +1 -1
  22. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
  23. package/dist/cli/__tests__/auth.test.js +60 -6
  24. package/dist/cli/__tests__/auth.test.js.map +1 -1
  25. package/dist/cli/__tests__/codex-plugin-layout.test.js +80 -0
  26. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  27. package/dist/cli/__tests__/doctor-warning-copy.test.js +103 -1
  28. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  29. package/dist/cli/__tests__/exec.test.js +136 -0
  30. package/dist/cli/__tests__/exec.test.js.map +1 -1
  31. package/dist/cli/__tests__/index.test.js +258 -4
  32. package/dist/cli/__tests__/index.test.js.map +1 -1
  33. package/dist/cli/__tests__/launch-fallback.test.js +287 -1
  34. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  35. package/dist/cli/__tests__/mcp-parity.test.js +4 -1
  36. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  37. package/dist/cli/__tests__/native-hook-claim-journal.test.js +84 -6
  38. package/dist/cli/__tests__/native-hook-claim-journal.test.js.map +1 -1
  39. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +3 -3
  40. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  41. package/dist/cli/__tests__/ralph.test.js +3 -3
  42. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  43. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js +56 -171
  44. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js.map +1 -1
  45. package/dist/cli/__tests__/ralplan.test.js +37 -111
  46. package/dist/cli/__tests__/ralplan.test.js.map +1 -1
  47. package/dist/cli/__tests__/resume.test.js +11 -10
  48. package/dist/cli/__tests__/resume.test.js.map +1 -1
  49. package/dist/cli/__tests__/session-search-help.test.js +25 -1
  50. package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
  51. package/dist/cli/__tests__/setup-install-mode.test.js +42 -4
  52. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  53. package/dist/cli/__tests__/team.test.js +105 -7
  54. package/dist/cli/__tests__/team.test.js.map +1 -1
  55. package/dist/cli/__tests__/uninstall.test.js +46 -0
  56. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  57. package/dist/cli/__tests__/windows-popup-loop-contract.test.js +2 -1
  58. package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
  59. package/dist/cli/doctor.d.ts +3 -0
  60. package/dist/cli/doctor.d.ts.map +1 -1
  61. package/dist/cli/doctor.js +15 -2
  62. package/dist/cli/doctor.js.map +1 -1
  63. package/dist/cli/index.d.ts +4 -1
  64. package/dist/cli/index.d.ts.map +1 -1
  65. package/dist/cli/index.js +138 -38
  66. package/dist/cli/index.js.map +1 -1
  67. package/dist/cli/mcp-parity.js +2 -2
  68. package/dist/cli/mcp-parity.js.map +1 -1
  69. package/dist/cli/native-hook-claim-journal.d.ts +16 -5
  70. package/dist/cli/native-hook-claim-journal.d.ts.map +1 -1
  71. package/dist/cli/native-hook-claim-journal.js +42 -29
  72. package/dist/cli/native-hook-claim-journal.js.map +1 -1
  73. package/dist/cli/ralph.js +2 -2
  74. package/dist/cli/ralph.js.map +1 -1
  75. package/dist/cli/ralplan.d.ts +4 -8
  76. package/dist/cli/ralplan.d.ts.map +1 -1
  77. package/dist/cli/ralplan.js +19 -112
  78. package/dist/cli/ralplan.js.map +1 -1
  79. package/dist/cli/session-search.d.ts +5 -0
  80. package/dist/cli/session-search.d.ts.map +1 -1
  81. package/dist/cli/session-search.js +87 -0
  82. package/dist/cli/session-search.js.map +1 -1
  83. package/dist/cli/setup.d.ts +5 -0
  84. package/dist/cli/setup.d.ts.map +1 -1
  85. package/dist/cli/setup.js +80 -31
  86. package/dist/cli/setup.js.map +1 -1
  87. package/dist/cli/uninstall.d.ts +9 -0
  88. package/dist/cli/uninstall.d.ts.map +1 -1
  89. package/dist/cli/uninstall.js +61 -19
  90. package/dist/cli/uninstall.js.map +1 -1
  91. package/dist/compat/__tests__/rust-runtime-compat.test.js +1 -1
  92. package/dist/config/__tests__/generator-idempotent.test.js +330 -1
  93. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  94. package/dist/config/__tests__/generator-notify.test.js +2 -2
  95. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  96. package/dist/config/__tests__/models.test.js +67 -3
  97. package/dist/config/__tests__/models.test.js.map +1 -1
  98. package/dist/config/generator.d.ts +4 -20
  99. package/dist/config/generator.d.ts.map +1 -1
  100. package/dist/config/generator.js +499 -119
  101. package/dist/config/generator.js.map +1 -1
  102. package/dist/config/models.d.ts +12 -2
  103. package/dist/config/models.d.ts.map +1 -1
  104. package/dist/config/models.js +15 -1
  105. package/dist/config/models.js.map +1 -1
  106. package/dist/hooks/__tests__/agents-overlay.test.js +2 -2
  107. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  108. package/dist/hooks/__tests__/keyword-detector.test.js +4 -4
  109. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  110. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +238 -543
  111. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  112. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +38 -4
  113. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  114. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +96 -9
  115. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  116. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +531 -12
  117. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  118. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +155 -23
  119. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  120. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +482 -32
  121. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  122. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +68 -1
  123. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  124. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +7 -1
  125. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
  126. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +43 -4
  127. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  128. package/dist/hooks/__tests__/session.test.js +348 -2
  129. package/dist/hooks/__tests__/session.test.js.map +1 -1
  130. package/dist/hooks/agents-overlay.js +1 -1
  131. package/dist/hooks/session.d.ts +26 -2
  132. package/dist/hooks/session.d.ts.map +1 -1
  133. package/dist/hooks/session.js +169 -13
  134. package/dist/hooks/session.js.map +1 -1
  135. package/dist/leader/__tests__/contract.test.js +57 -7
  136. package/dist/leader/__tests__/contract.test.js.map +1 -1
  137. package/dist/leader/contract.d.ts +16 -0
  138. package/dist/leader/contract.d.ts.map +1 -1
  139. package/dist/leader/contract.js +79 -4
  140. package/dist/leader/contract.js.map +1 -1
  141. package/dist/mcp/__tests__/code-intel-server.test.js +31 -3
  142. package/dist/mcp/__tests__/code-intel-server.test.js.map +1 -1
  143. package/dist/mcp/__tests__/wiki-server.test.js +18 -0
  144. package/dist/mcp/__tests__/wiki-server.test.js.map +1 -1
  145. package/dist/mcp/code-intel-server.d.ts.map +1 -1
  146. package/dist/mcp/code-intel-server.js +13 -3
  147. package/dist/mcp/code-intel-server.js.map +1 -1
  148. package/dist/mcp/wiki-server.d.ts +11 -0
  149. package/dist/mcp/wiki-server.d.ts.map +1 -1
  150. package/dist/mcp/wiki-server.js +9 -2
  151. package/dist/mcp/wiki-server.js.map +1 -1
  152. package/dist/modes/__tests__/base-tmux-pane.test.js +4 -4
  153. package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
  154. package/dist/notifications/temp-contract.d.ts.map +1 -1
  155. package/dist/notifications/temp-contract.js +4 -0
  156. package/dist/notifications/temp-contract.js.map +1 -1
  157. package/dist/pipeline/__tests__/orchestrator.test.js +4 -4
  158. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  159. package/dist/pipeline/__tests__/stages.test.js +8 -8
  160. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  161. package/dist/ralplan/__tests__/consensus-gate.test.js +134 -10
  162. package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
  163. package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts +2 -0
  164. package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts.map +1 -0
  165. package/dist/ralplan/__tests__/documented-leader-preflight.test.js +46 -0
  166. package/dist/ralplan/__tests__/documented-leader-preflight.test.js.map +1 -0
  167. package/dist/ralplan/__tests__/runtime.test.js +8 -4
  168. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  169. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  170. package/dist/ralplan/consensus-gate.js +58 -50
  171. package/dist/ralplan/consensus-gate.js.map +1 -1
  172. package/dist/ralplan/documented-leader-preflight.d.ts +34 -0
  173. package/dist/ralplan/documented-leader-preflight.d.ts.map +1 -0
  174. package/dist/ralplan/documented-leader-preflight.js +58 -0
  175. package/dist/ralplan/documented-leader-preflight.js.map +1 -0
  176. package/dist/ralplan/runtime.d.ts +1 -0
  177. package/dist/ralplan/runtime.d.ts.map +1 -1
  178. package/dist/ralplan/runtime.js +11 -7
  179. package/dist/ralplan/runtime.js.map +1 -1
  180. package/dist/runtime/__tests__/bridge.test.js +42 -1
  181. package/dist/runtime/__tests__/bridge.test.js.map +1 -1
  182. package/dist/runtime/bridge.d.ts +12 -0
  183. package/dist/runtime/bridge.d.ts.map +1 -1
  184. package/dist/runtime/bridge.js +52 -1
  185. package/dist/runtime/bridge.js.map +1 -1
  186. package/dist/scripts/__tests__/codex-native-hook.test.js +2620 -487
  187. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  188. package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts +2 -0
  189. package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts.map +1 -0
  190. package/dist/scripts/__tests__/reasoning-artifact-contract.test.js +108 -0
  191. package/dist/scripts/__tests__/reasoning-artifact-contract.test.js.map +1 -0
  192. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js +46 -173
  193. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js.map +1 -1
  194. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js +39 -159
  195. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js.map +1 -1
  196. package/dist/scripts/__tests__/smoke-packed-install.test.js +254 -2
  197. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  198. package/dist/scripts/codex-native-hook.d.ts +4 -1
  199. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  200. package/dist/scripts/codex-native-hook.js +11013 -1153
  201. package/dist/scripts/codex-native-hook.js.map +1 -1
  202. package/dist/scripts/notify-fallback-watcher.js +82 -108
  203. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  204. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  205. package/dist/scripts/notify-hook/auto-nudge.js +74 -3
  206. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  207. package/dist/scripts/notify-hook/team-dispatch.d.ts +9 -0
  208. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  209. package/dist/scripts/notify-hook/team-dispatch.js +234 -79
  210. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  211. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  212. package/dist/scripts/notify-hook/team-leader-nudge.js +39 -13
  213. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  214. package/dist/scripts/notify-hook/team-tmux-guard.d.ts +12 -4
  215. package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
  216. package/dist/scripts/notify-hook/team-tmux-guard.js +365 -44
  217. package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
  218. package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
  219. package/dist/scripts/notify-hook/team-worker-stop.js +19 -18
  220. package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
  221. package/dist/scripts/notify-hook/team-worker.d.ts +3 -0
  222. package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
  223. package/dist/scripts/notify-hook/team-worker.js +37 -28
  224. package/dist/scripts/notify-hook/team-worker.js.map +1 -1
  225. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  226. package/dist/scripts/notify-hook/tmux-injection.js +28 -2
  227. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  228. package/dist/scripts/notify-hook.js +1 -0
  229. package/dist/scripts/notify-hook.js.map +1 -1
  230. package/dist/scripts/smoke-packed-install.d.ts +48 -3
  231. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  232. package/dist/scripts/smoke-packed-install.js +2796 -13
  233. package/dist/scripts/smoke-packed-install.js.map +1 -1
  234. package/dist/state/__tests__/mode-state-context.test.js +54 -0
  235. package/dist/state/__tests__/mode-state-context.test.js.map +1 -1
  236. package/dist/state/__tests__/operations.test.js +2 -0
  237. package/dist/state/__tests__/operations.test.js.map +1 -1
  238. package/dist/state/mode-state-context.d.ts +5 -0
  239. package/dist/state/mode-state-context.d.ts.map +1 -1
  240. package/dist/state/mode-state-context.js +59 -0
  241. package/dist/state/mode-state-context.js.map +1 -1
  242. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js +24 -245
  243. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js.map +1 -1
  244. package/dist/subagents/tracker.d.ts +1 -82
  245. package/dist/subagents/tracker.d.ts.map +1 -1
  246. package/dist/subagents/tracker.js +3 -275
  247. package/dist/subagents/tracker.js.map +1 -1
  248. package/dist/team/__tests__/api-interop.test.js +91 -1
  249. package/dist/team/__tests__/api-interop.test.js.map +1 -1
  250. package/dist/team/__tests__/delivery-e2e-smoke.test.js +52 -10
  251. package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
  252. package/dist/team/__tests__/idle-nudge.test.js +45 -15
  253. package/dist/team/__tests__/idle-nudge.test.js.map +1 -1
  254. package/dist/team/__tests__/mcp-comm.test.js +93 -27
  255. package/dist/team/__tests__/mcp-comm.test.js.map +1 -1
  256. package/dist/team/__tests__/model-contract.test.js +110 -3
  257. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  258. package/dist/team/__tests__/runtime-cli.test.js +9 -0
  259. package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
  260. package/dist/team/__tests__/runtime.test.js +3204 -335
  261. package/dist/team/__tests__/runtime.test.js.map +1 -1
  262. package/dist/team/__tests__/scaling.test.js +2359 -142
  263. package/dist/team/__tests__/scaling.test.js.map +1 -1
  264. package/dist/team/__tests__/state.test.js +804 -36
  265. package/dist/team/__tests__/state.test.js.map +1 -1
  266. package/dist/team/__tests__/team-ops-contract.test.js +6 -0
  267. package/dist/team/__tests__/team-ops-contract.test.js.map +1 -1
  268. package/dist/team/__tests__/tmux-session.test.js +3049 -784
  269. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  270. package/dist/team/__tests__/worker-runtime-identity.test.js +38 -2
  271. package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
  272. package/dist/team/__tests__/worktree.test.js +6 -0
  273. package/dist/team/__tests__/worktree.test.js.map +1 -1
  274. package/dist/team/api-interop.d.ts.map +1 -1
  275. package/dist/team/api-interop.js +15 -9
  276. package/dist/team/api-interop.js.map +1 -1
  277. package/dist/team/exact-pane.d.ts +23 -0
  278. package/dist/team/exact-pane.d.ts.map +1 -0
  279. package/dist/team/exact-pane.js +133 -0
  280. package/dist/team/exact-pane.js.map +1 -0
  281. package/dist/team/idle-nudge.d.ts +12 -1
  282. package/dist/team/idle-nudge.d.ts.map +1 -1
  283. package/dist/team/idle-nudge.js +41 -14
  284. package/dist/team/idle-nudge.js.map +1 -1
  285. package/dist/team/model-contract.d.ts +2 -5
  286. package/dist/team/model-contract.d.ts.map +1 -1
  287. package/dist/team/model-contract.js +59 -17
  288. package/dist/team/model-contract.js.map +1 -1
  289. package/dist/team/runtime.d.ts +1 -0
  290. package/dist/team/runtime.d.ts.map +1 -1
  291. package/dist/team/runtime.js +1244 -180
  292. package/dist/team/runtime.js.map +1 -1
  293. package/dist/team/scaling.d.ts +13 -1
  294. package/dist/team/scaling.d.ts.map +1 -1
  295. package/dist/team/scaling.js +1430 -384
  296. package/dist/team/scaling.js.map +1 -1
  297. package/dist/team/state/dispatch.d.ts.map +1 -1
  298. package/dist/team/state/dispatch.js +26 -8
  299. package/dist/team/state/dispatch.js.map +1 -1
  300. package/dist/team/state/locks.d.ts.map +1 -1
  301. package/dist/team/state/locks.js +61 -13
  302. package/dist/team/state/locks.js.map +1 -1
  303. package/dist/team/state/tasks.d.ts.map +1 -1
  304. package/dist/team/state/tasks.js +4 -3
  305. package/dist/team/state/tasks.js.map +1 -1
  306. package/dist/team/state.d.ts +67 -1
  307. package/dist/team/state.d.ts.map +1 -1
  308. package/dist/team/state.js +636 -129
  309. package/dist/team/state.js.map +1 -1
  310. package/dist/team/team-ops.d.ts +5 -0
  311. package/dist/team/team-ops.d.ts.map +1 -1
  312. package/dist/team/team-ops.js +4 -0
  313. package/dist/team/team-ops.js.map +1 -1
  314. package/dist/team/tmux-session.d.ts +123 -23
  315. package/dist/team/tmux-session.d.ts.map +1 -1
  316. package/dist/team/tmux-session.js +1250 -291
  317. package/dist/team/tmux-session.js.map +1 -1
  318. package/dist/team/worktree.d.ts.map +1 -1
  319. package/dist/team/worktree.js +4 -0
  320. package/dist/team/worktree.js.map +1 -1
  321. package/dist/utils/__tests__/file-durability.test.d.ts +2 -0
  322. package/dist/utils/__tests__/file-durability.test.d.ts.map +1 -0
  323. package/dist/utils/__tests__/file-durability.test.js +57 -0
  324. package/dist/utils/__tests__/file-durability.test.js.map +1 -0
  325. package/dist/utils/file-durability.d.ts +15 -0
  326. package/dist/utils/file-durability.d.ts.map +1 -0
  327. package/dist/utils/file-durability.js +37 -0
  328. package/dist/utils/file-durability.js.map +1 -0
  329. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +3 -0
  330. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
  331. package/dist/wiki/lint.d.ts +4 -1
  332. package/dist/wiki/lint.d.ts.map +1 -1
  333. package/dist/wiki/lint.js +3 -2
  334. package/dist/wiki/lint.js.map +1 -1
  335. package/package.json +1 -1
  336. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  337. package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +24 -0
  338. package/plugins/oh-my-codex/skills/ralph/SKILL.md +6 -4
  339. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +3 -2
  340. package/plugins/oh-my-codex/skills/team/SKILL.md +13 -9
  341. package/skills/ralph/SKILL.md +6 -4
  342. package/skills/ralplan/SKILL.md +3 -2
  343. package/skills/team/SKILL.md +13 -9
  344. package/src/scripts/__tests__/codex-native-hook.test.ts +2826 -497
  345. package/src/scripts/__tests__/reasoning-artifact-contract.test.ts +129 -0
  346. package/src/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.ts +48 -185
  347. package/src/scripts/__tests__/role-intent-durable-recovery-3181.test.ts +44 -177
  348. package/src/scripts/__tests__/smoke-packed-install.test.ts +320 -2
  349. package/src/scripts/codex-native-hook.ts +10715 -1069
  350. package/src/scripts/notify-fallback-watcher.ts +84 -115
  351. package/src/scripts/notify-hook/auto-nudge.ts +71 -3
  352. package/src/scripts/notify-hook/team-dispatch.ts +242 -75
  353. package/src/scripts/notify-hook/team-leader-nudge.ts +40 -18
  354. package/src/scripts/notify-hook/team-tmux-guard.ts +366 -39
  355. package/src/scripts/notify-hook/team-worker-stop.ts +20 -16
  356. package/src/scripts/notify-hook/team-worker.ts +39 -26
  357. package/src/scripts/notify-hook/tmux-injection.ts +28 -2
  358. package/src/scripts/notify-hook.ts +1 -0
  359. package/src/scripts/smoke-packed-install.ts +3196 -14
  360. package/dist/subagents/__tests__/adapted-role-binding.test.d.ts +0 -2
  361. package/dist/subagents/__tests__/adapted-role-binding.test.d.ts.map +0 -1
  362. package/dist/subagents/__tests__/adapted-role-binding.test.js +0 -988
  363. package/dist/subagents/__tests__/adapted-role-binding.test.js.map +0 -1
  364. package/dist/subagents/adapted-role-binding.d.ts +0 -17
  365. package/dist/subagents/adapted-role-binding.d.ts.map +0 -1
  366. package/dist/subagents/adapted-role-binding.js +0 -75
  367. package/dist/subagents/adapted-role-binding.js.map +0 -1
@@ -9,6 +9,158 @@ import {
9
9
  paneHasActiveTask,
10
10
  paneLooksReady,
11
11
  } from '../tmux-hook-engine.js';
12
+ import { readExactPaneProof } from '../../team/exact-pane.js';
13
+
14
+ export const EXACT_PANE_UNAVAILABLE_REASON = 'exact_pane_unavailable';
15
+ const EXACT_PANE_ID_RE = /^%\d+$/;
16
+
17
+ function explicitPaneIdentity(value: any): { provided: boolean; paneId: string } {
18
+ const paneId = safeString(value).trim();
19
+ return { provided: paneId !== '', paneId: EXACT_PANE_ID_RE.test(paneId) ? paneId : '' };
20
+ }
21
+
22
+ export function normalizeExactPaneId(value: any): string {
23
+ return explicitPaneIdentity(value).paneId;
24
+ }
25
+
26
+ function exactPaneBindingFailure(target: string, exactPaneId: any): any | null {
27
+ const identity = explicitPaneIdentity(exactPaneId);
28
+ const targetIsExactPane = EXACT_PANE_ID_RE.test(target);
29
+ if (targetIsExactPane && !identity.provided) {
30
+ return {
31
+ ok: false,
32
+ reason: EXACT_PANE_UNAVAILABLE_REASON,
33
+ paneId: target,
34
+ proof: { status: 'unavailable', paneId: target, reason: 'missing_exact_pane_id' },
35
+ };
36
+ }
37
+ if (identity.provided && !identity.paneId) {
38
+ return {
39
+ ok: false,
40
+ reason: EXACT_PANE_UNAVAILABLE_REASON,
41
+ paneId: '',
42
+ proof: { status: 'unavailable', paneId: safeString(exactPaneId).trim(), reason: 'invalid_pane_id' },
43
+ };
44
+ }
45
+ if (identity.paneId && identity.paneId !== target) {
46
+ return {
47
+ ok: false,
48
+ reason: EXACT_PANE_UNAVAILABLE_REASON,
49
+ paneId: identity.paneId,
50
+ proof: { status: 'unavailable', paneId: identity.paneId, reason: 'pane_target_mismatch' },
51
+ };
52
+ }
53
+ return null;
54
+ }
55
+
56
+ export async function verifyExactPaneLive(exactPaneId: any, expectedPanePid?: number): Promise<any> {
57
+ const identity = explicitPaneIdentity(exactPaneId);
58
+ if (!identity.provided) return { ok: true, paneId: '', proof: null };
59
+ if (!identity.paneId) {
60
+ return {
61
+ ok: false,
62
+ reason: EXACT_PANE_UNAVAILABLE_REASON,
63
+ paneId: '',
64
+ proof: {
65
+ status: 'unavailable',
66
+ paneId: safeString(exactPaneId).trim(),
67
+ reason: 'invalid_pane_id',
68
+ },
69
+ };
70
+ }
71
+
72
+ try {
73
+ const proof = await readExactPaneProof(identity.paneId);
74
+ if (proof.status === 'live' && proof.paneId === identity.paneId) {
75
+ if (typeof expectedPanePid === 'number' && proof.pid !== expectedPanePid) {
76
+ return {
77
+ ok: false,
78
+ reason: EXACT_PANE_UNAVAILABLE_REASON,
79
+ paneId: identity.paneId,
80
+ proof: { ...proof, status: 'unavailable', reason: 'pane_pid_changed', expectedPid: expectedPanePid },
81
+ };
82
+ }
83
+ return { ok: true, paneId: identity.paneId, proof };
84
+ }
85
+ return { ok: false, reason: EXACT_PANE_UNAVAILABLE_REASON, paneId: identity.paneId, proof };
86
+ } catch (error) {
87
+ return {
88
+ ok: false,
89
+ reason: EXACT_PANE_UNAVAILABLE_REASON,
90
+ paneId: identity.paneId,
91
+ proof: {
92
+ status: 'unavailable',
93
+ paneId: identity.paneId,
94
+ reason: 'query_failed',
95
+ detail: error instanceof Error ? error.message : safeString(error),
96
+ },
97
+ };
98
+ }
99
+ }
100
+
101
+ export async function verifyExactPaneOwnerLive(exactPaneId: any, expectedPanePid: number | undefined, expectedPaneOwnerId: any): Promise<any> {
102
+ const expectedOwner = safeString(expectedPaneOwnerId).trim();
103
+ if (!expectedOwner || !Number.isInteger(expectedPanePid) || Number(expectedPanePid) <= 0) {
104
+ return {
105
+ ok: false,
106
+ reason: EXACT_PANE_UNAVAILABLE_REASON,
107
+ paneId: normalizeExactPaneId(exactPaneId),
108
+ proof: {
109
+ status: 'unavailable',
110
+ paneId: normalizeExactPaneId(exactPaneId),
111
+ reason: !expectedOwner ? 'missing_expected_pane_owner' : 'missing_expected_pane_pid',
112
+ },
113
+ };
114
+ }
115
+ const beforeOwner = await verifyExactPaneLive(exactPaneId, expectedPanePid);
116
+ if (!beforeOwner.ok) return beforeOwner;
117
+ try {
118
+ const ownerResult = await runProcess(
119
+ 'tmux',
120
+ ['show-option', '-qv', '-p', '-t', beforeOwner.paneId, '@omx_team_pane_owner_id'],
121
+ 3000,
122
+ );
123
+ const actualOwner = safeString(ownerResult.stdout).trim();
124
+ if (actualOwner !== expectedOwner) {
125
+ return {
126
+ ok: false,
127
+ reason: EXACT_PANE_UNAVAILABLE_REASON,
128
+ paneId: beforeOwner.paneId,
129
+ proof: {
130
+ status: 'unavailable',
131
+ paneId: beforeOwner.paneId,
132
+ reason: actualOwner ? 'pane_owner_changed' : 'pane_owner_unavailable',
133
+ expectedOwner,
134
+ actualOwner: actualOwner || undefined,
135
+ },
136
+ };
137
+ }
138
+ } catch (error) {
139
+ return {
140
+ ok: false,
141
+ reason: EXACT_PANE_UNAVAILABLE_REASON,
142
+ paneId: beforeOwner.paneId,
143
+ proof: {
144
+ status: 'unavailable',
145
+ paneId: beforeOwner.paneId,
146
+ reason: 'pane_owner_unavailable',
147
+ detail: error instanceof Error ? error.message : safeString(error),
148
+ },
149
+ };
150
+ }
151
+ return verifyExactPaneLive(exactPaneId, expectedPanePid);
152
+ }
153
+
154
+ function exactPaneUnavailableResult(target: string, paneProof: any, extra: any = {}): any {
155
+ return {
156
+ ok: false,
157
+ sent: false,
158
+ reason: EXACT_PANE_UNAVAILABLE_REASON,
159
+ paneTarget: target,
160
+ exactPaneProof: paneProof.proof || null,
161
+ ...extra,
162
+ };
163
+ }
12
164
 
13
165
  export const PANE_READINESS_UNVERIFIED_REASON = 'pane_readiness_unverified';
14
166
  let nextTmuxBufferId = 0;
@@ -46,6 +198,10 @@ export async function evaluatePaneInjectionReadiness(paneTarget: any, {
46
198
  requireIdle = true,
47
199
  requireObservableState = false,
48
200
  requireCaptureEvidence = undefined,
201
+ exactPaneId = undefined,
202
+ expectedPanePid = undefined,
203
+ expectedPaneOwnerId = undefined,
204
+ expectedHudPaneId = undefined,
49
205
  } = {}): Promise<any> {
50
206
  const normalizedRequireObservableState = typeof requireCaptureEvidence === 'boolean' ? requireCaptureEvidence : requireObservableState;
51
207
  const target = safeString(paneTarget).trim();
@@ -59,24 +215,26 @@ export async function evaluatePaneInjectionReadiness(paneTarget: any, {
59
215
  paneCapture: '',
60
216
  };
61
217
  }
62
- if (skipIfScrolling) {
63
- try {
64
- const modeResult = await runProcess('tmux', buildPaneInModeArgv(target), 3000);
65
- if (safeString(modeResult.stdout).trim() === '1') {
66
- return {
67
- ok: false,
68
- sent: false,
69
- reason: 'scroll_active',
70
- paneTarget: target,
71
- paneCurrentCommand: '',
72
- paneCapture: '',
73
- };
74
- }
75
- } catch {
76
- // Non-fatal: continue with remaining preflight checks.
77
- }
78
- }
218
+ const bindingFailure = exactPaneBindingFailure(target, exactPaneId);
219
+ if (bindingFailure) return exactPaneUnavailableResult(target, bindingFailure);
79
220
 
221
+ const exactPaneIdentity = safeString(exactPaneId).trim();
222
+ const exactPaneIdentityProvided = explicitPaneIdentity(exactPaneId).provided;
223
+ const expectedOwner = safeString(expectedPaneOwnerId).trim();
224
+ const expectedHudPane = normalizeExactPaneId(expectedHudPaneId);
225
+ const requiresTeamReadAuthority = exactPaneIdentityProvided && (
226
+ expectedPanePid !== undefined || expectedOwner || expectedHudPane
227
+ );
228
+ let exactPaneProof: any = null;
229
+ let pinnedPanePid = typeof expectedPanePid === 'number' ? expectedPanePid : undefined;
230
+ const verifyExplicitPane = async () => {
231
+ const paneProof = requiresTeamReadAuthority
232
+ ? await verifyExactPaneOwnerLive(exactPaneIdentity, pinnedPanePid, expectedOwner)
233
+ : await verifyExactPaneLive(exactPaneIdentity, pinnedPanePid);
234
+ exactPaneProof = paneProof.proof || null;
235
+ if (paneProof.ok && typeof paneProof.proof?.pid === 'number') pinnedPanePid ??= paneProof.proof.pid;
236
+ return paneProof;
237
+ };
80
238
  let paneCurrentCommand = '';
81
239
  let paneRunningShell = false;
82
240
  const buildReadinessResult = (ok: boolean, reason: string, paneCapture: string, readinessEvidence: string) => ({
@@ -87,18 +245,91 @@ export async function evaluatePaneInjectionReadiness(paneTarget: any, {
87
245
  paneCurrentCommand,
88
246
  paneCapture,
89
247
  readinessEvidence,
248
+ exactPaneProof,
90
249
  });
91
- try {
92
- const result = await runProcess('tmux', buildPaneCurrentCommandArgv(target), 3000);
93
- paneCurrentCommand = safeString(result.stdout).trim();
94
- paneRunningShell = requireRunningAgent && isPaneRunningShell(paneCurrentCommand);
95
- } catch {
96
- paneCurrentCommand = '';
250
+ const exactPaneFailure = (paneProof: any, paneCapture = '') => exactPaneUnavailableResult(target, paneProof, {
251
+ paneCurrentCommand,
252
+ paneCapture,
253
+ readinessEvidence: 'exact_pane_unavailable',
254
+ });
255
+ if (expectedHudPane && expectedHudPane === exactPaneIdentity) {
256
+ return buildReadinessResult(false, 'hud_pane_target', '', 'hud_pane_rejected');
97
257
  }
98
258
 
99
- try {
100
- const capture = await runProcess('tmux', buildCapturePaneArgv(target, captureLines), 3000);
101
- const paneCapture = safeString(capture.stdout);
259
+ const readExplicitPane = async (argv: string[]) => {
260
+ const beforeRead = await verifyExplicitPane();
261
+ if (!beforeRead.ok) return { ok: false, paneProof: beforeRead, result: null };
262
+ try {
263
+ const result = await runProcess('tmux', argv, 3000);
264
+ if (requiresTeamReadAuthority) {
265
+ const afterRead = await verifyExplicitPane();
266
+ if (!afterRead.ok) return { ok: false, paneProof: afterRead, result: null };
267
+ }
268
+ return { ok: true, paneProof: beforeRead, result };
269
+ } catch (error) {
270
+ return { ok: false, paneProof: null, result: null, error };
271
+ }
272
+ };
273
+
274
+ if (skipIfScrolling) {
275
+ const read = await readExplicitPane(buildPaneInModeArgv(target));
276
+ if (!read.ok) {
277
+ if (read.paneProof) return exactPaneFailure(read.paneProof);
278
+ // Non-fatal: continue with remaining preflight checks.
279
+ } else if (safeString(read.result?.stdout).trim() === '1') {
280
+ return {
281
+ ok: false,
282
+ sent: false,
283
+ reason: 'scroll_active',
284
+ paneTarget: target,
285
+ paneCurrentCommand: '',
286
+ paneCapture: '',
287
+ exactPaneProof,
288
+ };
289
+ }
290
+ }
291
+
292
+ if (exactPaneIdentityProvided) {
293
+ const read = await readExplicitPane(['display-message', '-p', '-t', target, '#{pane_start_command}']);
294
+ if (!read.ok) {
295
+ if (read.paneProof) return exactPaneFailure(read.paneProof);
296
+ return buildReadinessResult(false, PANE_READINESS_UNVERIFIED_REASON, '', 'start_command_failed');
297
+ }
298
+ if (/\bomx\b.*\bhud\b.*--watch/i.test(safeString(read.result?.stdout))) {
299
+ return buildReadinessResult(false, 'hud_pane_target', '', 'hud_pane_rejected');
300
+ }
301
+ }
302
+
303
+ {
304
+ const read = await readExplicitPane(buildPaneCurrentCommandArgv(target));
305
+ if (!read.ok) {
306
+ if (read.paneProof) return exactPaneFailure(read.paneProof);
307
+ if (exactPaneIdentityProvided) {
308
+ return buildReadinessResult(false, PANE_READINESS_UNVERIFIED_REASON, '', 'command_failed');
309
+ }
310
+ paneCurrentCommand = '';
311
+ } else {
312
+ paneCurrentCommand = safeString(read.result?.stdout).trim();
313
+ paneRunningShell = requireRunningAgent && isPaneRunningShell(paneCurrentCommand);
314
+ }
315
+ }
316
+
317
+ {
318
+ const read = await readExplicitPane(buildCapturePaneArgv(target, captureLines));
319
+ if (!read.ok) {
320
+ if (read.paneProof) return exactPaneFailure(read.paneProof);
321
+ if (exactPaneIdentityProvided) {
322
+ return buildReadinessResult(false, PANE_READINESS_UNVERIFIED_REASON, '', 'capture_failed');
323
+ }
324
+ if (paneRunningShell) {
325
+ return buildReadinessResult(false, 'pane_running_shell', '', 'capture_failed');
326
+ }
327
+ if (normalizedRequireObservableState) {
328
+ return buildReadinessResult(false, PANE_READINESS_UNVERIFIED_REASON, '', 'capture_failed');
329
+ }
330
+ return buildReadinessResult(true, 'ok', '', paneCurrentCommand ? 'command_only' : 'none');
331
+ }
332
+ const paneCapture = safeString(read.result?.stdout);
102
333
  const hasCaptureEvidence = paneCapture.trim() !== '';
103
334
  if (hasCaptureEvidence) {
104
335
  const paneShowsLiveAgent = paneLooksReady(paneCapture) || paneHasActiveTask(paneCapture);
@@ -122,6 +353,7 @@ export async function evaluatePaneInjectionReadiness(paneTarget: any, {
122
353
  paneTarget: target,
123
354
  paneCurrentCommand,
124
355
  paneCapture,
356
+ exactPaneProof,
125
357
  };
126
358
  }
127
359
  }
@@ -133,20 +365,13 @@ export async function evaluatePaneInjectionReadiness(paneTarget: any, {
133
365
  paneTarget: target,
134
366
  paneCurrentCommand,
135
367
  paneCapture,
368
+ exactPaneProof,
136
369
  };
137
370
  }
138
371
  if (normalizedRequireObservableState && !hasCaptureEvidence && !paneCurrentCommand) {
139
372
  return buildReadinessResult(false, PANE_READINESS_UNVERIFIED_REASON, paneCapture, 'capture_empty');
140
373
  }
141
374
  return buildReadinessResult(true, 'ok', paneCapture, hasCaptureEvidence ? 'captured' : (paneCurrentCommand ? 'command_only' : 'none'));
142
- } catch {
143
- if (paneRunningShell) {
144
- return buildReadinessResult(false, 'pane_running_shell', '', 'capture_failed');
145
- }
146
- if (normalizedRequireObservableState) {
147
- return buildReadinessResult(false, PANE_READINESS_UNVERIFIED_REASON, '', 'capture_failed');
148
- }
149
- return buildReadinessResult(true, 'ok', '', paneCurrentCommand ? 'command_only' : 'none');
150
375
  }
151
376
  }
152
377
 
@@ -157,11 +382,59 @@ export async function sendPaneInput({
157
382
  submitDelayMs = 0,
158
383
  typePrompt = true,
159
384
  queueFirstSubmit = false,
385
+ exactPaneId = undefined,
386
+ expectedPanePid = undefined,
387
+ expectedPaneOwnerId = undefined,
388
+ expectedHudPaneId = undefined,
160
389
  }: any): Promise<any> {
161
390
  const target = safeString(paneTarget).trim();
162
391
  if (!target) {
163
392
  return { ok: false, sent: false, reason: 'missing_pane_target', paneTarget: '' };
164
393
  }
394
+ const bindingFailure = exactPaneBindingFailure(target, exactPaneId);
395
+ if (bindingFailure) return exactPaneUnavailableResult(target, bindingFailure);
396
+
397
+ const exactPaneIdentity = safeString(exactPaneId).trim();
398
+ let exactPaneProof: any = null;
399
+ let pinnedPanePid = typeof expectedPanePid === 'number' ? expectedPanePid : undefined;
400
+ const verifyExplicitPane = async () => {
401
+ const paneProof = await verifyExactPaneLive(exactPaneIdentity, pinnedPanePid);
402
+ exactPaneProof = paneProof.proof || null;
403
+ if (paneProof.ok && typeof paneProof.proof?.pid === 'number') pinnedPanePid ??= paneProof.proof.pid;
404
+ return paneProof;
405
+ };
406
+ const expectedOwner = safeString(expectedPaneOwnerId).trim();
407
+ const expectedHudPane = normalizeExactPaneId(expectedHudPaneId);
408
+ if (expectedHudPane && expectedHudPane === exactPaneIdentity) {
409
+ return { ok: false, sent: false, reason: 'hud_pane_target', paneTarget: target };
410
+ }
411
+ const verifyForEffect = async () => {
412
+ if (expectedOwner) {
413
+ const paneProof = await verifyExactPaneOwnerLive(exactPaneIdentity, pinnedPanePid, expectedOwner);
414
+ exactPaneProof = paneProof.proof || null;
415
+ return paneProof;
416
+ }
417
+ return verifyExplicitPane();
418
+ };
419
+ const initialProof = await verifyForEffect();
420
+ if (!initialProof.ok) return exactPaneUnavailableResult(target, initialProof);
421
+ try {
422
+ const startCommandResult = await runProcess('tmux', ['display-message', '-p', '-t', target, '#{pane_start_command}'], 3000);
423
+ if (/\bomx\b.*\bhud\b.*--watch/i.test(safeString(startCommandResult.stdout))) {
424
+ return { ok: false, sent: false, reason: 'hud_pane_target', paneTarget: target, exactPaneProof };
425
+ }
426
+ } catch {
427
+ return {
428
+ ok: false,
429
+ sent: false,
430
+ reason: PANE_READINESS_UNVERIFIED_REASON,
431
+ paneTarget: target,
432
+ exactPaneProof,
433
+ readinessEvidence: 'start_command_failed',
434
+ };
435
+ }
436
+ const postHudProof = await verifyForEffect();
437
+ if (!postHudProof.ok) return exactPaneUnavailableResult(target, postHudProof);
165
438
 
166
439
  const normalizedSubmitKeyPresses = Number.isFinite(submitKeyPresses)
167
440
  ? Math.max(0, Math.floor(submitKeyPresses))
@@ -176,7 +449,7 @@ export async function sendPaneInput({
176
449
  submitKeyPresses: normalizedSubmitKeyPresses,
177
450
  })?.submitArgv;
178
451
  if (!submitArgv) {
179
- return { ok: false, sent: false, reason: 'send_failed', paneTarget: target };
452
+ return { ok: false, sent: false, reason: 'send_failed', paneTarget: target, exactPaneProof };
180
453
  }
181
454
  const pasteArgv = buildSafePasteArgv(target, literalPrompt);
182
455
  const argv = {
@@ -203,6 +476,7 @@ export async function sendPaneInput({
203
476
  reason: 'buffer_set_failed',
204
477
  paneTarget: target,
205
478
  argv,
479
+ exactPaneProof,
206
480
  error: error instanceof Error ? error.message : safeString(error),
207
481
  };
208
482
  }
@@ -216,6 +490,7 @@ export async function sendPaneInput({
216
490
  reason: 'buffer_show_failed',
217
491
  paneTarget: target,
218
492
  argv,
493
+ exactPaneProof,
219
494
  error: error instanceof Error ? error.message : safeString(error),
220
495
  };
221
496
  }
@@ -226,12 +501,31 @@ export async function sendPaneInput({
226
501
  reason: 'buffer_verify_failed',
227
502
  paneTarget: target,
228
503
  argv,
504
+ exactPaneProof,
229
505
  expectedBytes: literalPrompt.length,
230
506
  actualBytes: verifiedBuffer.stdout.length,
231
507
  };
232
508
  }
509
+
510
+ const clearProof = await verifyForEffect();
511
+ if (!clearProof.ok) return exactPaneUnavailableResult(target, clearProof, { argv });
233
512
  try {
234
513
  await runProcess('tmux', pasteArgv.clearComposerArgv, 3000);
514
+ } catch (error) {
515
+ return {
516
+ ok: false,
517
+ sent: false,
518
+ reason: 'buffer_paste_failed',
519
+ paneTarget: target,
520
+ argv,
521
+ exactPaneProof,
522
+ error: error instanceof Error ? error.message : safeString(error),
523
+ };
524
+ }
525
+
526
+ const pasteProof = await verifyForEffect();
527
+ if (!pasteProof.ok) return exactPaneUnavailableResult(target, pasteProof, { argv });
528
+ try {
235
529
  await runProcess('tmux', pasteArgv.pasteBufferArgv, 3000);
236
530
  } catch (error) {
237
531
  return {
@@ -240,11 +534,14 @@ export async function sendPaneInput({
240
534
  reason: 'buffer_paste_failed',
241
535
  paneTarget: target,
242
536
  argv,
537
+ exactPaneProof,
243
538
  error: error instanceof Error ? error.message : safeString(error),
244
539
  };
245
540
  }
246
541
  }
247
542
  if (queueFirstSubmit && argv.submitArgv.length > 0) {
543
+ const queueProof = await verifyForEffect();
544
+ if (!queueProof.ok) return exactPaneUnavailableResult(target, queueProof, { argv });
248
545
  await runProcess('tmux', ['send-keys', '-t', target, 'Tab'], 3000);
249
546
  if (submitDelayMs > 0) {
250
547
  await new Promise((resolve) => setTimeout(resolve, submitDelayMs));
@@ -254,9 +551,11 @@ export async function sendPaneInput({
254
551
  if (submitDelayMs > 0) {
255
552
  await new Promise((resolve) => setTimeout(resolve, submitDelayMs));
256
553
  }
554
+ const submitProof = await verifyForEffect();
555
+ if (!submitProof.ok) return exactPaneUnavailableResult(target, submitProof, { argv });
257
556
  await runProcess('tmux', submit, 3000);
258
557
  }
259
- return { ok: true, sent: true, reason: 'sent', paneTarget: target, argv };
558
+ return { ok: true, sent: true, reason: 'sent', paneTarget: target, argv, exactPaneProof };
260
559
  } catch (error) {
261
560
  return {
262
561
  ok: false,
@@ -264,6 +563,7 @@ export async function sendPaneInput({
264
563
  reason: 'send_failed',
265
564
  paneTarget: target,
266
565
  argv,
566
+ exactPaneProof,
267
567
  error: error instanceof Error ? error.message : safeString(error),
268
568
  };
269
569
  } finally {
@@ -277,24 +577,49 @@ export async function queuePaneInput({
277
577
  paneTarget,
278
578
  prompt,
279
579
  submitDelayMs = 80,
580
+ exactPaneId = undefined,
280
581
  }: any): Promise<any> {
582
+ const target = safeString(paneTarget).trim();
583
+ const exactPaneIdentity = safeString(exactPaneId).trim();
584
+ const bindingFailure = exactPaneBindingFailure(target, exactPaneId);
585
+ if (bindingFailure) return exactPaneUnavailableResult(target, bindingFailure);
281
586
  const sendResult = await sendPaneInput({
282
587
  paneTarget,
283
588
  prompt,
284
589
  submitKeyPresses: 0,
590
+ exactPaneId: exactPaneIdentity,
285
591
  });
286
592
  if (!sendResult.ok) return sendResult;
287
593
 
288
- const target = safeString(paneTarget).trim();
594
+ let exactPaneProof = sendResult.exactPaneProof || null;
595
+ let expectedPanePid = typeof sendResult.exactPaneProof?.pid === 'number' ? sendResult.exactPaneProof.pid : undefined;
596
+ const verifyExplicitPane = async () => {
597
+ const paneProof = await verifyExactPaneLive(exactPaneIdentity, expectedPanePid);
598
+ exactPaneProof = paneProof.proof || null;
599
+ if (paneProof.ok && typeof paneProof.proof?.pid === 'number') expectedPanePid ??= paneProof.proof.pid;
600
+ return paneProof;
601
+ };
289
602
  const submitArgv = [
290
603
  ['send-keys', '-t', target, 'Tab'],
291
604
  ['send-keys', '-t', target, 'C-m'],
292
605
  ];
606
+ const firstSubmitProof = await verifyExplicitPane();
607
+ if (!firstSubmitProof.ok) {
608
+ return exactPaneUnavailableResult(target, firstSubmitProof, {
609
+ argv: { typeArgv: sendResult.argv?.typeArgv || null, submitArgv },
610
+ });
611
+ }
293
612
  try {
294
613
  await runProcess('tmux', submitArgv[0], 3000);
295
614
  if (submitDelayMs > 0) {
296
615
  await new Promise((resolve) => setTimeout(resolve, submitDelayMs));
297
616
  }
617
+ const secondSubmitProof = await verifyExplicitPane();
618
+ if (!secondSubmitProof.ok) {
619
+ return exactPaneUnavailableResult(target, secondSubmitProof, {
620
+ argv: { typeArgv: sendResult.argv?.typeArgv || null, submitArgv },
621
+ });
622
+ }
298
623
  await runProcess('tmux', submitArgv[1], 3000);
299
624
  return {
300
625
  ok: true,
@@ -302,6 +627,7 @@ export async function queuePaneInput({
302
627
  reason: 'queued',
303
628
  paneTarget: target,
304
629
  argv: { typeArgv: sendResult.argv?.typeArgv || null, submitArgv },
630
+ exactPaneProof,
305
631
  };
306
632
  } catch (error) {
307
633
  return {
@@ -310,11 +636,12 @@ export async function queuePaneInput({
310
636
  reason: 'queue_failed',
311
637
  paneTarget: target,
312
638
  argv: { typeArgv: sendResult.argv?.typeArgv || null, submitArgv },
639
+ exactPaneProof,
313
640
  error: error instanceof Error ? error.message : safeString(error),
314
641
  };
315
642
  }
316
643
  }
317
644
 
318
- export async function checkPaneReadyForTeamSendKeys(paneTarget: any): Promise<any> {
319
- return evaluatePaneInjectionReadiness(paneTarget);
645
+ export async function checkPaneReadyForTeamSendKeys(paneTarget: any, exactPaneId: any): Promise<any> {
646
+ return evaluatePaneInjectionReadiness(paneTarget, { exactPaneId });
320
647
  }
@@ -14,8 +14,7 @@ import { appendTeamDeliveryLog } from '../../team/delivery-log.js';
14
14
  import { safeString, asNumber, isTerminalPhase } from './utils.js';
15
15
  import { readJsonIfExists } from './state-io.js';
16
16
  import { logTmuxHookEvent } from './log.js';
17
- import { evaluatePaneInjectionReadiness, sendPaneInput } from './team-tmux-guard.js';
18
- import { resolvePaneTarget } from './tmux-injection.js';
17
+ import { evaluatePaneInjectionReadiness, normalizeExactPaneId, sendPaneInput } from './team-tmux-guard.js';
19
18
  import { readTeamWorkersForIdleCheck } from './team-worker.js';
20
19
 
21
20
  const STOP_NUDGE_COOLDOWN_MS = 30_000;
@@ -134,17 +133,8 @@ function resolveWorkerStopCooldownMs() {
134
133
  return STOP_NUDGE_COOLDOWN_MS;
135
134
  }
136
135
 
137
- async function resolveCanonicalLeaderPaneId(leaderPaneId) {
138
- const normalizedLeaderPaneId = safeString(leaderPaneId).trim();
139
- if (!normalizedLeaderPaneId) return '';
140
- try {
141
- const resolved = await resolvePaneTarget({ type: 'pane', value: normalizedLeaderPaneId }, '', '', '', {});
142
- const paneTarget = safeString(resolved?.paneTarget).trim();
143
- if (paneTarget) return paneTarget;
144
- } catch {
145
- // Fall back to the recorded pane id; readiness guard remains authoritative.
146
- }
147
- return normalizedLeaderPaneId;
136
+ function resolveCanonicalLeaderPaneId(leaderPaneId) {
137
+ return normalizeExactPaneId(leaderPaneId);
148
138
  }
149
139
 
150
140
  async function recordSuppressedWorkerStopNudge({
@@ -304,9 +294,16 @@ export async function maybeNudgeLeaderForAllowedWorkerStop({
304
294
  const teamInfo = await readTeamWorkersForIdleCheck(stateDir, teamName);
305
295
  if (!teamInfo) return { ok: false, result: 'unresolved' };
306
296
  ({ tmuxSession, leaderPaneId } = teamInfo);
307
- const tmuxTarget = await resolveCanonicalLeaderPaneId(leaderPaneId);
308
-
309
- if (!tmuxTarget) {
297
+ const leaderPanePid = Number.isInteger(teamInfo.leaderPanePid) && Number(teamInfo.leaderPanePid) > 0
298
+ ? Number(teamInfo.leaderPanePid)
299
+ : undefined;
300
+ const leaderPaneOwnerId = safeString(teamInfo.tmuxPaneOwnerId).trim();
301
+ const resolvedLeaderPaneId = await resolveCanonicalLeaderPaneId(leaderPaneId);
302
+ const tmuxTarget = resolvedLeaderPaneId && resolvedLeaderPaneId !== normalizeExactPaneId(teamInfo.hudPaneId)
303
+ ? resolvedLeaderPaneId
304
+ : '';
305
+
306
+ if (!tmuxTarget || !leaderPanePid || !leaderPaneOwnerId) {
310
307
  if (!(await teamStateAllowsWorkerStopNudge(stateDir, teamName))) {
311
308
  await recordShutdownSuppressionOnce();
312
309
  return { ok: true, result: TEAM_SHUTDOWN_NO_INJECTION_REASON };
@@ -330,6 +327,9 @@ export async function maybeNudgeLeaderForAllowedWorkerStop({
330
327
  requireRunningAgent: true,
331
328
  requireReady: false,
332
329
  requireIdle: false,
330
+ exactPaneId: tmuxTarget,
331
+ expectedPanePid: leaderPanePid,
332
+ expectedPaneOwnerId: leaderPaneOwnerId,
333
333
  });
334
334
  if (!paneGuard.ok) {
335
335
  if (!(await teamStateAllowsWorkerStopNudge(stateDir, teamName))) {
@@ -367,6 +367,10 @@ export async function maybeNudgeLeaderForAllowedWorkerStop({
367
367
  prompt,
368
368
  submitKeyPresses: 2,
369
369
  submitDelayMs: 100,
370
+ exactPaneId: tmuxTarget,
371
+ expectedPanePid: leaderPanePid,
372
+ expectedPaneOwnerId: leaderPaneOwnerId,
373
+ expectedHudPaneId: teamInfo.hudPaneId,
370
374
  });
371
375
  if (!sendResult.ok) throw new Error(sendResult.error || sendResult.reason || 'send_failed');
372
376
  const deliveryMode = leaderHasActiveTask ? 'steered' : 'sent';