oh-my-codex 0.20.2 → 0.20.5

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 (805) hide show
  1. package/Cargo.lock +36 -18
  2. package/Cargo.toml +1 -1
  3. package/README.md +28 -2
  4. package/crates/omx-runtime/Cargo.toml +4 -0
  5. package/crates/omx-runtime/src/main.rs +415 -2
  6. package/crates/omx-runtime/tests/execution.rs +483 -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/adapt/__tests__/herdr-target.test.d.ts +2 -0
  11. package/dist/adapt/__tests__/herdr-target.test.d.ts.map +1 -0
  12. package/dist/adapt/__tests__/herdr-target.test.js +72 -0
  13. package/dist/adapt/__tests__/herdr-target.test.js.map +1 -0
  14. package/dist/adapt/contracts.d.ts +1 -1
  15. package/dist/adapt/contracts.d.ts.map +1 -1
  16. package/dist/adapt/contracts.js +1 -1
  17. package/dist/adapt/contracts.js.map +1 -1
  18. package/dist/adapt/herdr/__tests__/authority.test.d.ts +2 -0
  19. package/dist/adapt/herdr/__tests__/authority.test.d.ts.map +1 -0
  20. package/dist/adapt/herdr/__tests__/authority.test.js +49 -0
  21. package/dist/adapt/herdr/__tests__/authority.test.js.map +1 -0
  22. package/dist/adapt/herdr/__tests__/bridge.test.d.ts +2 -0
  23. package/dist/adapt/herdr/__tests__/bridge.test.d.ts.map +1 -0
  24. package/dist/adapt/herdr/__tests__/bridge.test.js +176 -0
  25. package/dist/adapt/herdr/__tests__/bridge.test.js.map +1 -0
  26. package/dist/adapt/herdr/__tests__/sanitize.test.d.ts +2 -0
  27. package/dist/adapt/herdr/__tests__/sanitize.test.d.ts.map +1 -0
  28. package/dist/adapt/herdr/__tests__/sanitize.test.js +45 -0
  29. package/dist/adapt/herdr/__tests__/sanitize.test.js.map +1 -0
  30. package/dist/adapt/herdr/__tests__/semantic.test.d.ts +2 -0
  31. package/dist/adapt/herdr/__tests__/semantic.test.d.ts.map +1 -0
  32. package/dist/adapt/herdr/__tests__/semantic.test.js +130 -0
  33. package/dist/adapt/herdr/__tests__/semantic.test.js.map +1 -0
  34. package/dist/adapt/herdr/__tests__/seq-store.test.d.ts +2 -0
  35. package/dist/adapt/herdr/__tests__/seq-store.test.d.ts.map +1 -0
  36. package/dist/adapt/herdr/__tests__/seq-store.test.js +54 -0
  37. package/dist/adapt/herdr/__tests__/seq-store.test.js.map +1 -0
  38. package/dist/adapt/herdr/__tests__/socket-live.test.d.ts +2 -0
  39. package/dist/adapt/herdr/__tests__/socket-live.test.d.ts.map +1 -0
  40. package/dist/adapt/herdr/__tests__/socket-live.test.js +104 -0
  41. package/dist/adapt/herdr/__tests__/socket-live.test.js.map +1 -0
  42. package/dist/adapt/herdr/__tests__/team-map.test.d.ts +2 -0
  43. package/dist/adapt/herdr/__tests__/team-map.test.d.ts.map +1 -0
  44. package/dist/adapt/herdr/__tests__/team-map.test.js +46 -0
  45. package/dist/adapt/herdr/__tests__/team-map.test.js.map +1 -0
  46. package/dist/adapt/herdr/__tests__/transport.test.d.ts +2 -0
  47. package/dist/adapt/herdr/__tests__/transport.test.d.ts.map +1 -0
  48. package/dist/adapt/herdr/__tests__/transport.test.js +160 -0
  49. package/dist/adapt/herdr/__tests__/transport.test.js.map +1 -0
  50. package/dist/adapt/herdr/__tests__/wiring.test.d.ts +2 -0
  51. package/dist/adapt/herdr/__tests__/wiring.test.d.ts.map +1 -0
  52. package/dist/adapt/herdr/__tests__/wiring.test.js +118 -0
  53. package/dist/adapt/herdr/__tests__/wiring.test.js.map +1 -0
  54. package/dist/adapt/herdr/authority.d.ts +34 -0
  55. package/dist/adapt/herdr/authority.d.ts.map +1 -0
  56. package/dist/adapt/herdr/authority.js +71 -0
  57. package/dist/adapt/herdr/authority.js.map +1 -0
  58. package/dist/adapt/herdr/bridge.d.ts +84 -0
  59. package/dist/adapt/herdr/bridge.d.ts.map +1 -0
  60. package/dist/adapt/herdr/bridge.js +222 -0
  61. package/dist/adapt/herdr/bridge.js.map +1 -0
  62. package/dist/adapt/herdr/sanitize.d.ts +20 -0
  63. package/dist/adapt/herdr/sanitize.d.ts.map +1 -0
  64. package/dist/adapt/herdr/sanitize.js +70 -0
  65. package/dist/adapt/herdr/sanitize.js.map +1 -0
  66. package/dist/adapt/herdr/semantic.d.ts +67 -0
  67. package/dist/adapt/herdr/semantic.d.ts.map +1 -0
  68. package/dist/adapt/herdr/semantic.js +170 -0
  69. package/dist/adapt/herdr/semantic.js.map +1 -0
  70. package/dist/adapt/herdr/seq-store.d.ts +32 -0
  71. package/dist/adapt/herdr/seq-store.d.ts.map +1 -0
  72. package/dist/adapt/herdr/seq-store.js +126 -0
  73. package/dist/adapt/herdr/seq-store.js.map +1 -0
  74. package/dist/adapt/herdr/team-map.d.ts +30 -0
  75. package/dist/adapt/herdr/team-map.d.ts.map +1 -0
  76. package/dist/adapt/herdr/team-map.js +37 -0
  77. package/dist/adapt/herdr/team-map.js.map +1 -0
  78. package/dist/adapt/herdr/transport.d.ts +115 -0
  79. package/dist/adapt/herdr/transport.d.ts.map +1 -0
  80. package/dist/adapt/herdr/transport.js +260 -0
  81. package/dist/adapt/herdr/transport.js.map +1 -0
  82. package/dist/adapt/herdr/wiring.d.ts +24 -0
  83. package/dist/adapt/herdr/wiring.d.ts.map +1 -0
  84. package/dist/adapt/herdr/wiring.js +62 -0
  85. package/dist/adapt/herdr/wiring.js.map +1 -0
  86. package/dist/adapt/herdr.d.ts +19 -0
  87. package/dist/adapt/herdr.d.ts.map +1 -0
  88. package/dist/adapt/herdr.js +115 -0
  89. package/dist/adapt/herdr.js.map +1 -0
  90. package/dist/adapt/index.d.ts +4 -0
  91. package/dist/adapt/index.d.ts.map +1 -1
  92. package/dist/adapt/index.js +48 -0
  93. package/dist/adapt/index.js.map +1 -1
  94. package/dist/adapt/registry.d.ts.map +1 -1
  95. package/dist/adapt/registry.js +11 -0
  96. package/dist/adapt/registry.js.map +1 -1
  97. package/dist/agents/__tests__/definitions.test.js +81 -0
  98. package/dist/agents/__tests__/definitions.test.js.map +1 -1
  99. package/dist/agents/__tests__/native-config.test.js +25 -0
  100. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  101. package/dist/agents/native-config.d.ts +2 -1
  102. package/dist/agents/native-config.d.ts.map +1 -1
  103. package/dist/agents/native-config.js.map +1 -1
  104. package/dist/auth/__tests__/hotswap.test.js +60 -1
  105. package/dist/auth/__tests__/hotswap.test.js.map +1 -1
  106. package/dist/auth/__tests__/storage.test.js +33 -1
  107. package/dist/auth/__tests__/storage.test.js.map +1 -1
  108. package/dist/auth/hotswap.d.ts +22 -2
  109. package/dist/auth/hotswap.d.ts.map +1 -1
  110. package/dist/auth/hotswap.js +107 -22
  111. package/dist/auth/hotswap.js.map +1 -1
  112. package/dist/auth/storage.js +1 -1
  113. package/dist/auth/storage.js.map +1 -1
  114. package/dist/autopilot/__tests__/ralplan-gate.test.js +86 -1333
  115. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
  116. package/dist/autopilot/ralplan-gate.d.ts.map +1 -1
  117. package/dist/autopilot/ralplan-gate.js +7 -6
  118. package/dist/autopilot/ralplan-gate.js.map +1 -1
  119. package/dist/cli/__tests__/agents-init.test.js +4 -23
  120. package/dist/cli/__tests__/agents-init.test.js.map +1 -1
  121. package/dist/cli/__tests__/api.test.js +61 -24
  122. package/dist/cli/__tests__/api.test.js.map +1 -1
  123. package/dist/cli/__tests__/auth.test.js +63 -7
  124. package/dist/cli/__tests__/auth.test.js.map +1 -1
  125. package/dist/cli/__tests__/codex-feature-probe.test.js +66 -1
  126. package/dist/cli/__tests__/codex-feature-probe.test.js.map +1 -1
  127. package/dist/cli/__tests__/codex-plugin-layout.test.js +150 -2
  128. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  129. package/dist/cli/__tests__/detached-leader-teardown.test.d.ts +2 -0
  130. package/dist/cli/__tests__/detached-leader-teardown.test.d.ts.map +1 -0
  131. package/dist/cli/__tests__/detached-leader-teardown.test.js +473 -0
  132. package/dist/cli/__tests__/detached-leader-teardown.test.js.map +1 -0
  133. package/dist/cli/__tests__/doctor-process-identity-readiness.test.d.ts +2 -0
  134. package/dist/cli/__tests__/doctor-process-identity-readiness.test.d.ts.map +1 -0
  135. package/dist/cli/__tests__/doctor-process-identity-readiness.test.js +103 -0
  136. package/dist/cli/__tests__/doctor-process-identity-readiness.test.js.map +1 -0
  137. package/dist/cli/__tests__/doctor-state-root-binding.test.d.ts +2 -0
  138. package/dist/cli/__tests__/doctor-state-root-binding.test.d.ts.map +1 -0
  139. package/dist/cli/__tests__/doctor-state-root-binding.test.js +139 -0
  140. package/dist/cli/__tests__/doctor-state-root-binding.test.js.map +1 -0
  141. package/dist/cli/__tests__/doctor-warning-copy.test.js +135 -1
  142. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  143. package/dist/cli/__tests__/exec.test.js +211 -0
  144. package/dist/cli/__tests__/exec.test.js.map +1 -1
  145. package/dist/cli/__tests__/index.test.js +878 -463
  146. package/dist/cli/__tests__/index.test.js.map +1 -1
  147. package/dist/cli/__tests__/launch-fallback.test.js +1290 -127
  148. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  149. package/dist/cli/__tests__/mcp-parity.test.js +8 -4
  150. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  151. package/dist/cli/__tests__/native-assets.test.js +398 -16
  152. package/dist/cli/__tests__/native-assets.test.js.map +1 -1
  153. package/dist/cli/__tests__/native-hook-claim-journal.test.js +132 -6
  154. package/dist/cli/__tests__/native-hook-claim-journal.test.js.map +1 -1
  155. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +3 -3
  156. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  157. package/dist/cli/__tests__/ralph.test.js +3 -3
  158. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  159. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js +49 -164
  160. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js.map +1 -1
  161. package/dist/cli/__tests__/ralplan-child-binding-3212.test.d.ts +2 -0
  162. package/dist/cli/__tests__/ralplan-child-binding-3212.test.d.ts.map +1 -0
  163. package/dist/cli/__tests__/ralplan-child-binding-3212.test.js +56 -0
  164. package/dist/cli/__tests__/ralplan-child-binding-3212.test.js.map +1 -0
  165. package/dist/cli/__tests__/ralplan.test.js +107 -109
  166. package/dist/cli/__tests__/ralplan.test.js.map +1 -1
  167. package/dist/cli/__tests__/resume.test.js +11 -10
  168. package/dist/cli/__tests__/resume.test.js.map +1 -1
  169. package/dist/cli/__tests__/session-scoped-runtime.test.js +1118 -43
  170. package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
  171. package/dist/cli/__tests__/session-search-help.test.js +25 -1
  172. package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
  173. package/dist/cli/__tests__/setup-agents-overwrite.test.js +8 -55
  174. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  175. package/dist/cli/__tests__/setup-install-mode.test.js +224 -7
  176. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  177. package/dist/cli/__tests__/sparkshell-cli.test.js +93 -41
  178. package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
  179. package/dist/cli/__tests__/team.test.js +150 -8
  180. package/dist/cli/__tests__/team.test.js.map +1 -1
  181. package/dist/cli/__tests__/ultragoal.test.js +117 -2
  182. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  183. package/dist/cli/__tests__/uninstall.test.js +95 -4
  184. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  185. package/dist/cli/__tests__/update.test.js +669 -177
  186. package/dist/cli/__tests__/update.test.js.map +1 -1
  187. package/dist/cli/__tests__/windows-popup-loop-contract.test.js +33 -2
  188. package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
  189. package/dist/cli/api.d.ts.map +1 -1
  190. package/dist/cli/api.js +9 -10
  191. package/dist/cli/api.js.map +1 -1
  192. package/dist/cli/codex-feature-probe.d.ts +16 -0
  193. package/dist/cli/codex-feature-probe.d.ts.map +1 -1
  194. package/dist/cli/codex-feature-probe.js +44 -0
  195. package/dist/cli/codex-feature-probe.js.map +1 -1
  196. package/dist/cli/codex-home.d.ts.map +1 -1
  197. package/dist/cli/codex-home.js +7 -4
  198. package/dist/cli/codex-home.js.map +1 -1
  199. package/dist/cli/doctor.d.ts +20 -0
  200. package/dist/cli/doctor.d.ts.map +1 -1
  201. package/dist/cli/doctor.js +290 -3
  202. package/dist/cli/doctor.js.map +1 -1
  203. package/dist/cli/index.d.ts +234 -12
  204. package/dist/cli/index.d.ts.map +1 -1
  205. package/dist/cli/index.js +2409 -746
  206. package/dist/cli/index.js.map +1 -1
  207. package/dist/cli/mcp-parity.js +2 -2
  208. package/dist/cli/mcp-parity.js.map +1 -1
  209. package/dist/cli/native-assets.d.ts +25 -0
  210. package/dist/cli/native-assets.d.ts.map +1 -1
  211. package/dist/cli/native-assets.js +448 -51
  212. package/dist/cli/native-assets.js.map +1 -1
  213. package/dist/cli/native-hook-claim-journal.d.ts +20 -5
  214. package/dist/cli/native-hook-claim-journal.d.ts.map +1 -1
  215. package/dist/cli/native-hook-claim-journal.js +67 -32
  216. package/dist/cli/native-hook-claim-journal.js.map +1 -1
  217. package/dist/cli/package-manager-ownership.d.ts +63 -0
  218. package/dist/cli/package-manager-ownership.d.ts.map +1 -0
  219. package/dist/cli/package-manager-ownership.js +323 -0
  220. package/dist/cli/package-manager-ownership.js.map +1 -0
  221. package/dist/cli/ralph.js +2 -2
  222. package/dist/cli/ralph.js.map +1 -1
  223. package/dist/cli/ralplan.d.ts +5 -7
  224. package/dist/cli/ralplan.d.ts.map +1 -1
  225. package/dist/cli/ralplan.js +83 -134
  226. package/dist/cli/ralplan.js.map +1 -1
  227. package/dist/cli/session-search.d.ts +5 -0
  228. package/dist/cli/session-search.d.ts.map +1 -1
  229. package/dist/cli/session-search.js +87 -0
  230. package/dist/cli/session-search.js.map +1 -1
  231. package/dist/cli/setup-preferences.d.ts +15 -0
  232. package/dist/cli/setup-preferences.d.ts.map +1 -1
  233. package/dist/cli/setup-preferences.js +27 -1
  234. package/dist/cli/setup-preferences.js.map +1 -1
  235. package/dist/cli/setup.d.ts +8 -0
  236. package/dist/cli/setup.d.ts.map +1 -1
  237. package/dist/cli/setup.js +119 -47
  238. package/dist/cli/setup.js.map +1 -1
  239. package/dist/cli/sparkshell.d.ts.map +1 -1
  240. package/dist/cli/sparkshell.js +52 -9
  241. package/dist/cli/sparkshell.js.map +1 -1
  242. package/dist/cli/ultragoal.d.ts.map +1 -1
  243. package/dist/cli/ultragoal.js +2 -2
  244. package/dist/cli/ultragoal.js.map +1 -1
  245. package/dist/cli/uninstall.d.ts +9 -0
  246. package/dist/cli/uninstall.d.ts.map +1 -1
  247. package/dist/cli/uninstall.js +71 -30
  248. package/dist/cli/uninstall.js.map +1 -1
  249. package/dist/cli/update-worker.d.ts +2 -0
  250. package/dist/cli/update-worker.d.ts.map +1 -0
  251. package/dist/cli/update-worker.js +142 -0
  252. package/dist/cli/update-worker.js.map +1 -0
  253. package/dist/cli/update.d.ts +14 -22
  254. package/dist/cli/update.d.ts.map +1 -1
  255. package/dist/cli/update.js +192 -241
  256. package/dist/cli/update.js.map +1 -1
  257. package/dist/compat/__tests__/rust-runtime-compat.test.js +1 -1
  258. package/dist/config/__tests__/generator-idempotent.test.js +503 -30
  259. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  260. package/dist/config/__tests__/generator-notify.test.js +5 -3
  261. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  262. package/dist/config/__tests__/models.test.js +67 -3
  263. package/dist/config/__tests__/models.test.js.map +1 -1
  264. package/dist/config/generator.d.ts +27 -21
  265. package/dist/config/generator.d.ts.map +1 -1
  266. package/dist/config/generator.js +610 -152
  267. package/dist/config/generator.js.map +1 -1
  268. package/dist/config/models.d.ts +12 -2
  269. package/dist/config/models.d.ts.map +1 -1
  270. package/dist/config/models.js +15 -1
  271. package/dist/config/models.js.map +1 -1
  272. package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js +25 -0
  273. package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js.map +1 -1
  274. package/dist/goal-workflows/codex-goal-snapshot.d.ts +1 -1
  275. package/dist/goal-workflows/codex-goal-snapshot.d.ts.map +1 -1
  276. package/dist/goal-workflows/codex-goal-snapshot.js +2 -0
  277. package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -1
  278. package/dist/hooks/__tests__/agents-overlay.test.js +5 -3
  279. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  280. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +27 -4
  281. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  282. package/dist/hooks/__tests__/issue-3293-autopilot-handoff.test.d.ts +2 -0
  283. package/dist/hooks/__tests__/issue-3293-autopilot-handoff.test.d.ts.map +1 -0
  284. package/dist/hooks/__tests__/issue-3293-autopilot-handoff.test.js +172 -0
  285. package/dist/hooks/__tests__/issue-3293-autopilot-handoff.test.js.map +1 -0
  286. package/dist/hooks/__tests__/keyword-detector.test.js +176 -37
  287. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  288. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +402 -545
  289. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  290. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +39 -4
  291. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  292. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +161 -26
  293. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  294. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +27 -0
  295. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
  296. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +10 -118
  297. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  298. package/dist/hooks/__tests__/notify-hook-regression-205.test.js +7 -0
  299. package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
  300. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +533 -14
  301. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  302. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +159 -24
  303. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  304. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +482 -32
  305. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  306. package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.js +2 -1
  307. package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.js.map +1 -1
  308. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +68 -1
  309. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  310. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +7 -1
  311. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
  312. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +44 -4
  313. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  314. package/dist/hooks/__tests__/prompt-guidance-contract.test.js +9 -0
  315. package/dist/hooks/__tests__/prompt-guidance-contract.test.js.map +1 -1
  316. package/dist/hooks/__tests__/session.test.js +3130 -365
  317. package/dist/hooks/__tests__/session.test.js.map +1 -1
  318. package/dist/hooks/agents-overlay.js +1 -1
  319. package/dist/hooks/agents-overlay.js.map +1 -1
  320. package/dist/hooks/extensibility/__tests__/dispatcher.test.js +108 -2
  321. package/dist/hooks/extensibility/__tests__/dispatcher.test.js.map +1 -1
  322. package/dist/hooks/extensibility/__tests__/sdk.test.js +151 -1
  323. package/dist/hooks/extensibility/__tests__/sdk.test.js.map +1 -1
  324. package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
  325. package/dist/hooks/extensibility/dispatcher.js +20 -3
  326. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  327. package/dist/hooks/extensibility/plugin-runner.js +1 -0
  328. package/dist/hooks/extensibility/plugin-runner.js.map +1 -1
  329. package/dist/hooks/extensibility/runtime.d.ts.map +1 -1
  330. package/dist/hooks/extensibility/runtime.js.map +1 -1
  331. package/dist/hooks/extensibility/sdk/paths.d.ts +1 -1
  332. package/dist/hooks/extensibility/sdk/paths.d.ts.map +1 -1
  333. package/dist/hooks/extensibility/sdk/paths.js +2 -2
  334. package/dist/hooks/extensibility/sdk/paths.js.map +1 -1
  335. package/dist/hooks/extensibility/sdk/runtime-state.d.ts +1 -1
  336. package/dist/hooks/extensibility/sdk/runtime-state.d.ts.map +1 -1
  337. package/dist/hooks/extensibility/sdk/runtime-state.js +55 -15
  338. package/dist/hooks/extensibility/sdk/runtime-state.js.map +1 -1
  339. package/dist/hooks/extensibility/sdk.d.ts +1 -0
  340. package/dist/hooks/extensibility/sdk.d.ts.map +1 -1
  341. package/dist/hooks/extensibility/sdk.js +1 -1
  342. package/dist/hooks/extensibility/sdk.js.map +1 -1
  343. package/dist/hooks/extensibility/types.d.ts +7 -0
  344. package/dist/hooks/extensibility/types.d.ts.map +1 -1
  345. package/dist/hooks/keyword-detector.d.ts +10 -3
  346. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  347. package/dist/hooks/keyword-detector.js +116 -20
  348. package/dist/hooks/keyword-detector.js.map +1 -1
  349. package/dist/hooks/session.d.ts +204 -14
  350. package/dist/hooks/session.d.ts.map +1 -1
  351. package/dist/hooks/session.js +1500 -223
  352. package/dist/hooks/session.js.map +1 -1
  353. package/dist/hud/__tests__/hud-tmux-injection.test.js +6 -6
  354. package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
  355. package/dist/hud/__tests__/index.test.js +26 -16
  356. package/dist/hud/__tests__/index.test.js.map +1 -1
  357. package/dist/hud/__tests__/reconcile.test.js +83 -78
  358. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  359. package/dist/hud/__tests__/state.test.js +36 -31
  360. package/dist/hud/__tests__/state.test.js.map +1 -1
  361. package/dist/hud/__tests__/tmux.test.js +155 -198
  362. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  363. package/dist/hud/tmux.d.ts +42 -4
  364. package/dist/hud/tmux.d.ts.map +1 -1
  365. package/dist/hud/tmux.js +1017 -139
  366. package/dist/hud/tmux.js.map +1 -1
  367. package/dist/leader/__tests__/contract.test.js +124 -37
  368. package/dist/leader/__tests__/contract.test.js.map +1 -1
  369. package/dist/leader/contract.d.ts +17 -6
  370. package/dist/leader/contract.d.ts.map +1 -1
  371. package/dist/leader/contract.js +102 -26
  372. package/dist/leader/contract.js.map +1 -1
  373. package/dist/mcp/__tests__/code-intel-server.test.js +31 -3
  374. package/dist/mcp/__tests__/code-intel-server.test.js.map +1 -1
  375. package/dist/mcp/__tests__/hermes-bridge.test.js +17 -0
  376. package/dist/mcp/__tests__/hermes-bridge.test.js.map +1 -1
  377. package/dist/mcp/__tests__/server-lifecycle.test.js +4 -4
  378. package/dist/mcp/__tests__/state-paths.test.js +735 -4
  379. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  380. package/dist/mcp/__tests__/state-server.test.js +1 -1
  381. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  382. package/dist/mcp/__tests__/trace-server.test.js +2 -2
  383. package/dist/mcp/__tests__/trace-server.test.js.map +1 -1
  384. package/dist/mcp/__tests__/wiki-server.test.js +18 -0
  385. package/dist/mcp/__tests__/wiki-server.test.js.map +1 -1
  386. package/dist/mcp/code-intel-server.d.ts.map +1 -1
  387. package/dist/mcp/code-intel-server.js +13 -3
  388. package/dist/mcp/code-intel-server.js.map +1 -1
  389. package/dist/mcp/hermes-bridge.d.ts.map +1 -1
  390. package/dist/mcp/hermes-bridge.js +2 -2
  391. package/dist/mcp/hermes-bridge.js.map +1 -1
  392. package/dist/mcp/state-paths.d.ts +78 -4
  393. package/dist/mcp/state-paths.d.ts.map +1 -1
  394. package/dist/mcp/state-paths.js +543 -40
  395. package/dist/mcp/state-paths.js.map +1 -1
  396. package/dist/mcp/state-server.d.ts +4 -4
  397. package/dist/mcp/state-server.d.ts.map +1 -1
  398. package/dist/mcp/wiki-server.d.ts +11 -0
  399. package/dist/mcp/wiki-server.d.ts.map +1 -1
  400. package/dist/mcp/wiki-server.js +9 -2
  401. package/dist/mcp/wiki-server.js.map +1 -1
  402. package/dist/modes/__tests__/base-autopilot-gates.test.js +36 -7
  403. package/dist/modes/__tests__/base-autopilot-gates.test.js.map +1 -1
  404. package/dist/modes/__tests__/base-multi-state-compat.test.js +1 -1
  405. package/dist/modes/__tests__/base-multi-state-compat.test.js.map +1 -1
  406. package/dist/modes/__tests__/base-session-scope.test.js +17 -8
  407. package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
  408. package/dist/modes/__tests__/base-tmux-pane.test.js +4 -4
  409. package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
  410. package/dist/modes/base.d.ts +5 -5
  411. package/dist/modes/base.d.ts.map +1 -1
  412. package/dist/modes/base.js +76 -21
  413. package/dist/modes/base.js.map +1 -1
  414. package/dist/native-assets/archive.d.ts +17 -0
  415. package/dist/native-assets/archive.d.ts.map +1 -0
  416. package/dist/native-assets/archive.js +238 -0
  417. package/dist/native-assets/archive.js.map +1 -0
  418. package/dist/native-assets/policy.d.ts +42 -0
  419. package/dist/native-assets/policy.d.ts.map +1 -0
  420. package/dist/native-assets/policy.js +155 -0
  421. package/dist/native-assets/policy.js.map +1 -0
  422. package/dist/notifications/temp-contract.d.ts.map +1 -1
  423. package/dist/notifications/temp-contract.js +4 -0
  424. package/dist/notifications/temp-contract.js.map +1 -1
  425. package/dist/pipeline/__tests__/orchestrator.test.js +164 -42
  426. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  427. package/dist/pipeline/__tests__/stages.test.js +72 -155
  428. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  429. package/dist/pipeline/orchestrator.d.ts.map +1 -1
  430. package/dist/pipeline/orchestrator.js +124 -18
  431. package/dist/pipeline/orchestrator.js.map +1 -1
  432. package/dist/pipeline/stages/ralplan.d.ts.map +1 -1
  433. package/dist/pipeline/stages/ralplan.js +36 -8
  434. package/dist/pipeline/stages/ralplan.js.map +1 -1
  435. package/dist/ralplan/__tests__/consensus-gate.test.js +73 -1316
  436. package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
  437. package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts +2 -0
  438. package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts.map +1 -0
  439. package/dist/ralplan/__tests__/documented-leader-preflight.test.js +335 -0
  440. package/dist/ralplan/__tests__/documented-leader-preflight.test.js.map +1 -0
  441. package/dist/ralplan/__tests__/runtime.test.js +85 -101
  442. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  443. package/dist/ralplan/consensus-gate.d.ts +9 -1
  444. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  445. package/dist/ralplan/consensus-gate.js +82 -338
  446. package/dist/ralplan/consensus-gate.js.map +1 -1
  447. package/dist/ralplan/documented-leader-preflight.d.ts +41 -0
  448. package/dist/ralplan/documented-leader-preflight.d.ts.map +1 -0
  449. package/dist/ralplan/documented-leader-preflight.js +360 -0
  450. package/dist/ralplan/documented-leader-preflight.js.map +1 -0
  451. package/dist/ralplan/runtime.d.ts +2 -1
  452. package/dist/ralplan/runtime.d.ts.map +1 -1
  453. package/dist/ralplan/runtime.js +67 -168
  454. package/dist/ralplan/runtime.js.map +1 -1
  455. package/dist/runtime/__tests__/bridge.test.js +42 -1
  456. package/dist/runtime/__tests__/bridge.test.js.map +1 -1
  457. package/dist/runtime/bridge.d.ts +12 -0
  458. package/dist/runtime/bridge.d.ts.map +1 -1
  459. package/dist/runtime/bridge.js +52 -1
  460. package/dist/runtime/bridge.js.map +1 -1
  461. package/dist/runtime/run-state.d.ts +5 -1
  462. package/dist/runtime/run-state.d.ts.map +1 -1
  463. package/dist/runtime/run-state.js +15 -8
  464. package/dist/runtime/run-state.js.map +1 -1
  465. package/dist/scripts/__tests__/codex-native-hook.test.js +16323 -8978
  466. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  467. package/dist/scripts/__tests__/issue-3293-callsite-parity.test.d.ts +2 -0
  468. package/dist/scripts/__tests__/issue-3293-callsite-parity.test.d.ts.map +1 -0
  469. package/dist/scripts/__tests__/issue-3293-callsite-parity.test.js +225 -0
  470. package/dist/scripts/__tests__/issue-3293-callsite-parity.test.js.map +1 -0
  471. package/dist/scripts/__tests__/issue-3293-cancel-transaction.test.d.ts +2 -0
  472. package/dist/scripts/__tests__/issue-3293-cancel-transaction.test.d.ts.map +1 -0
  473. package/dist/scripts/__tests__/issue-3293-cancel-transaction.test.js +188 -0
  474. package/dist/scripts/__tests__/issue-3293-cancel-transaction.test.js.map +1 -0
  475. package/dist/scripts/__tests__/issue-3293-hook-owned-cancel.test.d.ts +2 -0
  476. package/dist/scripts/__tests__/issue-3293-hook-owned-cancel.test.d.ts.map +1 -0
  477. package/dist/scripts/__tests__/issue-3293-hook-owned-cancel.test.js +259 -0
  478. package/dist/scripts/__tests__/issue-3293-hook-owned-cancel.test.js.map +1 -0
  479. package/dist/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.d.ts +2 -0
  480. package/dist/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.d.ts.map +1 -0
  481. package/dist/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.js +491 -0
  482. package/dist/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.js.map +1 -0
  483. package/dist/scripts/__tests__/issue-3358-ultragoal-cancel-lifecycle.test.d.ts +2 -0
  484. package/dist/scripts/__tests__/issue-3358-ultragoal-cancel-lifecycle.test.d.ts.map +1 -0
  485. package/dist/scripts/__tests__/issue-3358-ultragoal-cancel-lifecycle.test.js +708 -0
  486. package/dist/scripts/__tests__/issue-3358-ultragoal-cancel-lifecycle.test.js.map +1 -0
  487. package/dist/scripts/__tests__/issue-3369-autopilot-ralplan-state-machine.test.d.ts +2 -0
  488. package/dist/scripts/__tests__/issue-3369-autopilot-ralplan-state-machine.test.d.ts.map +1 -0
  489. package/dist/scripts/__tests__/issue-3369-autopilot-ralplan-state-machine.test.js +208 -0
  490. package/dist/scripts/__tests__/issue-3369-autopilot-ralplan-state-machine.test.js.map +1 -0
  491. package/dist/scripts/__tests__/notify-state-io.test.js +26 -0
  492. package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
  493. package/dist/scripts/__tests__/notify-tmux-injection.test.js +23 -1
  494. package/dist/scripts/__tests__/notify-tmux-injection.test.js.map +1 -1
  495. package/dist/scripts/__tests__/postinstall.test.js +19 -1
  496. package/dist/scripts/__tests__/postinstall.test.js.map +1 -1
  497. package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts +2 -0
  498. package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts.map +1 -0
  499. package/dist/scripts/__tests__/reasoning-artifact-contract.test.js +108 -0
  500. package/dist/scripts/__tests__/reasoning-artifact-contract.test.js.map +1 -0
  501. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js +63 -173
  502. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js.map +1 -1
  503. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js +74 -159
  504. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js.map +1 -1
  505. package/dist/scripts/__tests__/smoke-packed-install.test.js +404 -13
  506. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  507. package/dist/scripts/__tests__/verify-native-release-assets.test.d.ts +2 -0
  508. package/dist/scripts/__tests__/verify-native-release-assets.test.d.ts.map +1 -0
  509. package/dist/scripts/__tests__/verify-native-release-assets.test.js +235 -0
  510. package/dist/scripts/__tests__/verify-native-release-assets.test.js.map +1 -0
  511. package/dist/scripts/codex-native-hook.d.ts +53 -1
  512. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  513. package/dist/scripts/codex-native-hook.js +14448 -1480
  514. package/dist/scripts/codex-native-hook.js.map +1 -1
  515. package/dist/scripts/codex-native-pre-post.d.ts +9 -0
  516. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  517. package/dist/scripts/codex-native-pre-post.js +35 -12
  518. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  519. package/dist/scripts/generate-native-release-manifest.js +80 -51
  520. package/dist/scripts/generate-native-release-manifest.js.map +1 -1
  521. package/dist/scripts/notify-fallback-watcher.js +78 -115
  522. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  523. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  524. package/dist/scripts/notify-hook/auto-nudge.js +74 -3
  525. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  526. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  527. package/dist/scripts/notify-hook/state-io.js +5 -0
  528. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  529. package/dist/scripts/notify-hook/team-dispatch.d.ts +9 -0
  530. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  531. package/dist/scripts/notify-hook/team-dispatch.js +234 -79
  532. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  533. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  534. package/dist/scripts/notify-hook/team-leader-nudge.js +71 -16
  535. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  536. package/dist/scripts/notify-hook/team-tmux-guard.d.ts +12 -4
  537. package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
  538. package/dist/scripts/notify-hook/team-tmux-guard.js +365 -44
  539. package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
  540. package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
  541. package/dist/scripts/notify-hook/team-worker-stop.js +42 -21
  542. package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
  543. package/dist/scripts/notify-hook/team-worker.d.ts +3 -0
  544. package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
  545. package/dist/scripts/notify-hook/team-worker.js +82 -33
  546. package/dist/scripts/notify-hook/team-worker.js.map +1 -1
  547. package/dist/scripts/notify-hook/tmux-injection.d.ts +1 -0
  548. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  549. package/dist/scripts/notify-hook/tmux-injection.js +32 -3
  550. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  551. package/dist/scripts/notify-hook.js +79 -82
  552. package/dist/scripts/notify-hook.js.map +1 -1
  553. package/dist/scripts/postinstall-advisory.d.ts +14 -0
  554. package/dist/scripts/postinstall-advisory.d.ts.map +1 -0
  555. package/dist/scripts/postinstall-advisory.js +49 -0
  556. package/dist/scripts/postinstall-advisory.js.map +1 -0
  557. package/dist/scripts/postinstall.d.ts +1 -1
  558. package/dist/scripts/postinstall.d.ts.map +1 -1
  559. package/dist/scripts/postinstall.js +1 -1
  560. package/dist/scripts/postinstall.js.map +1 -1
  561. package/dist/scripts/smoke-packed-install.d.ts +80 -4
  562. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  563. package/dist/scripts/smoke-packed-install.js +3162 -26
  564. package/dist/scripts/smoke-packed-install.js.map +1 -1
  565. package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
  566. package/dist/scripts/sync-plugin-mirror.js +16 -1
  567. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  568. package/dist/scripts/verify-native-release-assets.js +37 -23
  569. package/dist/scripts/verify-native-release-assets.js.map +1 -1
  570. package/dist/state/__tests__/mode-state-context.test.js +61 -0
  571. package/dist/state/__tests__/mode-state-context.test.js.map +1 -1
  572. package/dist/state/__tests__/operations.test.js +462 -850
  573. package/dist/state/__tests__/operations.test.js.map +1 -1
  574. package/dist/state/__tests__/skill-active.test.js +400 -2
  575. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  576. package/dist/state/mode-state-context.d.ts +8 -0
  577. package/dist/state/mode-state-context.d.ts.map +1 -1
  578. package/dist/state/mode-state-context.js +73 -0
  579. package/dist/state/mode-state-context.js.map +1 -1
  580. package/dist/state/operations.d.ts +3 -0
  581. package/dist/state/operations.d.ts.map +1 -1
  582. package/dist/state/operations.js +144 -40
  583. package/dist/state/operations.js.map +1 -1
  584. package/dist/state/skill-active.d.ts +18 -1
  585. package/dist/state/skill-active.d.ts.map +1 -1
  586. package/dist/state/skill-active.js +420 -23
  587. package/dist/state/skill-active.js.map +1 -1
  588. package/dist/state/workflow-transition-reconcile.d.ts +3 -0
  589. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  590. package/dist/state/workflow-transition-reconcile.js +10 -4
  591. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  592. package/dist/state/workflow-transition.d.ts.map +1 -1
  593. package/dist/state/workflow-transition.js +3 -1
  594. package/dist/state/workflow-transition.js.map +1 -1
  595. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js +53 -247
  596. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js.map +1 -1
  597. package/dist/subagents/__tests__/tracker.test.js +837 -1048
  598. package/dist/subagents/__tests__/tracker.test.js.map +1 -1
  599. package/dist/subagents/tracker.d.ts +58 -135
  600. package/dist/subagents/tracker.d.ts.map +1 -1
  601. package/dist/subagents/tracker.js +811 -632
  602. package/dist/subagents/tracker.js.map +1 -1
  603. package/dist/team/__tests__/api-interop.test.js +91 -1
  604. package/dist/team/__tests__/api-interop.test.js.map +1 -1
  605. package/dist/team/__tests__/delivery-e2e-smoke.test.js +58 -17
  606. package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
  607. package/dist/team/__tests__/idle-nudge.test.js +45 -15
  608. package/dist/team/__tests__/idle-nudge.test.js.map +1 -1
  609. package/dist/team/__tests__/mcp-comm.test.js +197 -30
  610. package/dist/team/__tests__/mcp-comm.test.js.map +1 -1
  611. package/dist/team/__tests__/model-contract.test.js +110 -3
  612. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  613. package/dist/team/__tests__/notice-ledger.test.d.ts +2 -0
  614. package/dist/team/__tests__/notice-ledger.test.d.ts.map +1 -0
  615. package/dist/team/__tests__/notice-ledger.test.js +131 -0
  616. package/dist/team/__tests__/notice-ledger.test.js.map +1 -0
  617. package/dist/team/__tests__/pane-teardown-residue-e2e.test.d.ts +2 -0
  618. package/dist/team/__tests__/pane-teardown-residue-e2e.test.d.ts.map +1 -0
  619. package/dist/team/__tests__/pane-teardown-residue-e2e.test.js +410 -0
  620. package/dist/team/__tests__/pane-teardown-residue-e2e.test.js.map +1 -0
  621. package/dist/team/__tests__/phase-controller.test.js +20 -0
  622. package/dist/team/__tests__/phase-controller.test.js.map +1 -1
  623. package/dist/team/__tests__/runtime-cli.test.js +9 -0
  624. package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
  625. package/dist/team/__tests__/runtime.test.js +3963 -428
  626. package/dist/team/__tests__/runtime.test.js.map +1 -1
  627. package/dist/team/__tests__/scaling.test.js +2405 -203
  628. package/dist/team/__tests__/scaling.test.js.map +1 -1
  629. package/dist/team/__tests__/state-root.test.js +117 -2
  630. package/dist/team/__tests__/state-root.test.js.map +1 -1
  631. package/dist/team/__tests__/state.test.js +804 -36
  632. package/dist/team/__tests__/state.test.js.map +1 -1
  633. package/dist/team/__tests__/team-ops-contract.test.js +6 -0
  634. package/dist/team/__tests__/team-ops-contract.test.js.map +1 -1
  635. package/dist/team/__tests__/tmux-session.test.js +4099 -1039
  636. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  637. package/dist/team/__tests__/tmux-source-authority-realtmux.test.d.ts +2 -0
  638. package/dist/team/__tests__/tmux-source-authority-realtmux.test.d.ts.map +1 -0
  639. package/dist/team/__tests__/tmux-source-authority-realtmux.test.js +149 -0
  640. package/dist/team/__tests__/tmux-source-authority-realtmux.test.js.map +1 -0
  641. package/dist/team/__tests__/tmux-test-fixture.d.ts +69 -0
  642. package/dist/team/__tests__/tmux-test-fixture.d.ts.map +1 -1
  643. package/dist/team/__tests__/tmux-test-fixture.js +242 -15
  644. package/dist/team/__tests__/tmux-test-fixture.js.map +1 -1
  645. package/dist/team/__tests__/tmux-test-fixture.test.js +177 -1
  646. package/dist/team/__tests__/tmux-test-fixture.test.js.map +1 -1
  647. package/dist/team/__tests__/worker-runtime-identity.test.js +53 -44
  648. package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
  649. package/dist/team/__tests__/worktree.test.js +6 -0
  650. package/dist/team/__tests__/worktree.test.js.map +1 -1
  651. package/dist/team/api-interop.d.ts.map +1 -1
  652. package/dist/team/api-interop.js +15 -9
  653. package/dist/team/api-interop.js.map +1 -1
  654. package/dist/team/exact-pane.d.ts +23 -0
  655. package/dist/team/exact-pane.d.ts.map +1 -0
  656. package/dist/team/exact-pane.js +133 -0
  657. package/dist/team/exact-pane.js.map +1 -0
  658. package/dist/team/idle-nudge.d.ts +12 -1
  659. package/dist/team/idle-nudge.d.ts.map +1 -1
  660. package/dist/team/idle-nudge.js +41 -14
  661. package/dist/team/idle-nudge.js.map +1 -1
  662. package/dist/team/model-contract.d.ts +2 -5
  663. package/dist/team/model-contract.d.ts.map +1 -1
  664. package/dist/team/model-contract.js +59 -17
  665. package/dist/team/model-contract.js.map +1 -1
  666. package/dist/team/notice-ledger.d.ts +54 -0
  667. package/dist/team/notice-ledger.d.ts.map +1 -0
  668. package/dist/team/notice-ledger.js +259 -0
  669. package/dist/team/notice-ledger.js.map +1 -0
  670. package/dist/team/phase-controller.d.ts.map +1 -1
  671. package/dist/team/phase-controller.js +2 -0
  672. package/dist/team/phase-controller.js.map +1 -1
  673. package/dist/team/runtime.d.ts +4 -1
  674. package/dist/team/runtime.d.ts.map +1 -1
  675. package/dist/team/runtime.js +1527 -291
  676. package/dist/team/runtime.js.map +1 -1
  677. package/dist/team/scaling.d.ts +13 -1
  678. package/dist/team/scaling.d.ts.map +1 -1
  679. package/dist/team/scaling.js +1503 -387
  680. package/dist/team/scaling.js.map +1 -1
  681. package/dist/team/state/dispatch.d.ts.map +1 -1
  682. package/dist/team/state/dispatch.js +26 -8
  683. package/dist/team/state/dispatch.js.map +1 -1
  684. package/dist/team/state/locks.d.ts.map +1 -1
  685. package/dist/team/state/locks.js +61 -13
  686. package/dist/team/state/locks.js.map +1 -1
  687. package/dist/team/state/monitor.d.ts +10 -0
  688. package/dist/team/state/monitor.d.ts.map +1 -1
  689. package/dist/team/state/monitor.js +13 -0
  690. package/dist/team/state/monitor.js.map +1 -1
  691. package/dist/team/state/tasks.d.ts.map +1 -1
  692. package/dist/team/state/tasks.js +4 -3
  693. package/dist/team/state/tasks.js.map +1 -1
  694. package/dist/team/state/types.d.ts +15 -0
  695. package/dist/team/state/types.d.ts.map +1 -1
  696. package/dist/team/state/types.js.map +1 -1
  697. package/dist/team/state-root.d.ts +12 -0
  698. package/dist/team/state-root.d.ts.map +1 -1
  699. package/dist/team/state-root.js +177 -0
  700. package/dist/team/state-root.js.map +1 -1
  701. package/dist/team/state.d.ts +85 -1
  702. package/dist/team/state.d.ts.map +1 -1
  703. package/dist/team/state.js +672 -129
  704. package/dist/team/state.js.map +1 -1
  705. package/dist/team/team-ops.d.ts +5 -0
  706. package/dist/team/team-ops.d.ts.map +1 -1
  707. package/dist/team/team-ops.js +4 -0
  708. package/dist/team/team-ops.js.map +1 -1
  709. package/dist/team/tmux-session.d.ts +163 -27
  710. package/dist/team/tmux-session.d.ts.map +1 -1
  711. package/dist/team/tmux-session.js +1682 -376
  712. package/dist/team/tmux-session.js.map +1 -1
  713. package/dist/team/worktree.d.ts.map +1 -1
  714. package/dist/team/worktree.js +4 -0
  715. package/dist/team/worktree.js.map +1 -1
  716. package/dist/ultragoal/__tests__/artifacts.test.js +950 -2
  717. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  718. package/dist/ultragoal/__tests__/docs-contract.test.js +1 -0
  719. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
  720. package/dist/ultragoal/artifacts.d.ts +35 -1
  721. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  722. package/dist/ultragoal/artifacts.js +232 -46
  723. package/dist/ultragoal/artifacts.js.map +1 -1
  724. package/dist/utils/__tests__/file-durability.test.d.ts +2 -0
  725. package/dist/utils/__tests__/file-durability.test.d.ts.map +1 -0
  726. package/dist/utils/__tests__/file-durability.test.js +114 -0
  727. package/dist/utils/__tests__/file-durability.test.js.map +1 -0
  728. package/dist/utils/__tests__/paths.test.js +21 -1
  729. package/dist/utils/__tests__/paths.test.js.map +1 -1
  730. package/dist/utils/file-durability.d.ts +25 -0
  731. package/dist/utils/file-durability.d.ts.map +1 -0
  732. package/dist/utils/file-durability.js +67 -0
  733. package/dist/utils/file-durability.js.map +1 -0
  734. package/dist/utils/paths.d.ts.map +1 -1
  735. package/dist/utils/paths.js +14 -6
  736. package/dist/utils/paths.js.map +1 -1
  737. package/dist/verification/__tests__/ci-rust-gates.test.js +49 -4
  738. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  739. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +3 -0
  740. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
  741. package/dist/verification/__tests__/explore-harness-release-workflow.test.js +60 -0
  742. package/dist/verification/__tests__/explore-harness-release-workflow.test.js.map +1 -1
  743. package/dist/verification/__tests__/native-release-manifest.test.js +157 -4
  744. package/dist/verification/__tests__/native-release-manifest.test.js.map +1 -1
  745. package/dist/wiki/lint.d.ts +4 -1
  746. package/dist/wiki/lint.d.ts.map +1 -1
  747. package/dist/wiki/lint.js +3 -2
  748. package/dist/wiki/lint.js.map +1 -1
  749. package/package.json +13 -5
  750. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  751. package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +105 -17
  752. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +13 -5
  753. package/plugins/oh-my-codex/skills/cancel/SKILL.md +30 -85
  754. package/plugins/oh-my-codex/skills/ralph/SKILL.md +6 -4
  755. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +16 -17
  756. package/plugins/oh-my-codex/skills/team/SKILL.md +13 -9
  757. package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +3 -2
  758. package/skills/autopilot/SKILL.md +13 -5
  759. package/skills/cancel/SKILL.md +30 -85
  760. package/skills/ralph/SKILL.md +6 -4
  761. package/skills/ralplan/SKILL.md +16 -17
  762. package/skills/team/SKILL.md +13 -9
  763. package/skills/ultragoal/SKILL.md +3 -2
  764. package/src/scripts/__tests__/codex-native-hook.test.ts +8831 -951
  765. package/src/scripts/__tests__/issue-3293-callsite-parity.test.ts +187 -0
  766. package/src/scripts/__tests__/issue-3293-cancel-transaction.test.ts +189 -0
  767. package/src/scripts/__tests__/issue-3293-hook-owned-cancel.test.ts +187 -0
  768. package/src/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.ts +591 -0
  769. package/src/scripts/__tests__/issue-3358-ultragoal-cancel-lifecycle.test.ts +701 -0
  770. package/src/scripts/__tests__/issue-3369-autopilot-ralplan-state-machine.test.ts +211 -0
  771. package/src/scripts/__tests__/notify-state-io.test.ts +29 -0
  772. package/src/scripts/__tests__/notify-tmux-injection.test.ts +26 -1
  773. package/src/scripts/__tests__/postinstall.test.ts +23 -1
  774. package/src/scripts/__tests__/reasoning-artifact-contract.test.ts +129 -0
  775. package/src/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.ts +67 -185
  776. package/src/scripts/__tests__/role-intent-durable-recovery-3181.test.ts +79 -178
  777. package/src/scripts/__tests__/smoke-packed-install.test.ts +525 -16
  778. package/src/scripts/__tests__/verify-native-release-assets.test.ts +240 -0
  779. package/src/scripts/codex-native-hook.ts +14238 -1429
  780. package/src/scripts/codex-native-pre-post.ts +43 -12
  781. package/src/scripts/generate-native-release-manifest.ts +83 -79
  782. package/src/scripts/notify-fallback-watcher.ts +91 -122
  783. package/src/scripts/notify-hook/auto-nudge.ts +71 -3
  784. package/src/scripts/notify-hook/state-io.ts +11 -0
  785. package/src/scripts/notify-hook/team-dispatch.ts +242 -75
  786. package/src/scripts/notify-hook/team-leader-nudge.ts +72 -22
  787. package/src/scripts/notify-hook/team-tmux-guard.ts +366 -39
  788. package/src/scripts/notify-hook/team-worker-stop.ts +43 -20
  789. package/src/scripts/notify-hook/team-worker.ts +82 -31
  790. package/src/scripts/notify-hook/tmux-injection.ts +33 -3
  791. package/src/scripts/notify-hook.ts +84 -82
  792. package/src/scripts/postinstall-advisory.ts +67 -0
  793. package/src/scripts/postinstall.ts +1 -1
  794. package/src/scripts/smoke-packed-install.ts +3608 -39
  795. package/src/scripts/sync-plugin-mirror.ts +17 -1
  796. package/src/scripts/verify-native-release-assets.ts +39 -31
  797. package/templates/AGENTS.md +1 -0
  798. package/dist/subagents/__tests__/adapted-role-binding.test.d.ts +0 -2
  799. package/dist/subagents/__tests__/adapted-role-binding.test.d.ts.map +0 -1
  800. package/dist/subagents/__tests__/adapted-role-binding.test.js +0 -988
  801. package/dist/subagents/__tests__/adapted-role-binding.test.js.map +0 -1
  802. package/dist/subagents/adapted-role-binding.d.ts +0 -17
  803. package/dist/subagents/adapted-role-binding.d.ts.map +0 -1
  804. package/dist/subagents/adapted-role-binding.js +0 -75
  805. package/dist/subagents/adapted-role-binding.js.map +0 -1
