oh-my-codex 0.18.6 → 0.18.8

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 (444) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +59 -10
  4. package/crates/omx-sparkshell/tests/execution.rs +1 -1
  5. package/dist/agents/__tests__/definitions.test.js +11 -0
  6. package/dist/agents/__tests__/definitions.test.js.map +1 -1
  7. package/dist/agents/__tests__/native-config.test.js +56 -6
  8. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  9. package/dist/agents/definitions.d.ts +10 -0
  10. package/dist/agents/definitions.d.ts.map +1 -1
  11. package/dist/agents/definitions.js +5 -1
  12. package/dist/agents/definitions.js.map +1 -1
  13. package/dist/agents/native-config.d.ts +5 -1
  14. package/dist/agents/native-config.d.ts.map +1 -1
  15. package/dist/agents/native-config.js +19 -4
  16. package/dist/agents/native-config.js.map +1 -1
  17. package/dist/autopilot/__tests__/fsm.test.d.ts +2 -0
  18. package/dist/autopilot/__tests__/fsm.test.d.ts.map +1 -0
  19. package/dist/autopilot/__tests__/fsm.test.js +75 -0
  20. package/dist/autopilot/__tests__/fsm.test.js.map +1 -0
  21. package/dist/autopilot/__tests__/ralplan-gate.test.d.ts +2 -0
  22. package/dist/autopilot/__tests__/ralplan-gate.test.d.ts.map +1 -0
  23. package/dist/autopilot/__tests__/ralplan-gate.test.js +79 -0
  24. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -0
  25. package/dist/autopilot/deep-interview-gate.d.ts +18 -0
  26. package/dist/autopilot/deep-interview-gate.d.ts.map +1 -0
  27. package/dist/autopilot/deep-interview-gate.js +256 -0
  28. package/dist/autopilot/deep-interview-gate.js.map +1 -0
  29. package/dist/autopilot/fsm.d.ts +13 -0
  30. package/dist/autopilot/fsm.d.ts.map +1 -0
  31. package/dist/autopilot/fsm.js +70 -0
  32. package/dist/autopilot/fsm.js.map +1 -0
  33. package/dist/autopilot/ralplan-gate.d.ts +17 -0
  34. package/dist/autopilot/ralplan-gate.d.ts.map +1 -0
  35. package/dist/autopilot/ralplan-gate.js +61 -0
  36. package/dist/autopilot/ralplan-gate.js.map +1 -0
  37. package/dist/cli/__tests__/codex-plugin-layout.test.js +512 -1
  38. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  39. package/dist/cli/__tests__/doctor-warning-copy.test.js +39 -0
  40. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  41. package/dist/cli/__tests__/index.test.js +83 -7
  42. package/dist/cli/__tests__/index.test.js.map +1 -1
  43. package/dist/cli/__tests__/launch-fallback.test.js +175 -6
  44. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  45. package/dist/cli/__tests__/package-bin-contract.test.js +8 -4
  46. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  47. package/dist/cli/__tests__/question.test.js +100 -0
  48. package/dist/cli/__tests__/question.test.js.map +1 -1
  49. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +13 -0
  50. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  51. package/dist/cli/__tests__/ralph.test.js +14 -0
  52. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  53. package/dist/cli/__tests__/setup-install-mode.test.js +89 -0
  54. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  55. package/dist/cli/__tests__/setup-refresh.test.js +83 -0
  56. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  57. package/dist/cli/__tests__/state.test.js +21 -0
  58. package/dist/cli/__tests__/state.test.js.map +1 -1
  59. package/dist/cli/__tests__/team.test.js +2 -2
  60. package/dist/cli/__tests__/team.test.js.map +1 -1
  61. package/dist/cli/__tests__/update.test.js +110 -2
  62. package/dist/cli/__tests__/update.test.js.map +1 -1
  63. package/dist/cli/doctor.d.ts.map +1 -1
  64. package/dist/cli/doctor.js +8 -1
  65. package/dist/cli/doctor.js.map +1 -1
  66. package/dist/cli/index.d.ts +14 -3
  67. package/dist/cli/index.d.ts.map +1 -1
  68. package/dist/cli/index.js +298 -50
  69. package/dist/cli/index.js.map +1 -1
  70. package/dist/cli/plugin-marketplace.d.ts +14 -2
  71. package/dist/cli/plugin-marketplace.d.ts.map +1 -1
  72. package/dist/cli/plugin-marketplace.js +62 -15
  73. package/dist/cli/plugin-marketplace.js.map +1 -1
  74. package/dist/cli/question.d.ts.map +1 -1
  75. package/dist/cli/question.js +36 -5
  76. package/dist/cli/question.js.map +1 -1
  77. package/dist/cli/ralph.d.ts.map +1 -1
  78. package/dist/cli/ralph.js +3 -1
  79. package/dist/cli/ralph.js.map +1 -1
  80. package/dist/cli/setup-preferences.d.ts +2 -0
  81. package/dist/cli/setup-preferences.d.ts.map +1 -1
  82. package/dist/cli/setup-preferences.js +4 -0
  83. package/dist/cli/setup-preferences.js.map +1 -1
  84. package/dist/cli/setup.d.ts +3 -0
  85. package/dist/cli/setup.d.ts.map +1 -1
  86. package/dist/cli/setup.js +166 -27
  87. package/dist/cli/setup.js.map +1 -1
  88. package/dist/cli/state.d.ts.map +1 -1
  89. package/dist/cli/state.js +8 -1
  90. package/dist/cli/state.js.map +1 -1
  91. package/dist/cli/tmux-hook.d.ts.map +1 -1
  92. package/dist/cli/tmux-hook.js +16 -0
  93. package/dist/cli/tmux-hook.js.map +1 -1
  94. package/dist/cli/update.d.ts +2 -0
  95. package/dist/cli/update.d.ts.map +1 -1
  96. package/dist/cli/update.js +47 -3
  97. package/dist/cli/update.js.map +1 -1
  98. package/dist/config/__tests__/deep-interview.test.js +7 -6
  99. package/dist/config/__tests__/deep-interview.test.js.map +1 -1
  100. package/dist/config/__tests__/generator-notify.test.js +1 -0
  101. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  102. package/dist/config/deep-interview.d.ts.map +1 -1
  103. package/dist/config/deep-interview.js +14 -4
  104. package/dist/config/deep-interview.js.map +1 -1
  105. package/dist/config/generator.d.ts +2 -2
  106. package/dist/config/generator.d.ts.map +1 -1
  107. package/dist/config/generator.js +2 -2
  108. package/dist/config/generator.js.map +1 -1
  109. package/dist/config/team-mode.d.ts +12 -0
  110. package/dist/config/team-mode.d.ts.map +1 -0
  111. package/dist/config/team-mode.js +91 -0
  112. package/dist/config/team-mode.js.map +1 -0
  113. package/dist/hooks/__tests__/agents-overlay.test.js +88 -0
  114. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  115. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +8 -0
  116. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  117. package/dist/hooks/__tests__/code-review-skill-contract.test.js +8 -0
  118. package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
  119. package/dist/hooks/__tests__/deep-interview-contract.test.js +10 -0
  120. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  121. package/dist/hooks/__tests__/keyword-detector.test.js +1072 -14
  122. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  123. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +64 -1
  124. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  125. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +189 -0
  126. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  127. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +35 -2
  128. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  129. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +3 -3
  130. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  131. package/dist/hooks/__tests__/session.test.js +25 -0
  132. package/dist/hooks/__tests__/session.test.js.map +1 -1
  133. package/dist/hooks/__tests__/skill-guidance-contract.test.js +21 -0
  134. package/dist/hooks/__tests__/skill-guidance-contract.test.js.map +1 -1
  135. package/dist/hooks/agents-overlay.d.ts.map +1 -1
  136. package/dist/hooks/agents-overlay.js +36 -50
  137. package/dist/hooks/agents-overlay.js.map +1 -1
  138. package/dist/hooks/deep-interview-config-instruction.js +1 -1
  139. package/dist/hooks/deep-interview-config-instruction.js.map +1 -1
  140. package/dist/hooks/extensibility/__tests__/plugin-runner.test.js +31 -0
  141. package/dist/hooks/extensibility/__tests__/plugin-runner.test.js.map +1 -1
  142. package/dist/hooks/extensibility/plugin-runner.js +17 -21
  143. package/dist/hooks/extensibility/plugin-runner.js.map +1 -1
  144. package/dist/hooks/keyword-detector.d.ts +1 -0
  145. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  146. package/dist/hooks/keyword-detector.js +428 -32
  147. package/dist/hooks/keyword-detector.js.map +1 -1
  148. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  149. package/dist/hooks/keyword-registry.js +1 -0
  150. package/dist/hooks/keyword-registry.js.map +1 -1
  151. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  152. package/dist/hooks/prompt-guidance-contract.js +6 -0
  153. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  154. package/dist/hooks/session.d.ts +3 -0
  155. package/dist/hooks/session.d.ts.map +1 -1
  156. package/dist/hooks/session.js +13 -5
  157. package/dist/hooks/session.js.map +1 -1
  158. package/dist/hud/__tests__/authority.test.js +469 -31
  159. package/dist/hud/__tests__/authority.test.js.map +1 -1
  160. package/dist/hud/__tests__/hud-tmux-injection.test.js +2 -1
  161. package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
  162. package/dist/hud/__tests__/index.test.js +210 -2
  163. package/dist/hud/__tests__/index.test.js.map +1 -1
  164. package/dist/hud/__tests__/reconcile.test.js +588 -28
  165. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  166. package/dist/hud/__tests__/render.test.js +61 -0
  167. package/dist/hud/__tests__/render.test.js.map +1 -1
  168. package/dist/hud/__tests__/state.test.js +208 -0
  169. package/dist/hud/__tests__/state.test.js.map +1 -1
  170. package/dist/hud/__tests__/tmux.test.js +314 -22
  171. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  172. package/dist/hud/authority.d.ts +5 -0
  173. package/dist/hud/authority.d.ts.map +1 -1
  174. package/dist/hud/authority.js +337 -30
  175. package/dist/hud/authority.js.map +1 -1
  176. package/dist/hud/index.d.ts +20 -2
  177. package/dist/hud/index.d.ts.map +1 -1
  178. package/dist/hud/index.js +103 -26
  179. package/dist/hud/index.js.map +1 -1
  180. package/dist/hud/reconcile.d.ts +3 -3
  181. package/dist/hud/reconcile.d.ts.map +1 -1
  182. package/dist/hud/reconcile.js +129 -20
  183. package/dist/hud/reconcile.js.map +1 -1
  184. package/dist/hud/render.d.ts.map +1 -1
  185. package/dist/hud/render.js +35 -0
  186. package/dist/hud/render.js.map +1 -1
  187. package/dist/hud/state.d.ts.map +1 -1
  188. package/dist/hud/state.js +64 -50
  189. package/dist/hud/state.js.map +1 -1
  190. package/dist/hud/tmux.d.ts +26 -6
  191. package/dist/hud/tmux.d.ts.map +1 -1
  192. package/dist/hud/tmux.js +173 -38
  193. package/dist/hud/tmux.js.map +1 -1
  194. package/dist/hud/types.d.ts +11 -0
  195. package/dist/hud/types.d.ts.map +1 -1
  196. package/dist/hud/types.js.map +1 -1
  197. package/dist/mcp/__tests__/hermes-bridge.test.js +203 -7
  198. package/dist/mcp/__tests__/hermes-bridge.test.js.map +1 -1
  199. package/dist/mcp/__tests__/state-paths.test.js +71 -1
  200. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  201. package/dist/mcp/__tests__/state-server.test.js +13 -1
  202. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  203. package/dist/mcp/hermes-bridge.d.ts +12 -2
  204. package/dist/mcp/hermes-bridge.d.ts.map +1 -1
  205. package/dist/mcp/hermes-bridge.js +83 -9
  206. package/dist/mcp/hermes-bridge.js.map +1 -1
  207. package/dist/mcp/state-paths.d.ts +32 -0
  208. package/dist/mcp/state-paths.d.ts.map +1 -1
  209. package/dist/mcp/state-paths.js +113 -17
  210. package/dist/mcp/state-paths.js.map +1 -1
  211. package/dist/mcp/state-server.d.ts +4 -4
  212. package/dist/modes/__tests__/base-autoresearch-contract.test.js +7 -1
  213. package/dist/modes/__tests__/base-autoresearch-contract.test.js.map +1 -1
  214. package/dist/pipeline/__tests__/stages.test.js +130 -0
  215. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  216. package/dist/pipeline/orchestrator.js +1 -1
  217. package/dist/pipeline/orchestrator.js.map +1 -1
  218. package/dist/pipeline/stages/ralplan.d.ts +1 -0
  219. package/dist/pipeline/stages/ralplan.d.ts.map +1 -1
  220. package/dist/pipeline/stages/ralplan.js +14 -5
  221. package/dist/pipeline/stages/ralplan.js.map +1 -1
  222. package/dist/question/__tests__/deep-interview.test.js +160 -2
  223. package/dist/question/__tests__/deep-interview.test.js.map +1 -1
  224. package/dist/question/__tests__/policy.test.js +63 -3
  225. package/dist/question/__tests__/policy.test.js.map +1 -1
  226. package/dist/question/__tests__/renderer.test.js +191 -2
  227. package/dist/question/__tests__/renderer.test.js.map +1 -1
  228. package/dist/question/__tests__/state.test.js +94 -3
  229. package/dist/question/__tests__/state.test.js.map +1 -1
  230. package/dist/question/__tests__/ui.test.js +4 -0
  231. package/dist/question/__tests__/ui.test.js.map +1 -1
  232. package/dist/question/autopilot-wait.d.ts +12 -2
  233. package/dist/question/autopilot-wait.d.ts.map +1 -1
  234. package/dist/question/autopilot-wait.js +158 -47
  235. package/dist/question/autopilot-wait.js.map +1 -1
  236. package/dist/question/deep-interview.d.ts.map +1 -1
  237. package/dist/question/deep-interview.js +22 -6
  238. package/dist/question/deep-interview.js.map +1 -1
  239. package/dist/question/policy.d.ts.map +1 -1
  240. package/dist/question/policy.js +2 -5
  241. package/dist/question/policy.js.map +1 -1
  242. package/dist/question/renderer.d.ts +12 -0
  243. package/dist/question/renderer.d.ts.map +1 -1
  244. package/dist/question/renderer.js +87 -3
  245. package/dist/question/renderer.js.map +1 -1
  246. package/dist/question/state.d.ts +8 -1
  247. package/dist/question/state.d.ts.map +1 -1
  248. package/dist/question/state.js +54 -14
  249. package/dist/question/state.js.map +1 -1
  250. package/dist/question/types.d.ts +1 -1
  251. package/dist/question/types.d.ts.map +1 -1
  252. package/dist/question/ui.d.ts +1 -0
  253. package/dist/question/ui.d.ts.map +1 -1
  254. package/dist/question/ui.js +1 -0
  255. package/dist/question/ui.js.map +1 -1
  256. package/dist/ralplan/__tests__/runtime.test.js +191 -0
  257. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  258. package/dist/ralplan/consensus-gate.d.ts +9 -1
  259. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  260. package/dist/ralplan/consensus-gate.js +84 -2
  261. package/dist/ralplan/consensus-gate.js.map +1 -1
  262. package/dist/ralplan/runtime.d.ts +9 -0
  263. package/dist/ralplan/runtime.d.ts.map +1 -1
  264. package/dist/ralplan/runtime.js +32 -11
  265. package/dist/ralplan/runtime.js.map +1 -1
  266. package/dist/scripts/__tests__/codex-native-hook.test.js +2315 -280
  267. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  268. package/dist/scripts/__tests__/notify-state-io.test.js +72 -1
  269. package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
  270. package/dist/scripts/__tests__/notify-tmux-injection.test.d.ts +2 -0
  271. package/dist/scripts/__tests__/notify-tmux-injection.test.d.ts.map +1 -0
  272. package/dist/scripts/__tests__/notify-tmux-injection.test.js +57 -0
  273. package/dist/scripts/__tests__/notify-tmux-injection.test.js.map +1 -0
  274. package/dist/scripts/__tests__/run-test-files.test.js +74 -0
  275. package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
  276. package/dist/scripts/__tests__/verify-native-agents.test.js +65 -0
  277. package/dist/scripts/__tests__/verify-native-agents.test.js.map +1 -1
  278. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  279. package/dist/scripts/codex-native-hook.js +431 -56
  280. package/dist/scripts/codex-native-hook.js.map +1 -1
  281. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  282. package/dist/scripts/codex-native-pre-post.js +79 -1
  283. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  284. package/dist/scripts/eval/eval-parity-smoke.js +1 -1
  285. package/dist/scripts/eval/eval-parity-smoke.js.map +1 -1
  286. package/dist/scripts/hook-payload-guard.d.ts +9 -0
  287. package/dist/scripts/hook-payload-guard.d.ts.map +1 -0
  288. package/dist/scripts/hook-payload-guard.js +111 -0
  289. package/dist/scripts/hook-payload-guard.js.map +1 -0
  290. package/dist/scripts/notify-fallback-watcher.js +8 -1
  291. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  292. package/dist/scripts/notify-hook/__tests__/payload-guard.test.d.ts +2 -0
  293. package/dist/scripts/notify-hook/__tests__/payload-guard.test.d.ts.map +1 -0
  294. package/dist/scripts/notify-hook/__tests__/payload-guard.test.js +39 -0
  295. package/dist/scripts/notify-hook/__tests__/payload-guard.test.js.map +1 -0
  296. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  297. package/dist/scripts/notify-hook/auto-nudge.js +3 -1
  298. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  299. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
  300. package/dist/scripts/notify-hook/ralph-session-resume.js +3 -10
  301. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  302. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  303. package/dist/scripts/notify-hook/state-io.js +62 -38
  304. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  305. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  306. package/dist/scripts/notify-hook/team-leader-nudge.js +7 -0
  307. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  308. package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
  309. package/dist/scripts/notify-hook/team-worker-stop.js +234 -86
  310. package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
  311. package/dist/scripts/notify-hook/tmux-injection.d.ts +7 -0
  312. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  313. package/dist/scripts/notify-hook/tmux-injection.js +24 -18
  314. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  315. package/dist/scripts/notify-hook.js +86 -13
  316. package/dist/scripts/notify-hook.js.map +1 -1
  317. package/dist/scripts/run-test-files.js +193 -22
  318. package/dist/scripts/run-test-files.js.map +1 -1
  319. package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
  320. package/dist/scripts/sync-plugin-mirror.js +61 -3
  321. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  322. package/dist/scripts/verify-native-agents.d.ts.map +1 -1
  323. package/dist/scripts/verify-native-agents.js +58 -1
  324. package/dist/scripts/verify-native-agents.js.map +1 -1
  325. package/dist/state/__tests__/operations.test.js +1125 -1
  326. package/dist/state/__tests__/operations.test.js.map +1 -1
  327. package/dist/state/__tests__/skill-active.test.js +46 -1
  328. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  329. package/dist/state/__tests__/workflow-transition.test.js +98 -7
  330. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  331. package/dist/state/operations.d.ts.map +1 -1
  332. package/dist/state/operations.js +159 -2
  333. package/dist/state/operations.js.map +1 -1
  334. package/dist/state/skill-active.js +6 -8
  335. package/dist/state/skill-active.js.map +1 -1
  336. package/dist/state/workflow-transition-reconcile.d.ts +6 -0
  337. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  338. package/dist/state/workflow-transition-reconcile.js +38 -15
  339. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  340. package/dist/state/workflow-transition.d.ts.map +1 -1
  341. package/dist/state/workflow-transition.js +10 -3
  342. package/dist/state/workflow-transition.js.map +1 -1
  343. package/dist/subagents/__tests__/tracker.test.js +139 -0
  344. package/dist/subagents/__tests__/tracker.test.js.map +1 -1
  345. package/dist/subagents/tracker.d.ts +3 -0
  346. package/dist/subagents/tracker.d.ts.map +1 -1
  347. package/dist/subagents/tracker.js +41 -4
  348. package/dist/subagents/tracker.js.map +1 -1
  349. package/dist/team/__tests__/coordination-protocol.test.d.ts +2 -0
  350. package/dist/team/__tests__/coordination-protocol.test.d.ts.map +1 -0
  351. package/dist/team/__tests__/coordination-protocol.test.js +173 -0
  352. package/dist/team/__tests__/coordination-protocol.test.js.map +1 -0
  353. package/dist/team/__tests__/runtime.test.js +52 -3
  354. package/dist/team/__tests__/runtime.test.js.map +1 -1
  355. package/dist/team/__tests__/scaling.test.js +9 -4
  356. package/dist/team/__tests__/scaling.test.js.map +1 -1
  357. package/dist/team/__tests__/state.test.js +83 -0
  358. package/dist/team/__tests__/state.test.js.map +1 -1
  359. package/dist/team/__tests__/tmux-session.test.js +240 -2
  360. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  361. package/dist/team/__tests__/worker-bootstrap.test.js +84 -0
  362. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  363. package/dist/team/__tests__/worker-runtime-identity.test.js +4 -2
  364. package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
  365. package/dist/team/coordination-protocol.d.ts +14 -0
  366. package/dist/team/coordination-protocol.d.ts.map +1 -0
  367. package/dist/team/coordination-protocol.js +244 -0
  368. package/dist/team/coordination-protocol.js.map +1 -0
  369. package/dist/team/runtime.d.ts +1 -0
  370. package/dist/team/runtime.d.ts.map +1 -1
  371. package/dist/team/runtime.js +19 -3
  372. package/dist/team/runtime.js.map +1 -1
  373. package/dist/team/scaling.d.ts.map +1 -1
  374. package/dist/team/scaling.js +3 -2
  375. package/dist/team/scaling.js.map +1 -1
  376. package/dist/team/state/tasks.d.ts.map +1 -1
  377. package/dist/team/state/tasks.js +24 -0
  378. package/dist/team/state/tasks.js.map +1 -1
  379. package/dist/team/state/types.d.ts +21 -1
  380. package/dist/team/state/types.d.ts.map +1 -1
  381. package/dist/team/state/types.js.map +1 -1
  382. package/dist/team/state.d.ts +17 -1
  383. package/dist/team/state.d.ts.map +1 -1
  384. package/dist/team/state.js +12 -5
  385. package/dist/team/state.js.map +1 -1
  386. package/dist/team/team-ops.d.ts +1 -1
  387. package/dist/team/team-ops.d.ts.map +1 -1
  388. package/dist/team/team-ops.js.map +1 -1
  389. package/dist/team/tmux-session.d.ts +2 -0
  390. package/dist/team/tmux-session.d.ts.map +1 -1
  391. package/dist/team/tmux-session.js +161 -13
  392. package/dist/team/tmux-session.js.map +1 -1
  393. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  394. package/dist/team/worker-bootstrap.js +63 -0
  395. package/dist/team/worker-bootstrap.js.map +1 -1
  396. package/dist/utils/__tests__/agents-model-table.test.js +4 -2
  397. package/dist/utils/__tests__/agents-model-table.test.js.map +1 -1
  398. package/dist/utils/agents-model-table.d.ts.map +1 -1
  399. package/dist/utils/agents-model-table.js +3 -0
  400. package/dist/utils/agents-model-table.js.map +1 -1
  401. package/dist/verification/__tests__/ci-rust-gates.test.js +81 -1
  402. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  403. package/package.json +8 -8
  404. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  405. package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +334 -21
  406. package/plugins/oh-my-codex/hooks/hooks.json +1 -2
  407. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +13 -6
  408. package/plugins/oh-my-codex/skills/code-review/SKILL.md +7 -7
  409. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +9 -4
  410. package/plugins/oh-my-codex/skills/ralph/SKILL.md +22 -22
  411. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +12 -0
  412. package/plugins/oh-my-codex/skills/team/SKILL.md +16 -0
  413. package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +9 -0
  414. package/plugins/oh-my-codex/skills/worker/SKILL.md +14 -0
  415. package/skills/autopilot/SKILL.md +13 -6
  416. package/skills/code-review/SKILL.md +7 -7
  417. package/skills/deep-interview/SKILL.md +9 -4
  418. package/skills/ralph/SKILL.md +22 -22
  419. package/skills/ralplan/SKILL.md +12 -0
  420. package/skills/team/SKILL.md +16 -0
  421. package/skills/ultraqa/SKILL.md +9 -0
  422. package/skills/worker/SKILL.md +14 -0
  423. package/src/scripts/__tests__/codex-native-hook.test.ts +4435 -2083
  424. package/src/scripts/__tests__/notify-state-io.test.ts +95 -0
  425. package/src/scripts/__tests__/notify-tmux-injection.test.ts +82 -0
  426. package/src/scripts/__tests__/run-test-files.test.ts +102 -0
  427. package/src/scripts/__tests__/verify-native-agents.test.ts +75 -0
  428. package/src/scripts/codex-native-hook.ts +536 -51
  429. package/src/scripts/codex-native-pre-post.ts +80 -0
  430. package/src/scripts/demo-team-e2e.sh +10 -7
  431. package/src/scripts/eval/eval-parity-smoke.ts +1 -1
  432. package/src/scripts/hook-payload-guard.ts +113 -0
  433. package/src/scripts/notify-fallback-watcher.ts +8 -1
  434. package/src/scripts/notify-hook/__tests__/payload-guard.test.ts +41 -0
  435. package/src/scripts/notify-hook/auto-nudge.ts +3 -1
  436. package/src/scripts/notify-hook/ralph-session-resume.ts +2 -8
  437. package/src/scripts/notify-hook/state-io.ts +75 -37
  438. package/src/scripts/notify-hook/team-leader-nudge.ts +7 -0
  439. package/src/scripts/notify-hook/team-worker-stop.ts +193 -52
  440. package/src/scripts/notify-hook/tmux-injection.ts +35 -19
  441. package/src/scripts/notify-hook.ts +105 -6
  442. package/src/scripts/run-test-files.ts +192 -22
  443. package/src/scripts/sync-plugin-mirror.ts +98 -9
  444. package/src/scripts/verify-native-agents.ts +65 -1
