oh-my-codex 0.17.3 → 0.18.1

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 (381) hide show
  1. package/Cargo.lock +13 -5
  2. package/Cargo.toml +2 -1
  3. package/README.md +44 -19
  4. package/crates/omx-api/Cargo.toml +19 -0
  5. package/crates/omx-api/src/lib.rs +2997 -0
  6. package/crates/omx-api/src/main.rs +10 -0
  7. package/crates/omx-api/tests/cli.rs +558 -0
  8. package/crates/omx-explore/src/main.rs +4 -0
  9. package/crates/omx-sparkshell/src/codex_bridge.rs +437 -123
  10. package/crates/omx-sparkshell/src/exec.rs +127 -1
  11. package/crates/omx-sparkshell/src/main.rs +829 -30
  12. package/crates/omx-sparkshell/src/prompt.rs +25 -3
  13. package/crates/omx-sparkshell/src/redaction.rs +241 -0
  14. package/crates/omx-sparkshell/tests/execution.rs +702 -237
  15. package/dist/cli/__tests__/api.test.d.ts +2 -0
  16. package/dist/cli/__tests__/api.test.d.ts.map +1 -0
  17. package/dist/cli/__tests__/api.test.js +175 -0
  18. package/dist/cli/__tests__/api.test.js.map +1 -0
  19. package/dist/cli/__tests__/ask.test.js +72 -5
  20. package/dist/cli/__tests__/ask.test.js.map +1 -1
  21. package/dist/cli/__tests__/autoresearch-goal.test.js +14 -1
  22. package/dist/cli/__tests__/autoresearch-goal.test.js.map +1 -1
  23. package/dist/cli/__tests__/codex-plugin-layout.test.js +15 -7
  24. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  25. package/dist/cli/__tests__/doctor-warning-copy.test.js +76 -3
  26. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  27. package/dist/cli/__tests__/explore.test.js +23 -0
  28. package/dist/cli/__tests__/explore.test.js.map +1 -1
  29. package/dist/cli/__tests__/index.test.js +171 -5
  30. package/dist/cli/__tests__/index.test.js.map +1 -1
  31. package/dist/cli/__tests__/install-docs-contract.test.d.ts +2 -0
  32. package/dist/cli/__tests__/install-docs-contract.test.d.ts.map +1 -0
  33. package/dist/cli/__tests__/install-docs-contract.test.js +55 -0
  34. package/dist/cli/__tests__/install-docs-contract.test.js.map +1 -0
  35. package/dist/cli/__tests__/launch-fallback.test.js +191 -0
  36. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  37. package/dist/cli/__tests__/package-bin-contract.test.js +4 -3
  38. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  39. package/dist/cli/__tests__/question.test.js +27 -41
  40. package/dist/cli/__tests__/question.test.js.map +1 -1
  41. package/dist/cli/__tests__/setup-install-mode.test.js +232 -35
  42. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  43. package/dist/cli/__tests__/sparkshell-cli.test.js +25 -1
  44. package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
  45. package/dist/cli/__tests__/sparkshell-packaging.test.js +1 -0
  46. package/dist/cli/__tests__/sparkshell-packaging.test.js.map +1 -1
  47. package/dist/cli/__tests__/ultragoal.test.js +227 -4
  48. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  49. package/dist/cli/__tests__/update.test.js +72 -1
  50. package/dist/cli/__tests__/update.test.js.map +1 -1
  51. package/dist/cli/__tests__/version-sync-contract.test.js +4 -0
  52. package/dist/cli/__tests__/version-sync-contract.test.js.map +1 -1
  53. package/dist/cli/__tests__/windows-popup-loop-contract.test.js +1 -1
  54. package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
  55. package/dist/cli/api.d.ts +26 -0
  56. package/dist/cli/api.d.ts.map +1 -0
  57. package/dist/cli/api.js +153 -0
  58. package/dist/cli/api.js.map +1 -0
  59. package/dist/cli/codex-feature-probe.d.ts +5 -0
  60. package/dist/cli/codex-feature-probe.d.ts.map +1 -1
  61. package/dist/cli/codex-feature-probe.js +13 -7
  62. package/dist/cli/codex-feature-probe.js.map +1 -1
  63. package/dist/cli/doctor.d.ts +7 -0
  64. package/dist/cli/doctor.d.ts.map +1 -1
  65. package/dist/cli/doctor.js +119 -10
  66. package/dist/cli/doctor.js.map +1 -1
  67. package/dist/cli/explore.d.ts +2 -0
  68. package/dist/cli/explore.d.ts.map +1 -1
  69. package/dist/cli/explore.js +43 -1
  70. package/dist/cli/explore.js.map +1 -1
  71. package/dist/cli/index.d.ts +12 -4
  72. package/dist/cli/index.d.ts.map +1 -1
  73. package/dist/cli/index.js +460 -87
  74. package/dist/cli/index.js.map +1 -1
  75. package/dist/cli/native-assets.d.ts +2 -1
  76. package/dist/cli/native-assets.d.ts.map +1 -1
  77. package/dist/cli/native-assets.js +1 -0
  78. package/dist/cli/native-assets.js.map +1 -1
  79. package/dist/cli/plugin-marketplace.d.ts +2 -0
  80. package/dist/cli/plugin-marketplace.d.ts.map +1 -1
  81. package/dist/cli/plugin-marketplace.js +15 -1
  82. package/dist/cli/plugin-marketplace.js.map +1 -1
  83. package/dist/cli/setup.d.ts.map +1 -1
  84. package/dist/cli/setup.js +71 -11
  85. package/dist/cli/setup.js.map +1 -1
  86. package/dist/cli/sparkshell.d.ts +7 -1
  87. package/dist/cli/sparkshell.d.ts.map +1 -1
  88. package/dist/cli/sparkshell.js +31 -4
  89. package/dist/cli/sparkshell.js.map +1 -1
  90. package/dist/cli/ultragoal.d.ts +1 -1
  91. package/dist/cli/ultragoal.d.ts.map +1 -1
  92. package/dist/cli/ultragoal.js +184 -10
  93. package/dist/cli/ultragoal.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 +14 -3
  97. package/dist/cli/update.js.map +1 -1
  98. package/dist/compat/__tests__/doctor-contract.test.js +3 -0
  99. package/dist/compat/__tests__/doctor-contract.test.js.map +1 -1
  100. package/dist/config/__tests__/codex-feature-flags.test.js +11 -1
  101. package/dist/config/__tests__/codex-feature-flags.test.js.map +1 -1
  102. package/dist/config/__tests__/codex-hooks.test.js +19 -8
  103. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  104. package/dist/config/__tests__/commit-lore-guard.test.d.ts +2 -0
  105. package/dist/config/__tests__/commit-lore-guard.test.d.ts.map +1 -0
  106. package/dist/config/__tests__/commit-lore-guard.test.js +20 -0
  107. package/dist/config/__tests__/commit-lore-guard.test.js.map +1 -0
  108. package/dist/config/codex-feature-flags.d.ts +4 -0
  109. package/dist/config/codex-feature-flags.d.ts.map +1 -1
  110. package/dist/config/codex-feature-flags.js +4 -0
  111. package/dist/config/codex-feature-flags.js.map +1 -1
  112. package/dist/config/codex-hooks.js +6 -6
  113. package/dist/config/codex-hooks.js.map +1 -1
  114. package/dist/config/commit-lore-guard.d.ts +1 -0
  115. package/dist/config/commit-lore-guard.d.ts.map +1 -1
  116. package/dist/config/commit-lore-guard.js +29 -3
  117. package/dist/config/commit-lore-guard.js.map +1 -1
  118. package/dist/config/generator.d.ts +3 -1
  119. package/dist/config/generator.d.ts.map +1 -1
  120. package/dist/config/generator.js +114 -10
  121. package/dist/config/generator.js.map +1 -1
  122. package/dist/goal-workflows/codex-goal-snapshot.d.ts +1 -0
  123. package/dist/goal-workflows/codex-goal-snapshot.d.ts.map +1 -1
  124. package/dist/goal-workflows/codex-goal-snapshot.js +5 -1
  125. package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -1
  126. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +10 -6
  127. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  128. package/dist/hooks/__tests__/best-practice-research-skill.test.d.ts +2 -0
  129. package/dist/hooks/__tests__/best-practice-research-skill.test.d.ts.map +1 -0
  130. package/dist/hooks/__tests__/best-practice-research-skill.test.js +27 -0
  131. package/dist/hooks/__tests__/best-practice-research-skill.test.js.map +1 -0
  132. package/dist/hooks/__tests__/consensus-execution-handoff.test.d.ts +1 -1
  133. package/dist/hooks/__tests__/consensus-execution-handoff.test.js +13 -11
  134. package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
  135. package/dist/hooks/__tests__/deep-interview-contract.test.js +4 -3
  136. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  137. package/dist/hooks/__tests__/keyword-detector.test.js +15 -3
  138. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  139. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +6 -0
  140. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  141. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +33 -0
  142. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  143. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js +4 -0
  144. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js.map +1 -1
  145. package/dist/hooks/extensibility/__tests__/dispatcher.test.js +26 -3
  146. package/dist/hooks/extensibility/__tests__/dispatcher.test.js.map +1 -1
  147. package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
  148. package/dist/hooks/extensibility/dispatcher.js +29 -14
  149. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  150. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  151. package/dist/hooks/keyword-detector.js +8 -3
  152. package/dist/hooks/keyword-detector.js.map +1 -1
  153. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  154. package/dist/hooks/keyword-registry.js +1 -0
  155. package/dist/hooks/keyword-registry.js.map +1 -1
  156. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  157. package/dist/hooks/prompt-guidance-contract.js +3 -2
  158. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  159. package/dist/hud/__tests__/hud-tmux-injection.test.js +14 -8
  160. package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
  161. package/dist/hud/__tests__/reconcile.test.js +4 -4
  162. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  163. package/dist/hud/__tests__/resource-leak-watch.test.d.ts +2 -0
  164. package/dist/hud/__tests__/resource-leak-watch.test.d.ts.map +1 -0
  165. package/dist/hud/__tests__/resource-leak-watch.test.js +28 -0
  166. package/dist/hud/__tests__/resource-leak-watch.test.js.map +1 -0
  167. package/dist/hud/__tests__/tmux.test.js +23 -18
  168. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  169. package/dist/hud/index.d.ts +1 -1
  170. package/dist/hud/index.d.ts.map +1 -1
  171. package/dist/hud/index.js +10 -4
  172. package/dist/hud/index.js.map +1 -1
  173. package/dist/hud/tmux.d.ts.map +1 -1
  174. package/dist/hud/tmux.js +9 -8
  175. package/dist/hud/tmux.js.map +1 -1
  176. package/dist/mcp/__tests__/bootstrap.test.js +75 -1
  177. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  178. package/dist/mcp/bootstrap.d.ts +3 -1
  179. package/dist/mcp/bootstrap.d.ts.map +1 -1
  180. package/dist/mcp/bootstrap.js +71 -2
  181. package/dist/mcp/bootstrap.js.map +1 -1
  182. package/dist/notifications/__tests__/http-client-resource.test.d.ts +2 -0
  183. package/dist/notifications/__tests__/http-client-resource.test.d.ts.map +1 -0
  184. package/dist/notifications/__tests__/http-client-resource.test.js +41 -0
  185. package/dist/notifications/__tests__/http-client-resource.test.js.map +1 -0
  186. package/dist/notifications/__tests__/verbosity.test.js +20 -0
  187. package/dist/notifications/__tests__/verbosity.test.js.map +1 -1
  188. package/dist/notifications/config.d.ts.map +1 -1
  189. package/dist/notifications/config.js +6 -3
  190. package/dist/notifications/config.js.map +1 -1
  191. package/dist/notifications/http-client.d.ts.map +1 -1
  192. package/dist/notifications/http-client.js +78 -27
  193. package/dist/notifications/http-client.js.map +1 -1
  194. package/dist/notifications/types.d.ts +2 -0
  195. package/dist/notifications/types.d.ts.map +1 -1
  196. package/dist/openclaw/__tests__/dispatcher.test.js +49 -1
  197. package/dist/openclaw/__tests__/dispatcher.test.js.map +1 -1
  198. package/dist/openclaw/dispatcher.d.ts +7 -4
  199. package/dist/openclaw/dispatcher.d.ts.map +1 -1
  200. package/dist/openclaw/dispatcher.js +32 -69
  201. package/dist/openclaw/dispatcher.js.map +1 -1
  202. package/dist/pipeline/__tests__/orchestrator.test.js +65 -3
  203. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  204. package/dist/pipeline/__tests__/stages.test.js +50 -5
  205. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  206. package/dist/pipeline/index.d.ts +8 -2
  207. package/dist/pipeline/index.d.ts.map +1 -1
  208. package/dist/pipeline/index.js +5 -2
  209. package/dist/pipeline/index.js.map +1 -1
  210. package/dist/pipeline/orchestrator.d.ts +5 -4
  211. package/dist/pipeline/orchestrator.d.ts.map +1 -1
  212. package/dist/pipeline/orchestrator.js +56 -15
  213. package/dist/pipeline/orchestrator.js.map +1 -1
  214. package/dist/pipeline/stages/code-review.d.ts +2 -2
  215. package/dist/pipeline/stages/code-review.d.ts.map +1 -1
  216. package/dist/pipeline/stages/code-review.js +5 -3
  217. package/dist/pipeline/stages/code-review.js.map +1 -1
  218. package/dist/pipeline/stages/deep-interview.d.ts +15 -0
  219. package/dist/pipeline/stages/deep-interview.d.ts.map +1 -0
  220. package/dist/pipeline/stages/deep-interview.js +32 -0
  221. package/dist/pipeline/stages/deep-interview.js.map +1 -0
  222. package/dist/pipeline/stages/ralph-verify.d.ts +5 -5
  223. package/dist/pipeline/stages/ralph-verify.d.ts.map +1 -1
  224. package/dist/pipeline/stages/ralph-verify.js +2 -2
  225. package/dist/pipeline/stages/ralph-verify.js.map +1 -1
  226. package/dist/pipeline/stages/ultragoal.d.ts +19 -0
  227. package/dist/pipeline/stages/ultragoal.d.ts.map +1 -0
  228. package/dist/pipeline/stages/ultragoal.js +38 -0
  229. package/dist/pipeline/stages/ultragoal.js.map +1 -0
  230. package/dist/pipeline/stages/ultraqa.d.ts +30 -0
  231. package/dist/pipeline/stages/ultraqa.d.ts.map +1 -0
  232. package/dist/pipeline/stages/ultraqa.js +46 -0
  233. package/dist/pipeline/stages/ultraqa.js.map +1 -0
  234. package/dist/pipeline/types.d.ts +8 -6
  235. package/dist/pipeline/types.d.ts.map +1 -1
  236. package/dist/pipeline/types.js +2 -2
  237. package/dist/scripts/__tests__/codex-native-hook.test.js +1488 -117
  238. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  239. package/dist/scripts/__tests__/notify-dispatcher.test.js +183 -1
  240. package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -1
  241. package/dist/scripts/__tests__/smoke-packed-install.test.js +27 -2
  242. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  243. package/dist/scripts/__tests__/verify-native-agents.test.js +16 -1
  244. package/dist/scripts/__tests__/verify-native-agents.test.js.map +1 -1
  245. package/dist/scripts/build-api.d.ts +2 -0
  246. package/dist/scripts/build-api.d.ts.map +1 -0
  247. package/dist/scripts/build-api.js +44 -0
  248. package/dist/scripts/build-api.js.map +1 -0
  249. package/dist/scripts/cleanup-explore-harness.js +1 -0
  250. package/dist/scripts/cleanup-explore-harness.js.map +1 -1
  251. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  252. package/dist/scripts/codex-native-hook.js +364 -16
  253. package/dist/scripts/codex-native-hook.js.map +1 -1
  254. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  255. package/dist/scripts/codex-native-pre-post.js +98 -25
  256. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  257. package/dist/scripts/notify-dispatcher.js +88 -0
  258. package/dist/scripts/notify-dispatcher.js.map +1 -1
  259. package/dist/scripts/notify-hook/process-runner.d.ts.map +1 -1
  260. package/dist/scripts/notify-hook/process-runner.js +39 -17
  261. package/dist/scripts/notify-hook/process-runner.js.map +1 -1
  262. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  263. package/dist/scripts/notify-hook/team-dispatch.js +36 -14
  264. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  265. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  266. package/dist/scripts/notify-hook/team-leader-nudge.js +26 -11
  267. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  268. package/dist/scripts/notify-hook/team-tmux-guard.d.ts +2 -1
  269. package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
  270. package/dist/scripts/notify-hook/team-tmux-guard.js +45 -1
  271. package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
  272. package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
  273. package/dist/scripts/notify-hook/team-worker-stop.js +27 -14
  274. package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
  275. package/dist/scripts/run-provider-advisor.js +9 -3
  276. package/dist/scripts/run-provider-advisor.js.map +1 -1
  277. package/dist/scripts/smoke-packed-install.d.ts +4 -1
  278. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  279. package/dist/scripts/smoke-packed-install.js +101 -1
  280. package/dist/scripts/smoke-packed-install.js.map +1 -1
  281. package/dist/scripts/sync-plugin-mirror.js +2 -2
  282. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  283. package/dist/scripts/verify-native-agents.js +2 -2
  284. package/dist/scripts/verify-native-agents.js.map +1 -1
  285. package/dist/sidecar/__tests__/resource-leak-watch.test.d.ts +2 -0
  286. package/dist/sidecar/__tests__/resource-leak-watch.test.d.ts.map +1 -0
  287. package/dist/sidecar/__tests__/resource-leak-watch.test.js +38 -0
  288. package/dist/sidecar/__tests__/resource-leak-watch.test.js.map +1 -0
  289. package/dist/sidecar/index.d.ts +1 -1
  290. package/dist/sidecar/index.d.ts.map +1 -1
  291. package/dist/sidecar/index.js +29 -12
  292. package/dist/sidecar/index.js.map +1 -1
  293. package/dist/state/__tests__/operations-ralph-phase.test.js +88 -1
  294. package/dist/state/__tests__/operations-ralph-phase.test.js.map +1 -1
  295. package/dist/state/operations.d.ts.map +1 -1
  296. package/dist/state/operations.js +11 -0
  297. package/dist/state/operations.js.map +1 -1
  298. package/dist/team/__tests__/runtime.test.js +2 -2
  299. package/dist/team/__tests__/runtime.test.js.map +1 -1
  300. package/dist/team/__tests__/tmux-session.test.js +207 -22
  301. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  302. package/dist/team/tmux-session.d.ts +1 -0
  303. package/dist/team/tmux-session.d.ts.map +1 -1
  304. package/dist/team/tmux-session.js +73 -28
  305. package/dist/team/tmux-session.js.map +1 -1
  306. package/dist/ultragoal/__tests__/artifacts.test.js +714 -10
  307. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  308. package/dist/ultragoal/__tests__/docs-contract.test.js +57 -1
  309. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
  310. package/dist/ultragoal/__tests__/steering-fixtures.d.ts +68 -0
  311. package/dist/ultragoal/__tests__/steering-fixtures.d.ts.map +1 -0
  312. package/dist/ultragoal/__tests__/steering-fixtures.js +259 -0
  313. package/dist/ultragoal/__tests__/steering-fixtures.js.map +1 -0
  314. package/dist/ultragoal/__tests__/steering-fixtures.test.d.ts +2 -0
  315. package/dist/ultragoal/__tests__/steering-fixtures.test.d.ts.map +1 -0
  316. package/dist/ultragoal/__tests__/steering-fixtures.test.js +65 -0
  317. package/dist/ultragoal/__tests__/steering-fixtures.test.js.map +1 -0
  318. package/dist/ultragoal/artifacts.d.ts +97 -2
  319. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  320. package/dist/ultragoal/artifacts.js +811 -256
  321. package/dist/ultragoal/artifacts.js.map +1 -1
  322. package/dist/utils/__tests__/sleep-resource.test.d.ts +2 -0
  323. package/dist/utils/__tests__/sleep-resource.test.d.ts.map +1 -0
  324. package/dist/utils/__tests__/sleep-resource.test.js +39 -0
  325. package/dist/utils/__tests__/sleep-resource.test.js.map +1 -0
  326. package/dist/utils/sleep.d.ts.map +1 -1
  327. package/dist/utils/sleep.js +17 -6
  328. package/dist/utils/sleep.js.map +1 -1
  329. package/dist/verification/__tests__/ci-rust-gates.test.js +85 -10
  330. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  331. package/dist/verification/__tests__/explore-harness-release-workflow.test.js +1 -0
  332. package/dist/verification/__tests__/explore-harness-release-workflow.test.js.map +1 -1
  333. package/package.json +5 -3
  334. package/plugins/oh-my-codex/.codex-plugin/plugin.json +4 -3
  335. package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +56 -0
  336. package/plugins/oh-my-codex/hooks/hooks.json +77 -0
  337. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +77 -47
  338. package/plugins/oh-my-codex/skills/best-practice-research/SKILL.md +83 -0
  339. package/plugins/oh-my-codex/skills/cancel/SKILL.md +2 -2
  340. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +9 -8
  341. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +1 -1
  342. package/plugins/oh-my-codex/skills/pipeline/SKILL.md +22 -11
  343. package/plugins/oh-my-codex/skills/plan/SKILL.md +8 -8
  344. package/plugins/oh-my-codex/skills/ralph/SKILL.md +7 -0
  345. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +5 -5
  346. package/plugins/oh-my-codex/skills/team/SKILL.md +1 -1
  347. package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +38 -4
  348. package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +1 -1
  349. package/prompts/planner.md +1 -1
  350. package/prompts/researcher.md +15 -10
  351. package/skills/autopilot/SKILL.md +77 -47
  352. package/skills/best-practice-research/SKILL.md +83 -0
  353. package/skills/cancel/SKILL.md +2 -2
  354. package/skills/deep-interview/SKILL.md +9 -8
  355. package/skills/omx-setup/SKILL.md +1 -1
  356. package/skills/pipeline/SKILL.md +22 -11
  357. package/skills/plan/SKILL.md +8 -8
  358. package/skills/ralph/SKILL.md +7 -0
  359. package/skills/ralplan/SKILL.md +5 -5
  360. package/skills/team/SKILL.md +1 -1
  361. package/skills/ultragoal/SKILL.md +38 -4
  362. package/skills/ultrawork/SKILL.md +1 -1
  363. package/src/scripts/__tests__/codex-native-hook.test.ts +1758 -166
  364. package/src/scripts/__tests__/notify-dispatcher.test.ts +223 -1
  365. package/src/scripts/__tests__/smoke-packed-install.test.ts +39 -2
  366. package/src/scripts/__tests__/verify-native-agents.test.ts +21 -1
  367. package/src/scripts/build-api.ts +48 -0
  368. package/src/scripts/cleanup-explore-harness.ts +1 -0
  369. package/src/scripts/codex-native-hook.ts +416 -18
  370. package/src/scripts/codex-native-pre-post.ts +119 -25
  371. package/src/scripts/notify-dispatcher.ts +97 -0
  372. package/src/scripts/notify-hook/process-runner.ts +40 -16
  373. package/src/scripts/notify-hook/team-dispatch.ts +36 -13
  374. package/src/scripts/notify-hook/team-leader-nudge.ts +25 -11
  375. package/src/scripts/notify-hook/team-tmux-guard.ts +49 -0
  376. package/src/scripts/notify-hook/team-worker-stop.ts +24 -13
  377. package/src/scripts/run-provider-advisor.ts +11 -3
  378. package/src/scripts/smoke-packed-install.ts +107 -0
  379. package/src/scripts/sync-plugin-mirror.ts +3 -3
  380. package/src/scripts/verify-native-agents.ts +2 -2
  381. package/templates/catalog-manifest.json +7 -0