@@ -7,7 +7,7 @@ import { join } from 'node:path';
7
7
  import { spawn, spawnSync } from 'node:child_process';
8
8
  import { randomUUID } from 'node:crypto';
9
9
  import { initTeamState, enqueueDispatchRequest, readDispatchRequest } from '../../team/state.js';
10
- import { buildTmuxSessionName, buildWindowsMsysBackgroundHelperBootstrapScript } from '../../cli/index.js';
10
+ import { buildWindowsMsysBackgroundHelperBootstrapScript } from '../../cli/index.js';
11
11
  import { writeSessionStart } from '../session.js';
12
12
  const DEFAULT_AUTO_NUDGE_RESPONSE = 'continue with the current task only if it is already authorized';
13
13
  const INHERITED_OMX_ENV_KEYS = [
@@ -104,13 +104,15 @@ async function writeCanonicalWatcherTeamFixture(wd, { teamName = 'dispatch-team'
104
104
  },
105
105
  tmux_session: `${teamName}:0`,
106
106
  leader_pane_id: '%42',
107
+ leader_pane_pid: 4242,
108
+ tmux_pane_owner_id: `team:${teamName}`,
107
109
  hud_pane_id: null,
108
110
  resize_hook_name: null,
109
111
  resize_hook_target: null,
110
112
  worker_count: 1,
111
113
  next_task_id: 1,
112
114
  workers: [
113
- { name: 'worker-1', index: 1, pane_id: '%42', role: 'executor' },
115
+ { name: 'worker-1', index: 1, pane_id: '%42', pid: 4242, role: 'executor' },
114
116
  ],
115
117
  created_at: nowIso,
116
118
  };
@@ -119,8 +121,12 @@ async function writeCanonicalWatcherTeamFixture(wd, { teamName = 'dispatch-team'
119
121
  name: teamName,
120
122
  tmux_session: `${teamName}:0`,
121
123
  leader_pane_id: '%42',
124
+ leader_pane_pid: 4242,
125
+ tmux_pane_owner_id: `team:${teamName}`,
126
+ hud_pane_id: null,
127
+ hud_pane_pid: null,
122
128
  workers: [
123
- { name: 'worker-1', pane_id: '%42' },
129
+ { name: 'worker-1', pane_id: '%42', pid: 4242 },
124
130
  ],
125
131
  }, null, 2));