@@ -84,6 +84,139 @@ describe('reconcileHudForPromptSubmit', () => {
84
84
  assert.equal(resized.length, 1);
85
85
  assert.equal(resized[0]?.heightLines, HUD_TMUX_HEIGHT_LINES);
86
86
  });
87
+ it('reaps orphaned same-session HUD panes whose leader pane was destroyed, then recreates a single HUD', async () => {
88
+ // Regression for the "team mode leaves only stacked HUD strips" bug: the leader
89
+ // pane (%21) was destroyed but its owner-tagged HUD panes remained, all pointing
90
+ // at the dead leader id. They match neither findHudWatchPaneIds (leader mismatch)
91
+ // nor findLegacyFocusedHudWatchPaneIds (they carry owner metadata), so each prompt
92
+ // submit previously appended a fresh HUD instead of reclaiming the orphans.
93
+ const killed = [];
94
+ const created = [];
95
+ const orphan = (paneId) => ({
96
+ paneId,
97
+ currentCommand: 'node',
98
+ startCommand: `exec env OMX_SESSION_ID='sess-a' OMX_TMUX_HUD_OWNER='1' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%21' node omx hud --watch --preset=focused`,
99
+ });
100
+ const result = await reconcileHudForPromptSubmit('/repo', {
101
+ env: { TMUX: '1', TMUX_PANE: '%33', OMX_SESSION_ID: 'sess-a', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
102
+ listCurrentWindowPanes: () => [
103
+ // %33 is the current (live) leader pane; %21 is gone from the window.
104
+ { paneId: '%33', currentCommand: 'codex', startCommand: 'codex' },
105
+ orphan('%34'),
106
+ orphan('%42'),
107
+ orphan('%47'),
108
+ ],
109
+ killTmuxPane: (paneId) => {
110
+ killed.push(paneId);
111
+ return true;
112
+ },
113
+ resizeTmuxPane: () => true,
114
+ createHudWatchPane: (_cwd, cmd, options) => {
115
+ created.push({ cmd, options });
116
+ return '%50';
117
+ },
118
+ resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
119
+ });
120
+ // All three dead-leader orphans are reaped, then exactly one fresh HUD is created.
121
+ assert.deepEqual(killed.sort(), ['%34', '%42', '%47']);
122
+ assert.equal(result.status, 'recreated');
123
+ assert.equal(result.paneId, '%50');
124
+ assert.equal(created.length, 1);
125
+ assert.equal(created[0]?.options?.targetPaneId, '%33');
126
+ assert.match(created[0]?.cmd || '', new RegExp(`${OMX_TMUX_HUD_LEADER_PANE_ENV}='%33'`));
127
+ });
128
+ it('reaps orphaned HUD panes tagged with an equivalent native session id', async () => {
129
+ // #2684 lets HUD dedupe treat the OMX owner id and Codex native session id as
130
+ // equivalent. Orphan reaping must use the same identity set so a canonical
131
+ // owner reconcile still reclaims dead-leader HUDs tagged with the native id.
132
+ const killed = [];
133
+ const result = await reconcileHudForPromptSubmit('/repo', {
134
+ env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'codex-native-uuid', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
135
+ sessionId: 'omx-owner-abc',
136
+ sessionIds: ['omx-owner-abc', 'codex-native-uuid'],
137
+ listCurrentWindowPanes: () => [
138
+ { paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
139
+ {
140
+ paneId: '%2',
141
+ currentCommand: 'node',
142
+ startCommand: `env OMX_SESSION_ID='codex-native-uuid' OMX_TMUX_HUD_OWNER='1' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%21' node omx hud --watch`,
143
+ },
144
+ ],
145
+ killTmuxPane: (paneId) => {
146
+ killed.push(paneId);
147
+ return true;
148
+ },
149
+ resizeTmuxPane: () => true,
150
+ createHudWatchPane: () => '%9',
151
+ resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
152
+ });
153
+ assert.deepEqual(killed, ['%2']);
154
+ assert.equal(result.status, 'recreated');
155
+ assert.equal(result.paneId, '%9');
156
+ });
157
+ it('does not reap an orphaned HUD pane that belongs to a different session', async () => {
158
+ // A HUD owned by another session's leader (which may live in a different tmux
159
+ // window we cannot see here) must survive even when that leader is absent.
160
+ const killed = [];
161
+ const result = await reconcileHudForPromptSubmit('/repo', {
162
+ env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'sess-a', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
163
+ listCurrentWindowPanes: () => [
164
+ { paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
165
+ {
166
+ paneId: '%4',
167
+ currentCommand: 'node',
168
+ startCommand: `env OMX_SESSION_ID='sess-b' OMX_TMUX_HUD_OWNER='1' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%5' node omx hud --watch`,
169
+ },
170
+ ],
171
+ killTmuxPane: (paneId) => {
172
+ killed.push(paneId);
173
+ return true;
174
+ },
175
+ resizeTmuxPane: () => true,
176
+ createHudWatchPane: () => '%9',
177
+ resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
178
+ });
179
+ // sess-b orphan is left untouched; this session simply creates its own HUD.
180
+ assert.deepEqual(killed, []);
181
+ assert.equal(result.status, 'recreated');
182
+ assert.equal(result.paneId, '%9');
183
+ });
184
+ it('reaps a same-session orphan whose recorded leader is itself another HUD pane', async () => {
185
+ // Review follow-up (#2682): when a HUD pane was mistakenly used as a leader, an
186
+ // orphan can name another HUD pane as its leader. That referenced HUD must not
187
+ // count as a live leader, or the orphan survives while the referenced HUD is
188
+ // reaped — leaving a dangling strip that still never matches the real pane.
189
+ const killed = [];
190
+ const result = await reconcileHudForPromptSubmit('/repo', {
191
+ env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'sess-a', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
192
+ listCurrentWindowPanes: () => [
193
+ { paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
194
+ {
195
+ // orphan whose recorded leader (%3) is itself another HUD pane
196
+ paneId: '%2',
197
+ currentCommand: 'node',
198
+ startCommand: `exec env OMX_SESSION_ID='sess-a' OMX_TMUX_HUD_OWNER='1' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%3' node omx hud --watch --preset=focused`,
199
+ },
200
+ {
201
+ // the referenced HUD %3, itself orphaned (its leader %21 is gone)
202
+ paneId: '%3',
203
+ currentCommand: 'node',
204
+ startCommand: `exec env OMX_SESSION_ID='sess-a' OMX_TMUX_HUD_OWNER='1' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%21' node omx hud --watch --preset=focused`,
205
+ },
206
+ ],
207
+ killTmuxPane: (paneId) => {
208
+ killed.push(paneId);
209
+ return true;
210
+ },
211
+ resizeTmuxPane: () => true,
212
+ createHudWatchPane: () => '%9',
213
+ resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
214
+ });
215
+ // Both HUD-led and dead-leader orphans are reaped; a single fresh HUD is created.
216
+ assert.deepEqual(killed.sort(), ['%2', '%3']);
217
+ assert.equal(result.status, 'recreated');
218
+ assert.equal(result.paneId, '%9');
219
+ });
87
220
  it('prefers an explicit session override when recreating HUD', async () => {
88
221
  const created = [];
89
222
  const result = await reconcileHudForPromptSubmit('/repo', {
@@ -128,6 +261,87 @@ describe('reconcileHudForPromptSubmit', () => {
128
261
  assert.equal(created.length, 1);
129
262
  assert.match(created[0]?.cmd || '', /^exec env OMX_SESSION_ID='sess boxed' OMX_TMUX_HUD_OWNER='1' OMX_TMUX_HUD_LEADER_PANE='%1' OMX_ROOT='\/tmp\/boxed root\/it'\\''s\/\$\(literal\)' '.*' '.*omx\.js' hud --watch/);
130
263
  });
264
+ it('forwards OMX_STATE_ROOT when recreating HUD with shell-safe quoting', async () => {
265
+ const created = [];
266
+ const result = await reconcileHudForPromptSubmit('/repo', {
267
+ env: {
268
+ TMUX: '1',
269
+ TMUX_PANE: '%1',
270
+ OMX_SESSION_ID: 'sess-a',
271
+ OMX_STATE_ROOT: '/boxed state/root',
272
+ [OMX_TMUX_HUD_OWNER_ENV]: '1',
273
+ },
274
+ listCurrentWindowPanes: () => [{ paneId: '%1', currentCommand: 'codex', startCommand: 'codex' }],
275
+ createHudWatchPane: (_cwd, hudCmd) => {
276
+ created.push(hudCmd);
277
+ return '%9';
278
+ },
279
+ resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
280
+ resizeTmuxPane: () => true,
281
+ readHudConfig: async () => ({ preset: 'focused', git: { display: 'branch' }, statusLine: { preset: 'focused' } }),
282
+ readAllState: async () => ({
283
+ version: null,
284
+ gitBranch: null,
285
+ ralph: null,
286
+ ultragoal: null,
287
+ ultrawork: null,
288
+ autopilot: null,
289
+ ralplan: null,
290
+ deepInterview: null,
291
+ autoresearch: null,
292
+ ultraqa: null,
293
+ team: null,
294
+ metrics: null,
295
+ hudNotify: null,
296
+ session: null,
297
+ }),
298
+ });
299
+ assert.equal(result.status, 'recreated');
300
+ assert.match(created[0] ?? '', /OMX_STATE_ROOT='\/boxed state\/root'/);
301
+ assert.doesNotMatch(created[0] ?? '', /OMX_ROOT=/);
302
+ });
303
+ it('forwards OMX_TEAM_STATE_ROOT before boxed roots when recreating HUD', async () => {
304
+ const created = [];
305
+ const result = await reconcileHudForPromptSubmit('/repo', {
306
+ env: {
307
+ TMUX: '1',
308
+ TMUX_PANE: '%1',
309
+ OMX_SESSION_ID: 'sess-a',
310
+ OMX_ROOT: '/boxed-root',
311
+ OMX_STATE_ROOT: '/boxed-state-root',
312
+ OMX_TEAM_STATE_ROOT: '/team-state-root',
313
+ [OMX_TMUX_HUD_OWNER_ENV]: '1',
314
+ },
315
+ listCurrentWindowPanes: () => [{ paneId: '%1', currentCommand: 'codex', startCommand: 'codex' }],
316
+ createHudWatchPane: (_cwd, hudCmd) => {
317
+ created.push(hudCmd);
318
+ return '%9';
319
+ },
320
+ resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
321
+ resizeTmuxPane: () => true,
322
+ readHudConfig: async () => ({ preset: 'focused', git: { display: 'branch' }, statusLine: { preset: 'focused' } }),
323
+ readAllState: async () => ({
324
+ version: null,
325
+ gitBranch: null,
326
+ ralph: null,
327
+ ultragoal: null,
328
+ ultrawork: null,
329
+ autopilot: null,
330
+ ralplan: null,
331
+ deepInterview: null,
332
+ autoresearch: null,
333
+ ultraqa: null,
334
+ team: null,
335
+ metrics: null,
336
+ hudNotify: null,
337
+ session: null,
338
+ }),
339
+ });
340
+ assert.equal(result.status, 'recreated');
341
+ assert.match(created[0] ?? '', /OMX_TEAM_STATE_ROOT='\/team-state-root'/);
342
+ assert.doesNotMatch(created[0] ?? '', /OMX_ROOT=/);
343
+ assert.doesNotMatch(created[0] ?? '', /OMX_STATE_ROOT=/);
344
+ });
131
345
  it('targets the emitting pane window when listing and creating HUD panes', async () => {
132
346
  const listArgs = [];
133
347
  const created = [];
@@ -147,11 +361,141 @@ describe('reconcileHudForPromptSubmit', () => {
147
361
  resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
148
362
  });
149
363
  assert.equal(result.status, 'recreated');
150
- assert.deepEqual(listArgs, ['%leader']);
364
+ assert.deepEqual(listArgs, ['%leader', '%leader']);
151
365
  assert.equal(created[0]?.options?.targetPaneId, '%leader');
152
366
  });
153
- it('kills duplicate HUD panes and recreates one full-width pane', async () => {
367
+ it('keeps prompt-submit HUD recreation scoped to the emitting pane in multi-pane windows', async () => {
368
+ const created = [];
369
+ const result = await reconcileHudForPromptSubmit('/repo', {
370
+ env: { TMUX: '1', TMUX_PANE: '%right', OMX_SESSION_ID: 'sess-right', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
371
+ listCurrentWindowPanes: () => [
372
+ { paneId: '%left', currentCommand: 'codex', startCommand: 'codex' },
373
+ { paneId: '%right', currentCommand: 'codex', startCommand: 'codex' },
374
+ ],
375
+ createHudWatchPane: (_cwd, _cmd, options) => {
376
+ created.push({ options });
377
+ return '%hud-right';
378
+ },
379
+ resizeTmuxPane: () => true,
380
+ resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
381
+ });
382
+ assert.equal(result.status, 'recreated');
383
+ assert.equal(created[0]?.options?.targetPaneId, '%right');
384
+ assert.equal(Object.hasOwn(created[0]?.options ?? {}, 'fullWidth'), false);
385
+ });
386
+ it('collapses same-owner HUD panes that appear during the create race window', async () => {
387
+ const killed = [];
388
+ const resized = [];
389
+ let listCount = 0;
390
+ const result = await reconcileHudForPromptSubmit('/repo', {
391
+ env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'sess-race', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
392
+ listCurrentWindowPanes: () => {
393
+ listCount += 1;
394
+ if (listCount === 1) {
395
+ return [
396
+ { paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
397
+ ];
398
+ }
399
+ return [
400
+ { paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
401
+ {
402
+ paneId: '%8',
403
+ currentCommand: 'node',
404
+ startCommand: `exec env OMX_SESSION_ID='sess-race' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' /node /omx.js hud --watch`,
405
+ },
406
+ {
407
+ paneId: '%9',
408
+ currentCommand: 'node',
409
+ startCommand: `exec env OMX_SESSION_ID='sess-race' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' /node /omx.js hud --watch --preset=focused`,
410
+ },
411
+ ];
412
+ },
413
+ createHudWatchPane: () => '%9',
414
+ killTmuxPane: (paneId) => {
415
+ killed.push(paneId);
416
+ return true;
417
+ },
418
+ resizeTmuxPane: (paneId, heightLines) => {
419
+ resized.push({ paneId, heightLines });
420
+ return true;
421
+ },
422
+ resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
423
+ });
424
+ assert.equal(result.status, 'replaced_duplicates');
425
+ assert.equal(result.paneId, '%9');
426
+ assert.equal(result.duplicateCount, 1);
427
+ assert.deepEqual(killed, ['%8']);
428
+ assert.deepEqual(resized, [{ paneId: '%9', heightLines: HUD_TMUX_HEIGHT_LINES }]);
429
+ });
430
+ it('keeps an observed same-owner HUD when the returned create pane is absent from the post-create scan', async () => {
154
431
  const killed = [];
432
+ const resized = [];
433
+ let listCount = 0;
434
+ const result = await reconcileHudForPromptSubmit('/repo', {
435
+ env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'sess-race', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
436
+ listCurrentWindowPanes: () => {
437
+ listCount += 1;
438
+ if (listCount === 1)
439
+ return [{ paneId: '%1', currentCommand: 'codex', startCommand: 'codex' }];
440
+ return [
441
+ { paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
442
+ {
443
+ paneId: '%8',
444
+ currentCommand: 'node',
445
+ startCommand: `exec env OMX_SESSION_ID='sess-race' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' /node /omx.js hud --watch`,
446
+ },
447
+ ];
448
+ },
449
+ createHudWatchPane: () => '%9',
450
+ killTmuxPane: (paneId) => { killed.push(paneId); return true; },
451
+ resizeTmuxPane: (paneId, heightLines) => { resized.push({ paneId, heightLines }); return true; },
452
+ resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
453
+ });
454
+ assert.equal(result.status, 'recreated');
455
+ assert.equal(result.paneId, '%8');
456
+ assert.equal(result.duplicateCount, 0);
457
+ assert.deepEqual(killed, []);
458
+ assert.deepEqual(resized, [{ paneId: '%8', heightLines: HUD_TMUX_HEIGHT_LINES }]);
459
+ });
460
+ it('kills post-create duplicate HUD panes even when the keeper cannot be resized', async () => {
461
+ const killed = [];
462
+ const registered = [];
463
+ let listCount = 0;
464
+ const result = await reconcileHudForPromptSubmit('/repo', {
465
+ env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'sess-race', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
466
+ listCurrentWindowPanes: () => {
467
+ listCount += 1;
468
+ if (listCount === 1)
469
+ return [{ paneId: '%1', currentCommand: 'codex', startCommand: 'codex' }];
470
+ return [
471
+ { paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
472
+ {
473
+ paneId: '%8',
474
+ currentCommand: 'node',
475
+ startCommand: `exec env OMX_SESSION_ID='sess-race' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' /node /omx.js hud --watch`,
476
+ },
477
+ {
478
+ paneId: '%9',
479
+ currentCommand: 'node',
480
+ startCommand: `exec env OMX_SESSION_ID='sess-race' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' /node /omx.js hud --watch`,
481
+ },
482
+ ];
483
+ },
484
+ createHudWatchPane: () => '%9',
485
+ killTmuxPane: (paneId) => { killed.push(paneId); return true; },
486
+ resizeTmuxPane: () => false,
487
+ registerHudResizeHook: (paneId) => { registered.push(paneId); return true; },
488
+ resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
489
+ });
490
+ assert.equal(result.status, 'failed');
491
+ assert.equal(result.paneId, '%9');
492
+ assert.equal(result.duplicateCount, 1);
493
+ assert.deepEqual(killed, ['%8']);
494
+ assert.deepEqual(registered, []);
495
+ });
496
+ it('kills duplicate HUD panes and reuses one existing pane', async () => {
497
+ const killed = [];
498
+ const resized = [];
155
499
  const created = [];
156
500
  const result = await reconcileHudForPromptSubmit('/repo', {
157
501
  env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'sess-a', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
@@ -173,19 +517,179 @@ describe('reconcileHudForPromptSubmit', () => {
173
517
  killed.push(paneId);
174
518
  return true;
175
519
  },
176
- createHudWatchPane: (_cwd, cmd, options) => {
520
+ createHudWatchPane: (_cwd, cmd) => {
177
521
  created.push({ cmd });
178
- assert.equal(options?.fullWidth, true);
179
- assert.equal(options?.heightLines, HUD_TMUX_HEIGHT_LINES);
180
522
  return '%9';
181
523
  },
182
- resizeTmuxPane: () => true,
524
+ resizeTmuxPane: (paneId, heightLines) => {
525
+ resized.push({ paneId, heightLines });
526
+ return true;
527
+ },
183
528
  resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
184
529
  });
185
530
  assert.equal(result.status, 'replaced_duplicates');
186
- assert.deepEqual(killed, ['%2', '%3']);
187
- assert.match(created[0]?.cmd || '', /OMX_SESSION_ID='sess-a'/);
188
- assert.match(created[0]?.cmd || '', /OMX_TMUX_HUD_OWNER='1'/);
531
+ assert.equal(result.paneId, '%2');
532
+ assert.deepEqual(killed, ['%3']);
533
+ assert.deepEqual(resized, [{ paneId: '%2', heightLines: HUD_TMUX_HEIGHT_LINES }]);
534
+ assert.deepEqual(created, []);
535
+ });
536
+ it('deduplicates same-leader HUD panes tagged with equivalent owner and canonical session ids', async () => {
537
+ const killed = [];
538
+ const resized = [];
539
+ const created = [];
540
+ const result = await reconcileHudForPromptSubmit('/repo', {
541
+ env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'codex-native-uuid', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
542
+ sessionId: 'omx-owner-abc',
543
+ sessionIds: ['omx-owner-abc', 'codex-native-uuid'],
544
+ listCurrentWindowPanes: () => [
545
+ { paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
546
+ {
547
+ paneId: '%2',
548
+ currentCommand: 'node',
549
+ startCommand: `env OMX_SESSION_ID='omx-owner-abc' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' node omx hud --watch`,
550
+ },
551
+ {
552
+ paneId: '%3',
553
+ currentCommand: 'node',
554
+ startCommand: `env OMX_SESSION_ID='codex-native-uuid' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' node omx hud --watch`,
555
+ },
556
+ {
557
+ // Same equivalent session, but its recorded leader is itself a HUD pane;
558
+ // the orphan reaper should remove it before normal same-leader dedupe.
559
+ paneId: '%4',
560
+ currentCommand: 'node',
561
+ startCommand: `env OMX_SESSION_ID='codex-native-uuid' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%4' node omx hud --watch`,
562
+ },
563
+ ],
564
+ killTmuxPane: (paneId) => {
565
+ killed.push(paneId);
566
+ return true;
567
+ },
568
+ createHudWatchPane: (_cwd, cmd) => {
569
+ created.push({ cmd });
570
+ return '%9';
571
+ },
572
+ resizeTmuxPane: (paneId, heightLines) => {
573
+ resized.push({ paneId, heightLines });
574
+ return true;
575
+ },
576
+ resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
577
+ });
578
+ assert.equal(result.status, 'replaced_duplicates');
579
+ assert.equal(result.paneId, '%2');
580
+ assert.equal(result.duplicateCount, 1);
581
+ assert.deepEqual(killed, ['%4', '%3']);
582
+ assert.deepEqual(resized, [{ paneId: '%2', heightLines: HUD_TMUX_HEIGHT_LINES }]);
583
+ assert.deepEqual(created, []);
584
+ });
585
+ it('reuses and deduplicates legacy unowned focused HUD watch panes before recreating', async () => {
586
+ const killed = [];
587
+ const resized = [];
588
+ const created = [];
589
+ const result = await reconcileHudForPromptSubmit('/repo', {
590
+ env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'sess-a', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
591
+ listCurrentWindowPanes: () => [
592
+ { paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
593
+ { paneId: '%2', currentCommand: 'node', startCommand: 'node /tmp/bin/omx.js hud --watch --preset=focused' },
594
+ { paneId: '%3', currentCommand: 'node', startCommand: 'node /tmp/bin/omx.js hud --watch --preset=focused' },
595
+ { paneId: '%4', currentCommand: 'node', startCommand: 'node /tmp/bin/omx.js hud --watch --preset=minimal' },
596
+ { paneId: '%5', currentCommand: 'node', startCommand: 'node /tmp/bin/omx.js hud --tmux --preset=focused' },
597
+ ],
598
+ killTmuxPane: (paneId) => { killed.push(paneId); return true; },
599
+ createHudWatchPane: () => { created.push('create'); return '%9'; },
600
+ resizeTmuxPane: (paneId, heightLines) => { resized.push({ paneId, heightLines }); return true; },
601
+ resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
602
+ });
603
+ assert.equal(result.status, 'replaced_duplicates');
604
+ assert.equal(result.paneId, '%2');
605
+ assert.equal(result.duplicateCount, 1);
606
+ assert.deepEqual(killed, ['%3']);
607
+ assert.deepEqual(resized, [{ paneId: '%2', heightLines: HUD_TMUX_HEIGHT_LINES }]);
608
+ assert.deepEqual(created, []);
609
+ });
610
+ it('treats an extra legacy focused pane as stale when an owned HUD already exists', async () => {
611
+ const killed = [];
612
+ const resized = [];
613
+ const result = await reconcileHudForPromptSubmit('/repo', {
614
+ env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'sess-a', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
615
+ listCurrentWindowPanes: () => [
616
+ { paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
617
+ {
618
+ paneId: '%2',
619
+ currentCommand: 'node',
620
+ startCommand: `env OMX_SESSION_ID='sess-a' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' node omx hud --watch --preset=focused`,
621
+ },
622
+ { paneId: '%3', currentCommand: 'node', startCommand: 'node /tmp/bin/omx.js hud --watch --preset=focused' },
623
+ ],
624
+ killTmuxPane: (paneId) => { killed.push(paneId); return true; },
625
+ resizeTmuxPane: (paneId, heightLines) => { resized.push({ paneId, heightLines }); return true; },
626
+ resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
627
+ });
628
+ assert.equal(result.status, 'replaced_duplicates');
629
+ assert.equal(result.paneId, '%2');
630
+ assert.equal(result.duplicateCount, 1);
631
+ assert.deepEqual(killed, ['%3']);
632
+ assert.deepEqual(resized, [{ paneId: '%2', heightLines: HUD_TMUX_HEIGHT_LINES }]);
633
+ });
634
+ it('deduplicates legacy focused panes that appear during the prompt-submit create race', async () => {
635
+ const killed = [];
636
+ const resized = [];
637
+ let listCount = 0;
638
+ const result = await reconcileHudForPromptSubmit('/repo', {
639
+ env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'sess-race', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
640
+ listCurrentWindowPanes: () => {
641
+ listCount += 1;
642
+ if (listCount === 1)
643
+ return [{ paneId: '%1', currentCommand: 'codex', startCommand: 'codex' }];
644
+ return [
645
+ { paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
646
+ { paneId: '%8', currentCommand: 'node', startCommand: 'node /tmp/bin/omx.js hud --watch --preset=focused' },
647
+ {
648
+ paneId: '%9',
649
+ currentCommand: 'node',
650
+ startCommand: `exec env OMX_SESSION_ID='sess-race' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' /node /omx.js hud --watch --preset=focused`,
651
+ },
652
+ ];
653
+ },
654
+ createHudWatchPane: () => '%9',
655
+ killTmuxPane: (paneId) => { killed.push(paneId); return true; },
656
+ resizeTmuxPane: (paneId, heightLines) => { resized.push({ paneId, heightLines }); return true; },
657
+ resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
658
+ });
659
+ assert.equal(result.status, 'replaced_duplicates');
660
+ assert.equal(result.paneId, '%9');
661
+ assert.equal(result.duplicateCount, 1);
662
+ assert.deepEqual(killed, ['%8']);
663
+ assert.deepEqual(resized, [{ paneId: '%9', heightLines: HUD_TMUX_HEIGHT_LINES }]);
664
+ });
665
+ it('kills existing duplicate HUD panes even when the keeper cannot be resized', async () => {
666
+ const killed = [];
667
+ const registered = [];
668
+ const result = await reconcileHudForPromptSubmit('/repo', {
669
+ env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'sess-a', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
670
+ listCurrentWindowPanes: () => [
671
+ { paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
672
+ {
673
+ paneId: '%8',
674
+ currentCommand: 'node',
675
+ startCommand: `env OMX_SESSION_ID='sess-a' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' node omx hud --watch`,
676
+ },
677
+ {
678
+ paneId: '%9',
679
+ currentCommand: 'node',
680
+ startCommand: `env OMX_SESSION_ID='sess-a' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' node omx hud --watch`,
681
+ },
682
+ ],
683
+ killTmuxPane: (paneId) => { killed.push(paneId); return true; },
684
+ resizeTmuxPane: () => false,
685
+ registerHudResizeHook: (paneId) => { registered.push(paneId); return true; },
686
+ resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
687
+ });
688
+ assert.equal(result.status, 'failed');
689
+ assert.equal(result.paneId, '%8');
690
+ assert.equal(result.duplicateCount, 1);
691
+ assert.deepEqual(killed, ['%9']);
692
+ assert.deepEqual(registered, []);
189
693
  });
190
694
  it('does not resize, kill, or reuse another active leader session HUD in the same tmux window', async () => {
191
695
  const killed = [];
@@ -243,6 +747,11 @@ describe('reconcileHudForPromptSubmit', () => {
243
747
  {
244
748
  paneId: '%4',
245
749
  currentCommand: 'node',
750
+ startCommand: `env OMX_SESSION_ID='sess-b' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' node omx hud --watch`,
751
+ },
752
+ {
753
+ paneId: '%5',
754
+ currentCommand: 'node',
246
755
  startCommand: `env OMX_SESSION_ID='sess-b' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%5' node omx hud --watch`,
247
756
  },
248
757
  ],
@@ -255,7 +764,59 @@ describe('reconcileHudForPromptSubmit', () => {
255
764
  resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
256
765
  });
257
766
  assert.equal(result.status, 'replaced_duplicates');
258
- assert.deepEqual(killed, ['%2', '%3']);
767
+ assert.equal(result.paneId, '%2');
768
+ assert.deepEqual(killed, ['%3']);
769
+ });
770
+ it('deduplicates same-leader node HUD panes while preserving active ultragoal height despite empty mode state', async () => {
771
+ const killed = [];
772
+ const resized = [];
773
+ const created = [];
774
+ const result = await reconcileHudForPromptSubmit('/repo', {
775
+ env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'sess-a', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
776
+ listCurrentWindowPanes: () => [
777
+ { paneId: '%1', currentCommand: 'zsh', startCommand: 'zsh' },
778
+ { paneId: '%2', currentCommand: 'node', startCommand: `exec env OMX_SESSION_ID='sess-a' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' /node /omx.js hud --watch` },
779
+ { paneId: '%3', currentCommand: 'node', startCommand: `exec env OMX_SESSION_ID='sess-a' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' /node /omx.js hud --watch` },
780
+ ],
781
+ readHudConfig: async () => ({ preset: 'focused', git: { display: 'branch' }, statusLine: { preset: 'focused' } }),
782
+ readAllState: async () => ({
783
+ version: null,
784
+ gitBranch: null,
785
+ ralph: null,
786
+ ultragoal: {
787
+ active: true,
788
+ status: 'in_progress',
789
+ total: 1,
790
+ complete: 0,
791
+ pending: 0,
792
+ inProgress: 1,
793
+ failed: 0,
794
+ reviewBlocked: 0,
795
+ needsUserDecision: 0,
796
+ progressTotal: 1,
797
+ },
798
+ ultrawork: null,
799
+ autopilot: null,
800
+ ralplan: null,
801
+ deepInterview: null,
802
+ autoresearch: null,
803
+ ultraqa: null,
804
+ team: null,
805
+ metrics: null,
806
+ hudNotify: null,
807
+ session: null,
808
+ }),
809
+ killTmuxPane: (paneId) => { killed.push(paneId); return true; },
810
+ resizeTmuxPane: (paneId, heightLines) => { resized.push({ paneId, heightLines }); return true; },
811
+ createHudWatchPane: () => { created.push('create'); return '%9'; },
812
+ resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
813
+ });
814
+ assert.equal(result.status, 'replaced_duplicates');
815
+ assert.equal(result.paneId, '%2');
816
+ assert.equal(result.duplicateCount, 1);
817
+ assert.deepEqual(killed, ['%3']);
818
+ assert.deepEqual(resized, [{ paneId: '%2', heightLines: HUD_TMUX_ULTRAGOAL_HEIGHT_LINES }]);
819
+ assert.deepEqual(created, []);
259
820
  });
260
821
  it('resizes an existing single HUD pane instead of recreating it', async () => {
261
822
  const resized = [];
@@ -329,7 +890,7 @@ describe('reconcileHudForPromptSubmit', () => {
329
890
  assert.equal(result.status, 'resized');
330
891
  assert.deepEqual(resized, [{ paneId: '%2', heightLines: HUD_TMUX_ULTRAGOAL_HEIGHT_LINES }]);
331
892
  });
332
- it('resizes an existing owner-tagged same-leader HUD pane instead of creating a duplicate during prompt revive', async () => {
893
+ it('recreates instead of reusing a leader-only HUD pane when reviving with a canonical session id', async () => {
333
894
  const resized = [];
334
895
  const created = [];
335
896
  const result = await reconcileHudForPromptSubmit('/repo', {
@@ -352,10 +913,10 @@ describe('reconcileHudForPromptSubmit', () => {
352
913
  },
353
914
  resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
354
915
  });
355
- assert.equal(result.status, 'resized');
356
- assert.equal(result.paneId, '%2');
357
- assert.deepEqual(created, []);
358
- assert.deepEqual(resized, [{ paneId: '%2', heightLines: HUD_TMUX_HEIGHT_LINES }]);
916
+ assert.equal(result.status, 'recreated');
917
+ assert.equal(result.paneId, '%9');
918
+ assert.deepEqual(created, ['create']);
919
+ assert.deepEqual(resized, [{ paneId: '%9', heightLines: HUD_TMUX_HEIGHT_LINES }]);
359
920
  });
360
921
  it('deduplicates same-leader HUD panes without creating a new pane when session id is unavailable', async () => {
361
922
  const killed = [];
@@ -421,15 +982,15 @@ describe('reconcileHudForPromptSubmit', () => {
421
982
  },
422
983
  ],
423
984
  resizeTmuxPane: () => true,
424
- registerHudResizeHook: (hudPaneId, currentPaneId, heightLines) => {
425
- registered.push({ hudPaneId, currentPaneId, heightLines });
985
+ registerHudResizeHook: (hudPaneId, leaderPaneId, heightLines) => {
986
+ registered.push({ hudPaneId, leaderPaneId, heightLines });
426
987
  return true;
427
988
  },
428
989
  resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
429
990
  });
430
991
  assert.equal(registered.length, 1);
431
992
  assert.equal(registered[0]?.hudPaneId, '%2');
432
- assert.equal(registered[0]?.currentPaneId, '%1');
993
+ assert.equal(registered[0]?.leaderPaneId, '%1');
433
994
  assert.equal(registered[0]?.heightLines, HUD_TMUX_HEIGHT_LINES);
434
995
  });
435
996
  it('registers client-resized hook scoped from the emitting pane after creating a new HUD pane', async () => {
@@ -441,18 +1002,18 @@ describe('reconcileHudForPromptSubmit', () => {
441
1002
  ],
442
1003
  createHudWatchPane: () => '%9',
443
1004
  resizeTmuxPane: () => true,
444
- registerHudResizeHook: (hudPaneId, currentPaneId, heightLines) => {
445
- registered.push({ hudPaneId, currentPaneId, heightLines });
1005
+ registerHudResizeHook: (hudPaneId, leaderPaneId, heightLines) => {
1006
+ registered.push({ hudPaneId, leaderPaneId, heightLines });
446
1007
  return true;
447
1008
  },
448
1009
  resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
449
1010
  });
450
1011
  assert.equal(registered.length, 1);
451
1012
  assert.equal(registered[0]?.hudPaneId, '%9');
452
- assert.equal(registered[0]?.currentPaneId, '%1');
1013
+ assert.equal(registered[0]?.leaderPaneId, '%1');
453
1014
  assert.equal(registered[0]?.heightLines, HUD_TMUX_HEIGHT_LINES);
454
1015
  });
455
- it('unregisters existing hook before killing duplicates and re-registers for the new pane', async () => {
1016
+ it('keeps the resize hook on the reused duplicate keeper pane', async () => {
456
1017
  const unregistered = [];
457
1018
  const registered = [];
458
1019
  await reconcileHudForPromptSubmit('/repo', {
@@ -465,15 +1026,14 @@ describe('reconcileHudForPromptSubmit', () => {
465
1026
  killTmuxPane: () => true,
466
1027
  createHudWatchPane: () => '%9',
467
1028
  resizeTmuxPane: () => true,
468
- unregisterHudResizeHook: (currentPaneId) => { unregistered.push(currentPaneId); return true; },
469
- registerHudResizeHook: (hudPaneId, currentPaneId) => { registered.push({ hudPaneId, currentPaneId }); return true; },
1029
+ unregisterHudResizeHook: (leaderPaneId) => { unregistered.push(leaderPaneId); return true; },
1030
+ registerHudResizeHook: (hudPaneId, leaderPaneId) => { registered.push({ hudPaneId, leaderPaneId }); return true; },
470
1031
  resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
471
1032
  });
472
- assert.equal(unregistered.length, 1);
473
- assert.equal(unregistered[0], '%1');
1033
+ assert.deepEqual(unregistered, []);
474
1034
  assert.equal(registered.length, 1);
475
- assert.equal(registered[0]?.hudPaneId, '%9');
476
- assert.equal(registered[0]?.currentPaneId, '%1');
1035
+ assert.equal(registered[0]?.hudPaneId, '%2');
1036
+ assert.equal(registered[0]?.leaderPaneId, '%1');
477
1037
  });
478
1038
  });
479
1039
  //# sourceMappingURL=reconcile.test.js.map