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
package/dist/hud/tmux.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { randomUUID } from 'node:crypto';
1
2
  import { execFileSync } from 'child_process';
2
3
  import { existsSync } from 'fs';
3
4
  import { HUD_RESIZE_RECONCILE_DELAY_SECONDS, HUD_TMUX_HEIGHT_LINES } from './constants.js';
@@ -9,6 +10,70 @@ export const TMUX_PANE_FIELD_SEPARATOR = '\x1f';
9
10
  export const TMUX_PANE_FIELD_SEPARATOR_OCTAL_ESCAPE = '\\037';
10
11
  /** Upper bound for tmux hook indices (signed 32-bit max). */
11
12
  const TMUX_HOOK_INDEX_MAX = 2147483647;
13
+ const PSMUX_PANE_ID_MAX = 0xffffffffn;
14
+ const PSMUX_PANE_ID_MAX_DIGITS = PSMUX_PANE_ID_MAX.toString().length;
15
+ export function parseCanonicalTmuxPaneId(value) {
16
+ if (typeof value !== 'string')
17
+ return null;
18
+ const match = /^%(0|[1-9]\d*)$/.exec(value);
19
+ if (!match || match[1].length > PSMUX_PANE_ID_MAX_DIGITS)
20
+ return null;
21
+ try {
22
+ return BigInt(match[1]) <= PSMUX_PANE_ID_MAX ? value : null;
23
+ }
24
+ catch {
25
+ return null;
26
+ }
27
+ }
28
+ /**
29
+ * Parses tmux output used as authority. Authority frames are deliberately
30
+ * transport-strict: a nonempty body followed by exactly one LF or CRLF.
31
+ */
32
+ export function parseExactTmuxAuthorityLines(output) {
33
+ if (typeof output !== 'string' || output.length < 2)
34
+ return null;
35
+ const terminator = output.endsWith('\r\n') ? '\r\n' : output.endsWith('\n') ? '\n' : null;
36
+ if (!terminator)
37
+ return null;
38
+ const body = output.slice(0, -terminator.length);
39
+ if (body === '' || body.endsWith('\n') || body.includes('\r'))
40
+ return null;
41
+ return body.split('\n');
42
+ }
43
+ /** Parses one exact, nonempty tmux authority scalar. */
44
+ export function parseExactTmuxAuthorityScalar(output) {
45
+ const lines = parseExactTmuxAuthorityLines(output);
46
+ return lines?.length === 1 && lines[0] !== '' ? lines[0] : null;
47
+ }
48
+ /**
49
+ * Tolerant pane parsing is for display metadata only. Callers using its output
50
+ * as authority MUST first validate framing with parseExactTmuxAuthorityLines.
51
+ */
52
+ function canonicalizePaneSnapshotBatch(panes) {
53
+ const paneIds = new Set();
54
+ const canonicalPanes = [];
55
+ for (const pane of panes) {
56
+ const paneId = parseCanonicalTmuxPaneId(pane.paneId);
57
+ if (!paneId || paneIds.has(paneId))
58
+ return null;
59
+ paneIds.add(paneId);
60
+ canonicalPanes.push({ ...pane, paneId });
61
+ }
62
+ return canonicalPanes;
63
+ }
64
+ function parseCanonicalPaneIdSnapshot(output) {
65
+ const lines = parseExactTmuxAuthorityLines(output);
66
+ if (!lines)
67
+ return null;
68
+ const paneIds = new Set();
69
+ for (const line of lines) {
70
+ const paneId = parseCanonicalTmuxPaneId(line);
71
+ if (!paneId || paneIds.has(paneId))
72
+ return null;
73
+ paneIds.add(paneId);
74
+ }
75
+ return paneIds;
76
+ }
12
77
  function defaultExecTmuxSync(args) {
13
78
  try {
14
79
  return execFileSync(resolveTmuxBinaryForPlatform() || 'tmux', args, {
@@ -25,28 +90,36 @@ function defaultExecTmuxSync(args) {
25
90
  }
26
91
  }
27
92
  function parseNonNegativeInteger(value) {
28
- if (typeof value !== 'string' || value.trim() === '')
93
+ if (typeof value !== 'string' || !/^(?:0|[1-9][0-9]*)$/.test(value))
29
94
  return null;
30
- const parsed = Number.parseInt(value.trim(), 10);
31
- return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
95
+ const parsed = Number(value);
96
+ return Number.isSafeInteger(parsed) && parsed <= 4_294_967_295 ? parsed : null;
32
97
  }
33
98
  function parsePositiveInteger(value) {
34
99
  const parsed = parseNonNegativeInteger(value);
35
100
  return parsed !== null && parsed > 0 ? parsed : null;
36
101
  }
37
- export function parseTmuxPaneSnapshot(output) {
38
- return output
39
- .split('\n')
40
- .map((line) => line.trim())
41
- .filter(Boolean)
42
- .map((line) => {
102
+ export function parseTmuxPaneSnapshot(output, authoritativePaneIds) {
103
+ const lines = output.split('\n');
104
+ if (lines.at(-1) === '')
105
+ lines.pop();
106
+ if (lines.length === 0)
107
+ return [];
108
+ const panes = [];
109
+ for (const line of lines) {
110
+ if (line === '')
111
+ return [];
43
112
  const fieldSeparator = line.includes(TMUX_PANE_FIELD_SEPARATOR)
44
113
  ? TMUX_PANE_FIELD_SEPARATOR
45
114
  : line.includes(TMUX_PANE_FIELD_SEPARATOR_OCTAL_ESCAPE)
46
115
  ? TMUX_PANE_FIELD_SEPARATOR_OCTAL_ESCAPE
47
116
  : '\t';
48
117
  const parts = line.split(fieldSeparator);
49
- const [paneId = '', currentCommand = ''] = parts;
118
+ const [rawPaneId = '', currentCommand = ''] = parts;
119
+ const paneId = parseCanonicalTmuxPaneId(rawPaneId);
120
+ if (!paneId)
121
+ return [];
122
+ const hasGeometryFields = parts.length >= 9;
50
123
  const paneLeft = parseNonNegativeInteger(parts[2]);
51
124
  const paneTop = parseNonNegativeInteger(parts[3]);
52
125
  const paneWidth = parsePositiveInteger(parts[4]);
@@ -61,16 +134,25 @@ export function parseTmuxPaneSnapshot(output) {
61
134
  && paneBottom !== null
62
135
  && windowWidth !== null
63
136
  && windowHeight !== null);
137
+ if (hasGeometryFields && !hasGeometry)
138
+ return [];
64
139
  const payloadParts = hasGeometry ? parts.slice(9) : parts.slice(2);
65
- const hasCurrentPathColumn = payloadParts.length >= 2;
66
- const currentPath = hasCurrentPathColumn ? (payloadParts.at(-1) ?? '') : '';
67
- const startCommandParts = hasCurrentPathColumn ? payloadParts.slice(0, -1) : payloadParts;
140
+ const hasIncarnationFields = hasGeometry
141
+ && payloadParts.length >= 4
142
+ && /^(?:0|1)$/.test(payloadParts.at(-2) ?? '')
143
+ && parsePositiveInteger(payloadParts.at(-1)) !== null;
144
+ const incarnationParts = hasIncarnationFields ? payloadParts.slice(-2) : [];
145
+ const commandParts = hasIncarnationFields ? payloadParts.slice(0, -2) : payloadParts;
146
+ const hasCurrentPathColumn = commandParts.length >= 2;
147
+ const currentPath = hasCurrentPathColumn ? (commandParts.at(-1) ?? '') : '';
148
+ const startCommandParts = hasCurrentPathColumn ? commandParts.slice(0, -1) : commandParts;
68
149
  const trimmedCurrentPath = currentPath.trim();
69
- return {
70
- paneId: paneId.trim(),
150
+ panes.push({
151
+ paneId,
71
152
  currentCommand: currentCommand.trim(),
72
153
  startCommand: startCommandParts.join('\t').trim(),
73
154
  ...(trimmedCurrentPath ? { currentPath: trimmedCurrentPath } : {}),
155
+ ...(hasIncarnationFields ? { paneDead: incarnationParts[0] === '1', panePid: incarnationParts[1] } : {}),
74
156
  ...(hasGeometry
75
157
  ? {
76
158
  paneLeft,
@@ -82,9 +164,16 @@ export function parseTmuxPaneSnapshot(output) {
82
164
  windowHeight,
83
165
  }
84
166
  : {}),
85
- };
86
- })
87
- .filter((pane) => pane.paneId.startsWith('%'));
167
+ });
168
+ }
169
+ const canonicalPanes = canonicalizePaneSnapshotBatch(panes);
170
+ if (!canonicalPanes)
171
+ return [];
172
+ if (authoritativePaneIds
173
+ && (canonicalPanes.length !== authoritativePaneIds.size
174
+ || canonicalPanes.some((pane) => !authoritativePaneIds.has(pane.paneId))))
175
+ return [];
176
+ return canonicalPanes;
88
177
  }
89
178
  export function isHudWatchPane(pane) {
90
179
  const command = `${pane.startCommand} ${pane.currentCommand}`.toLowerCase();
@@ -92,53 +181,302 @@ export function isHudWatchPane(pane) {
92
181
  && /--watch\b/.test(command)
93
182
  && (/\bomx(?:\.js)?\b/.test(command) || /\bnode\b/.test(command)));
94
183
  }
184
+ const HUD_OWNER_ENV_KEYS = [
185
+ OMX_TMUX_HUD_OWNER_ENV,
186
+ 'OMX_SESSION_ID',
187
+ OMX_TMUX_HUD_LEADER_PANE_ENV,
188
+ ];
189
+ function isHudOwnerEnvKey(key) {
190
+ return HUD_OWNER_ENV_KEYS.some((ownerKey) => ownerKey === key);
191
+ }
192
+ function isPowerShellHudOwnerEnvKey(key) {
193
+ return HUD_OWNER_ENV_KEYS.some((ownerKey) => ownerKey === key.toUpperCase());
194
+ }
195
+ function lexPosixWords(command) {
196
+ const words = [];
197
+ let index = 0;
198
+ while (index < command.length) {
199
+ while (/\s/.test(command[index] ?? ''))
200
+ index += 1;
201
+ if (index >= command.length)
202
+ break;
203
+ if (command[index] === '#')
204
+ break;
205
+ let word = '';
206
+ let quoted = false;
207
+ while (index < command.length && !/\s/.test(command[index] ?? '')) {
208
+ const char = command[index];
209
+ if (';|&()<>`'.includes(char) || char === '\n' || char === '\r')
210
+ return null;
211
+ if (char === '\\') {
212
+ index += 1;
213
+ if (index >= command.length)
214
+ return null;
215
+ word += command[index];
216
+ index += 1;
217
+ continue;
218
+ }
219
+ if (char === "'") {
220
+ quoted = true;
221
+ index += 1;
222
+ while (index < command.length && command[index] !== "'") {
223
+ word += command[index];
224
+ index += 1;
225
+ }
226
+ if (index >= command.length)
227
+ return null;
228
+ index += 1;
229
+ continue;
230
+ }
231
+ if (char === '"') {
232
+ quoted = true;
233
+ index += 1;
234
+ while (index < command.length && command[index] !== '"') {
235
+ if (command[index] === '\\') {
236
+ index += 1;
237
+ if (index >= command.length)
238
+ return null;
239
+ }
240
+ word += command[index];
241
+ index += 1;
242
+ }
243
+ if (index >= command.length)
244
+ return null;
245
+ index += 1;
246
+ continue;
247
+ }
248
+ word += char;
249
+ index += 1;
250
+ }
251
+ if (!word && !quoted)
252
+ return null;
253
+ words.push(word);
254
+ }
255
+ return words;
256
+ }
257
+ function parsePosixHudEnvAssignments(command) {
258
+ const values = new Map();
259
+ const attempted = HUD_OWNER_ENV_KEYS.some((key) => command.includes(key));
260
+ const words = lexPosixWords(command);
261
+ if (!words)
262
+ return { attempted, valid: false, values };
263
+ const inspect = (tokens) => {
264
+ let commandStarted = false;
265
+ let acceptsEnvAssignments = false;
266
+ for (let index = 0; index < tokens.length; index += 1) {
267
+ const token = tokens[index];
268
+ const assignment = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/.exec(token);
269
+ if (assignment && isHudOwnerEnvKey(assignment[1]) && (!commandStarted || acceptsEnvAssignments)) {
270
+ const key = assignment[1];
271
+ const existing = values.get(key) ?? [];
272
+ existing.push(assignment[2]);
273
+ values.set(key, existing);
274
+ continue;
275
+ }
276
+ if (token === '-c' && index + 1 < tokens.length) {
277
+ const nested = lexPosixWords(tokens[index + 1]);
278
+ if (!nested || !inspect(nested))
279
+ return false;
280
+ commandStarted = true;
281
+ continue;
282
+ }
283
+ if (token === 'exec' && !commandStarted)
284
+ continue;
285
+ if (!commandStarted && token === 'env') {
286
+ commandStarted = true;
287
+ acceptsEnvAssignments = true;
288
+ continue;
289
+ }
290
+ commandStarted = true;
291
+ acceptsEnvAssignments = false;
292
+ }
293
+ return true;
294
+ };
295
+ return { attempted, valid: inspect(words), values };
296
+ }
297
+ function parseShellEnvAssignments(command, key) {
298
+ const parsed = parsePosixHudEnvAssignments(command);
299
+ return parsed.valid ? (parsed.values.get(key) ?? []) : [];
300
+ }
95
301
  function parseShellEnvAssignment(command, key) {
96
- const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
97
- const match = command.match(new RegExp(`(?:^|\\s)(?:'${escapedKey}=([^']*)'|${escapedKey}=(?:'((?:'\\\\''|[^'])*)'|([^\\s]+)))`));
98
- const fallbackMatch = match
99
- ? null
100
- : command.match(new RegExp(`(?:^|[\\s'])${escapedKey}=([^'\\s]+)`));
101
- const raw = match?.[1] ?? match?.[2] ?? match?.[3] ?? fallbackMatch?.[1];
102
- if (typeof raw !== 'string')
103
- return undefined;
104
- const value = raw.replace(/'\\''/g, "'").trim();
105
- return value === '' ? undefined : value;
302
+ const values = parseShellEnvAssignments(command, key);
303
+ return values.length === 1 && values[0] !== '' ? values[0] : undefined;
304
+ }
305
+ function containsPowerShellHudOwnerReferenceOutsideQuotes(command) {
306
+ let quote = null;
307
+ let blockComment = false;
308
+ let lineComment = false;
309
+ for (let index = 0; index < command.length; index += 1) {
310
+ const char = command[index];
311
+ const next = command[index + 1];
312
+ if (blockComment) {
313
+ if (char === '#' && next === '>') {
314
+ blockComment = false;
315
+ index += 1;
316
+ }
317
+ continue;
318
+ }
319
+ if (lineComment) {
320
+ if (char === '\n' || char === '\r')
321
+ lineComment = false;
322
+ continue;
323
+ }
324
+ if (quote === 'single') {
325
+ if (char === "'" && next === "'")
326
+ index += 1;
327
+ else if (char === "'")
328
+ quote = null;
329
+ continue;
330
+ }
331
+ if (quote === 'double') {
332
+ if (char === '`')
333
+ index += 1;
334
+ else if (char === '"')
335
+ quote = null;
336
+ continue;
337
+ }
338
+ if (char === '<' && next === '#') {
339
+ blockComment = true;
340
+ index += 1;
341
+ continue;
342
+ }
343
+ if (char === '#') {
344
+ lineComment = true;
345
+ continue;
346
+ }
347
+ if (char === "'") {
348
+ quote = 'single';
349
+ continue;
350
+ }
351
+ if (char === '"') {
352
+ quote = 'double';
353
+ continue;
354
+ }
355
+ if (char === '`') {
356
+ index += 1;
357
+ continue;
358
+ }
359
+ if (command.slice(index, index + 5).toLowerCase() === '$env:') {
360
+ const match = /^\$env:([A-Za-z_][A-Za-z0-9_]*)/i.exec(command.slice(index));
361
+ if (match && isPowerShellHudOwnerEnvKey(match[1]))
362
+ return true;
363
+ }
364
+ }
365
+ return false;
366
+ }
367
+ function unwrapKnownPowerShellCommand(command) {
368
+ const match = /^\s*powershell(?:\.exe)?\s+-NoLogo\s+-NoExit\s+-Command\s+'((?:''|[^'])*)'(.*)$/i.exec(command);
369
+ if (!match)
370
+ return null;
371
+ return { command: match[1].replace(/''/g, "'"), remainder: match[2] };
372
+ }
373
+ function parsePowerShellHudEnvPrefix(command) {
374
+ const unwrapped = unwrapKnownPowerShellCommand(command);
375
+ const source = unwrapped?.command ?? command;
376
+ const present = containsPowerShellHudOwnerReferenceOutsideQuotes(source);
377
+ const values = new Map();
378
+ if (!present)
379
+ return { present: false, valid: false, values, remainder: command };
380
+ const prefixMatch = /^\s*((?:\$env:[A-Za-z_][A-Za-z0-9_]*\s*=\s*'(?:''|[^'])*'\s*;\s*)+)&(?:\s|$)/i.exec(source);
381
+ if (!prefixMatch)
382
+ return { present: true, valid: false, values, remainder: command };
383
+ const assignmentPattern = /\$env:([A-Za-z_][A-Za-z0-9_]*)\s*=\s*'((?:''|[^'])*)'\s*;/gi;
384
+ for (const match of prefixMatch[1].matchAll(assignmentPattern)) {
385
+ const key = match[1].toUpperCase();
386
+ const existing = values.get(key) ?? [];
387
+ existing.push(match[2].replace(/''/g, "'"));
388
+ values.set(key, existing);
389
+ }
390
+ const remainder = `${source.slice(prefixMatch[0].length)}${unwrapped?.remainder ?? ''}`;
391
+ return { present: true, valid: true, values, remainder };
392
+ }
393
+ function hasHudOwnerMetadataAttempt(command) {
394
+ return parsePowerShellHudEnvPrefix(command).present || parsePosixHudEnvAssignments(command).attempted;
395
+ }
396
+ function isInvalidHudOwnerValue(key, value) {
397
+ return value === '' || (key === OMX_TMUX_HUD_OWNER_ENV && value !== '1');
398
+ }
399
+ function parseHudEnvAssignment(command, key) {
400
+ const posix = parsePosixHudEnvAssignments(command);
401
+ const powerShellPrefix = parsePowerShellHudEnvPrefix(command);
402
+ if (powerShellPrefix.present)
403
+ return powerShellPrefix.valid ? (powerShellPrefix.values.get(key.toUpperCase())?.length === 1 ? powerShellPrefix.values.get(key.toUpperCase())[0] : undefined) : undefined;
404
+ return posix.valid ? parseShellEnvAssignment(command, key) : undefined;
405
+ }
406
+ function hasAmbiguousHudOwnerMetadata(command) {
407
+ const posix = parsePosixHudEnvAssignments(command);
408
+ const powerShellPrefix = parsePowerShellHudEnvPrefix(command);
409
+ if (powerShellPrefix.present) {
410
+ if (!powerShellPrefix.valid
411
+ || posix.values.size > 0
412
+ || containsPowerShellHudOwnerReferenceOutsideQuotes(powerShellPrefix.remainder)
413
+ || parsePosixHudEnvAssignments(powerShellPrefix.remainder).attempted)
414
+ return true;
415
+ return HUD_OWNER_ENV_KEYS.some((key) => {
416
+ const values = powerShellPrefix.values.get(key) ?? [];
417
+ return values.length > 1 || (values.length === 1 && isInvalidHudOwnerValue(key, values[0]));
418
+ });
419
+ }
420
+ if (posix.attempted && (!posix.valid || posix.values.size === 0))
421
+ return true;
422
+ return HUD_OWNER_ENV_KEYS.some((key) => {
423
+ const values = posix.values.get(key) ?? [];
424
+ return values.length > 1 || (values.length === 1 && isInvalidHudOwnerValue(key, values[0]));
425
+ });
106
426
  }