126
132
  await writeFile(join(teamDir, 'phase.json'), JSON.stringify({
@@ -129,6 +135,22 @@ async function writeCanonicalWatcherTeamFixture(wd, { teamName = 'dispatch-team'
129
135
  transitions: terminal ? [{ from: 'team-exec', to: 'complete', at: nowIso }] : [],
130
136
  }, null, 2));
131
137
  }
138
+ async function bindCanonicalDispatchWorkerFixture(wd, { teamName = 'dispatch-team', paneId = '%42', panePid = 4242, ownerId = `team:${teamName}`, hudPaneId = null, } = {}) {
139
+ for (const fileName of ['config.json', 'manifest.v2.json']) {
140
+ const statePath = join(wd, '.omx', 'state', 'team', teamName, fileName);
141
+ const state = JSON.parse(await readFile(statePath, 'utf8'));
142
+ const workers = Array.isArray(state.workers) ? state.workers : [];
143
+ state.tmux_pane_owner_id = ownerId;
144
+ state.leader_pane_id = paneId;
145
+ state.leader_pane_pid = panePid;
146
+ state.hud_pane_id = hudPaneId;
147
+ state.hud_pane_pid = null;
148
+ state.workers = workers.map((worker, index) => (index === 0 && worker && typeof worker === 'object'
149
+ ? { ...worker, pane_id: paneId, pid: panePid }
150
+ : worker));
151
+ await writeFile(statePath, JSON.stringify(state, null, 2));
152
+ }
153
+ }
132
154
  async function sleep(ms) {
133
155
  await new Promise(resolve => setTimeout(resolve, ms));
134
156
  }
@@ -171,6 +193,19 @@ set -eu
171
193
  echo "$@" >> "${tmuxLogPath}"
172
194
  cmd="$1"
173
195
  shift || true
196
+ if [[ "$cmd" == "list-panes" && "$#" -eq 3 && "$1" == "-a" && "$2" == "-F" && "$3" == "#{pane_id}\t#{pane_dead}\t#{pane_pid}" ]]; then
197
+ printf '%%42\t0\t${options.panePid ?? 4242}\n'
198
+ ${options.ralphPane ? `printf '${options.ralphPane.paneId.replace('%', '%%')}\\t0\\t${options.ralphPane.panePid}\\n'` : ''}
199
+ exit 0
200
+ fi
201
+ if [[ "$cmd" == "show-option" && "\${@: -1}" == "@omx_ralph_pane_owner_id" ]]; then
202
+ printf '%s\n' '${options.ralphPane?.paneOwner ?? options.paneOwner ?? 'ralph:owner'}'
203
+ exit 0
204
+ fi
205
+ if [[ "$cmd" == "show-option" && "\${@: -1}" == "@omx_team_pane_owner_id" ]]; then
206
+ printf '%s\n' '${options.paneOwner ?? 'team:dispatch-team'}'
207
+ exit 0
208
+ fi
174
209
  if [[ "$cmd" == "capture-pane" ]]; then
175
210
  if [[ -n "\${OMX_TEST_CAPTURE_SEQUENCE_FILE:-}" && -f "\${OMX_TEST_CAPTURE_SEQUENCE_FILE}" ]]; then
176
211
  counterFile="\${OMX_TEST_CAPTURE_COUNTER_FILE:-\${OMX_TEST_CAPTURE_SEQUENCE_FILE}.idx}"
@@ -217,12 +252,16 @@ if [[ "$cmd" == "display-message" ]]; then
217
252
  dirname "${tmuxLogPath}"
218
253
  exit 0
219
254
  fi
255
+ if [[ "$fmt" == *"#{pane_id}"* && "$fmt" == *"#{session_id}"* && "$fmt" == *"#{window_id}"* ]]; then
256
+ printf '%s\\037%s\\037%s\\037%s\\037%s\\037%s\\037%s\\037%s\n' '${options.ralphPane?.paneId ?? '%42'}' '${options.ralphPane?.panePid ?? options.panePid ?? 4242}' '${options.paneSession ?? 'session-test'}' '${options.paneSessionId ?? '$7'}' '${options.paneWindowId ?? '@9'}' '${options.ralphPane?.paneOwner ?? options.paneOwner ?? 'ralph:owner'}' '${options.paneCurrentCommand ?? 'codex'}' '${options.paneStartCommand ?? 'codex'}'
257
+ exit 0
258
+ fi
220
259
  if [[ "$fmt" == "#{pane_current_command}" ]]; then
221
- echo "codex"
260
+ echo "${options.paneCurrentCommand ?? 'codex'}"
222
261
  exit 0
223
262
  fi
224
263
  if [[ "$fmt" == "#S" ]]; then
225
- echo "\${OMX_TEST_TMUX_SESSION_NAME:-session-test}"
264
+ echo "${options.paneSession ?? '${OMX_TEST_TMUX_SESSION_NAME:-session-test}'}"
226
265
  exit 0
227
266
  fi
228
267
  exit 0
@@ -252,105 +291,74 @@ if [[ "$cmd" == "delete-buffer" ]]; then
252
291
  rm -f "${tmuxLogPath}.buffer"
253
292
  exit 0
254
293
  fi
255
- if [[ "$cmd" == "send-keys" ]]; then
256
- sendKeysArgs="$*"
257
- if [[ "${options.failSendKeys === true ? '1' : '0'}" == "1" ]]; then
258
- echo "send failed" >&2
259
- exit 1
260
- fi
261
- if [[ -n "${options.failSendKeysMatch || ''}" && "$sendKeysArgs" == *"${options.failSendKeysMatch || ''}"* ]]; then
262
- echo "send failed" >&2
263
- exit 1
264
- fi
265
- exit 0
266
- fi
267
- if [[ "$cmd" == "list-panes" ]]; then
294
+ if [[ "$cmd" == "if-shell" ]]; then
295
+ forceFormat=0
268
296
  target=""
269
- while [[ "$#" -gt 0 ]]; do
270
- case "$1" in
271
- -t)
297
+ while [[ "\$#" -gt 0 ]]; do
298
+ case "\$1" in
299
+ -F)
300
+ forceFormat=1
272
301
  shift