@@ -2,7 +2,11 @@ import {
2
2
  buildDocumentRefreshAdvisoryOutput,
3
3
  evaluateStagedDocumentRefresh,
4
4
  } from "../document-refresh/enforcer.js";
5
- import { isLoreCommitGuardEnabled } from "../config/commit-lore-guard.js";
5
+ import {
6
+ OMX_LORE_COMMIT_GUARD_ENV,
7
+ isLoreCommitGuardEnabled,
8
+ readConfiguredLoreCommitGuardValue,
9
+ } from "../config/commit-lore-guard.js";
6
10
  import { resolveCodexExecutionSurface } from "./codex-execution-surface.js";
7
11
 
8
12
  type CodexHookPayload = Record<string, unknown>;
@@ -103,8 +107,8 @@ export function normalizePostToolUsePayload(
103
107
  const exitCode = safeInteger(parsedToolResponse?.exit_code)
104
108
  ?? safeInteger(parsedToolResponse?.exitCode)
105
109
  ?? null;
106
- const rawText = safeString(rawToolResponse).trim();
107
- const stdoutText = safeString(parsedToolResponse?.stdout).trim() || rawText;
110
+ const rawToolResponseText = safeString(rawToolResponse).trim();
111
+ const stdoutText = safeString(parsedToolResponse?.stdout).trim() || rawToolResponseText;
108
112
  const stderrText = safeString(parsedToolResponse?.stderr).trim();
109
113
 
110
114
  return {
@@ -149,30 +153,49 @@ type OmxParityCommand =
149
153
  | "trace"
150
154
  | "code-intel";
151
155
 
156
+ function joinNonEmptyText(parts: string[]): string {
157
+ return parts
158
+ .filter(Boolean)
159
+ .join("\n")
160
+ .trim();
161
+ }
162
+
163
+ function structuredMcpTransportText(normalized: NormalizedPostToolUsePayload): string {
164
+ return joinNonEmptyText([
165
+ safeString(normalized.parsedToolResponse?.error),
166
+ safeString(normalized.parsedToolResponse?.message),
167
+ safeString(normalized.parsedToolResponse?.details),
168
+ ]);
169
+ }
170
+
171
+ function hasMcpTransportContext(text: string): boolean {
172
+ return /\bmcp\b/i.test(text)
173
+ || /\bomx-(?:state|memory|trace|code-intel)-server\b/i.test(text);
174
+ }
175
+
176
+ function hasMcpTransportFailurePattern(text: string): boolean {
177
+ return MCP_TRANSPORT_FAILURE_PATTERNS.some((pattern) => pattern.test(text));
178
+ }
179
+
152
180
  export function detectMcpTransportFailure(
153
181
  payload: CodexHookPayload,
154
182
  ): McpTransportFailureSignal | null {
155
183
  const normalized = normalizePostToolUsePayload(payload);
156
184
  if (normalized.isBash) return null;
157
- const combined = [
185
+
186
+ const isMcpTool = isMcpLikeToolName(normalized.toolName);
187
+ const structuredText = structuredMcpTransportText(normalized);
188
+ const rawText = joinNonEmptyText([
158
189
  normalized.stderrText,
159
190
  normalized.stdoutText,
160
- safeString(normalized.parsedToolResponse?.error),
161
- safeString(normalized.parsedToolResponse?.message),
162
- safeString(normalized.parsedToolResponse?.details),
163
- ]
164
- .filter(Boolean)
165
- .join("\n")
166
- .trim();
191
+ ]);
192
+ const combined = isMcpTool
193
+ ? joinNonEmptyText([rawText, structuredText])
194
+ : structuredText;
167
195
 
168
- const mcpContextDetected = isMcpLikeToolName(normalized.toolName)
169
- || /\bmcp\b/i.test(combined)
170
- || /\bomx-(?:state|memory|trace|code-intel)-server\b/i.test(combined);
171
- if (!mcpContextDetected) return null;
172
196
  if (!combined) return null;
173
- if (!MCP_TRANSPORT_FAILURE_PATTERNS.some((pattern) => pattern.test(combined))) {
174
- return null;
175
- }
197
+ if (!isMcpTool && !hasMcpTransportContext(structuredText)) return null;
198
+ if (!hasMcpTransportFailurePattern(combined)) return null;
176
199
 
177
200
  return {
178
201
  toolName: normalized.toolName,
@@ -728,6 +751,17 @@ function buildEffectiveLoreCommitGuardEnv(parsed: GitCommitCommandParseResult):
728
751
  for (const [name, value] of Object.entries(parsed.inlineEnvironment)) {
729
752
  if (typeof value === "string") effectiveEnvironment[name] = value;
730
753
  }
754
+
755
+ if (
756
+ !parsed.environmentStartsClean
757
+ && !parsed.unsetEnvironmentNames.includes(OMX_LORE_COMMIT_GUARD_ENV)
758
+ && typeof effectiveEnvironment[OMX_LORE_COMMIT_GUARD_ENV] !== "string"
759
+ ) {
760
+ const configuredValue = readConfiguredLoreCommitGuardValue(effectiveEnvironment);
761
+ if (typeof configuredValue === "string") {
762
+ effectiveEnvironment[OMX_LORE_COMMIT_GUARD_ENV] = configuredValue;
763
+ }
764
+ }
731
765
  return effectiveEnvironment;
732
766
  }
733
767
 
@@ -976,15 +1010,75 @@ function buildSloppyFallbackPreToolUseOutput(commandText: string): Record<string
976
1010
  };
977
1011
  }
978
1012
 
1013
+ function removeHereDocBodies(command: string): string {
1014
+ const lines = command.split(/\r?\n/);
1015
+ const retained: string[] = [];
1016
+ let pendingDelimiter: string | null = null;
1017
+
1018
+ for (const line of lines) {
1019
+ if (pendingDelimiter) {
1020
+ if (line.trim() === pendingDelimiter) {
1021
+ pendingDelimiter = null;
1022
+ }
1023
+ continue;
1024
+ }
1025
+
1026
+ retained.push(line);
1027
+ const match = /<<-?\s*(?:"([^"]+)"|'([^']+)'|([A-Za-z0-9_.-]+))/.exec(line);
1028
+ if (match) pendingDelimiter = match[1] || match[2] || match[3] || null;
1029
+ }
1030
+
1031
+ return retained.join("\n");
1032
+ }
1033
+
979
1034
  function commandInvokesOmxQuestion(command: string): boolean {
980
- const tokens = tokenizeShellCommand(command)?.map((token) => token.toLowerCase()) ?? [];
981
- for (let index = 0; index < tokens.length; index += 1) {
982
- const rawToken = tokens[index] || '';
983
- const token = rawToken.replace(/\\/g, '/').split('/').pop() || '';
984
- if ((token === 'omx' || token === 'omx.js') && tokens[index + 1] === 'question') return true;
985
- if ((token === 'node' || token === 'node.exe') && /(?:^|\/)omx\.js$/.test(tokens[index + 1] || '') && tokens[index + 2] === 'question') return true;
1035
+ const tokens = tokenizeShellCommandWithBoundaries(removeHereDocBodies(command))
1036
+ ?.map((token) => ({ ...token, value: token.value.toLowerCase() }))
1037
+ ?? [];
1038
+
1039
+ for (let commandStart = 0; commandStart < tokens.length; commandStart = nextCommandStart(tokens, commandStart)) {
1040
+ const commandEnd = nextCommandStart(tokens, commandStart);
1041
+ let index = commandStart;
1042
+
1043
+ while (index < commandEnd && isInlineShellEnvAssignment(tokens[index]?.value ?? "")) {
1044
+ index += 1;
1045
+ }
1046
+
1047
+ while (index < commandEnd && isEnvExecutableToken(tokens[index]?.value ?? "")) {
1048
+ index += 1;
1049
+ while (index < commandEnd) {
1050
+ const token = tokens[index]?.value ?? "";
1051
+ if (token === "--") {
1052
+ index += 1;
1053
+ break;
1054
+ }
1055
+ if (isInlineShellEnvAssignment(token)) {
1056
+ index += 1;
1057
+ continue;
1058
+ }
1059
+ if (token === "-i" || token === "--ignore-environment" || token.startsWith("--unset=")) {
1060
+ index += 1;
1061
+ continue;
1062
+ }
1063
+ if (token.startsWith("-")) {
1064
+ index += envOptionConsumesNextValue(token) ? 2 : 1;
1065
+ continue;
1066
+ }
1067
+ break;
1068
+ }
1069
+ }
1070
+
1071
+ const rawToken = tokens[index]?.value || "";
1072
+ const token = rawToken.replace(/\\/g, "/").split("/").pop() || "";
1073
+ if ((token === "omx" || token === "omx.js") && tokens[index + 1]?.value === "question") return true;
1074
+ if (
1075
+ (token === "node" || token === "node.exe")
1076
+ && /(?:^|\/)omx\.js$/.test(tokens[index + 1]?.value || "")
1077
+ && tokens[index + 2]?.value === "question"
1078
+ ) return true;
986
1079
  }
987
- return /\bomx\s+question\b/i.test(command) || /\bomx\.js['"]?\s+question\b/i.test(command);
1080
+
1081
+ return false;
988
1082
  }
989
1083
 
990
1084
  function isQuestionReturnPaneAssignment(token: string): boolean {
@@ -52,6 +52,21 @@ function resolveNotifyEntrypoint(command: readonly string[]): string | undefined
52
52
  return command.slice(1).find((arg) => !arg.startsWith("-"));
53
53
  }
54
54
 
55
+ function getPreviousNotifyWrapperValue(
56
+ command: readonly string[],
57
+ ): string | undefined {
58
+ for (let index = 0; index < command.length; index += 1) {
59
+ const part = command[index];
60
+ if (part === "--previous-notify") {
61
+ return command[index + 1];
62
+ }
63
+ if (part.startsWith("--previous-notify=")) {
64
+ return part.slice("--previous-notify=".length);
65
+ }
66
+ }
67
+ return undefined;
68
+ }
69
+
55
70
  function isOmxManagedNotifyCommand(command: readonly string[] | null | undefined): boolean {
56
71
  if (!command) return false;
57
72
  const entrypoint = resolveNotifyEntrypoint(command);
@@ -62,12 +77,94 @@ function isOmxManagedNotifyCommand(command: readonly string[] | null | undefined
62
77
  return /(?:^|[\\/])oh-my-codex(?:[\\/]|$)/.test(entrypoint);
63
78
  }
64
79
 
80
+ function isOmxDispatcherMetadataCommand(command: readonly string[] | null | undefined): boolean {
81
+ if (!command) return false;
82
+ const entrypoint = resolveNotifyEntrypoint(command);
83
+ if (!entrypoint || !/(?:^|[\\/])notify-dispatcher\.js$/.test(entrypoint)) {
84
+ return false;
85
+ }
86
+ const metadataIndex = command.indexOf("--metadata");
87
+ const metadataPath = metadataIndex >= 0 ? command[metadataIndex + 1] : undefined;
88
+ return typeof metadataPath === "string" && /(?:^|[\\/])(?:\.omx[\\/])?notify-dispatch\.json$/.test(metadataPath);
89
+ }
90
+
91
+ function isOmxManagedPayloadText(value: string): boolean {
92
+ const containsManagedPackageNotify =
93
+ /(?:^|[\\/])notify-(?:hook|dispatcher)\.js(?:\s|$|["'])/.test(
94
+ value,
95
+ ) && /(?:^|[\\/])oh-my-codex(?:[\\/]|$)/.test(value);
96
+ const containsDispatcherMetadataNotify =
97
+ /(?:^|[\\/])notify-dispatcher\.js(?:\s|$|["'])/.test(value) &&
98
+ /--metadata(?:\s|=)/.test(value) &&
99
+ /(?:^|[\\/])(?:\.omx[\\/])?notify-dispatch\.json(?:\s|$|["'])/.test(value);
100
+ return containsManagedPackageNotify || containsDispatcherMetadataNotify;
101
+ }
102
+
103
+ function parseJsonString(value: string): unknown | undefined {
104
+ const trimmed = value.trim();
105
+ if (!trimmed) return undefined;
106
+ const first = trimmed[0];
107
+ if (first !== "[" && first !== "{" && first !== '"') return undefined;
108
+ try {
109
+ return JSON.parse(trimmed) as unknown;
110
+ } catch {
111
+ return undefined;
112
+ }
113
+ }
114
+
115
+ function containsOmxManagedNotifyPayload(value: unknown, depth = 0): boolean {
116
+ if (depth > 8 || value == null) return false;
117
+ if (typeof value === "string") {
118
+ const parsed = parseJsonString(value);
119
+ if (parsed !== undefined && parsed !== value) {
120
+ return containsOmxManagedNotifyPayload(parsed, depth + 1);
121
+ }
122
+ return isOmxManagedPayloadText(value);
123
+ }
124
+ if (Array.isArray(value)) {
125
+ if (value.every((item) => typeof item === "string")) {
126
+ const command = value as string[];
127
+ return (
128
+ isOmxManagedNotifyCommand(command) ||
129
+ isOmxDispatcherMetadataCommand(command) ||
130
+ isOmxManagedPreviousNotifyWrapper(command)
131
+ );
132
+ }
133
+ return value.some((item) => containsOmxManagedNotifyPayload(item, depth + 1));
134
+ }
135
+ if (typeof value === "object") {
136
+ const record = value as Record<string, unknown>;
137
+ return [
138
+ record.previousNotify,
139
+ record.previous_notify,
140
+ record.notify,
141
+ record.command,
142
+ record.argv,
143
+ record.args,
144
+ ].some((item) => containsOmxManagedNotifyPayload(item, depth + 1));
145
+ }
146
+ return false;
147
+ }
148
+
149
+ function isOmxManagedPreviousNotifyWrapper(
150
+ command: readonly string[] | null | undefined,
151
+ ): boolean {
152
+ if (!command) return false;
153
+ if (!command.some((part) => part === "turn-ended")) return false;
154
+ const previousNotify = getPreviousNotifyWrapperValue(command);
155
+ if (!previousNotify) return false;
156
+
157
+ return containsOmxManagedNotifyPayload(previousNotify);
158
+ }
159
+
65
160
  function isManagedPreviousNotify(
66
161
  previousNotify: readonly string[] | null | undefined,
67
162
  metadata: NotifyDispatcherMetadata | null,
68
163
  ): boolean {
69
164
  return (
70
165
  isOmxManagedNotifyCommand(previousNotify) ||
166
+ isOmxDispatcherMetadataCommand(previousNotify) ||
167
+ isOmxManagedPreviousNotifyWrapper(previousNotify) ||
71
168
  sameCommand(previousNotify, metadata?.omxNotify) ||
72
169
  sameCommand(previousNotify, metadata?.dispatcherNotify)
73
170
  );
@@ -17,35 +17,59 @@ export function runProcess(command: string, args: string[], timeoutMs = 3000): P
17
17
  let stdout = '';
18
18
  let stderr = '';
19
19
  let finished = false;
20
+ let sigkillTimer: ReturnType<typeof setTimeout> | undefined;
20
21
 
21
- const timer = setTimeout(() => {
22
- if (finished) return;
23
- finished = true;
24
- child.kill('SIGTERM');
25
- reject(new Error(`timeout after ${effectiveTimeoutMs}ms`));
26
- }, effectiveTimeoutMs);
22
+ const cleanup = (clearPendingSigkill = true) => {
23
+ clearTimeout(timer);
24
+ if (clearPendingSigkill && sigkillTimer) {
25
+ clearTimeout(sigkillTimer);
26
+ sigkillTimer = undefined;
27
+ }
28
+ child.stdout.off('data', onStdout);
29
+ child.stderr.off('data', onStderr);
30
+ child.off('error', onError);
31
+ child.off('close', onClose);
32
+ };
27
33
 
28
- child.stdout.on('data', (chunk: Buffer) => {
34
+ const onStdout = (chunk: Buffer) => {
29
35
  stdout += chunk.toString();
30
- });
31
- child.stderr.on('data', (chunk: Buffer) => {
36
+ };
37
+ const onStderr = (chunk: Buffer) => {
32
38
  stderr += chunk.toString();
33
- });
34
- child.on('error', (err: Error) => {
39
+ };
40
+ const onError = (err: Error) => {
35
41
  if (finished) return;
36
42
  finished = true;
37
- clearTimeout(timer);
43
+ cleanup();
38
44
  reject(err);
39
- });
40
- child.on('close', (code: number | null) => {
45
+ };
46
+ const onClose = (code: number | null) => {
41
47
  if (finished) return;
42
48
  finished = true;
43
- clearTimeout(timer);
49
+ cleanup();
44
50
  if (code === 0) {
45
51
  resolve({ stdout, stderr, code });
46
52
  } else {
47
53
  reject(new Error(stderr.trim() || `${command} exited ${code}`));
48
54
  }
49
- });
55
+ };
56
+
57
+ const timer = setTimeout(() => {
58
+ if (finished) return;
59
+ finished = true;
60
+ child.kill('SIGTERM');
61
+ sigkillTimer = setTimeout(() => {
62
+ sigkillTimer = undefined;
63
+ child.kill('SIGKILL');
64
+ }, 250);
65
+ sigkillTimer.unref?.();
66
+ cleanup(false);
67
+ reject(new Error(`timeout after ${effectiveTimeoutMs}ms`));
68
+ }, effectiveTimeoutMs);
69
+
70
+ child.stdout.on('data', onStdout);
71
+ child.stderr.on('data', onStderr);
72
+ child.on('error', onError);
73
+ child.on('close', onClose);
50
74
  });
51
75
  }
@@ -726,12 +726,14 @@ function resolveWorkerCliForRequest(request, config) {
726
726
 
727
727
  function capturedPaneContainsTrigger(captured, trigger) {
728
728
  if (!captured || !trigger) return false;
729
- return normalizeTmuxCapture(captured).includes(normalizeTmuxCapture(trigger));
729
+ const normalizeForDraftMatch = (value) => normalizeTmuxCapture(value).replace(/-\s+/g, '-');
730
+ return normalizeForDraftMatch(captured).includes(normalizeForDraftMatch(trigger));
730
731
  }
731
732
 
732
733
  function capturedPaneContainsTriggerNearTail(captured, trigger, nonEmptyTailLines = 24) {
733
734
  if (!captured || !trigger) return false;
734
- const normalizedTrigger = normalizeTmuxCapture(trigger);
735
+ const normalizeForDraftMatch = (value) => normalizeTmuxCapture(value).replace(/-\s+/g, '-');
736
+ const normalizedTrigger = normalizeForDraftMatch(trigger);
735
737
  if (!normalizedTrigger) return false;
736
738
  const lines = safeString(captured)
737
739
  .split('\n')
@@ -739,7 +741,13 @@ function capturedPaneContainsTriggerNearTail(captured, trigger, nonEmptyTailLine
739
741
  .filter((line) => line.length > 0);
740
742
  if (lines.length === 0) return false;
741
743
  const tail = lines.slice(-Math.max(1, nonEmptyTailLines)).join(' ');
742
- return normalizeTmuxCapture(tail).includes(normalizedTrigger);
744
+ return normalizeForDraftMatch(tail).includes(normalizedTrigger);
745
+ }
746
+
747
+ function buildJoinedCapturePaneArgv(paneTarget, tailLines = 80) {
748
+ // Join wrapped visual lines so long path-like trigger text split by tmux
749
+ // remains comparable with the original trigger.
750
+ return ['capture-pane', '-J', '-t', paneTarget, '-p', '-S', `-${tailLines}`];
743
751
  }
744
752
 
745
753
  const INJECT_VERIFY_DELAY_MS = 250;
@@ -791,7 +799,7 @@ async function injectDispatchRequest(request, config, cwd, stateDir) {
791
799
  if (attemptCountAtStart >= 1) {
792
800
  try {
793
801
  // Narrow capture (8 lines) to scope check to input area, not scrollback output
794
- const preCapture = await runProcess('tmux', buildCapturePaneArgv(resolution.paneTarget, 8), 2000);
802
+ const preCapture = await runProcess('tmux', buildJoinedCapturePaneArgv(resolution.paneTarget, 8), 2000);
795
803
  preCaptureHasTrigger = capturedPaneContainsTrigger(preCapture.stdout, request.trigger_message);
796
804
  } catch {
797
805
  preCaptureHasTrigger = false;
@@ -814,6 +822,7 @@ async function injectDispatchRequest(request, config, cwd, stateDir) {
814
822
  prompt: request.trigger_message,
815
823
  submitKeyPresses,
816
824
  typePrompt: shouldTypePrompt,
825
+ queueFirstSubmit: leaderTargeted,
817
826
  });
818
827
  if (!sendResult.ok) {
819
828
  return {
@@ -830,8 +839,8 @@ async function injectDispatchRequest(request, config, cwd, stateDir) {
830
839
  // Post-injection verification: confirm the trigger text was consumed.
831
840
  // Fixes #391: without this, dispatch marks 'notified' even when the worker
832
841
  // pane is sitting on an unsent draft (C-m was not effectively applied).
833
- const verifyNarrowArgv = buildCapturePaneArgv(resolution.paneTarget, 8);
834
- const verifyWideArgv = buildCapturePaneArgv(resolution.paneTarget);
842
+ const verifyNarrowArgv = buildJoinedCapturePaneArgv(resolution.paneTarget, 8);
843
+ const verifyWideArgv = buildJoinedCapturePaneArgv(resolution.paneTarget);
835
844
  for (let round = 0; round < INJECT_VERIFY_ROUNDS; round++) {
836
845
  await new Promise((r) => setTimeout(r, INJECT_VERIFY_DELAY_MS));
837
846
  try {
@@ -842,6 +851,19 @@ async function injectDispatchRequest(request, config, cwd, stateDir) {
842
851
  // full-scrollback false positives.
843
852
  const narrowCap = await runProcess('tmux', verifyNarrowArgv, 2000);
844
853
  const wideCap = await runProcess('tmux', verifyWideArgv, 2000);
854
+ const triggerInNarrow = capturedPaneContainsTrigger(narrowCap.stdout, request.trigger_message);
855
+ const triggerNearTail = capturedPaneContainsTriggerNearTail(wideCap.stdout, request.trigger_message);
856
+ if (triggerInNarrow || triggerNearTail) {
857
+ // Draft is still visible, so C-m has not actually submitted it yet.
858
+ // Do not let transient spinner/active-task text mask an unsent draft.
859
+ await sendPaneInput({
860
+ paneTarget: resolution.paneTarget,
861
+ prompt: request.trigger_message,
862
+ submitKeyPresses,
863
+ typePrompt: false,
864
+ }).catch(() => {});
865
+ continue;
866
+ }
845
867
  // Worker is actively processing (mirrors sync path tmux-session.ts:1292-1294)
846
868
  if (paneHasActiveTask(wideCap.stdout)) {
847
869
  runtimeExec({ command: 'MarkDelivered', request_id: request.request_id }, stateDir, request.team_name);
@@ -855,15 +877,15 @@ async function injectDispatchRequest(request, config, cwd, stateDir) {
855
877
  tmux_injection_attempted: true,
856
878
  };
857
879
  }
858
- // Do not declare success while a *worker* pane is still bootstrapping / not
859
- // input-ready. Otherwise a pre-ready send can be marked "confirmed" and later
860
- // appear as a stuck unsent draft once the UI finishes loading.
861
- // Keep leader-fixed behavior unchanged to avoid regressing leader notification flow.
862
- if (request.to_worker !== 'leader-fixed' && !paneLooksReady(wideCap.stdout)) {
880
+ // Do not declare success while a pane is not input-ready. Otherwise a
881
+ // pre-ready send can be marked "confirmed" and later appear as a stuck
882
+ // unsent draft once the UI finishes loading. This includes leader-fixed:
883
+ // its Codex UI can show "tab to queue message" while busy, and marking
884
+ // delivered before queue/consumption confirmation loses the orchestration
885
+ // nudge until a human presses Tab manually.
886
+ if (!paneLooksReady(wideCap.stdout)) {
863
887
  continue;
864
888
  }
865
- const triggerInNarrow = capturedPaneContainsTrigger(narrowCap.stdout, request.trigger_message);
866
- const triggerNearTail = capturedPaneContainsTriggerNearTail(wideCap.stdout, request.trigger_message);
867
889
  if (!triggerInNarrow && !triggerNearTail) {
868
890
  runtimeExec({ command: 'MarkDelivered', request_id: request.request_id }, stateDir, request.team_name);
869
891
  return {
@@ -885,6 +907,7 @@ async function injectDispatchRequest(request, config, cwd, stateDir) {
885
907
  prompt: request.trigger_message,
886
908
  submitKeyPresses,
887
909
  typePrompt: false,
910
+ queueFirstSubmit: leaderTargeted,
888
911
  }).catch(() => {});
889
912
  }
890
913
 
@@ -11,14 +11,14 @@ import { asNumber, safeString, isTerminalPhase } from './utils.js';
11
11
  import { readJsonIfExists, getScopedStateDirsForCurrentSession } from './state-io.js';
12
12
  import { runProcess } from './process-runner.js';
13
13
  import { logTmuxHookEvent } from './log.js';
14
- import { evaluatePaneInjectionReadiness, sendPaneInput } from './team-tmux-guard.js';
14
+ import { evaluatePaneInjectionReadiness, queuePaneInput, sendPaneInput } from './team-tmux-guard.js';
15
15
  import { resolvePaneTarget } from './tmux-injection.js';
16
16
  import { listNotifyCanonicalActiveTeams } from './active-team.js';
17
17
  import {
18
18
  classifyLeaderActionState,
19
19
  resolveLeaderNudgeIntent,
20
20
  } from './orchestration-intent.js';
21
- import { DEFAULT_MARKER } from '../tmux-hook-engine.js';
21
+ import { DEFAULT_MARKER, paneHasActiveTask } from '../tmux-hook-engine.js';
22
22
  import { isLeaderRuntimeStale } from '../../team/leader-activity.js';
23
23
  import { appendTeamDeliveryLog } from '../../team/delivery-log.js';
24
24
  import { writeTeamLeaderAttention } from '../../team/state.js';
@@ -967,14 +967,27 @@ export async function maybeNudgeTeamLeader({
967
967
  }
968
968
 
969
969
  try {
970
- const sendResult = await sendPaneInput({
971
- paneTarget: tmuxTarget,
972
- prompt: markedText,
973
- submitKeyPresses: 2,
974
- submitDelayMs: 100,
975
- });
976
- if (!sendResult.ok) {
977
- throw new Error(sendResult.error || sendResult.reason);
970
+ const leaderHasActiveTask = paneHasActiveTask(paneGuard.paneCapture);
971
+ let deliveryMode = 'sent';
972
+ if (leaderHasActiveTask) {
973
+ const sendResult = await queuePaneInput({
974
+ paneTarget: tmuxTarget,
975
+ prompt: markedText,
976
+ });
977
+ if (!sendResult.ok) {
978
+ throw new Error(sendResult.error || sendResult.reason);
979
+ }
980
+ deliveryMode = 'queued';
981
+ } else {
982
+ const sendResult = await sendPaneInput({
983
+ paneTarget: tmuxTarget,
984
+ prompt: markedText,
985
+ submitKeyPresses: 2,
986
+ submitDelayMs: 100,
987
+ });
988
+ if (!sendResult.ok) {
989
+ throw new Error(sendResult.error || sendResult.reason);
990
+ }
978
991
  }
979
992
  nudgeState.last_nudged_by_team[teamName] = {
980
993
  at: nowIso,
@@ -1005,6 +1018,7 @@ export async function maybeNudgeTeamLeader({
1005
1018
  message_count: messages.length,
1006
1019
  stalled_for_ms: undefined,
1007
1020
  missing_signal_workers: progressSnapshot.missingSignalWorkers,
1021
+ delivery: deliveryMode,
1008
1022
  });
1009
1023
  } catch { /* ignore */ }
1010
1024
  await appendTeamDeliveryLog(logsDir, {
@@ -1013,7 +1027,7 @@ export async function maybeNudgeTeamLeader({
1013
1027
  team: teamName,
1014
1028
  to_worker: 'leader-fixed',
1015
1029
  transport: 'send-keys',
1016
- result: 'sent',
1030
+ result: deliveryMode,
1017
1031
  reason: nudgeReason,
1018
1032
  orchestration_intent: orchestrationIntent,
1019
1033
  }).catch(() => {});
@@ -134,6 +134,7 @@ export async function sendPaneInput({
134
134
  submitKeyPresses = 2,
135
135
  submitDelayMs = 0,
136
136
  typePrompt = true,
137
+ queueFirstSubmit = false,
137
138
  }: any): Promise<any> {
138
139
  const target = safeString(paneTarget).trim();
139
140
  if (!target) {
@@ -163,6 +164,12 @@ export async function sendPaneInput({
163
164
  if (typePrompt) {
164
165
  await runProcess('tmux', argv.typeArgv, 3000);
165
166
  }
167
+ if (queueFirstSubmit && argv.submitArgv.length > 0) {
168
+ await runProcess('tmux', ['send-keys', '-t', target, 'Tab'], 3000);
169
+ if (submitDelayMs > 0) {
170
+ await new Promise((resolve) => setTimeout(resolve, submitDelayMs));
171
+ }
172
+ }
166
173
  for (const submit of argv.submitArgv) {
167
174
  if (submitDelayMs > 0) {
168
175
  await new Promise((resolve) => setTimeout(resolve, submitDelayMs));
@@ -182,6 +189,48 @@ export async function sendPaneInput({
182
189
  }
183
190
  }
184
191
 
192
+ export async function queuePaneInput({
193
+ paneTarget,
194
+ prompt,
195
+ submitDelayMs = 80,
196
+ }: any): Promise<any> {
197
+ const sendResult = await sendPaneInput({
198
+ paneTarget,
199
+ prompt,
200
+ submitKeyPresses: 0,
201
+ });
202
+ if (!sendResult.ok) return sendResult;
203
+
204
+ const target = safeString(paneTarget).trim();
205
+ const submitArgv = [
206
+ ['send-keys', '-t', target, 'Tab'],
207
+ ['send-keys', '-t', target, 'C-m'],
208
+ ];
209
+ try {
210
+ await runProcess('tmux', submitArgv[0], 3000);
211
+ if (submitDelayMs > 0) {
212
+ await new Promise((resolve) => setTimeout(resolve, submitDelayMs));
213
+ }
214
+ await runProcess('tmux', submitArgv[1], 3000);
215
+ return {
216
+ ok: true,
217
+ sent: true,
218
+ reason: 'queued',
219
+ paneTarget: target,
220
+ argv: { typeArgv: sendResult.argv?.typeArgv || null, submitArgv },
221
+ };
222
+ } catch (error) {
223
+ return {
224
+ ok: false,
225
+ sent: false,
226
+ reason: 'queue_failed',
227
+ paneTarget: target,
228
+ argv: { typeArgv: sendResult.argv?.typeArgv || null, submitArgv },
229
+ error: error instanceof Error ? error.message : safeString(error),
230
+ };
231
+ }
232
+ }
233
+
185
234
  export async function checkPaneReadyForTeamSendKeys(paneTarget: any): Promise<any> {
186
235
  return evaluatePaneInjectionReadiness(paneTarget);
187
236
  }