107
427
  export function readHudPaneOwner(pane) {
108
428
  const command = `${pane.startCommand} ${pane.currentCommand}`;
429
+ const rawLeaderPaneId = parseHudEnvAssignment(command, OMX_TMUX_HUD_LEADER_PANE_ENV);
430
+ if (hasAmbiguousHudOwnerMetadata(command)
431
+ || (rawLeaderPaneId !== undefined && !parseCanonicalTmuxPaneId(rawLeaderPaneId))) {
432
+ return { sessionId: undefined, leaderPaneId: undefined };
433
+ }
109
434
  return {
110
- sessionId: parseShellEnvAssignment(command, 'OMX_SESSION_ID'),
111
- leaderPaneId: parseShellEnvAssignment(command, OMX_TMUX_HUD_LEADER_PANE_ENV),
435
+ sessionId: parseHudEnvAssignment(command, 'OMX_SESSION_ID'),
436
+ leaderPaneId: rawLeaderPaneId ? parseCanonicalTmuxPaneId(rawLeaderPaneId) ?? undefined : undefined,
112
437
  };
113
438
  }
114
439
  function hasHudPaneOwnerMetadata(pane) {
115
440
  const command = `${pane.startCommand} ${pane.currentCommand}`;
116
- const owner = readHudPaneOwner(pane);
117
- return parseShellEnvAssignment(command, OMX_TMUX_HUD_OWNER_ENV) === '1'
118
- || Boolean(owner.sessionId || owner.leaderPaneId);
441
+ return hasHudOwnerMetadataAttempt(command) && !hasAmbiguousHudOwnerMetadata(command);
442
+ }
443
+ export function hasValidHudOwnerMarker(pane) {
444
+ const command = `${pane.startCommand} ${pane.currentCommand}`;
445
+ if (hasAmbiguousHudOwnerMetadata(command))
446
+ return false;
447
+ return parseHudEnvAssignment(command, OMX_TMUX_HUD_OWNER_ENV) === '1';
119
448
  }