273
- target="$1"
302
+ ;;
303
+ -t)
304
+ target="\$2"
305
+ shift 2
306
+ ;;
307
+ *)
308
+ break
274
309
  ;;
275
310
  esac
276
- shift || true
277
311
  done
278
- if [[ -n "$target" ]]; then
279
- printf "%%42\tcodex\tcodex\n"
312
+ condition="\${1:-}"
313
+ success="\${2:-}"
314
+ denied="\${3:-}"
315
+ currentCommand="${options.paneCurrentCommand ?? 'codex'}"
316
+ startCommand="${options.paneStartCommand ?? 'codex'}"
317
+ sessionName="${options.paneSession ?? 'session-test'}"
318
+ sessionId='${options.paneSessionId ?? '$7'}'
319
+ windowId='${options.paneWindowId ?? '@9'}'
320
+ paneId="${options.ralphPane?.paneId ?? '%42'}"
321
+ panePid="${options.ralphPane?.panePid ?? options.panePid ?? 4242}"
322
+ paneOwner="${options.ralphPane?.paneOwner ?? options.paneOwner ?? 'ralph:owner'}"
323
+ if [[ "$forceFormat" != "1" || "$target" != "$paneId" || "$condition" != *"#{pane_id},$paneId"* || "$condition" != *"#{pane_dead},0"* || "$condition" != *"#{pane_pid},$panePid"* || "$condition" != *"#{session_name},$sessionName"* || "$condition" != *"#{session_id},$sessionId"* || "$condition" != *"#{window_id},$windowId"* || "$condition" != *"#{@omx_ralph_pane_owner_id},$paneOwner"* || "$condition" != *"#{pane_current_command},$currentCommand"* || "$condition" != *"#{pane_start_command},$startCommand"* ]]; then
324
+ if [[ "\$denied" == "display-message -p __omx_ralph_input_denied__" ]]; then
325
+ printf '__omx_ralph_input_denied__\n'
326
+ fi
280
327
  exit 0
281
328
  fi
282
- echo "%42 1"
283
- exit 0
284
- fi
285
- exit 0
286
- `;
287
- }
288
- function buildManagedRalphTmux(tmuxLogPath, options) {
289
- const { cwd, managedSessionName, anchorPane, livePane, codexPanes, missingAnchor = false } = options;
290
- const panes = (codexPanes && codexPanes.length > 0)
291
- ? codexPanes
292
- : [{ paneId: livePane, active: true, currentCommand: 'codex', startCommand: 'codex' }];
293
- const listPaneOutput = panes
294
- .map((pane) => {
295
- const paneId = pane.paneId;
296
- const active = pane.active ? '1' : '0';
297
- const currentCommand = pane.currentCommand || 'codex';
298
- const startCommand = pane.startCommand || 'codex';
299
- return `${paneId}\t${active}\t${currentCommand}\t${startCommand}`;
300
- })
301
- .join('\n');
302
- const paneCommandBranches = panes
303
- .map((pane) => {
304
- const currentCommand = (pane.currentCommand || 'codex').replace(/"/g, '\\"');
305
- const startCommand = (pane.startCommand || 'codex').replace(/"/g, '\\"');
306
- return ` if [[ "$format" == "#{pane_current_command}" && "$target" == "${pane.paneId}" ]]; then
307
- echo "${currentCommand}"
329
+ receiptSeparator=' \\; display-message -p '
330
+ if [[ "\$success" != *"\$receiptSeparator"* ]]; then
331
+ printf '__omx_ralph_input_denied__\n'
308
332
  exit 0
309
333
  fi
310
- if [[ "$format" == "#{pane_start_command}" && "$target" == "${pane.paneId}" ]]; then
311
- echo "${startCommand}"
334
+ sendCommand="\${success%%"\$receiptSeparator"*}"
335
+ receipt="\${success#*"\$receiptSeparator"}"
336
+ if [[ -z "\$sendCommand" || ! "\$receipt" =~ ^__omx_ralph_input_[a-z0-9]+__$ ]]; then
337
+ printf '__omx_ralph_input_denied__\n'
312
338
  exit 0
313
- fi`;
314
- })
315
- .join('\n');
316
- return `#!/usr/bin/env bash
317
- set -eu
318
- echo "$@" >> "${tmuxLogPath}"
319
- cmd="$1"
320
- shift || true
321
- if [[ "$cmd" == "display-message" ]]; then
322
- target=""
323
- format=""
324
- while [[ "$#" -gt 0 ]]; do
325
- case "$1" in
326
- -p) shift ;;
327
- -t) target="$2"; shift 2 ;;
328
- *) format="$1"; shift ;;
329
- esac
330
- done
331
- if [[ "$target" == "${anchorPane}" && "${missingAnchor ? '1' : '0'}" == "1" ]]; then
332
- echo "pane missing" >&2
333
- exit 1
334
339
  fi
335
- if [[ "$format" == "#{pane_in_mode}" ]]; then
336
- echo "0"
340
+ eval "set -- \$sendCommand"
341
+ if [[ "\$#" -lt 4 || "\$1" != "send-keys" || "\$2" != "-t" || "\$3" != "\$paneId" || ( "\$4" != "-l" && "\$4" != "C-m" ) || ( "\$4" == "-l" && "\$#" -ne 5 ) || ( "\$4" == "C-m" && "\$#" -ne 4 ) ]]; then
342
+ printf '__omx_ralph_input_denied__\n'
337
343
  exit 0
338
344
  fi
339
- if [[ "$format" == "#{pane_id}" ]]; then
340
- echo "$target"
341
- exit 0
345
+ sendKeysArgs="\${*:2}"
346
+ if [[ "${options.failSendKeys === true ? '1' : '0'}" == "1" || ( -n "${options.failSendKeysMatch ?? ''}" && "\$sendKeysArgs" == *"${options.failSendKeysMatch ?? ''}"* ) ]]; then
347
+ echo 'send failed' >&2
348
+ exit 1
342
349
  fi
343
- if [[ "$format" == "#{pane_current_path}" ]]; then
344
- echo "${cwd}"
345
- exit 0
350
+ echo "send-keys \$sendKeysArgs" >> "${tmuxLogPath}"
351
+ printf '%s\n' "\$receipt"
352
+ exit 0
353
+ fi
354
+ if [[ "$cmd" == "send-keys" ]]; then
355
+ sendKeysArgs="$*"
356
+ if [[ "${options.failSendKeys === true ? '1' : '0'}" == "1" ]]; then
357
+ echo "send failed" >&2
358
+ exit 1
346
359
  fi
347
- ${paneCommandBranches}
348
- if [[ "$format" == "#S" ]]; then
349
- if [[ "$target" == "${anchorPane}" || "$target" == "${livePane}" ]]; then
350
- echo "${managedSessionName}"
351
- exit 0
352
- fi
353
- echo "unknown target" >&2
360
+ if [[ -n "${options.failSendKeysMatch || ''}" && "$sendKeysArgs" == *"${options.failSendKeysMatch || ''}"* ]]; then
361
+ echo "send failed" >&2
354
362
  exit 1
355
363
  fi
356
364
  exit 0
@@ -359,56 +367,28 @@ if [[ "$cmd" == "list-panes" ]]; then
359
367
  target=""
360
368
  while [[ "$#" -gt 0 ]]; do
361
369
  case "$1" in
362
- -F) shift 2 ;;
363
- -t) shift; target="$1" ;;
370
+ -t)
371
+ shift
372
+ target="$1"
373
+ ;;
364
374
  esac
365
375
  shift || true
366
376
  done
367
- if [[ "$target" == "${managedSessionName}" ]]; then
368
- printf '%s\n' "${listPaneOutput}"
377
+ if [[ -n "$target" ]]; then
378
+ printf "%%42\tcodex\tcodex\n"
369
379
  exit 0
370
380
  fi
371
- echo "can't find session" >&2
372
- exit 1
373
- fi
374
- if [[ "$cmd" == "capture-pane" ]]; then
375
- exit 0
376
- fi
377
- if [[ "$cmd" == "set-buffer" ]]; then
378
- printf '%s' "\${@: -1}" > "${tmuxLogPath}.buffer"
379
- exit 0
380
- fi
381
- if [[ "$cmd" == "show-buffer" ]]; then
382
- if [[ -f "${tmuxLogPath}.buffer" ]]; then cat "${tmuxLogPath}.buffer"; fi
383
- exit 0
384
- fi
385
- if [[ "$cmd" == "paste-buffer" ]]; then
386
- target=""
387
- while [[ "$#" -gt 0 ]]; do
388
- case "$1" in
389
- -t) target="$2"; shift 2 ;;
390
- *) shift ;;
391
- esac
392
- done
393
- if [[ -f "${tmuxLogPath}.buffer" ]]; then
394
- echo "send-keys -t \${target} -l $(cat "${tmuxLogPath}.buffer")" >> "${tmuxLogPath}"
395
- fi
396
- exit 0
397
- fi
398
- if [[ "$cmd" == "delete-buffer" ]]; then
399
- rm -f "${tmuxLogPath}.buffer"
400
- exit 0
401
- fi
402
- if [[ "$cmd" == "send-keys" ]]; then
381
+ echo "%42 1"
403
382
  exit 0
404
383
  fi
405
384
  exit 0