120
449
  function hasOmxCliToken(command) {
121
450
  return /(?:^|[\s'"])(?:[^\s'"]*\/)?omx(?:\.js)?(?=$|[\s'"])/.test(command);
122
451
  }
452
+ function hasPowerShellEnvironmentAssignmentPrefix(command) {
453
+ return /^\s*(?:\$env:[A-Za-z_][A-Za-z0-9_]*\s*=\s*(?:'(?:''|[^'])*'|"(?:`.|[^"])*")\s*;\s*)+&(?:\s|$)/i.test(command);
454
+ }
123
455
  function isLegacyFocusedHudWatchPane(pane) {
124
456
  // Migration-only heuristic for prompt-submit auto-HUD reconciliation: older
125
457
  // focused auto-HUD panes lacked owner metadata, so keep this deliberately
126
458
  // narrower than general HUD ownership/reaping.
127
- if (!isHudWatchPane(pane) || hasHudPaneOwnerMetadata(pane))
128
- return false;
129
459
  const command = `${pane.startCommand} ${pane.currentCommand}`;
460
+ if (!isHudWatchPane(pane)
461
+ || hasHudOwnerMetadataAttempt(command)
462
+ || hasPowerShellEnvironmentAssignmentPrefix(command))
463
+ return false;
130
464
  return hasOmxCliToken(command)
131
465
  && !/(?:^|[\s'"])--tmux(?:[\s'"]|$)/.test(command)
132
466
  && /(?:^|[\s'"])--preset=focused(?:[\s'"]|$)/.test(command);
133
467
  }
134
468
  export function findLegacyFocusedHudWatchPaneIds(panes, currentPaneId) {
135
- return panes
136
- .filter((pane) => pane.paneId !== currentPaneId)
469
+ const canonicalPanes = canonicalizePaneSnapshotBatch(panes);
470
+ const canonicalCurrentPaneId = currentPaneId ? parseCanonicalTmuxPaneId(currentPaneId) : null;
471
+ if (!canonicalPanes || (currentPaneId && !canonicalCurrentPaneId))
472
+ return [];
473
+ return canonicalPanes
474
+ .filter((pane) => pane.paneId !== canonicalCurrentPaneId)
137
475
  .filter((pane) => isLegacyFocusedHudWatchPane(pane))
138
476
  .map((pane) => pane.paneId);
139
477
  }
140
478
  export function hudPaneMatchesOwner(pane, owner = {}) {
141
- if (!isHudWatchPane(pane))
479
+ if (!parseCanonicalTmuxPaneId(pane.paneId) || !isHudWatchPane(pane))
142
480
  return false;
143
481
  const wantedSessionIds = [
144
482
  typeof owner.sessionId === 'string' ? owner.sessionId.trim() : '',
@@ -146,11 +484,16 @@ export function hudPaneMatchesOwner(pane, owner = {}) {
146
484
  ]
147
485
  .map((sessionId) => sessionId.trim())
148
486
  .filter((sessionId, index, sessionIds) => sessionId !== '' && sessionIds.indexOf(sessionId) === index);
149
- const wantedLeaderPaneId = typeof owner.leaderPaneId === 'string' ? owner.leaderPaneId.trim() : '';
487
+ const rawWantedLeaderPaneId = typeof owner.leaderPaneId === 'string' ? owner.leaderPaneId : '';
488
+ const wantedLeaderPaneId = rawWantedLeaderPaneId ? parseCanonicalTmuxPaneId(rawWantedLeaderPaneId) : null;
489
+ if (rawWantedLeaderPaneId && !wantedLeaderPaneId)
490
+ return false;
150
491
  const wantsSession = wantedSessionIds.length > 0;
151
- const wantsLeaderPane = wantedLeaderPaneId !== '';
492
+ const wantsLeaderPane = wantedLeaderPaneId !== null;
152
493
  if (!wantsSession && !wantsLeaderPane)
153
494
  return true;
495
+ if (hasAmbiguousHudOwnerMetadata(`${pane.startCommand} ${pane.currentCommand}`))
496
+ return false;
154
497
  const paneOwner = readHudPaneOwner(pane);
155
498
  const sessionMatches = wantsSession && wantedSessionIds.includes(paneOwner.sessionId ?? '');
156
499
  const leaderPaneMatches = wantsLeaderPane && paneOwner.leaderPaneId === wantedLeaderPaneId;
@@ -165,8 +508,12 @@ export function hudPaneMatchesOwner(pane, owner = {}) {
165
508
  return leaderPaneMatches;
166
509
  }
167
510
  export function findHudWatchPaneIds(panes, currentPaneId, owner = {}) {
168
- return panes
169
- .filter((pane) => pane.paneId !== currentPaneId)
511
+ const canonicalPanes = canonicalizePaneSnapshotBatch(panes);
512
+ const canonicalCurrentPaneId = currentPaneId ? parseCanonicalTmuxPaneId(currentPaneId) : null;
513
+ if (!canonicalPanes || (currentPaneId && !canonicalCurrentPaneId))
514
+ return [];
515
+ return canonicalPanes
516
+ .filter((pane) => pane.paneId !== canonicalCurrentPaneId)
170
517
  .filter((pane) => hudPaneMatchesOwner(pane, owner))
171
518
  .map((pane) => pane.paneId);
172
519
  }
@@ -184,8 +531,13 @@ function isDoctorSmokeSessionId(sessionId) {
184
531
  function shouldReapDeletedCwdHudPane(pane, isLivePane) {
185
532
  // A deleted tmux launch cwd is not enough to prove a HUD is dead: watch mode can
186
533
  // keep serving from a resolved live cwd. Reap only explicit doctor smoke panes or
187
- // owner-tagged panes whose leader is gone.
188
- if (!hasHudPaneOwnerMetadata(pane) || !hasDeletedTmuxPaneMarker(pane.currentPath))
534
+ // owner-tagged panes whose canonical leader is gone.
535
+ const command = `${pane.startCommand} ${pane.currentCommand}`;
536
+ const rawLeaderPaneId = parseHudEnvAssignment(command, OMX_TMUX_HUD_LEADER_PANE_ENV);
537
+ if (!hasHudPaneOwnerMetadata(pane)
538
+ || !hasDeletedTmuxPaneMarker(pane.currentPath)
539
+ || hasAmbiguousHudOwnerMetadata(command)
540
+ || (rawLeaderPaneId !== undefined && !parseCanonicalTmuxPaneId(rawLeaderPaneId)))
189
541
  return false;
190
542
  const owner = readHudPaneOwner(pane);
191
543
  if (isDoctorSmokeSessionId(owner.sessionId))
@@ -195,18 +547,27 @@ function shouldReapDeletedCwdHudPane(pane, isLivePane) {
195
547
  return !owner.leaderPaneId || !isLivePane(owner.leaderPaneId);
196
548
  }
197
549
  export function reapDeadHudPanes(panes, opts = {}) {
198
- const livePaneIds = new Set(panes.map((pane) => pane.paneId));
550
+ const canonicalPanes = canonicalizePaneSnapshotBatch(panes);
551
+ if (!canonicalPanes)
552
+ return { reaped: [], preserved: panes.map((pane) => pane.paneId) };
553
+ const livePaneIds = new Set(canonicalPanes.map((pane) => pane.paneId));
199
554
  const isLivePane = opts.isLivePane ?? ((paneId) => livePaneIds.has(paneId));
200
555
  const killPane = opts.killPane ?? ((paneId) => killTmuxPane(paneId));
201
556
  const reaped = [];
202
557
  const preserved = [];
203
- for (const pane of panes) {
558
+ for (const pane of canonicalPanes) {
204
559
  if (!isHudWatchPane(pane))
205
560
  continue;
206
561
  if (shouldReapDeletedCwdHudPane(pane, isLivePane) && killPane(pane.paneId)) {
207
562
  reaped.push(pane.paneId);
208
563
  continue;
209
564
  }
565
+ // Never let HUD-looking command text authorize a destructive pane mutation.
566
+ // Only metadata accepted by the strict owner parsers may do so.
567
+ if (!hasHudPaneOwnerMetadata(pane)) {
568
+ preserved.push(pane.paneId);
569
+ continue;
570
+ }
210
571
  const leaderPaneId = readHudPaneOwner(pane).leaderPaneId;
211
572
  if (!leaderPaneId) {
212
573
  preserved.push(pane.paneId);
@@ -226,8 +587,10 @@ export function reapDeadHudPanes(panes, opts = {}) {
226
587
  return { reaped, preserved };
227
588
  }
228
589
  export function parsePaneIdFromTmuxOutput(rawOutput) {
229
- const paneId = rawOutput.split('\n')[0]?.trim() || '';
230
- return paneId.startsWith('%') ? paneId : null;
590
+ const lines = rawOutput.split('\n');
591
+ if (lines.at(-1) === '')
592
+ lines.pop();
593
+ return lines.length === 1 ? parseCanonicalTmuxPaneId(lines[0]) : null;
231
594
  }
232
595
  export function shellEscapeSingle(value) {
233
596
  return `'${value.replace(/'/g, `'\\''`)}'`;
@@ -242,9 +605,6 @@ function isTmuxSessionId(value) {
242
605
  function isTmuxWindowId(value) {
243
606
  return /^@\d+$/.test(value);
244
607
  }
245
- function isTmuxPaneId(value) {
246
- return /^%\d+$/.test(value);
247
- }
248
608
  export function buildHudResizeHookName(sessionId, windowId, leaderPaneId) {
249
609
  return [
250
610
  'omx_hud_resize',
@@ -253,13 +613,6 @@ export function buildHudResizeHookName(sessionId, windowId, leaderPaneId) {
253
613
  normalizeTmuxHookToken(leaderPaneId),
254
614
  ].join('_');
255
615
  }
256
- function buildLegacyHudResizeHookName(sessionId, windowId) {
257
- return [
258
- 'omx_hud_resize',
259
- normalizeTmuxHookToken(sessionId),
260
- normalizeTmuxHookToken(windowId),
261
- ].join('_');
262
- }
263
616
  export function buildHudResizeHookSlot(hookName) {
264
617
  let hash = 0;
265
618
  for (let i = 0; i < hookName.length; i++) {
@@ -274,35 +627,101 @@ export function buildHudLayoutHookSlot(hookName) {
274
627
  }
275
628
  return `window-layout-changed[${Math.abs(hash) % TMUX_HOOK_INDEX_MAX}]`;
276
629
  }
277
- export function parseHudResizeHookContext(output, leaderPaneId) {
278
- const [sessionId = '', windowId = ''] = output
279
- .split('\n')[0]
280
- ?.split('\t')
281
- .map((part) => part.trim()) ?? [];
282
- const normalizedLeaderPaneId = leaderPaneId.trim();
283
- if (!isTmuxSessionId(sessionId) || !isTmuxWindowId(windowId) || !isTmuxPaneId(normalizedLeaderPaneId))
630
+ function hudHookIdentityOption(hookSlot) {
631
+ const match = /^(client-resized|window-layout-changed)\[([0-9]+)\]$/.exec(hookSlot);
632
+ if (!match)
633
+ throw new Error('invalid_tmux_hook_slot');
634
+ return `@omx_hook_identity_${match[1].replaceAll('-', '_')}_${match[2]}`;
635
+ }
636
+ function hudHookIdentityToken(hookName, hookSlot) {
637
+ let hash = 2166136261;
638
+ for (const char of `${hookName}:${hookSlot}`)
639
+ hash = Math.imul(hash ^ char.charCodeAt(0), 16777619);
640
+ return `omx-${(hash >>> 0).toString(16)}`;
641
+ }
642
+ function buildHudHookRegistrationSuffix(context, hookSlot) {
643
+ return [
644
+ '\\;', 'set-option', '-t', context.sessionId,
645
+ hudHookIdentityOption(hookSlot), hudHookIdentityToken(context.hookName, hookSlot),
646
+ ];
647
+ }
648
+ function buildGuardedHudHookUnregisterArgs(context, hookSlot) {
649
+ const identityOption = hudHookIdentityOption(hookSlot);
650
+ return [
651
+ 'if-shell', '-F', '-t', context.sessionId,
652
+ `#{==:${identityOption},${hudHookIdentityToken(context.hookName, hookSlot)}}`,
653
+ `set-hook -u -t ${context.sessionId} ${hookSlot} \\; set-option -u -t ${context.sessionId} ${identityOption}`,
654
+ '',
655
+ ];
656
+ }
657
+ function readHudResizeHookPaneIncarnations(hudPaneId, leaderPaneId, execTmuxSync) {
658
+ try {
659
+ const lines = parseExactTmuxAuthorityLines(execTmuxSync(['list-panes', '-a', '-F', '#{pane_id} #{pane_dead} #{pane_pid}']));
660
+ if (!lines)
661
+ return null;
662
+ const seen = new Set();
663
+ let leaderPanePid = null;
664
+ let hudPanePid = null;
665
+ for (const line of lines) {
666
+ const match = /^(%\S+) ([01]) ([1-9][0-9]*)$/.exec(line);
667
+ const paneId = match ? parseCanonicalTmuxPaneId(match[1]) : null;
668
+ if (!match || !paneId || paneId !== match[1] || seen.has(paneId))
669
+ return null;
670
+ seen.add(paneId);
671
+ if (match[2] === '1')
672
+ continue;
673
+ if (paneId === leaderPaneId)
674
+ leaderPanePid = match[3];
675
+ if (paneId === hudPaneId)
676
+ hudPanePid = match[3];
677
+ }
678
+ return leaderPanePid && hudPanePid ? { leaderPanePid, hudPanePid } : null;
679
+ }
680
+ catch {
681
+ return null;
682
+ }
683
+ }
684
+ export function parseHudResizeHookContext(output, leaderPaneId, hudPaneId = leaderPaneId, incarnations = { leaderPanePid: '1', hudPanePid: '1' }) {
685
+ const line = parseExactTmuxAuthorityScalar(output);
686
+ if (line === null)
687
+ return null;
688
+ const parts = line.split('\t');
689
+ if (parts.length !== 2 || parts.some((part) => part.trim() !== part || part === ''))
690
+ return null;
691
+ const [sessionId = '', windowId = ''] = parts;
692
+ const normalizedLeaderPaneId = parseCanonicalTmuxPaneId(leaderPaneId);
693
+ const normalizedHudPaneId = parseCanonicalTmuxPaneId(hudPaneId);
694
+ if (!isTmuxSessionId(sessionId) || !isTmuxWindowId(windowId) || !normalizedLeaderPaneId || !normalizedHudPaneId)
284
695
  return null;
285
696
  const hookName = buildHudResizeHookName(sessionId, windowId, normalizedLeaderPaneId);
286
697
  return {
287
698
  sessionId,
288
699
  windowId,
289
700
  leaderPaneId: normalizedLeaderPaneId,
701
+ leaderPanePid: incarnations.leaderPanePid,
702
+ hudPaneId: normalizedHudPaneId,
703
+ hudPanePid: incarnations.hudPanePid,
290
704
  hookName,
291
705
  hookSlot: buildHudResizeHookSlot(hookName),
292
706
  layoutHookSlot: buildHudLayoutHookSlot(hookName),
293
707
  };
294
708
  }
295
- export function readHudResizeHookContext(leaderPaneId, execTmuxSync = defaultExecTmuxSync) {
296
- if (!leaderPaneId || !isTmuxPaneId(leaderPaneId))
709
+ export function readHudResizeHookContext(hudPaneId, leaderPaneId, execTmuxSync = defaultExecTmuxSync) {
710
+ const canonicalLeaderPaneId = parseCanonicalTmuxPaneId(leaderPaneId);
711
+ const canonicalHudPaneId = parseCanonicalTmuxPaneId(hudPaneId);
712
+ if (!canonicalLeaderPaneId || !canonicalHudPaneId)
713
+ return null;
714
+ const incarnations = readHudResizeHookPaneIncarnations(canonicalHudPaneId, canonicalLeaderPaneId, execTmuxSync);
715
+ if (!incarnations)
297
716
  return null;
298
717
  try {
299
718
  return parseHudResizeHookContext(execTmuxSync([
300
719
  'display-message',
301
720
  '-p',
302
721
  '-t',
303
- leaderPaneId,
722
+ canonicalLeaderPaneId,
304
723
  '#{session_id}\t#{window_id}',
305
- ]), leaderPaneId);
724
+ ]), canonicalLeaderPaneId, canonicalHudPaneId, incarnations);
306
725
  }
307
726
  catch {
308
727
  return null;
@@ -312,22 +731,54 @@ function buildNestedTmuxCommand(tmuxBin, args, tmuxEnv) {
312
731
  const command = [tmuxBin, ...args].map((part) => shellEscapeSingle(part)).join(' ');
313
732
  return `${buildEnvPrefix({ TMUX: tmuxEnv })}${command}`;
314
733
  }
315
- function buildHudResizeHookCommand(tmuxBin, hudPaneId, height, context, tmuxEnv) {
316
- const resize = buildNestedTmuxCommand(tmuxBin, ['resize-pane', '-t', hudPaneId, '-y', height], tmuxEnv);
317
- const unregister = buildHudHookUnregisterCommand(tmuxBin, context, tmuxEnv);
318
- const resizeOrUnregister = `${resize} >/dev/null 2>&1 || (${unregister})`;
319
- return `${resizeOrUnregister}; sleep ${HUD_RESIZE_RECONCILE_DELAY_SECONDS}; ${resizeOrUnregister}`;
734
+ function quoteHudHookShellArgument(value) {
735
+ return process.platform === 'win32'
736
+ ? `'${value.replace(/'/g, "''")}'`
737
+ : shellEscapeSingle(value);
738
+ }
739
+ function buildHudHookIncarnationCondition(paneId, panePid) {
740
+ return `#{&&:#{==:#{pane_id},${paneId}},#{&&:#{==:#{pane_dead},0},#{==:#{pane_pid},${panePid}}}}`;
741
+ }
742
+ function buildHudHookSelfUnregister(context) {
743
+ const identityOption = hudHookIdentityOption(context.hookSlot);
744
+ return `if-shell -F -t ${context.sessionId} ${quoteHudHookShellArgument(`#{==:${identityOption},${hudHookIdentityToken(context.hookName, context.hookSlot)}}`)} ${quoteHudHookShellArgument(`set-hook -u -t ${context.sessionId} ${context.hookSlot} \\; set-option -u -t ${context.sessionId} ${identityOption}`)} ''`;
320
745
  }
321
- function buildHudHookUnregisterCommand(tmuxBin, context, tmuxEnv) {
322
- const unregisterResize = buildNestedTmuxCommand(tmuxBin, ['set-hook', '-u', '-t', context.sessionId, context.hookSlot], tmuxEnv);
323
- const unregisterLayout = buildNestedTmuxCommand(tmuxBin, ['set-hook', '-u', '-t', context.sessionId, context.layoutHookSlot], tmuxEnv);
324
- return `${unregisterResize} >/dev/null 2>&1 || true; ${unregisterLayout} >/dev/null 2>&1 || true`;
746
+ function buildAtomicHudHookCommand(tmuxBin, context, success, tmuxEnv) {
747
+ const unregister = buildHudHookSelfUnregister(context);
748
+ const hudConditional = [
749
+ 'if-shell', '-F', '-t', context.hudPaneId,
750
+ quoteHudHookShellArgument(buildHudHookIncarnationCondition(context.hudPaneId, context.hudPanePid)),
751
+ quoteHudHookShellArgument(success),
752
+ quoteHudHookShellArgument(unregister),
753
+ ].join(' ');
754
+ const args = [
755
+ 'if-shell', '-F', '-t', context.leaderPaneId,
756
+ buildHudHookIncarnationCondition(context.leaderPaneId, context.leaderPanePid),
757
+ hudConditional,
758
+ unregister,
759
+ ];
760
+ if (process.platform === 'win32') {
761
+ const quotePowerShell = (value) => `'${value.replace(/'/g, "''")}'`;
762
+ const invoke = `& ${quotePowerShell(tmuxBin)} ${args.map(quotePowerShell).join(' ')}`;
763
+ return tmuxEnv
764
+ ? `& { $env:TMUX = ${quotePowerShell(tmuxEnv)}; ${invoke} } | Out-Null`
765
+ : `${invoke} | Out-Null`;
766
+ }
767
+ const command = buildNestedTmuxCommand(tmuxBin, args, tmuxEnv);
768
+ return `${command} >/dev/null 2>&1 || true`;
769
+ }
770
+ function buildAtomicHudResizeCommand(tmuxBin, hudPaneId, height, context, tmuxEnv) {
771
+ return buildAtomicHudHookCommand(tmuxBin, context, `resize-pane -t ${hudPaneId} -y ${height}`, tmuxEnv);
772
+ }
773
+ function buildHudResizeHookCommand(tmuxBin, hudPaneId, height, context, tmuxEnv) {
774
+ const atomicResize = buildAtomicHudResizeCommand(tmuxBin, hudPaneId, height, context, tmuxEnv);
775
+ return process.platform === 'win32'
776
+ ? `${atomicResize}; Start-Sleep -Seconds ${HUD_RESIZE_RECONCILE_DELAY_SECONDS}; ${atomicResize}`
777
+ : `${atomicResize}; sleep ${HUD_RESIZE_RECONCILE_DELAY_SECONDS}; ${atomicResize}`;
325
778
  }
326
779
  function buildHudLayoutReconcileHookCommand(tmuxBin, omxBin, leaderPaneId, context, options = {}) {
327
780
  const env = options.env ?? process.env;
328
781
  const cwd = options.cwd?.trim() || process.cwd();
329
- const leaderAlive = buildNestedTmuxCommand(tmuxBin, ['display-message', '-p', '-t', leaderPaneId, '#{pane_id}'], env.TMUX);
330
- const unregister = buildHudHookUnregisterCommand(tmuxBin, context, env.TMUX);
331
782
  const reconcileEnv = buildEnvPrefix({
332
783
  TMUX: env.TMUX,
333
784
  TMUX_PANE: leaderPaneId,
@@ -346,19 +797,20 @@ function buildHudLayoutReconcileHookCommand(tmuxBin, omxBin, leaderPaneId, conte
346
797
  'hud',
347
798
  '--reconcile-tmux',
348
799
  ].join(' ');
349
- return `${leaderAlive} >/dev/null 2>&1 && (${unregister}; ${reconcile} >/dev/null 2>&1; status=$?; exit $status) || (${unregister})`;
800
+ const layoutContext = { ...context, hookSlot: context.layoutHookSlot };
801
+ if (process.platform === 'win32') {
802
+ const nativeReconcile = `Set-Location -LiteralPath '${cwd.replace(/'/g, "''")}'; & '${process.execPath.replace(/'/g, "''")}' '${omxBin.replace(/'/g, "''")}' hud --reconcile-tmux | Out-Null`;
803
+ const success = `${buildHudHookSelfUnregister(layoutContext)} \\; run-shell -b ${quoteHudHookShellArgument(nativeReconcile)}`;
804
+ return buildAtomicHudHookCommand(tmuxBin, layoutContext, success, env.TMUX);
805
+ }
806
+ const success = `${buildHudHookSelfUnregister(layoutContext)} \\; run-shell -b ${quoteHudHookShellArgument(`${reconcile} >/dev/null 2>&1`)}`;
807
+ return buildAtomicHudHookCommand(tmuxBin, layoutContext, success, env.TMUX);
350
808
  }
351
809
  function unregisterLegacyHudResizeHook(context, execTmuxSync) {
352
- const legacyHookSlot = buildHudResizeHookSlot(buildLegacyHudResizeHookName(context.sessionId, context.windowId));
353
- if (legacyHookSlot === context.hookSlot)
354
- return;
355
- try {
356
- execTmuxSync(['set-hook', '-u', '-t', context.sessionId, legacyHookSlot]);
357
- }
358
- catch {
359
- // Best-effort migration cleanup: failure should not prevent the new
360
- // leader-scoped hook from being registered or unregistered.
361
- }
810
+ // Legacy registrations predate identity metadata. Leaving them untouched is
811
+ // safer than clearing a slot that may now belong to another registration.
812
+ void context;
813
+ void execTmuxSync;
362
814
  }
363
815
  function buildEnvPrefix(env) {
364
816
  const assignments = Object.entries(env)
@@ -367,9 +819,34 @@ function buildEnvPrefix(env) {
367
819
  .map(([key, value]) => `${key}=${shellEscapeSingle(value)}`);
368
820
  return assignments.length > 0 ? `env ${assignments.join(' ')} ` : '';
369
821
  }
822
+ /** The sole platform-aware serializer for tmux HUD watch commands. */
823
+ export function writeHudWatchCommand(options) {
824
+ const safePreset = options.preset === 'minimal' || options.preset === 'focused' || options.preset === 'full'
825
+ ? `--preset=${options.preset}`
826
+ : undefined;
827
+ const platform = options.platform ?? process.platform;
828
+ const presetArgument = safePreset ? ` ${shellEscapeSingle(safePreset)}` : '';
829
+ if (platform === 'win32') {
830
+ const quotePowerShell = (value) => `'${value.replace(/'/g, "''")}'`;
831
+ const assignments = Object.entries(options.runtimeEnv)
832
+ .map(([key, value]) => `$env:${key} = ${quotePowerShell(value)}`)
833
+ .join('; ');
834
+ const invocation = ['&', quotePowerShell(options.nodeCommand), quotePowerShell(options.omxEntry), 'hud', '--watch', ...(safePreset ? [safePreset] : [])].join(' ');
835
+ const script = `${assignments}; ${invocation}`;
836
+ return options.powerShellEnvelope
837
+ ? `powershell.exe -NoLogo -NoExit -Command ${quotePowerShell(script)}`
838
+ : script;
839
+ }
840
+ const assignments = Object.entries(options.runtimeEnv)
841
+ .map(([key, value]) => `${key}=${key === OMX_TMUX_HUD_OWNER_ENV && !options.quoteOwnerValue ? value : shellEscapeSingle(value)}`)
842
+ .join(' ');
843
+ const nodeCommand = options.nodeCommand === 'node' ? 'node' : shellEscapeSingle(options.nodeCommand);
844
+ return `exec ${assignments ? `env ${assignments} ` : ''}${nodeCommand} ${shellEscapeSingle(options.omxEntry)} hud --watch${presetArgument}`;
845
+ }
370
846
  export function buildHudRuntimeEnv(input = {}) {
371
847
  const sessionId = typeof input.sessionId === 'string' ? input.sessionId.trim() : '';
372
- const leaderPaneId = typeof input.leaderPaneId === 'string' ? input.leaderPaneId.trim() : '';
848
+ const rawLeaderPaneId = typeof input.leaderPaneId === 'string' ? input.leaderPaneId : '';
849
+ const leaderPaneId = rawLeaderPaneId ? parseCanonicalTmuxPaneId(rawLeaderPaneId) : null;
373
850
  const env = {};
374
851
  if (sessionId)
375
852
  env.OMX_SESSION_ID = sessionId;
@@ -393,23 +870,40 @@ export function buildHudRuntimeEnv(input = {}) {
393
870
  },
394
871
  };
395
872
  }
396
- export function buildHudWatchCommand(omxBin, preset, sessionId, omxRoot, leaderPaneId, rootEnv) {
397
- const safePreset = preset === 'minimal' || preset === 'focused' || preset === 'full'
398
- ? ` --preset=${preset}`
399
- : '';
400
- const envPrefix = buildEnvPrefix(buildHudRuntimeEnv({
873
+ export function buildHudWatchCommand(omxBin, preset, sessionId, omxRoot, leaderPaneId, rootEnv, platform = process.platform) {
874
+ const runtimeEnv = buildHudRuntimeEnv({
401
875
  sessionId,
402
876
  leaderPaneId,
403
877
  omxRoot,
404
878
  ...(rootEnv ?? { rootSource: 'omx-root-env' }),
405
- }).env);
406
- return `exec ${envPrefix}${shellEscapeSingle(process.execPath)} ${shellEscapeSingle(omxBin)} hud --watch${safePreset}`;
879
+ }).env;
880
+ return writeHudWatchCommand({
881
+ omxEntry: omxBin,
882
+ runtimeEnv,
883
+ nodeCommand: process.execPath,
884
+ preset,
885
+ platform,
886
+ powerShellEnvelope: platform === 'win32',
887
+ quoteOwnerValue: true,
888
+ });
407
889
  }
408
890
  export function listCurrentWindowPanes(execTmuxSync = defaultExecTmuxSync, currentPaneId) {
891
+ const canonicalCurrentPaneId = currentPaneId ? parseCanonicalTmuxPaneId(currentPaneId) : null;
892
+ if (currentPaneId && !canonicalCurrentPaneId)
893
+ return [];
894
+ const targetArgs = canonicalCurrentPaneId ? ['-t', canonicalCurrentPaneId] : [];
409
895
  try {
410
- return parseTmuxPaneSnapshot(execTmuxSync([
896
+ const paneIds = parseCanonicalPaneIdSnapshot(execTmuxSync([
897
+ 'list-panes',
898
+ ...targetArgs,
899
+ '-F',
900
+ '#{pane_id}',
901
+ ]));
902
+ if (!paneIds)
903
+ return [];
904
+ const paneSnapshotOutput = execTmuxSync([
411
905
  'list-panes',
412
- ...(currentPaneId ? ['-t', currentPaneId] : []),
906
+ ...targetArgs,
413
907
  '-F',
414
908
  [
415
909
  '#{pane_id}',
@@ -423,8 +917,13 @@ export function listCurrentWindowPanes(execTmuxSync = defaultExecTmuxSync, curre
423
917
  '#{window_height}',
424
918
  '#{pane_start_command}',
425
919
  '#{pane_current_path}',
920
+ '#{pane_dead}',
921
+ '#{pane_pid}',
426
922
  ].join(TMUX_PANE_FIELD_SEPARATOR),
427
- ]));
923
+ ]);
924
+ if (!parseExactTmuxAuthorityLines(paneSnapshotOutput))
925
+ return [];
926
+ return parseTmuxPaneSnapshot(paneSnapshotOutput, paneIds);
428
927
  }
429
928
  catch {
430
929
  return [];
@@ -432,7 +931,8 @@ export function listCurrentWindowPanes(execTmuxSync = defaultExecTmuxSync, curre
432
931
  }
433
932
  export function readActiveTmuxPaneId(execTmuxSync = defaultExecTmuxSync) {
434
933
  try {
435
- return parsePaneIdFromTmuxOutput(execTmuxSync(['display-message', '-p', '#{pane_id}']));
934
+ const paneId = parseExactTmuxAuthorityScalar(execTmuxSync(['display-message', '-p', '#{pane_id}']));
935
+ return parseCanonicalTmuxPaneId(paneId);
436
936
  }
437
937
  catch {
438
938
  return null;
@@ -442,56 +942,421 @@ export function listCurrentWindowHudPaneIds(currentPaneId, execTmuxSync = defaul
442
942
  return findHudWatchPaneIds(listCurrentWindowPanes(execTmuxSync, currentPaneId), currentPaneId, owner);
443
943
  }
444
944
  export function readCurrentWindowSize(execTmuxSync = defaultExecTmuxSync, currentPaneId) {
945
+ const canonicalCurrentPaneId = currentPaneId ? parseCanonicalTmuxPaneId(currentPaneId) : null;
946
+ if (currentPaneId && !canonicalCurrentPaneId)
947
+ return { width: null, height: null };
445
948
  try {
446
949
  const raw = execTmuxSync([
447
950
  'display-message',
448
951
  '-p',
449
- ...(currentPaneId ? ['-t', currentPaneId] : []),
952
+ ...(canonicalCurrentPaneId ? ['-t', canonicalCurrentPaneId] : []),
450
953
  '#{window_width}\t#{window_height}',
451
954
  ]);
452
- const [widthRaw = '', heightRaw = ''] = raw.split('\t');
453
- const width = Number.parseInt(widthRaw.trim(), 10);
454
- const height = Number.parseInt(heightRaw.trim(), 10);
455
- return {
456
- width: Number.isFinite(width) && width > 0 ? width : null,
457
- height: Number.isFinite(height) && height > 0 ? height : null,
458
- };
955
+ const framed = parseExactTmuxAuthorityScalar(raw);
956
+ if (framed === null)
957
+ return { width: null, height: null };
958
+ const fields = framed.split('\t');
959
+ if (fields.length !== 2)
960
+ return { width: null, height: null };
961
+ const width = parsePositiveInteger(fields[0]);
962
+ const height = parsePositiveInteger(fields[1]);
963
+ return { width, height };
459
964
  }
460
965
  catch {
461
966
  return { width: null, height: null };
462
967
  }
463
968
  }
969
+ function readHudSplitSourceAuthority(targetPaneId, execTmuxSync) {
970
+ try {
971
+ const source = parseExactTmuxAuthorityScalar(execTmuxSync([
972
+ 'display-message',
973
+ '-p',
974
+ ...(targetPaneId ? ['-t', targetPaneId] : []),
975
+ '#{pane_id}\t#{pane_dead}\t#{pane_pid}\t#{session_id}\t#{window_id}',
976
+ ]));
977
+ if (!source)
978
+ return null;
979
+ const fields = source.split('\t');
980
+ if (fields.length !== 5)
981
+ return null;
982
+ const paneId = parseCanonicalTmuxPaneId(fields[0]);
983
+ const paneDead = fields[1];
984
+ const panePid = fields[2];
985
+ const sessionId = fields[3];
986
+ const windowId = fields[4];
987
+ if (!paneId
988
+ || (targetPaneId && paneId !== targetPaneId)
989
+ || paneDead !== '0'
990
+ || !/^[1-9][0-9]*$/.test(panePid ?? '')
991
+ || !isTmuxSessionId(sessionId ?? '')
992
+ || !isTmuxWindowId(windowId ?? ''))
993
+ return null;
994
+ return { paneId, panePid: panePid, sessionId: sessionId, windowId: windowId };
995
+ }
996
+ catch {
997
+ return null;
998
+ }
999
+ }
1000
+ function buildHudSplitSourceCondition(source) {
1001
+ return `#{&&:#{==:#{pane_id},${source.paneId}},#{&&:#{==:#{pane_dead},0},#{&&:#{==:#{pane_pid},${source.panePid}},#{&&:#{==:#{session_id},${source.sessionId}},#{==:#{window_id},${source.windowId}}}}}}`;
1002
+ }
1003
+ const hudSplitAuthorities = new Map();
1004
+ function readCanonicalPaneIdSnapshot(execTmuxSync, targetPaneId, global = false) {
1005
+ try {
1006
+ return parseCanonicalPaneIdSnapshot(execTmuxSync([
1007
+ 'list-panes',
1008
+ ...(global ? ['-a'] : targetPaneId ? ['-t', targetPaneId] : []),
1009
+ '-F',
1010
+ '#{pane_id}',
1011
+ ]));
1012
+ }
1013
+ catch {
1014
+ return null;
1015
+ }
1016
+ }
1017
+ function deriveSingleNewPane(before, after) {
1018
+ if (after.size !== before.size + 1 || ![...before].every((paneId) => after.has(paneId)))
1019
+ return null;
1020
+ const created = [...after].filter((paneId) => !before.has(paneId));
1021
+ return created.length === 1 ? created[0] ?? null : null;
1022
+ }
1023
+ const OMX_TMUX_SPLIT_OPERATION_MARKER_ENV = 'OMX_TMUX_SPLIT_OPERATION_MARKER';
1024
+ function writeHudSplitOperationMarkedCommand(command, marker) {
1025
+ if (process.platform === 'win32')
1026
+ return `$env:${OMX_TMUX_SPLIT_OPERATION_MARKER_ENV} = '${marker}'; ${command}`;
1027
+ return `${OMX_TMUX_SPLIT_OPERATION_MARKER_ENV}='${marker}'; export ${OMX_TMUX_SPLIT_OPERATION_MARKER_ENV}; ${command}`;
1028
+ }
1029
+ function hasHudSplitOperationMarker(startCommand, marker) {
1030
+ const posixMarker = `${OMX_TMUX_SPLIT_OPERATION_MARKER_ENV}='${marker}'`;
1031
+ const powerShellMarker = `$env:${OMX_TMUX_SPLIT_OPERATION_MARKER_ENV} = '${marker}'`;
1032
+ return startCommand === posixMarker
1033
+ || startCommand.startsWith(`${posixMarker};`)
1034
+ || startCommand === powerShellMarker
1035
+ || startCommand.startsWith(`${powerShellMarker};`);
1036
+ }
1037
+ function findHudSplitOperationMarkerPaneId(marker, execTmuxSync) {
1038
+ try {
1039
+ const lines = parseExactTmuxAuthorityLines(execTmuxSync(['list-panes', '-a', '-F', '#{pane_id}\t#{pane_start_command}']));
1040
+ if (!lines)
1041
+ return null;
1042
+ let candidate = null;
1043
+ const seen = new Set();
1044
+ for (const rawLine of lines) {
1045
+ const fields = rawLine.split('\t');
1046
+ if (fields.length !== 2)
1047
+ return null;
1048
+ const paneId = parseCanonicalTmuxPaneId(fields[0]);
1049
+ if (!paneId || paneId !== fields[0] || seen.has(paneId))
1050
+ return null;
1051
+ seen.add(paneId);
1052
+ const command = fields[1] ?? '';
1053
+ if (!hasHudSplitOperationMarker(command, marker))
1054
+ continue;
1055
+ if (candidate)
1056
+ return null;
1057
+ candidate = paneId;
1058
+ }
1059
+ return candidate;
1060
+ }
1061
+ catch {
1062
+ return null;
1063
+ }
1064
+ }
1065
+ function recoverHudSplitPaneId(globalBefore, targetBefore, targetPaneId, marker, execTmuxSync) {
1066
+ for (let attempt = 0; attempt < 3; attempt += 1) {
1067
+ const globalAfter = readCanonicalPaneIdSnapshot(execTmuxSync, undefined, true);
1068
+ const targetAfter = targetPaneId ? readCanonicalPaneIdSnapshot(execTmuxSync, targetPaneId) : null;
1069
+ const globalCandidate = globalAfter ? deriveSingleNewPane(globalBefore, globalAfter) : null;
1070
+ const targetCandidate = targetPaneId ? (targetAfter ? deriveSingleNewPane(targetBefore, targetAfter) : null) : globalCandidate;
1071
+ const markerCandidate = findHudSplitOperationMarkerPaneId(marker, execTmuxSync);
1072
+ if (globalCandidate && globalCandidate === targetCandidate && markerCandidate === globalCandidate)
1073
+ return globalCandidate;
1074
+ if (markerCandidate && !globalBefore.has(markerCandidate))
1075
+ return markerCandidate;
1076
+ if (attempt < 2)
1077
+ sleepSync(50);
1078
+ }
1079
+ return null;
1080
+ }
1081
+ function sleepSync(milliseconds) {
1082
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
1083
+ }
1084
+ function readTmuxOptionExactly(execTmuxSync, option) {
1085
+ try {
1086
+ return parseExactTmuxAuthorityScalar(execTmuxSync(['show-options', '-g', '-v', option]));
1087
+ }
1088
+ catch {
1089
+ return null;
1090
+ }
1091
+ }
1092
+ function hasHudSplitAuthority(paneId, execTmuxSync = defaultExecTmuxSync) {
1093
+ const authority = hudSplitAuthorities.get(paneId);
1094
+ if (!authority)
1095
+ return false;
1096
+ const globalPanes = readCanonicalPaneIdSnapshot(execTmuxSync, undefined, true);
1097
+ if (!globalPanes?.has(paneId))
1098
+ return false;
1099
+ if (authority.targetPaneId) {
1100
+ const targetPanes = readCanonicalPaneIdSnapshot(execTmuxSync, authority.targetPaneId);
1101
+ if (!targetPanes?.has(paneId))
1102
+ return false;
1103
+ }
1104
+ const location = readHudPaneSessionAndWindow(paneId, execTmuxSync);
1105
+ return (findHudSplitOperationMarkerPaneId(authority.operationMarker, execTmuxSync) === paneId
1106
+ && readHudPaneIncarnation(paneId, execTmuxSync)?.panePid === authority.panePid
1107
+ && location?.sessionId === authority.sessionId
1108
+ && location?.windowId === authority.windowId
1109
+ && readTmuxOptionExactly(execTmuxSync, authority.proofOption) === authority.proofValue);
1110
+ }
1111
+ function readHudPaneIncarnation(paneId, execTmuxSync) {
1112
+ try {
1113
+ const lines = parseExactTmuxAuthorityLines(execTmuxSync(['list-panes', '-a', '-F', '#{pane_id} #{pane_dead} #{pane_pid}']));
1114
+ if (!lines)
1115
+ return null;
1116
+ const seenPaneIds = new Set();
1117
+ let incarnation = null;
1118
+ for (const line of lines) {
1119
+ const match = /^(%\S+) ([01]) ([0-9]+)$/.exec(line);
1120
+ const observedPaneId = match ? parseCanonicalTmuxPaneId(match[1]) : null;
1121
+ if (!match || !observedPaneId || observedPaneId !== match[1] || seenPaneIds.has(observedPaneId))
1122
+ return null;
1123
+ seenPaneIds.add(observedPaneId);
1124
+ if (match[2] === '1')
1125
+ continue;
1126
+ if (!/^[1-9][0-9]*$/.test(match[3]))
1127
+ return null;
1128
+ if (observedPaneId === paneId)
1129
+ incarnation = { paneDead: false, panePid: match[3] };
1130
+ }
1131
+ return incarnation;
1132
+ }
1133
+ catch {
1134
+ return null;
1135
+ }
1136
+ }
1137
+ function isPaneLiveInStrictGlobalProbe(paneId, expectedPid, execTmuxSync) {
1138
+ const incarnation = readHudPaneIncarnation(paneId, execTmuxSync);
1139
+ return Boolean(incarnation && !incarnation.paneDead && (!expectedPid || incarnation.panePid === expectedPid));
1140
+ }
1141
+ /** Freshly validates retained split proof and sustained global liveness before a HUD mutation sink. */
1142
+ export function verifyHudWatchPaneAuthority(paneId, execTmuxSync = defaultExecTmuxSync) {
1143
+ const canonicalPaneId = parseCanonicalTmuxPaneId(paneId);
1144
+ if (!canonicalPaneId || !hasHudSplitAuthority(canonicalPaneId, execTmuxSync))
1145
+ return false;
1146
+ for (let attempt = 0; attempt < 3; attempt += 1) {
1147
+ if (attempt > 0)
1148
+ sleepSync(100);
1149
+ if (!hasHudSplitAuthority(canonicalPaneId, execTmuxSync)
1150
+ || !isPaneLiveInStrictGlobalProbe(canonicalPaneId, hudSplitAuthorities.get(canonicalPaneId)?.panePid, execTmuxSync))
1151
+ return false;
1152
+ }
1153
+ return true;
1154
+ }
1155
+ /** Kills only a pane still bound to this process's exact split proof. */
1156
+ export function rollbackHudWatchPaneAuthority(paneId, execTmuxSync = defaultExecTmuxSync) {
1157
+ const canonicalPaneId = parseCanonicalTmuxPaneId(paneId);
1158
+ const authority = canonicalPaneId ? hudSplitAuthorities.get(canonicalPaneId) : undefined;
1159
+ if (!canonicalPaneId || !authority)
1160
+ return false;
1161
+ const killed = mutateHudWatchPaneIfCurrent(canonicalPaneId, authority.panePid, `kill-pane -t ${canonicalPaneId}`, execTmuxSync);
1162
+ if (killed)
1163
+ hudSplitAuthorities.delete(canonicalPaneId);
1164
+ return killed;
1165
+ }
1166
+ function rollbackRecoveredHudSplitPane(paneId, proofOption, proofValue, operationMarker, panePid, sessionId, windowId, execTmuxSync = defaultExecTmuxSync) {
1167
+ const canonicalPaneId = parseCanonicalTmuxPaneId(paneId);
1168
+ if (!canonicalPaneId || canonicalPaneId !== paneId)
1169
+ return false;
1170
+ const pidCondition = panePid && /^[1-9][0-9]*$/.test(panePid) ? `#{&&:#{==:#{pane_pid},${panePid}},` : '';
1171
+ const sessionCondition = sessionId && /^\$[0-9]+$/.test(sessionId) ? `#{&&:#{==:#{session_id},${sessionId}},` : '';
1172
+ const windowCondition = windowId && /^@[0-9]+$/.test(windowId) ? `#{&&:#{==:#{window_id},${windowId}},` : '';
1173
+ const closes = `${pidCondition ? '}' : ''}${sessionCondition ? '}' : ''}${windowCondition ? '}' : ''}`;
1174
+ const receipt = `__omx_hud_rollback_${randomUUID()}`;
1175
+ const condition = `#{&&:#{==:#{pane_id},${paneId}},#{&&:#{==:#{pane_dead},0},${pidCondition}${sessionCondition}${windowCondition}#{&&:#{==:#{${proofOption}},${proofValue}},#{m:*${operationMarker}*,#{pane_start_command}}}${closes}}`;
1176
+ try {
1177
+ return parseExactTmuxAuthorityScalar(execTmuxSync([
1178
+ 'if-shell', '-F', '-t', paneId,
1179
+ condition,
1180
+ `kill-pane -t ${paneId} \\; display-message -p ${receipt}`,
1181
+ `display-message -p __omx_hud_rollback_rejected_${receipt}`,
1182
+ ])) === receipt;
1183
+ }
1184
+ catch {
1185
+ return false;
1186
+ }
1187
+ }
464
1188
  export function createHudWatchPane(cwd, hudCmd, options = {}, execTmuxSync = defaultExecTmuxSync) {
1189
+ const canonicalTargetPaneId = options.targetPaneId ? parseCanonicalTmuxPaneId(options.targetPaneId) : null;
1190
+ if (options.targetPaneId && !canonicalTargetPaneId)
1191
+ return null;
1192
+ const sourceAuthority = readHudSplitSourceAuthority(canonicalTargetPaneId, execTmuxSync);
1193
+ if (!sourceAuthority)
1194
+ return null;
1195
+ const sourcePaneId = sourceAuthority.paneId;
1196
+ const globalBefore = readCanonicalPaneIdSnapshot(execTmuxSync, undefined, true);
1197
+ const targetBefore = readCanonicalPaneIdSnapshot(execTmuxSync, sourcePaneId);
1198
+ if (!globalBefore || !targetBefore)
1199
+ return null;
1200
+ const operationMarker = randomUUID();
465
1201
  const heightLines = Number.isFinite(options.heightLines) && (options.heightLines ?? 0) > 0
466
1202
  ? Math.floor(options.heightLines ?? HUD_TMUX_HEIGHT_LINES)
467
1203
  : HUD_TMUX_HEIGHT_LINES;
468
1204
  const args = [
469
- 'split-window',
470
- '-v',
471
- ...(options.fullWidth ? ['-f'] : []),
472
- '-l',
473
- String(heightLines),
474
- '-d',
475
- ...(options.targetPaneId ? ['-t', options.targetPaneId] : []),
476
- '-c',
477
- cwd,
478
- '-P',
479
- '-F',
480
- '#{pane_id}',
481
- hudCmd,
1205
+ 'split-window', '-v', ...(options.fullWidth ? ['-f'] : []), '-l', String(heightLines), '-d',
1206
+ '-t', sourcePaneId, '-c', shellEscapeSingle(cwd), writeHudSplitOperationMarkedCommand(hudCmd, operationMarker),
482
1207
  ];
1208
+ let paneId = null;
1209
+ const nonce = randomUUID();
1210
+ const proofOption = `@omx_hud_split_owner_nonce_${nonce}`;
1211
+ const provisionalProof = `pending:${nonce}`;
1212
+ const receipt = `__omx_hud_split_${randomUUID()}`;
1213
+ const splitCommand = args.join(' ');
1214
+ try {
1215
+ execTmuxSync(['set-option', '-g', proofOption, provisionalProof]);
1216
+ if (readTmuxOptionExactly(execTmuxSync, proofOption) !== provisionalProof)
1217
+ return null;
1218
+ const splitOutput = execTmuxSync([
1219
+ 'if-shell', '-F', '-t', sourcePaneId, buildHudSplitSourceCondition(sourceAuthority),
1220
+ `${splitCommand} \\; display-message -p ${receipt}`,
1221
+ `display-message -p __omx_hud_split_rejected_${receipt}`,
1222
+ ]);
1223
+ if (parseExactTmuxAuthorityScalar(splitOutput) !== receipt)
1224
+ return null;
1225
+ paneId = recoverHudSplitPaneId(globalBefore, targetBefore, sourcePaneId, operationMarker, execTmuxSync);
1226
+ const incarnation = paneId ? readHudPaneIncarnation(paneId, execTmuxSync) : null;
1227
+ const location = paneId ? readHudPaneSessionAndWindow(paneId, execTmuxSync) : null;
1228
+ if (!paneId
1229
+ || !incarnation
1230
+ || !location
1231
+ || location.sessionId !== sourceAuthority.sessionId
1232
+ || location.windowId !== sourceAuthority.windowId) {
1233
+ if (paneId) {
1234
+ rollbackRecoveredHudSplitPane(paneId, proofOption, provisionalProof, operationMarker, incarnation?.panePid, location?.sessionId ?? sourceAuthority.sessionId, location?.windowId ?? sourceAuthority.windowId, execTmuxSync);
1235
+ }
1236
+ return null;
1237
+ }
1238
+ hudSplitAuthorities.set(paneId, {
1239
+ proofOption,
1240
+ proofValue: provisionalProof,
1241
+ operationMarker,
1242
+ panePid: incarnation.panePid,
1243
+ sessionId: location.sessionId,
1244
+ windowId: location.windowId,
1245
+ targetPaneId: sourcePaneId,
1246
+ });
1247
+ if (!hasHudSplitAuthority(paneId, execTmuxSync)) {
1248
+ rollbackHudWatchPaneAuthority(paneId, execTmuxSync);
1249
+ return null;
1250
+ }
1251
+ const proofValue = `${paneId}:split:${nonce}`;
1252
+ execTmuxSync(['set-option', '-g', proofOption, proofValue]);
1253
+ hudSplitAuthorities.set(paneId, { proofOption, proofValue, operationMarker, panePid: incarnation.panePid, sessionId: location.sessionId, windowId: location.windowId, targetPaneId: sourcePaneId });
1254
+ if (readTmuxOptionExactly(execTmuxSync, proofOption) !== proofValue) {
1255
+ rollbackHudWatchPaneAuthority(paneId, execTmuxSync);
1256
+ return null;
1257
+ }
1258
+ if (!verifyHudWatchPaneAuthority(paneId, execTmuxSync)) {
1259
+ rollbackHudWatchPaneAuthority(paneId, execTmuxSync);
1260
+ return null;
1261
+ }
1262
+ return paneId;
1263
+ }
1264
+ catch {
1265
+ if (paneId) {
1266
+ rollbackHudWatchPaneAuthority(paneId, execTmuxSync);
1267
+ }
1268
+ else {
1269
+ const recoveredPaneId = recoverHudSplitPaneId(globalBefore, targetBefore, sourcePaneId, operationMarker, execTmuxSync);
1270
+ if (recoveredPaneId) {
1271
+ rollbackRecoveredHudSplitPane(recoveredPaneId, proofOption, provisionalProof, operationMarker, undefined, sourceAuthority.sessionId, sourceAuthority.windowId, execTmuxSync);
1272
+ }
1273
+ }
1274
+ return null;
1275
+ }
1276
+ }
1277
+ function readHudPaneSessionAndWindow(paneId, execTmuxSync) {
483
1278
  try {
484
- return parsePaneIdFromTmuxOutput(execTmuxSync(args));
1279
+ const value = parseExactTmuxAuthorityScalar(execTmuxSync([
1280
+ 'display-message', '-p', '-t', paneId, '#{session_id}\t#{window_id}',
1281
+ ]));
1282
+ if (!value)
1283
+ return null;
1284
+ const [sessionId, windowId, ...extra] = value.split('\t');
1285
+ return !extra.length && isTmuxSessionId(sessionId ?? '') && isTmuxWindowId(windowId ?? '')
1286
+ ? { sessionId: sessionId, windowId: windowId }
1287
+ : null;
485
1288
  }
486
1289
  catch {
487
1290
  return null;
488
1291
  }
489
1292
  }
1293
+ /** Executes a split-owned pane mutation only while its immutable owner proof and exact incarnation remain current. */
1294
+ export function mutateHudWatchPaneIfCurrent(paneId, expectedPanePid, mutation, execTmuxSync = defaultExecTmuxSync) {
1295
+ const canonicalPaneId = parseCanonicalTmuxPaneId(paneId);
1296
+ const authority = canonicalPaneId ? hudSplitAuthorities.get(canonicalPaneId) : undefined;
1297
+ if (!canonicalPaneId
1298
+ || !authority
1299
+ || authority.panePid !== expectedPanePid
1300
+ || !/^[1-9][0-9]*$/.test(expectedPanePid)
1301
+ || !/^(?:kill-pane|resize-pane) -t %(?:0|[1-9][0-9]*)(?: -y [1-9][0-9]*)?$/.test(mutation))
1302
+ return false;
1303
+ const marker = `__omx_hud_mutation_${randomUUID()}`;
1304
+ const condition = `#{&&:#{==:#{pane_id},${canonicalPaneId}},#{&&:#{==:#{pane_dead},0},#{&&:#{==:#{pane_pid},${expectedPanePid}},#{&&:#{==:#{session_id},${authority.sessionId}},#{&&:#{==:#{window_id},${authority.windowId}},#{&&:#{==:#{${authority.proofOption}},${authority.proofValue}},#{m:*${authority.operationMarker}*,#{pane_start_command}}}}}}}`;
1305
+ try {
1306
+ const output = execTmuxSync([
1307
+ 'if-shell', '-F', '-t', canonicalPaneId,
1308
+ condition,
1309
+ `${mutation} \\; display-message -p ${marker}`,
1310
+ `display-message -p __omx_hud_mutation_failed_${marker}`,
1311
+ ]);
1312
+ return parseExactTmuxAuthorityScalar(output) === marker;
1313
+ }
1314
+ catch {
1315
+ return false;
1316
+ }
1317
+ }
1318
+ /** Executes a retained pane mutation only while the exact target incarnation remains live. */
1319
+ function mutateTmuxPaneIfCurrent(paneId, expectedPanePid, mutation, execTmuxSync = defaultExecTmuxSync) {
1320
+ const canonicalPaneId = parseCanonicalTmuxPaneId(paneId);
1321
+ if (!canonicalPaneId || !/^[1-9][0-9]*$/.test(expectedPanePid))
1322
+ return false;
1323
+ const marker = `__omx_hud_mutation_${randomUUID()}`;
1324
+ try {
1325
+ const output = execTmuxSync([
1326
+ 'if-shell', '-F', '-t', canonicalPaneId,
1327
+ buildHudHookIncarnationCondition(canonicalPaneId, expectedPanePid),
1328
+ `${mutation} \\; display-message -p ${marker}`,
1329
+ `display-message -p __omx_hud_mutation_failed_${marker}`,
1330
+ ]);
1331
+ return parseExactTmuxAuthorityScalar(output) === marker;
1332
+ }
1333
+ catch {
1334
+ return false;
1335
+ }
1336
+ }
1337
+ /** Kills a pane only while its exact live incarnation remains the target. */
1338
+ export function killTmuxPaneIfCurrent(paneId, expectedPanePid, execTmuxSync = defaultExecTmuxSync) {
1339
+ const canonicalPaneId = parseCanonicalTmuxPaneId(paneId);
1340
+ return canonicalPaneId
1341
+ ? mutateTmuxPaneIfCurrent(canonicalPaneId, expectedPanePid, `kill-pane -t ${canonicalPaneId}`, execTmuxSync)
1342
+ : false;
1343
+ }
1344
+ /** Resizes a pane only while its exact live incarnation remains the target. */
1345
+ export function resizeTmuxPaneIfCurrent(paneId, expectedPanePid, heightLines, execTmuxSync = defaultExecTmuxSync) {
1346
+ const canonicalPaneId = parseCanonicalTmuxPaneId(paneId);
1347
+ if (!canonicalPaneId)
1348
+ return false;
1349
+ const height = Number.isFinite(heightLines) && heightLines > 0
1350
+ ? Math.floor(heightLines)
1351
+ : HUD_TMUX_HEIGHT_LINES;
1352
+ return mutateTmuxPaneIfCurrent(canonicalPaneId, expectedPanePid, `resize-pane -t ${canonicalPaneId} -y ${height}`, execTmuxSync);
1353
+ }
490
1354
  export function killTmuxPane(paneId, execTmuxSync = defaultExecTmuxSync) {
491
- if (!paneId.startsWith('%'))
1355
+ const canonicalPaneId = parseCanonicalTmuxPaneId(paneId);
1356
+ if (!canonicalPaneId)
492
1357
  return false;
493
1358
  try {
494
- execTmuxSync(['kill-pane', '-t', paneId]);
1359
+ execTmuxSync(['kill-pane', '-t', canonicalPaneId]);
495
1360
  return true;
496
1361
  }
497
1362
  catch {
@@ -499,13 +1364,14 @@ export function killTmuxPane(paneId, execTmuxSync = defaultExecTmuxSync) {
499
1364
  }
500
1365
  }
501
1366
  export function resizeTmuxPane(paneId, heightLines, execTmuxSync = defaultExecTmuxSync) {
502
- if (!paneId.startsWith('%'))
1367
+ const canonicalPaneId = parseCanonicalTmuxPaneId(paneId);
1368
+ if (!canonicalPaneId)
503
1369
  return false;
504
1370
  const height = Number.isFinite(heightLines) && heightLines > 0
505
1371
  ? Math.floor(heightLines)
506
1372
  : HUD_TMUX_HEIGHT_LINES;
507
1373
  try {
508
- execTmuxSync(['resize-pane', '-t', paneId, '-y', String(height)]);
1374
+ execTmuxSync(['resize-pane', '-t', canonicalPaneId, '-y', String(height)]);
509
1375
  return true;
510
1376
  }
511
1377
  catch {
@@ -517,26 +1383,28 @@ export function registerHudResizeHook(hudPaneId, leaderPaneId, heightLines, opti
517
1383
  const execTmuxSync = typeof optionsOrExecTmuxSync === 'function'
518
1384
  ? optionsOrExecTmuxSync
519
1385
  : (maybeExecTmuxSync ?? defaultExecTmuxSync);
520
- if (!hudPaneId.startsWith('%'))
1386
+ const canonicalHudPaneId = parseCanonicalTmuxPaneId(hudPaneId);
1387
+ const canonicalLeaderPaneId = parseCanonicalTmuxPaneId(leaderPaneId);
1388
+ if (!canonicalHudPaneId || !canonicalLeaderPaneId)
521
1389
  return false;
522
- const context = readHudResizeHookContext(leaderPaneId, execTmuxSync);
1390
+ const context = readHudResizeHookContext(canonicalHudPaneId, canonicalLeaderPaneId, execTmuxSync);
523
1391
  if (!context)
524
1392
  return false;
525
1393
  const tmuxBin = resolveTmuxBinaryForPlatform() || 'tmux';
526
1394
  const height = String(Math.max(1, Math.floor(heightLines)));
527
- const resizeCmd = shellEscapeSingle(buildHudResizeHookCommand(tmuxBin, hudPaneId, height, context, options.env?.TMUX));
1395
+ const resizeCmd = shellEscapeSingle(buildHudResizeHookCommand(tmuxBin, canonicalHudPaneId, height, context, options.env?.TMUX));
528
1396
  const omxBin = resolveOmxCliEntryPath({ cwd: options.cwd, env: options.env });
529
1397
  try {
530
- execTmuxSync(['set-hook', '-t', context.sessionId, context.hookSlot, `run-shell -b ${resizeCmd}`]);
1398
+ execTmuxSync(['set-hook', '-t', context.sessionId, context.hookSlot, `run-shell -b ${resizeCmd}`, ...buildHudHookRegistrationSuffix(context, context.hookSlot)]);
531
1399
  unregisterLegacyHudResizeHook(context, execTmuxSync);
532
1400
  }
533
1401
  catch {
534
1402
  return false;
535
1403
  }
536
- if (omxBin && leaderPaneId?.startsWith('%')) {
1404
+ if (omxBin) {
537
1405
  try {
538
- const reconcileCmd = shellEscapeSingle(buildHudLayoutReconcileHookCommand(tmuxBin, omxBin, leaderPaneId, context, options));
539
- execTmuxSync(['set-hook', '-t', context.sessionId, context.layoutHookSlot, `run-shell -b ${reconcileCmd}`]);
1406
+ const reconcileCmd = shellEscapeSingle(buildHudLayoutReconcileHookCommand(tmuxBin, omxBin, canonicalLeaderPaneId, context, options));
1407
+ execTmuxSync(['set-hook', '-t', context.sessionId, context.layoutHookSlot, `run-shell -b ${reconcileCmd}`, ...buildHudHookRegistrationSuffix(context, context.layoutHookSlot)]);
540
1408
  }
541
1409
  catch {
542
1410
  // Keep the resize hook installed so older tmux builds still recover on
@@ -547,19 +1415,29 @@ export function registerHudResizeHook(hudPaneId, leaderPaneId, heightLines, opti
547
1415
  return true;
548
1416
  }
549
1417
  export function unregisterHudResizeHook(leaderPaneId, execTmuxSync = defaultExecTmuxSync) {
550
- const context = readHudResizeHookContext(leaderPaneId, execTmuxSync);
1418
+ const canonicalLeaderPaneId = parseCanonicalTmuxPaneId(leaderPaneId);
1419
+ if (!canonicalLeaderPaneId)
1420
+ return false;
1421
+ let contextOutput;
1422
+ try {
1423
+ contextOutput = execTmuxSync(['display-message', '-p', '-t', canonicalLeaderPaneId, '#{session_id}\t#{window_id}']);
1424
+ }
1425
+ catch {
1426
+ return false;
1427
+ }
1428
+ const context = parseHudResizeHookContext(contextOutput, canonicalLeaderPaneId, canonicalLeaderPaneId, { leaderPanePid: '1', hudPanePid: '1' });
551
1429
  if (!context)
552
1430
  return false;
553
1431
  let ok = true;
554
1432
  try {
555
1433
  unregisterLegacyHudResizeHook(context, execTmuxSync);
556
- execTmuxSync(['set-hook', '-u', '-t', context.sessionId, context.hookSlot]);
1434
+ execTmuxSync(buildGuardedHudHookUnregisterArgs(context, context.hookSlot));
557
1435
  }
558
1436
  catch {
559
1437
  ok = false;
560
1438
  }
561
1439
  try {
562
- execTmuxSync(['set-hook', '-u', '-t', context.sessionId, context.layoutHookSlot]);
1440
+ execTmuxSync(buildGuardedHudHookUnregisterArgs(context, context.layoutHookSlot));
563
1441
  }
564
1442
  catch {
565
1443
  ok = false;