406
385
  `;
407
386
  }
408
387
  function buildCleanNotifyEnv(overrides = {}) {
409
- return {
388
+ const env = {
410
389
  ...process.env,
411
390
  OMX_TEAM_WORKER: '',
391
+ OMX_TEAM_INTERNAL_WORKER: '',
412
392
  OMX_TEAM_STATE_ROOT: '',
413
393
  OMX_TEAM_LEADER_CWD: '',
414
394
  OMX_MODEL_INSTRUCTIONS_FILE: '',
@@ -420,6 +400,10 @@ function buildCleanNotifyEnv(overrides = {}) {
420
400
  TMUX_PANE: '',
421
401
  ...overrides,
422
402
  };
403
+ if (overrides.OMX_TEAM_WORKER && overrides.OMX_TEAM_INTERNAL_WORKER === undefined) {
404
+ env.OMX_TEAM_INTERNAL_WORKER = overrides.OMX_TEAM_WORKER;
405
+ }
406
+ return env;
423
407
  }
424
408
  describe('notify-fallback watcher', () => {
425
409
  it('uses offset-bounded rollout reads instead of re-reading whole tracked files', async () => {
@@ -1011,6 +995,13 @@ describe('notify-fallback watcher', () => {
1011
995
  active: true,
1012
996
  current_phase: 'executing',
1013
997
  tmux_pane_id: '%42',
998
+ tmux_pane_pid: 4242,
999
+ tmux_session_name: 'session-test',
1000
+ tmux_session_id: '$7',
1001
+ tmux_window_id: '@9',
1002
+ tmux_pane_owner_id: 'ralph:owner',
1003
+ tmux_pane_current_command: 'codex',
1004
+ tmux_pane_start_command: 'codex',
1014
1005
  }, null, 2));
1015
1006
  await writeFile(join(wd, '.omx', 'state', 'team-state.json'), JSON.stringify({
1016
1007
  active: true,
@@ -1074,6 +1065,13 @@ describe('notify-fallback watcher', () => {
1074
1065
  active: true,
1075
1066
  current_phase: 'executing',
1076
1067
  tmux_pane_id: '%42',
1068
+ tmux_pane_pid: 4242,
1069
+ tmux_session_name: 'session-test',
1070
+ tmux_session_id: '$7',
1071
+ tmux_window_id: '@9',
1072
+ tmux_pane_owner_id: 'ralph:owner',
1073
+ tmux_pane_current_command: 'codex',
1074
+ tmux_pane_start_command: 'codex',
1077
1075
  }, null, 2));
1078
1076
  await writeFile(join(wd, '.omx', 'state', 'team-state.json'), JSON.stringify({
1079
1077
  active: true,
@@ -1129,6 +1127,8 @@ describe('notify-fallback watcher', () => {
1129
1127
  name: 'dispatch-team',
1130
1128
  tmux_session: 'omx-team-dispatch-team',
1131
1129
  leader_pane_id: '%42',
1130
+ leader_pane_pid: 4242,
1131
+ tmux_pane_owner_id: 'team:dispatch-team',
1132
1132
  }, null, 2));
1133
1133
  const watcherScript = new URL('../../../dist/scripts/notify-fallback-watcher.js', import.meta.url).pathname;
1134
1134
  const notifyHook = new URL('../../../dist/scripts/notify-hook.js', import.meta.url).pathname;
@@ -1296,6 +1296,45 @@ describe('notify-fallback watcher', () => {
1296
1296
  await rm(wd, { recursive: true, force: true });
1297
1297
  }
1298
1298
  });
1299
+ it('does not keep leader mailbox nudges alive after the team becomes terminal', async () => {
1300
+ const wd = await mkdtemp(join(tmpdir(), 'omx-fallback-terminal-mailbox-'));
1301
+ const fakeBinDir = join(wd, 'fake-bin');
1302
+ const tmuxLogPath = join(wd, 'tmux.log');
1303
+ try {
1304
+ await writeCanonicalWatcherTeamFixture(wd, { coarseState: 'inactive', terminal: true });
1305
+ await mkdir(join(wd, '.omx', 'state', 'team', 'dispatch-team', 'mailbox'), { recursive: true });
1306
+ await mkdir(fakeBinDir, { recursive: true });
1307
+ await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath));
1308
+ await chmod(join(fakeBinDir, 'tmux'), 0o755);
1309
+ await writeFile(join(wd, '.omx', 'state', 'team', 'dispatch-team', 'mailbox', 'leader-fixed.json'), JSON.stringify({
1310
+ worker: 'leader-fixed',
1311
+ messages: [{
1312
+ message_id: 'terminal-msg',
1313
+ from_worker: 'worker-1',
1314
+ to_worker: 'leader-fixed',
1315
+ body: 'must not wake a terminal team',
1316
+ created_at: new Date().toISOString(),
1317
+ }],
1318
+ }, null, 2));
1319
+ const watcherScript = new URL('../../../dist/scripts/notify-fallback-watcher.js', import.meta.url).pathname;
1320
+ const notifyHook = new URL('../../../dist/scripts/notify-hook.js', import.meta.url).pathname;
1321
+ const result = spawnSync(process.execPath, [watcherScript, '--once', '--cwd', wd, '--notify-script', notifyHook], {
1322
+ encoding: 'utf-8',
1323
+ env: buildCleanNotifyEnv({
1324
+ PATH: `${fakeBinDir}:${process.env.PATH || ''}`,
1325
+ OMX_SESSION_ID: 'sess-current',
1326
+ }),
1327
+ });
1328
+ assert.equal(result.status, 0, result.stderr || result.stdout);
1329
+ assert.equal(await readFile(tmuxLogPath, 'utf8').catch(() => ''), '');
1330
+ const logPath = join(wd, '.omx', 'logs', `notify-fallback-${new Date().toISOString().split('T')[0]}.jsonl`);
1331
+ const logEntries = await readJsonLines(logPath);
1332
+ assert.equal(logEntries.some((entry) => entry.type === 'leader_nudge_tick'), false);
1333
+ }
1334
+ finally {
1335
+ await rm(wd, { recursive: true, force: true });
1336
+ }
1337
+ });
1299
1338
  it('does not run stalled-worker leader nudges from the fallback watcher when the leader is not stale', async () => {
1300
1339
  const wd = await mkdtemp(join(tmpdir(), 'omx-fallback-worker-stall-nudge-'));
1301
1340
  const fakeBinDir = join(wd, 'fake-bin');
@@ -1390,7 +1429,7 @@ if [[ "$cmd" == "list-panes" ]]; then
1390
1429
  printf "%%42 12345\n%%10 12346\n%%11 12347\n"
1391
1430
  exit 0
1392
1431
  fi
1393
- echo "%42 1"
1432
+ printf "%%42\t0\t12345\n%%10\t0\t12346\n%%11\t0\t12347\n"
1394
1433
  exit 0
1395
1434
  fi
1396
1435
  exit 0
@@ -1411,9 +1450,10 @@ exit 0
1411
1450
  name: 'dispatch-team',
1412
1451
  tmux_session: 'omx-team-dispatch-team',
1413
1452
  leader_pane_id: '%42',
1453
+ leader_pane_pid: 12345,
1414
1454
  workers: [
1415
- { name: 'worker-1', index: 1, pane_id: '%10' },
1416
- { name: 'worker-2', index: 2, pane_id: '%11' },
1455
+ { name: 'worker-1', index: 1, pane_id: '%10', pid: 12346 },
1456
+ { name: 'worker-2', index: 2, pane_id: '%11', pid: 12347 },
1417
1457
  ],
1418
1458
  }, null, 2));
1419
1459
  await writeFile(join(wd, '.omx', 'state', 'team', 'dispatch-team', 'tasks', 'task-1.json'), JSON.stringify({
@@ -1480,7 +1520,7 @@ exit 0
1480
1520
  }),
1481
1521
  });
1482
1522
  assert.equal(result.status, 0, result.stderr || result.stdout);
1483
- const tmuxLog = await readFile(tmuxLogPath, 'utf8');
1523
+ const tmuxLog = await readFile(tmuxLogPath, 'utf8').catch(() => '');
1484
1524
  assert.doesNotMatch(tmuxLog, /worker panes stalled/);
1485
1525
  assert.doesNotMatch(tmuxLog, /no progress 3m/);
1486
1526
  assert.doesNotMatch(tmuxLog, /leader stale/);
@@ -1862,6 +1902,7 @@ exit 0
1862
1902
  await chmod(join(fakeBinDir, 'tmux'), 0o755);
1863
1903
  await writeFile(captureFile, '... ping ...');
1864
1904
  await initTeamState('dispatch-team', 'task', 'executor', 1, wd);
1905
+ await bindCanonicalDispatchWorkerFixture(wd);
1865
1906
  const queued = await enqueueDispatchRequest('dispatch-team', {
1866
1907
  kind: 'inbox',
1867
1908
  to_worker: 'worker-1',
@@ -1909,12 +1950,19 @@ exit 0
1909
1950
  try {
1910
1951
  await mkdir(stateDir, { recursive: true });
1911
1952
  await mkdir(fakeBinDir, { recursive: true });
1912
- await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath));
1953
+ await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath, { paneOwner: 'ralph:owner' }));
1913
1954
  await chmod(join(fakeBinDir, 'tmux'), 0o755);
1914
1955
  await writeFile(join(stateDir, 'ralph-state.json'), JSON.stringify({
1915
1956
  active: true,
1916
1957
  current_phase: 'executing',
1917
1958
  tmux_pane_id: '%42',
1959
+ tmux_pane_pid: 4242,
1960
+ tmux_session_name: 'session-test',
1961
+ tmux_session_id: '$7',
1962
+ tmux_window_id: '@9',
1963
+ tmux_pane_owner_id: 'ralph:owner',
1964
+ tmux_pane_current_command: 'codex',
1965
+ tmux_pane_start_command: 'codex',
1918
1966
  }, null, 2));
1919
1967
  await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({
1920
1968
  last_progress_at: new Date(Date.now() - 61_000).toISOString(),
@@ -1956,6 +2004,46 @@ exit 0
1956
2004
  await rm(wd, { recursive: true, force: true });
1957
2005
  }
1958
2006
  });
2007
+ it('fails closed for recycled, incomplete, HUD, taken-over, and command-drift Ralph pane bindings', async () => {
2008
+ const cases = [
2009
+ [{ tmux_pane_pid: 4242, tmux_session_name: 'session-test', tmux_session_id: '$7', tmux_window_id: '@9', tmux_pane_owner_id: 'ralph:owner' }, { paneOwner: 'ralph:owner', panePid: 4243 }],
2010
+ [{}, { paneOwner: 'ralph:owner' }],
2011
+ [{ tmux_pane_pid: 4242, tmux_session_name: 'session-test', tmux_session_id: '$7', tmux_window_id: '@9', tmux_pane_owner_id: 'team:dispatch-team' }, { paneOwner: 'team:dispatch-team' }],
2012
+ [{ tmux_pane_pid: 4242, tmux_session_name: 'session-test', tmux_session_id: '$7', tmux_window_id: '@9', tmux_pane_owner_id: 'ralph:owner' }, { paneOwner: 'ralph:owner', paneSession: 'other-session' }],
2013
+ [{ tmux_pane_pid: 4242, tmux_session_name: 'session-test', tmux_session_id: '$7', tmux_window_id: '@9', tmux_pane_owner_id: 'ralph:owner' }, { paneOwner: 'ralph:owner', paneSessionId: '$8' }],
2014
+ [{ tmux_pane_pid: 4242, tmux_session_name: 'session-test', tmux_session_id: '$7', tmux_window_id: '@9', tmux_pane_owner_id: 'ralph:owner' }, { paneOwner: 'ralph:owner', paneWindowId: '@10' }],
2015
+ [{ tmux_pane_pid: 4242, tmux_session_name: 'session-test', tmux_session_id: '$7', tmux_window_id: '@9', tmux_pane_owner_id: 'ralph:owner' }, { paneOwner: 'ralph:other' }],
2016
+ [{ tmux_pane_pid: 4242.9, tmux_session_name: 'session-test', tmux_session_id: '$7', tmux_window_id: '@9', tmux_pane_owner_id: 'ralph:owner' }, { paneOwner: 'ralph:owner' }],
2017
+ [{ tmux_pane_pid: '4.242e3', tmux_session_name: 'session-test', tmux_session_id: '$7', tmux_window_id: '@9', tmux_pane_owner_id: 'ralph:owner' }, { paneOwner: 'ralph:owner' }],
2018
+ [{ tmux_pane_pid: '9007199254740992', tmux_session_name: 'session-test', tmux_session_id: '$7', tmux_window_id: '@9', tmux_pane_owner_id: 'ralph:owner' }, { paneOwner: 'ralph:owner' }],
2019
+ [{ tmux_pane_pid: 4242, tmux_session_name: 'session-test', tmux_session_id: '$7', tmux_window_id: '@9', tmux_pane_owner_id: 'ralph:owner', tmux_pane_current_command: 'codex', tmux_pane_start_command: 'codex' }, { paneOwner: 'ralph:owner', paneCurrentCommand: 'node' }],
2020
+ ];
2021
+ const watcherScript = new URL('../../../dist/scripts/notify-fallback-watcher.js', import.meta.url).pathname;
2022
+ const notifyHook = new URL('../../../dist/scripts/notify-hook.js', import.meta.url).pathname;
2023
+ for (const [stateBinding, tmux] of cases) {
2024
+ const wd = await mkdtemp(join(tmpdir(), 'omx-fallback-ralph-binding-'));
2025
+ const stateDir = join(wd, '.omx', 'state');
2026
+ const fakeBinDir = join(wd, 'fake-bin');
2027
+ const tmuxLogPath = join(wd, 'tmux.log');
2028
+ try {
2029
+ await mkdir(stateDir, { recursive: true });
2030
+ await mkdir(fakeBinDir, { recursive: true });
2031
+ await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath, tmux));
2032
+ await chmod(join(fakeBinDir, 'tmux'), 0o755);
2033
+ await writeFile(join(stateDir, 'ralph-state.json'), JSON.stringify({ active: true, current_phase: 'executing', tmux_pane_id: '%42', ...stateBinding }));
2034
+ await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({ last_progress_at: new Date(Date.now() - 61_000).toISOString() }));
2035
+ await writeFile(join(stateDir, 'notify-fallback-state.json'), JSON.stringify({ ralph_continue_steer: { last_sent_at: new Date(Date.now() - 61_000).toISOString() } }));
2036
+ const result = spawnSync(process.execPath, [watcherScript, '--once', '--cwd', wd, '--notify-script', notifyHook], { encoding: 'utf-8', env: { ...buildCleanNotifyEnv(), PATH: `${fakeBinDir}:${process.env.PATH || ''}` } });
2037
+ assert.equal(result.status, 0, result.stderr || result.stdout);
2038
+ assert.doesNotMatch(await readFile(tmuxLogPath, 'utf-8').catch(() => ''), /send-keys -t %42/);
2039
+ if ('paneCurrentCommand' in tmux)
2040
+ assert.match(await readFile(tmuxLogPath, 'utf-8'), /if-shell -F -t %42/);
2041
+ }
2042
+ finally {
2043
+ await rm(wd, { recursive: true, force: true });
2044
+ }
2045
+ }
2046
+ });
1959
2047
  it('suppresses Ralph continue steer when hud progress is still fresh after cooldown', async () => {
1960
2048
  const wd = await mkdtemp(join(tmpdir(), 'omx-fallback-ralph-progress-fresh-'));
1961
2049
  const fakeBinDir = join(wd, 'fake-bin');
@@ -1971,6 +2059,13 @@ exit 0
1971
2059
  active: true,
1972
2060
  current_phase: 'executing',
1973
2061
  tmux_pane_id: '%42',
2062
+ tmux_pane_pid: 4242,
2063
+ tmux_session_name: 'session-test',
2064
+ tmux_session_id: '$7',
2065
+ tmux_window_id: '@9',
2066
+ tmux_pane_owner_id: 'ralph:owner',
2067
+ tmux_pane_current_command: 'codex',
2068
+ tmux_pane_start_command: 'codex',
1974
2069
  }, null, 2));
1975
2070
  await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({
1976
2071
  last_progress_at: new Date(Date.now() - 5_000).toISOString(),
@@ -2007,12 +2102,19 @@ exit 0
2007
2102
  try {
2008
2103
  await mkdir(stateDir, { recursive: true });
2009
2104
  await mkdir(fakeBinDir, { recursive: true });
2010
- await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath));
2105
+ await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath, { paneOwner: 'ralph:owner' }));
2011
2106
  await chmod(join(fakeBinDir, 'tmux'), 0o755);
2012
2107
  await writeFile(join(stateDir, 'ralph-state.json'), JSON.stringify({
2013
2108
  active: true,
2014
2109
  current_phase: 'executing',
2015
2110
  tmux_pane_id: '%42',
2111
+ tmux_pane_pid: 4242,
2112
+ tmux_session_name: 'session-test',
2113
+ tmux_session_id: '$7',
2114
+ tmux_window_id: '@9',
2115
+ tmux_pane_owner_id: 'ralph:owner',
2116
+ tmux_pane_current_command: 'codex',
2117
+ tmux_pane_start_command: 'codex',
2016
2118
  }, null, 2));
2017
2119
  await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({
2018
2120
  last_progress_at: new Date(Date.now() - 61_000).toISOString(),
@@ -2059,6 +2161,13 @@ exit 0
2059
2161
  current_phase: 'starting',
2060
2162
  started_at: new Date(Date.now() - 180_000).toISOString(),
2061
2163
  tmux_pane_id: '%42',
2164
+ tmux_pane_pid: 4242,
2165
+ tmux_session_name: 'session-test',
2166
+ tmux_session_id: '$7',
2167
+ tmux_window_id: '@9',
2168
+ tmux_pane_owner_id: 'ralph:owner',
2169
+ tmux_pane_current_command: 'codex',
2170
+ tmux_pane_start_command: 'codex',
2062
2171
  }, null, 2));
2063
2172
  await writeFile(join(sessionStateDir, 'hud-state.json'), JSON.stringify({
2064
2173
  last_progress_at: new Date(Date.now() - 180_000).toISOString(),
@@ -2105,6 +2214,13 @@ exit 0
2105
2214
  active: true,
2106
2215
  current_phase: 'executing',
2107
2216
  tmux_pane_id: '%42',
2217
+ tmux_pane_pid: 4242,
2218
+ tmux_session_name: 'session-test',
2219
+ tmux_session_id: '$7',
2220
+ tmux_window_id: '@9',
2221
+ tmux_pane_owner_id: 'ralph:owner',
2222
+ tmux_pane_current_command: 'codex',
2223
+ tmux_pane_start_command: 'codex',
2108
2224
  owner_omx_session_id: omxSessionId,
2109
2225
  owner_codex_session_id: codexSessionId,
2110
2226
  }, null, 2));
@@ -2179,6 +2295,13 @@ exit 0
2179
2295
  active: true,
2180
2296
  current_phase: 'executing',
2181
2297
  tmux_pane_id: '%42',
2298
+ tmux_pane_pid: 4242,
2299
+ tmux_session_name: 'session-test',
2300
+ tmux_session_id: '$7',
2301
+ tmux_window_id: '@9',
2302
+ tmux_pane_owner_id: 'ralph:owner',
2303
+ tmux_pane_current_command: 'codex',
2304
+ tmux_pane_start_command: 'codex',
2182
2305
  }, null, 2));
2183
2306
  await writeFile(statePath, JSON.stringify({
2184
2307
  ralph_continue_steer: {
@@ -2245,7 +2368,7 @@ exit 0
2245
2368
  const run = spawnSync(process.execPath, [watcherScript, '--once', '--cwd', wd, '--notify-script', notifyHook, '--poll-ms', '50'], { encoding: 'utf-8', env });
2246
2369
  assert.equal(run.status, 0, run.stderr || run.stdout);
2247
2370
  const watcherState = JSON.parse(await readFile(statePath, 'utf-8'));
2248
- assert.equal(watcherState.ralph_continue_steer?.last_reason, 'pane_missing');
2371
+ assert.equal(watcherState.ralph_continue_steer?.last_reason, 'pane_binding_missing');
2249
2372
  assert.equal(watcherState.ralph_continue_steer?.pane_id, '');
2250
2373
  const tmuxLog = await readFile(tmuxLogPath, 'utf8').catch(() => '');
2251
2374
  assert.equal(/send-keys -t %42 -l Ralph loop active continue \[OMX_TMUX_INJECT\]/.test(tmuxLog), false);
@@ -2255,412 +2378,52 @@ exit 0
2255
2378
  await rm(wd, { recursive: true, force: true });
2256
2379
  }
2257
2380
  });
2258
- it('rebinds a stale-but-present session-scoped Ralph shell pane to the live pane before continue steer', async () => {
2259
- const wd = await mkdtemp(join(tmpdir(), 'omx-fallback-ralph-rebind-stale-anchor-'));
2260
- const fakeBinDir = join(wd, 'fake-bin');
2261
- const stateDir = join(wd, '.omx', 'state');
2262
- const tmuxLogPath = join(wd, 'tmux.log');
2263
- const watcherStatePath = join(stateDir, 'notify-fallback-state.json');
2264
- const sessionId = 'sess-ralph-rebind';
2265
- const sessionStateDir = join(stateDir, 'sessions', sessionId);
2266
- const ralphStatePath = join(sessionStateDir, 'ralph-state.json');
2267
- const anchorPane = '%99';
2268
- const livePane = '%42';
2269
- try {
2270
- await mkdir(sessionStateDir, { recursive: true });
2271
- await mkdir(fakeBinDir, { recursive: true });
2272
- await writeSessionStart(wd, sessionId);
2273
- const managedSessionName = buildTmuxSessionName(wd, sessionId);
2274
- await writeFile(join(fakeBinDir, 'tmux'), buildManagedRalphTmux(tmuxLogPath, {
2275
- cwd: wd,
2276
- managedSessionName,
2277
- anchorPane,
2278
- livePane,
2279
- codexPanes: [
2280
- { paneId: anchorPane, active: false, currentCommand: 'sh', startCommand: 'bash' },
2281
- { paneId: '%41', active: false, currentCommand: 'codex', startCommand: 'codex' },
2282
- { paneId: livePane, active: true, currentCommand: 'codex', startCommand: 'codex' },
2283
- ],
2284
- }));
2285
- await chmod(join(fakeBinDir, 'tmux'), 0o755);
2286
- await writeFile(ralphStatePath, JSON.stringify({
2287
- active: true,
2288
- current_phase: 'executing',
2289
- tmux_pane_id: anchorPane,
2290
- }, null, 2));
2291
- await writeFile(join(sessionStateDir, 'hud-state.json'), JSON.stringify({
2292
- last_progress_at: new Date(Date.now() - 61_000).toISOString(),
2293
- }, null, 2));
2294
- await writeFile(watcherStatePath, JSON.stringify({
2295
- ralph_continue_steer: {
2296
- last_sent_at: new Date(Date.now() - 61_000).toISOString(),
2297
- },
2298
- }, null, 2));
2299
- const watcherScript = new URL('../../../dist/scripts/notify-fallback-watcher.js', import.meta.url).pathname;
2300
- const notifyHook = new URL('../../../dist/scripts/notify-hook.js', import.meta.url).pathname;
2301
- const run = spawnSync(process.execPath, [watcherScript, '--once', '--cwd', wd, '--notify-script', notifyHook, '--poll-ms', '50'], {
2302
- encoding: 'utf-8',
2303
- env: buildCleanNotifyEnv({
2304
- PATH: `${fakeBinDir}:${process.env.PATH || ''}`,
2305
- }),
2306
- });
2307
- assert.equal(run.status, 0, run.stderr || run.stdout);
2308
- const persistedRalph = JSON.parse(await readFile(ralphStatePath, 'utf-8'));
2309
- assert.equal(persistedRalph.tmux_pane_id, livePane);
2310
- assert.match(persistedRalph.tmux_pane_set_at ?? '', /^\d{4}-\d{2}-\d{2}T/);
2311
- const watcherState = JSON.parse(await readFile(watcherStatePath, 'utf-8'));
2312
- assert.equal(watcherState.ralph_continue_steer?.last_reason, 'sent');
2313
- assert.equal(watcherState.ralph_continue_steer?.pane_id, livePane);
2314
- const tmuxLog = await readFile(tmuxLogPath, 'utf8');
2315
- assert.match(tmuxLog, /send-keys -t %42 -l Ralph loop active continue \[OMX_TMUX_INJECT\]/);
2316
- assert.doesNotMatch(tmuxLog, /send-keys -t %99 -l Ralph loop active continue \[OMX_TMUX_INJECT\]/);
2317
- }
2318
- finally {
2319
- await rm(wd, { recursive: true, force: true });
2320
- }
2321
- });
2322
- it('preserves newer Ralph state fields when a pane rebound happens after the state file advances', async () => {
2323
- const wd = await mkdtemp(join(tmpdir(), 'omx-fallback-ralph-rebind-state-merge-'));
2324
- const fakeBinDir = join(wd, 'fake-bin');
2325
- const stateDir = join(wd, '.omx', 'state');
2326
- const tmuxLogPath = join(wd, 'tmux.log');
2327
- const watcherStatePath = join(stateDir, 'notify-fallback-state.json');
2328
- const sessionId = 'sess-ralph-rebind-merge';
2329
- const sessionStateDir = join(stateDir, 'sessions', sessionId);
2330
- const ralphStatePath = join(sessionStateDir, 'ralph-state.json');
2331
- const anchorPane = '%99';
2332
- const livePane = '%42';
2333
- try {
2334
- await mkdir(sessionStateDir, { recursive: true });
2335
- await mkdir(fakeBinDir, { recursive: true });
2336
- await writeSessionStart(wd, sessionId);
2337
- const managedSessionName = buildTmuxSessionName(wd, sessionId);
2338
- const fakeTmux = `#!/usr/bin/env bash
2339
- set -eu
2340
- echo "$@" >> "${tmuxLogPath}"
2341
- cmd="$1"
2342
- shift || true
2343
- if [[ "$cmd" == "display-message" ]]; then
2344
- target=""
2345
- format=""
2346
- while [[ "$#" -gt 0 ]]; do
2347
- case "$1" in
2348
- -p) shift ;;
2349
- -t) target="$2"; shift 2 ;;
2350
- *) format="$1"; shift ;;
2351
- esac
2352
- done
2353
- if [[ "$format" == "#{pane_in_mode}" ]]; then
2354
- echo "0"
2355
- exit 0
2356
- fi
2357
- if [[ "$format" == "#{pane_id}" ]]; then
2358
- echo "$target"
2359
- exit 0
2360
- fi
2361
- if [[ "$format" == "#{pane_current_path}" ]]; then
2362
- echo "${wd}"
2363
- exit 0
2364
- fi
2365
- if [[ "$format" == "#{pane_current_command}" && "$target" == "${anchorPane}" ]]; then
2366
- echo "sh"
2367
- exit 0
2368
- fi
2369
- if [[ "$format" == "#{pane_start_command}" && "$target" == "${anchorPane}" ]]; then
2370
- echo "bash"
2371
- exit 0
2372
- fi
2373
- if [[ "$format" == "#S" && "$target" == "${anchorPane}" ]]; then
2374
- echo "${managedSessionName}"
2375
- exit 0
2376
- fi
2377
- exit 0
2378
- fi
2379
- if [[ "$cmd" == "list-panes" ]]; then
2380
- target=""
2381
- while [[ "$#" -gt 0 ]]; do
2382
- case "$1" in
2383
- -F) shift 2 ;;
2384
- -t) shift; target="$1" ;;
2385
- esac
2386
- shift || true
2387
- done
2388
- if [[ "$target" == "${managedSessionName}" ]]; then
2389
- cat > "${ralphStatePath}" <<'JSON'
2390
- {
2391
- "active": true,
2392
- "current_phase": "reviewing",
2393
- "iteration": 11,
2394
- "owner_codex_session_id": "codex-updated-owner",
2395
- "tmux_pane_id": "%99"
2396
- }
2397
- JSON
2398
- printf "%%99\t0\tsh\tbash\n%%42\t1\tcodex\tcodex\n"
2399
- exit 0
2400
- fi
2401
- echo "can't find session" >&2
2402
- exit 1
2403
- fi
2404
- if [[ "$cmd" == "capture-pane" ]]; then
2405
- exit 0
2406
- fi
2407
- if [[ "$cmd" == "set-buffer" ]]; then
2408
- printf '%s' "\${@: -1}" > "${tmuxLogPath}.buffer"
2409
- exit 0
2410
- fi
2411
- if [[ "$cmd" == "show-buffer" ]]; then
2412
- if [[ -f "${tmuxLogPath}.buffer" ]]; then cat "${tmuxLogPath}.buffer"; fi
2413
- exit 0
2414
- fi
2415
- if [[ "$cmd" == "paste-buffer" ]]; then
2416
- target=""
2417
- while [[ "$#" -gt 0 ]]; do
2418
- case "$1" in
2419
- -t) target="$2"; shift 2 ;;
2420
- *) shift ;;
2421
- esac
2422
- done
2423
- if [[ -f "${tmuxLogPath}.buffer" ]]; then
2424
- echo "send-keys -t \${target} -l $(cat "${tmuxLogPath}.buffer")" >> "${tmuxLogPath}"
2425
- fi
2426
- exit 0
2427
- fi
2428
- if [[ "$cmd" == "delete-buffer" ]]; then
2429
- rm -f "${tmuxLogPath}.buffer"
2430
- exit 0
2431
- fi
2432
- if [[ "$cmd" == "send-keys" ]]; then
2433
- exit 0
2434
- fi
2435
- exit 0
2436
- `;
2437
- await writeFile(join(fakeBinDir, 'tmux'), fakeTmux);
2438
- await chmod(join(fakeBinDir, 'tmux'), 0o755);
2439
- await writeFile(ralphStatePath, JSON.stringify({
2440
- active: true,
2441
- current_phase: 'executing',
2442
- iteration: 1,
2443
- owner_codex_session_id: 'codex-stale-owner',
2444
- tmux_pane_id: anchorPane,
2445
- }, null, 2));
2446
- await writeFile(join(sessionStateDir, 'hud-state.json'), JSON.stringify({
2447
- last_progress_at: new Date(Date.now() - 61_000).toISOString(),
2448
- }, null, 2));
2449
- await writeFile(watcherStatePath, JSON.stringify({
2450
- ralph_continue_steer: {
2451
- last_sent_at: new Date(Date.now() - 61_000).toISOString(),
2452
- },
2453
- }, null, 2));
2454
- const watcherScript = new URL('../../../dist/scripts/notify-fallback-watcher.js', import.meta.url).pathname;
2455
- const notifyHook = new URL('../../../dist/scripts/notify-hook.js', import.meta.url).pathname;
2456
- const run = spawnSync(process.execPath, [watcherScript, '--once', '--cwd', wd, '--notify-script', notifyHook, '--poll-ms', '50'], {
2457
- encoding: 'utf-8',
2458
- env: buildCleanNotifyEnv({
2459
- PATH: `${fakeBinDir}:${process.env.PATH || ''}`,
2460
- }),
2461
- });
2462
- assert.equal(run.status, 0, run.stderr || run.stdout);
2463
- const persistedRalph = JSON.parse(await readFile(ralphStatePath, 'utf-8'));
2464
- assert.equal(persistedRalph.tmux_pane_id, livePane);
2465
- assert.equal(persistedRalph.current_phase, 'reviewing');
2466
- assert.equal(persistedRalph.iteration, 11);
2467
- assert.equal(persistedRalph.owner_codex_session_id, 'codex-updated-owner');
2468
- assert.match(persistedRalph.tmux_pane_set_at ?? '', /^\d{4}-\d{2}-\d{2}T/);
2469
- }
2470
- finally {
2471
- await rm(wd, { recursive: true, force: true });
2472
- }
2473
- });
2474
- it('keeps the verified Ralph anchor pane when another codex pane is focused in the same managed session', async () => {
2475
- const wd = await mkdtemp(join(tmpdir(), 'omx-fallback-ralph-keep-anchor-pane-'));
2476
- const fakeBinDir = join(wd, 'fake-bin');
2477
- const stateDir = join(wd, '.omx', 'state');
2478
- const tmuxLogPath = join(wd, 'tmux.log');
2479
- const watcherStatePath = join(stateDir, 'notify-fallback-state.json');
2480
- const sessionId = 'sess-ralph-keep-anchor';
2481
- const sessionStateDir = join(stateDir, 'sessions', sessionId);
2482
- const ralphStatePath = join(sessionStateDir, 'ralph-state.json');
2483
- const anchorPane = '%99';
2484
- const livePane = '%42';
2485
- try {
2486
- await mkdir(sessionStateDir, { recursive: true });
2487
- await mkdir(fakeBinDir, { recursive: true });
2488
- await writeSessionStart(wd, sessionId);
2489
- const managedSessionName = buildTmuxSessionName(wd, sessionId);
2490
- await writeFile(join(fakeBinDir, 'tmux'), buildManagedRalphTmux(tmuxLogPath, {
2491
- cwd: wd,
2492
- managedSessionName,
2493
- anchorPane,
2494
- livePane,
2495
- codexPanes: [
2496
- { paneId: anchorPane, active: false, currentCommand: 'codex', startCommand: 'codex' },
2497
- { paneId: livePane, active: true, currentCommand: 'codex', startCommand: 'codex' },
2498
- ],
2499
- }));
2500
- await chmod(join(fakeBinDir, 'tmux'), 0o755);
2501
- await writeFile(ralphStatePath, JSON.stringify({
2502
- active: true,
2503
- current_phase: 'executing',
2504
- tmux_pane_id: anchorPane,
2505
- }, null, 2));
2506
- await writeFile(join(sessionStateDir, 'hud-state.json'), JSON.stringify({
2507
- last_progress_at: new Date(Date.now() - 61_000).toISOString(),
2508
- }, null, 2));
2509
- await writeFile(watcherStatePath, JSON.stringify({
2510
- ralph_continue_steer: {
2511
- last_sent_at: new Date(Date.now() - 61_000).toISOString(),
2512
- },
2513
- }, null, 2));
2514
- const watcherScript = new URL('../../../dist/scripts/notify-fallback-watcher.js', import.meta.url).pathname;
2515
- const notifyHook = new URL('../../../dist/scripts/notify-hook.js', import.meta.url).pathname;
2516
- const run = spawnSync(process.execPath, [watcherScript, '--once', '--cwd', wd, '--notify-script', notifyHook, '--poll-ms', '50'], {
2517
- encoding: 'utf-8',
2518
- env: buildCleanNotifyEnv({
2519
- PATH: `${fakeBinDir}:${process.env.PATH || ''}`,
2520
- }),
2521
- });
2522
- assert.equal(run.status, 0, run.stderr || run.stdout);
2523
- const persistedRalph = JSON.parse(await readFile(ralphStatePath, 'utf-8'));
2524
- assert.equal(persistedRalph.tmux_pane_id, anchorPane);
2525
- assert.equal(typeof persistedRalph.tmux_pane_set_at, 'undefined');
2526
- const watcherState = JSON.parse(await readFile(watcherStatePath, 'utf-8'));
2527
- assert.equal(watcherState.ralph_continue_steer?.last_reason, 'sent');
2528
- assert.equal(watcherState.ralph_continue_steer?.pane_id, anchorPane);
2529
- const tmuxLog = await readFile(tmuxLogPath, 'utf8');
2530
- assert.match(tmuxLog, /send-keys -t %99 -l Ralph loop active continue \[OMX_TMUX_INJECT\]/);
2531
- assert.doesNotMatch(tmuxLog, /send-keys -t %42 -l Ralph loop active continue \[OMX_TMUX_INJECT\]/);
2532
- }
2533
- finally {
2534
- await rm(wd, { recursive: true, force: true });
2535
- }
2536
- });
2537
- it('rebinds a shell-degraded codex anchor to the live pane before continue steer', async () => {
2538
- const wd = await mkdtemp(join(tmpdir(), 'omx-fallback-ralph-rebind-degraded-codex-anchor-'));
2539
- const fakeBinDir = join(wd, 'fake-bin');
2540
- const stateDir = join(wd, '.omx', 'state');
2541
- const tmuxLogPath = join(wd, 'tmux.log');
2542
- const watcherStatePath = join(stateDir, 'notify-fallback-state.json');
2543
- const sessionId = 'sess-ralph-degraded-codex-anchor';
2544
- const sessionStateDir = join(stateDir, 'sessions', sessionId);
2545
- const ralphStatePath = join(sessionStateDir, 'ralph-state.json');
2546
- const anchorPane = '%99';
2547
- const livePane = '%42';
2548
- try {
2549
- await mkdir(sessionStateDir, { recursive: true });
2550
- await mkdir(fakeBinDir, { recursive: true });
2551
- await writeSessionStart(wd, sessionId);
2552
- const managedSessionName = buildTmuxSessionName(wd, sessionId);
2553
- await writeFile(join(fakeBinDir, 'tmux'), buildManagedRalphTmux(tmuxLogPath, {
2554
- cwd: wd,
2555
- managedSessionName,
2556
- anchorPane,
2557
- livePane,
2558
- codexPanes: [
2559
- { paneId: anchorPane, active: true, currentCommand: 'bash', startCommand: 'codex --model gpt-5' },
2560
- { paneId: livePane, active: false, currentCommand: 'codex', startCommand: 'codex' },
2561
- ],
2562
- }));
2563
- await chmod(join(fakeBinDir, 'tmux'), 0o755);
2564
- await writeFile(ralphStatePath, JSON.stringify({
2565
- active: true,
2566
- current_phase: 'executing',
2567
- tmux_pane_id: anchorPane,
2568
- }, null, 2));
2569
- await writeFile(join(sessionStateDir, 'hud-state.json'), JSON.stringify({
2570
- last_progress_at: new Date(Date.now() - 61_000).toISOString(),
2571
- }, null, 2));
2572
- await writeFile(watcherStatePath, JSON.stringify({
2573
- ralph_continue_steer: {
2574
- last_sent_at: new Date(Date.now() - 61_000).toISOString(),
2575
- },
2576
- }, null, 2));
2577
- const watcherScript = new URL('../../../dist/scripts/notify-fallback-watcher.js', import.meta.url).pathname;
2578
- const notifyHook = new URL('../../../dist/scripts/notify-hook.js', import.meta.url).pathname;
2579
- const run = spawnSync(process.execPath, [watcherScript, '--once', '--cwd', wd, '--notify-script', notifyHook, '--poll-ms', '50'], {
2580
- encoding: 'utf-8',
2581
- env: buildCleanNotifyEnv({
2582
- PATH: `${fakeBinDir}:${process.env.PATH || ''}`,
2583
- }),
2584
- });
2585
- assert.equal(run.status, 0, run.stderr || run.stdout);
2586
- const persistedRalph = JSON.parse(await readFile(ralphStatePath, 'utf-8'));
2587
- assert.equal(persistedRalph.tmux_pane_id, livePane);
2588
- assert.match(persistedRalph.tmux_pane_set_at ?? '', /^\d{4}-\d{2}-\d{2}T/);
2589
- const watcherState = JSON.parse(await readFile(watcherStatePath, 'utf-8'));
2590
- assert.equal(watcherState.ralph_continue_steer?.last_reason, 'sent');
2591
- assert.equal(watcherState.ralph_continue_steer?.pane_id, livePane);
2592
- const tmuxLog = await readFile(tmuxLogPath, 'utf8');
2593
- assert.match(tmuxLog, /send-keys -t %42 -l Ralph loop active continue \[OMX_TMUX_INJECT\]/);
2594
- assert.doesNotMatch(tmuxLog, /send-keys -t %99 -l Ralph loop active continue \[OMX_TMUX_INJECT\]/);
2595
- }
2596
- finally {
2597
- await rm(wd, { recursive: true, force: true });
2598
- }
2599
- });
2600
- it('falls back to the current managed session pane when the stored Ralph pane anchor is dead', async () => {
2601
- const wd = await mkdtemp(join(tmpdir(), 'omx-fallback-ralph-rebind-dead-anchor-'));
2602
- const fakeBinDir = join(wd, 'fake-bin');
2603
- const stateDir = join(wd, '.omx', 'state');
2604
- const tmuxLogPath = join(wd, 'tmux.log');
2605
- const watcherStatePath = join(stateDir, 'notify-fallback-state.json');
2606
- const sessionId = 'sess-ralph-dead-anchor';
2607
- const sessionStateDir = join(stateDir, 'sessions', sessionId);
2608
- const ralphStatePath = join(sessionStateDir, 'ralph-state.json');
2609
- const anchorPane = '%99';
2610
- const livePane = '%42';
2611
- try {
2612
- await mkdir(sessionStateDir, { recursive: true });
2613
- await mkdir(fakeBinDir, { recursive: true });
2614
- await writeSessionStart(wd, sessionId);
2615
- const managedSessionName = buildTmuxSessionName(wd, sessionId);
2616
- await writeFile(join(fakeBinDir, 'tmux'), buildManagedRalphTmux(tmuxLogPath, {
2617
- cwd: wd,
2618
- managedSessionName,
2619
- anchorPane,
2620
- livePane,
2621
- codexPanes: [
2622
- { paneId: '%41', active: false, currentCommand: 'codex', startCommand: 'codex' },
2623
- { paneId: livePane, active: true, currentCommand: 'codex', startCommand: 'codex' },
2624
- ],
2625
- missingAnchor: true,
2626
- }));
2627
- await chmod(join(fakeBinDir, 'tmux'), 0o755);
2628
- await writeFile(ralphStatePath, JSON.stringify({
2629
- active: true,
2630
- current_phase: 'executing',
2631
- tmux_pane_id: anchorPane,
2632
- }, null, 2));
2633
- await writeFile(join(sessionStateDir, 'hud-state.json'), JSON.stringify({
2634
- last_progress_at: new Date(Date.now() - 61_000).toISOString(),
2635
- }, null, 2));
2636
- await writeFile(watcherStatePath, JSON.stringify({
2637
- ralph_continue_steer: {
2638
- last_sent_at: new Date(Date.now() - 61_000).toISOString(),
2639
- },
2640
- }, null, 2));
2641
- const watcherScript = new URL('../../../dist/scripts/notify-fallback-watcher.js', import.meta.url).pathname;
2642
- const notifyHook = new URL('../../../dist/scripts/notify-hook.js', import.meta.url).pathname;
2643
- const run = spawnSync(process.execPath, [watcherScript, '--once', '--cwd', wd, '--notify-script', notifyHook, '--poll-ms', '50'], {
2644
- encoding: 'utf-8',
2645
- env: buildCleanNotifyEnv({
2646
- PATH: `${fakeBinDir}:${process.env.PATH || ''}`,
2647
- }),
2648
- });
2649
- assert.equal(run.status, 0, run.stderr || run.stdout);
2650
- const persistedRalph = JSON.parse(await readFile(ralphStatePath, 'utf-8'));
2651
- assert.equal(persistedRalph.tmux_pane_id, livePane);
2652
- assert.match(persistedRalph.tmux_pane_set_at ?? '', /^\d{4}-\d{2}-\d{2}T/);
2653
- const watcherState = JSON.parse(await readFile(watcherStatePath, 'utf-8'));
2654
- assert.equal(watcherState.ralph_continue_steer?.last_reason, 'sent');
2655
- assert.equal(watcherState.ralph_continue_steer?.pane_id, livePane);
2656
- const tmuxLog = await readFile(tmuxLogPath, 'utf8');
2657
- assert.match(tmuxLog, /display-message -p -t %99 #S/);
2658
- assert.match(tmuxLog, /list-panes -s -t .*sess-ralph-dead-anchor/);
2659
- assert.match(tmuxLog, /send-keys -t %42 -l Ralph loop active continue \[OMX_TMUX_INJECT\]/);
2660
- assert.doesNotMatch(tmuxLog, /send-keys -t %99 -l Ralph loop active continue \[OMX_TMUX_INJECT\]/);
2661
- }
2662
- finally {
2663
- await rm(wd, { recursive: true, force: true });
2381
+ it('fails closed without rebinding stale, degraded, or missing Ralph pane anchors', async () => {
2382
+ const watcherScript = new URL('../../../dist/scripts/notify-fallback-watcher.js', import.meta.url).pathname;
2383
+ const notifyHook = new URL('../../../dist/scripts/notify-hook.js', import.meta.url).pathname;
2384
+ for (const scenario of ['stale shell anchor', 'degraded codex anchor', 'missing anchor']) {
2385
+ const wd = await mkdtemp(join(tmpdir(), 'omx-fallback-ralph-no-rebind-'));
2386
+ const fakeBinDir = join(wd, 'fake-bin');
2387
+ const stateDir = join(wd, '.omx', 'state');
2388
+ const tmuxLogPath = join(wd, 'tmux.log');
2389
+ const ralphStatePath = join(stateDir, 'ralph-state.json');
2390
+ try {
2391
+ await mkdir(stateDir, { recursive: true });
2392
+ await mkdir(fakeBinDir, { recursive: true });
2393
+ await writeFile(tmuxLogPath, '');
2394
+ await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath, { paneOwner: 'ralph:owner' }));
2395
+ await chmod(join(fakeBinDir, 'tmux'), 0o755);
2396
+ await writeFile(ralphStatePath, JSON.stringify({
2397
+ active: true,
2398
+ current_phase: 'executing',
2399
+ tmux_pane_id: '%99',
2400
+ tmux_pane_pid: 4242,
2401
+ tmux_session_name: 'session-test',
2402
+ tmux_session_id: '$7',
2403
+ tmux_window_id: '@9',
2404
+ tmux_pane_owner_id: 'ralph:owner',
2405
+ tmux_pane_current_command: 'codex',
2406
+ tmux_pane_start_command: 'codex',
2407
+ scenario,
2408
+ }, null, 2));
2409
+ await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({ last_progress_at: new Date(Date.now() - 61_000).toISOString() }));
2410
+ await writeFile(join(stateDir, 'notify-fallback-state.json'), JSON.stringify({ ralph_continue_steer: { last_sent_at: new Date(Date.now() - 61_000).toISOString() } }));
2411
+ const run = spawnSync(process.execPath, [watcherScript, '--once', '--cwd', wd, '--notify-script', notifyHook], {
2412
+ encoding: 'utf-8',
2413
+ env: buildCleanNotifyEnv({ PATH: `${fakeBinDir}:${process.env.PATH || ''}` }),
2414
+ });
2415
+ assert.equal(run.status, 0, run.stderr || run.stdout);
2416
+ const persistedRalph = JSON.parse(await readFile(ralphStatePath, 'utf-8'));
2417
+ assert.equal(persistedRalph.tmux_pane_id, '%99', `${scenario} must retain its stored pane identity`);
2418
+ const watcherState = JSON.parse(await readFile(join(stateDir, 'notify-fallback-state.json'), 'utf-8'));
2419
+ assert.equal(watcherState.ralph_continue_steer?.last_reason, 'send_failed');
2420
+ const tmuxLog = await readFile(tmuxLogPath, 'utf8');
2421
+ assert.doesNotMatch(tmuxLog, /send-keys -t %(42|99) -l Ralph loop active continue/);
2422
+ assert.doesNotMatch(tmuxLog, /list-panes -s -t/, 'watcher must not discover a replacement pane');
2423
+ }
2424
+ finally {
2425
+ await rm(wd, { recursive: true, force: true });
2426
+ }
2664
2427
  }
2665
2428
  });
2666
2429
  it('sends the first Ralph continue steer immediately when persisted steer state is empty', async () => {
@@ -2672,12 +2435,19 @@ exit 0
2672
2435
  try {
2673
2436
  await mkdir(stateDir, { recursive: true });
2674
2437
  await mkdir(fakeBinDir, { recursive: true });
2675
- await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath));
2438
+ await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath, { paneOwner: 'ralph:owner' }));
2676
2439
  await chmod(join(fakeBinDir, 'tmux'), 0o755);
2677
2440
  await writeFile(join(stateDir, 'ralph-state.json'), JSON.stringify({
2678
2441
  active: true,
2679
2442
  current_phase: 'executing',
2680
2443
  tmux_pane_id: '%42',
2444
+ tmux_pane_pid: 4242,
2445
+ tmux_session_name: 'session-test',
2446
+ tmux_session_id: '$7',
2447
+ tmux_window_id: '@9',
2448
+ tmux_pane_owner_id: 'ralph:owner',
2449
+ tmux_pane_current_command: 'codex',
2450
+ tmux_pane_start_command: 'codex',
2681
2451
  }, null, 2));
2682
2452
  await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({
2683
2453
  last_progress_at: new Date(Date.now() - 61_000).toISOString(),
@@ -2716,12 +2486,19 @@ exit 0
2716
2486
  try {
2717
2487
  await mkdir(stateDir, { recursive: true });
2718
2488
  await mkdir(fakeBinDir, { recursive: true });
2719
- await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath));
2489
+ await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath, { paneOwner: 'ralph:owner' }));
2720
2490
  await chmod(join(fakeBinDir, 'tmux'), 0o755);
2721
2491
  await writeFile(join(stateDir, 'ralph-state.json'), JSON.stringify({
2722
2492
  active: true,
2723
2493
  current_phase: 'executing',
2724
2494
  tmux_pane_id: '%42',
2495
+ tmux_pane_pid: 4242,
2496
+ tmux_session_name: 'session-test',
2497
+ tmux_session_id: '$7',
2498
+ tmux_window_id: '@9',
2499
+ tmux_pane_owner_id: 'ralph:owner',
2500
+ tmux_pane_current_command: 'codex',
2501
+ tmux_pane_start_command: 'codex',
2725
2502
  }, null, 2));
2726
2503
  await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({
2727
2504
  last_progress_at: new Date(Date.now() - 61_000).toISOString(),
@@ -2767,6 +2544,13 @@ exit 0
2767
2544
  current_phase: 'blocked_on_user',
2768
2545
  completed_at: new Date().toISOString(),
2769
2546
  tmux_pane_id: '%42',
2547
+ tmux_pane_pid: 4242,
2548
+ tmux_session_name: 'session-test',
2549
+ tmux_session_id: '$7',
2550
+ tmux_window_id: '@9',
2551
+ tmux_pane_owner_id: 'ralph:owner',
2552
+ tmux_pane_current_command: 'codex',
2553
+ tmux_pane_start_command: 'codex',
2770
2554
  }, null, 2));
2771
2555
  await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({
2772
2556
  last_progress_at: new Date(Date.now() - 61_000).toISOString(),
@@ -2806,12 +2590,19 @@ exit 0
2806
2590
  try {
2807
2591
  await mkdir(stateDir, { recursive: true });
2808
2592
  await mkdir(fakeBinDir, { recursive: true });
2809
- await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath));
2593
+ await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath, { paneOwner: 'ralph:owner' }));
2810
2594
  await chmod(join(fakeBinDir, 'tmux'), 0o755);
2811
2595
  await writeFile(ralphStatePath, JSON.stringify({
2812
2596
  active: true,
2813
2597
  current_phase: 'executing',
2814
2598
  tmux_pane_id: '%42',
2599
+ tmux_pane_pid: 4242,
2600
+ tmux_session_name: 'session-test',
2601
+ tmux_session_id: '$7',
2602
+ tmux_window_id: '@9',
2603
+ tmux_pane_owner_id: 'ralph:owner',
2604
+ tmux_pane_current_command: 'codex',
2605
+ tmux_pane_start_command: 'codex',
2815
2606
  }, null, 2));
2816
2607
  await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({
2817
2608
  last_progress_at: new Date(Date.now() - 61_000).toISOString(),
@@ -2840,6 +2631,13 @@ exit 0
2840
2631
  current_phase: 'complete',
2841
2632
  completed_at: new Date().toISOString(),
2842
2633
  tmux_pane_id: '%42',
2634
+ tmux_pane_pid: 4242,
2635
+ tmux_session_name: 'session-test',
2636
+ tmux_session_id: '$7',
2637
+ tmux_window_id: '@9',
2638
+ tmux_pane_owner_id: 'ralph:owner',
2639
+ tmux_pane_current_command: 'codex',
2640
+ tmux_pane_start_command: 'codex',
2843
2641
  }, null, 2));
2844
2642
  const terminalRun = spawnSync(process.execPath, [watcherScript, '--once', '--cwd', wd, '--notify-script', notifyHook, '--poll-ms', '50'], { encoding: 'utf-8', env });
2845
2643
  assert.equal(terminalRun.status, 0, terminalRun.stderr || terminalRun.stdout);
@@ -2871,6 +2669,13 @@ exit 0
2871
2669
  current_phase: 'starting',
2872
2670
  started_at: staleStartedAt,
2873
2671
  tmux_pane_id: '%42',
2672
+ tmux_pane_pid: 4242,
2673
+ tmux_session_name: 'session-test',
2674
+ tmux_session_id: '$7',
2675
+ tmux_window_id: '@9',
2676
+ tmux_pane_owner_id: 'ralph:owner',
2677
+ tmux_pane_current_command: 'codex',
2678
+ tmux_pane_start_command: 'codex',
2874
2679
  }, null, 2));
2875
2680
  await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({
2876
2681
  last_progress_at: new Date(Date.now() - 5 * 60_000).toISOString(),
@@ -2916,6 +2721,13 @@ exit 0
2916
2721
  current_phase: 'executing',
2917
2722
  run_outcome: 'blocked_on_user',
2918
2723
  tmux_pane_id: '%42',
2724
+ tmux_pane_pid: 4242,
2725
+ tmux_session_name: 'session-test',
2726
+ tmux_session_id: '$7',
2727
+ tmux_window_id: '@9',
2728
+ tmux_pane_owner_id: 'ralph:owner',
2729
+ tmux_pane_current_command: 'codex',
2730
+ tmux_pane_start_command: 'codex',
2919
2731
  }, null, 2));
2920
2732
  await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({
2921
2733
  last_progress_at: new Date(Date.now() - 5 * 60_000).toISOString(),
@@ -2954,12 +2766,19 @@ exit 0
2954
2766
  try {
2955
2767
  await mkdir(stateDir, { recursive: true });
2956
2768
  await mkdir(fakeBinDir, { recursive: true });
2957
- await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath));
2769
+ await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath, { paneOwner: 'ralph:owner' }));
2958
2770
  await chmod(join(fakeBinDir, 'tmux'), 0o755);
2959
2771
  await writeFile(join(stateDir, 'ralph-state.json'), JSON.stringify({
2960
2772
  active: true,
2961
2773
  current_phase: 'executing',
2962
2774
  tmux_pane_id: '%42',
2775
+ tmux_pane_pid: 4242,
2776
+ tmux_session_name: 'session-test',
2777
+ tmux_session_id: '$7',
2778
+ tmux_window_id: '@9',
2779
+ tmux_pane_owner_id: 'ralph:owner',
2780
+ tmux_pane_current_command: 'codex',
2781
+ tmux_pane_start_command: 'codex',
2963
2782
  }, null, 2));
2964
2783
  await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({
2965
2784
  last_progress_at: new Date(Date.now() - 61_000).toISOString(),
@@ -3005,9 +2824,11 @@ exit 0
3005
2824
  await mkdir(fakeBinDir, { recursive: true });
3006
2825
  await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath, {
3007
2826
  failSendKeysMatch: 'Ralph loop active continue',
2827
+ ralphPane: { paneId: '%43', panePid: 4343, paneOwner: 'ralph:owner' },
3008
2828
  }));
3009
2829
  await chmod(join(fakeBinDir, 'tmux'), 0o755);
3010
2830
  await initTeamState('dispatch-team', 'task', 'executor', 1, wd);
2831
+ await bindCanonicalDispatchWorkerFixture(wd);
3011
2832
  const queued = await enqueueDispatchRequest('dispatch-team', {
3012
2833
  kind: 'inbox',
3013
2834
  to_worker: 'worker-1',
@@ -3017,7 +2838,14 @@ exit 0
3017
2838
  await writeFile(join(wd, '.omx', 'state', 'ralph-state.json'), JSON.stringify({
3018
2839
  active: true,
3019
2840
  current_phase: 'executing',
3020
- tmux_pane_id: '%42',
2841
+ tmux_pane_id: '%43',
2842
+ tmux_pane_pid: 4343,
2843
+ tmux_session_name: 'session-test',
2844
+ tmux_session_id: '$7',
2845
+ tmux_window_id: '@9',
2846
+ tmux_pane_owner_id: 'ralph:owner',
2847
+ tmux_pane_current_command: 'codex',
2848
+ tmux_pane_start_command: 'codex',
3021
2849
  }, null, 2));
3022
2850
  await writeFile(join(wd, '.omx', 'state', 'hud-state.json'), JSON.stringify({
3023
2851
  last_progress_at: new Date(Date.now() - 61_000).toISOString(),
@@ -3084,6 +2912,7 @@ exit 0
3084
2912
  'ready', 'ready', 'ping', 'ping', 'ping', 'ping', 'ping', 'ping',
3085
2913
  ].join('\n'));
3086
2914
  await initTeamState('dispatch-team', 'task', 'executor', 1, wd);
2915
+ await bindCanonicalDispatchWorkerFixture(wd);
3087
2916
  const queued = await enqueueDispatchRequest('dispatch-team', {
3088
2917
  kind: 'inbox',
3089
2918
  to_worker: 'worker-1',
@@ -3249,6 +3078,13 @@ exit 0
3249
3078
  active: true,
3250
3079
  current_phase: 'executing',
3251
3080
  tmux_pane_id: '%42',
3081
+ tmux_pane_pid: 4242,
3082
+ tmux_session_name: 'session-test',
3083
+ tmux_session_id: '$7',
3084
+ tmux_window_id: '@9',
3085
+ tmux_pane_owner_id: 'ralph:owner',
3086
+ tmux_pane_current_command: 'codex',
3087
+ tmux_pane_start_command: 'codex',
3252
3088
  }, null, 2));
3253
3089
  await writeFile(watcherStatePath, JSON.stringify({
3254
3090
  ralph_continue_steer: {
@@ -3292,6 +3128,13 @@ exit 0
3292
3128
  active: true,
3293
3129
  current_phase: 'executing',
3294
3130
  tmux_pane_id: '%42',
3131
+ tmux_pane_pid: 4242,
3132
+ tmux_session_name: 'session-test',
3133
+ tmux_session_id: '$7',
3134
+ tmux_window_id: '@9',
3135
+ tmux_pane_owner_id: 'ralph:owner',
3136
+ tmux_pane_current_command: 'codex',
3137
+ tmux_pane_start_command: 'codex',
3295
3138
  }, null, 2));
3296
3139
  await writeFile(watcherStatePath, JSON.stringify({
3297
3140
  ralph_continue_steer: {
@@ -3339,6 +3182,13 @@ exit 0
3339
3182
  active: true,
3340
3183
  current_phase: 'executing',
3341
3184
  tmux_pane_id: '%42',
3185
+ tmux_pane_pid: 4242,
3186
+ tmux_session_name: 'session-test',
3187
+ tmux_session_id: '$7',
3188
+ tmux_window_id: '@9',
3189
+ tmux_pane_owner_id: 'ralph:owner',
3190
+ tmux_pane_current_command: 'codex',
3191
+ tmux_pane_start_command: 'codex',
3342
3192
  }, null, 2));
3343
3193
  await writeFile(join(sessionStateDir, 'hud-state.json'), JSON.stringify({
3344
3194
  last_progress_at: new Date(Date.now() - 61_000).toISOString(),
@@ -3348,7 +3198,7 @@ exit 0
3348
3198
  last_sent_at: new Date(Date.now() - 61_000).toISOString(),
3349
3199
  },
3350
3200
  }, null, 2));
3351
- await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath));
3201
+ await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath, { paneOwner: 'ralph:owner' }));
3352
3202
  await chmod(join(fakeBinDir, 'tmux'), 0o755);
3353
3203
  const shortLivedParent = spawn(process.execPath, ['-e', 'setTimeout(() => process.exit(0), 10)'], {
3354
3204
  stdio: 'ignore',
@@ -3383,6 +3233,13 @@ exit 0
3383
3233
  current_phase: 'complete',
3384
3234
  completed_at: new Date().toISOString(),
3385
3235
  tmux_pane_id: '%42',
3236
+ tmux_pane_pid: 4242,
3237
+ tmux_session_name: 'session-test',
3238
+ tmux_session_id: '$7',
3239
+ tmux_window_id: '@9',
3240
+ tmux_pane_owner_id: 'ralph:owner',
3241
+ tmux_pane_current_command: 'codex',
3242
+ tmux_pane_start_command: 'codex',
3386
3243
  }, null, 2));
3387
3244
  await waitForExit(child, 4000);
3388
3245
  assert.equal(child.exitCode, 0);