oh-my-codex 0.13.2 → 0.14.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 (406) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/README.md +14 -8
  4. package/crates/omx-explore/src/main.rs +94 -1
  5. package/crates/omx-sparkshell/src/codex_bridge.rs +59 -12
  6. package/crates/omx-sparkshell/tests/execution.rs +48 -0
  7. package/dist/autoresearch/__tests__/skill-validation.test.d.ts +2 -0
  8. package/dist/autoresearch/__tests__/skill-validation.test.d.ts.map +1 -0
  9. package/dist/autoresearch/__tests__/skill-validation.test.js +91 -0
  10. package/dist/autoresearch/__tests__/skill-validation.test.js.map +1 -0
  11. package/dist/autoresearch/skill-validation.d.ts +13 -0
  12. package/dist/autoresearch/skill-validation.d.ts.map +1 -0
  13. package/dist/autoresearch/skill-validation.js +165 -0
  14. package/dist/autoresearch/skill-validation.js.map +1 -0
  15. package/dist/catalog/__tests__/schema.test.js +6 -0
  16. package/dist/catalog/__tests__/schema.test.js.map +1 -1
  17. package/dist/cli/__tests__/autoresearch-guided.test.js +236 -273
  18. package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
  19. package/dist/cli/__tests__/autoresearch.test.js +64 -653
  20. package/dist/cli/__tests__/autoresearch.test.js.map +1 -1
  21. package/dist/cli/__tests__/explore.test.js +33 -1
  22. package/dist/cli/__tests__/explore.test.js.map +1 -1
  23. package/dist/cli/__tests__/index.test.js +18 -2
  24. package/dist/cli/__tests__/index.test.js.map +1 -1
  25. package/dist/cli/__tests__/nested-help-routing.test.js +2 -1
  26. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  27. package/dist/cli/__tests__/package-bin-contract.test.js +5 -0
  28. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  29. package/dist/cli/__tests__/question.test.d.ts +2 -0
  30. package/dist/cli/__tests__/question.test.d.ts.map +1 -0
  31. package/dist/cli/__tests__/question.test.js +166 -0
  32. package/dist/cli/__tests__/question.test.js.map +1 -0
  33. package/dist/cli/__tests__/session-search-help.test.js +1 -1
  34. package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
  35. package/dist/cli/__tests__/setup-agents-overwrite.test.js +32 -7
  36. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  37. package/dist/cli/__tests__/setup-refresh.test.js +8 -6
  38. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  39. package/dist/cli/__tests__/setup-skills-overwrite.test.js +2 -0
  40. package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
  41. package/dist/cli/__tests__/sparkshell-cli.test.js +23 -0
  42. package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
  43. package/dist/cli/__tests__/uninstall.test.js +65 -5
  44. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  45. package/dist/cli/__tests__/update.test.js +360 -26
  46. package/dist/cli/__tests__/update.test.js.map +1 -1
  47. package/dist/cli/autoresearch-guided.d.ts +24 -7
  48. package/dist/cli/autoresearch-guided.d.ts.map +1 -1
  49. package/dist/cli/autoresearch-guided.js +189 -130
  50. package/dist/cli/autoresearch-guided.js.map +1 -1
  51. package/dist/cli/autoresearch.d.ts +3 -2
  52. package/dist/cli/autoresearch.d.ts.map +1 -1
  53. package/dist/cli/autoresearch.js +29 -305
  54. package/dist/cli/autoresearch.js.map +1 -1
  55. package/dist/cli/doctor.d.ts.map +1 -1
  56. package/dist/cli/doctor.js +43 -0
  57. package/dist/cli/doctor.js.map +1 -1
  58. package/dist/cli/explore.d.ts.map +1 -1
  59. package/dist/cli/explore.js +18 -3
  60. package/dist/cli/explore.js.map +1 -1
  61. package/dist/cli/index.d.ts +2 -1
  62. package/dist/cli/index.d.ts.map +1 -1
  63. package/dist/cli/index.js +15 -3
  64. package/dist/cli/index.js.map +1 -1
  65. package/dist/cli/question.d.ts +3 -0
  66. package/dist/cli/question.d.ts.map +1 -0
  67. package/dist/cli/question.js +182 -0
  68. package/dist/cli/question.js.map +1 -0
  69. package/dist/cli/setup.d.ts.map +1 -1
  70. package/dist/cli/setup.js +25 -3
  71. package/dist/cli/setup.js.map +1 -1
  72. package/dist/cli/sparkshell.d.ts.map +1 -1
  73. package/dist/cli/sparkshell.js +11 -1
  74. package/dist/cli/sparkshell.js.map +1 -1
  75. package/dist/cli/team.d.ts.map +1 -1
  76. package/dist/cli/team.js +159 -394
  77. package/dist/cli/team.js.map +1 -1
  78. package/dist/cli/uninstall.d.ts.map +1 -1
  79. package/dist/cli/uninstall.js +3 -1
  80. package/dist/cli/uninstall.js.map +1 -1
  81. package/dist/cli/update.d.ts +37 -9
  82. package/dist/cli/update.d.ts.map +1 -1
  83. package/dist/cli/update.js +204 -26
  84. package/dist/cli/update.js.map +1 -1
  85. package/dist/config/__tests__/generator-idempotent.test.js +51 -14
  86. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  87. package/dist/config/__tests__/generator-notify.test.js +35 -10
  88. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  89. package/dist/config/generator.d.ts +1 -0
  90. package/dist/config/generator.d.ts.map +1 -1
  91. package/dist/config/generator.js +61 -7
  92. package/dist/config/generator.js.map +1 -1
  93. package/dist/hooks/__tests__/analyze-routing-contract.test.js +22 -13
  94. package/dist/hooks/__tests__/analyze-routing-contract.test.js.map +1 -1
  95. package/dist/hooks/__tests__/anti-slop-workflow.test.js +3 -3
  96. package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
  97. package/dist/hooks/__tests__/code-review-skill-contract.test.d.ts +2 -0
  98. package/dist/hooks/__tests__/code-review-skill-contract.test.d.ts.map +1 -0
  99. package/dist/hooks/__tests__/code-review-skill-contract.test.js +56 -0
  100. package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -0
  101. package/dist/hooks/__tests__/debugger-log-recency-contract.test.js +2 -2
  102. package/dist/hooks/__tests__/debugger-log-recency-contract.test.js.map +1 -1
  103. package/dist/hooks/__tests__/deep-interview-contract.test.js +51 -5
  104. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  105. package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.d.ts +2 -0
  106. package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.d.ts.map +1 -0
  107. package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.js +43 -0
  108. package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.js.map +1 -0
  109. package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.d.ts +2 -0
  110. package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.d.ts.map +1 -0
  111. package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.js +38 -0
  112. package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.js.map +1 -0
  113. package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js +2 -2
  114. package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js.map +1 -1
  115. package/dist/hooks/__tests__/keyword-detector.test.js +308 -17
  116. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  117. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +570 -2
  118. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  119. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +717 -16
  120. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  121. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +25 -0
  122. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
  123. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +894 -1
  124. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  125. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +34 -0
  126. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
  127. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +132 -0
  128. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  129. package/dist/hooks/__tests__/prompt-guidance-contract.test.js +22 -4
  130. package/dist/hooks/__tests__/prompt-guidance-contract.test.js.map +1 -1
  131. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js +4 -2
  132. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js.map +1 -1
  133. package/dist/hooks/__tests__/prompt-guidance-test-helpers.d.ts +1 -0
  134. package/dist/hooks/__tests__/prompt-guidance-test-helpers.d.ts.map +1 -1
  135. package/dist/hooks/__tests__/prompt-guidance-test-helpers.js +19 -1
  136. package/dist/hooks/__tests__/prompt-guidance-test-helpers.js.map +1 -1
  137. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js +28 -0
  138. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js.map +1 -1
  139. package/dist/hooks/__tests__/prompt-orchestration-boundary.test.js +5 -4
  140. package/dist/hooks/__tests__/prompt-orchestration-boundary.test.js.map +1 -1
  141. package/dist/hooks/__tests__/prompt-team-routing.test.js +2 -2
  142. package/dist/hooks/__tests__/prompt-team-routing.test.js.map +1 -1
  143. package/dist/hooks/__tests__/triage-config.test.d.ts +2 -0
  144. package/dist/hooks/__tests__/triage-config.test.d.ts.map +1 -0
  145. package/dist/hooks/__tests__/triage-config.test.js +211 -0
  146. package/dist/hooks/__tests__/triage-config.test.js.map +1 -0
  147. package/dist/hooks/__tests__/triage-heuristic.test.d.ts +2 -0
  148. package/dist/hooks/__tests__/triage-heuristic.test.d.ts.map +1 -0
  149. package/dist/hooks/__tests__/triage-heuristic.test.js +230 -0
  150. package/dist/hooks/__tests__/triage-heuristic.test.js.map +1 -0
  151. package/dist/hooks/__tests__/triage-state.test.d.ts +2 -0
  152. package/dist/hooks/__tests__/triage-state.test.d.ts.map +1 -0
  153. package/dist/hooks/__tests__/triage-state.test.js +426 -0
  154. package/dist/hooks/__tests__/triage-state.test.js.map +1 -0
  155. package/dist/hooks/keyword-detector.d.ts +26 -7
  156. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  157. package/dist/hooks/keyword-detector.js +97 -26
  158. package/dist/hooks/keyword-detector.js.map +1 -1
  159. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  160. package/dist/hooks/keyword-registry.js +16 -9
  161. package/dist/hooks/keyword-registry.js.map +1 -1
  162. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  163. package/dist/hooks/prompt-guidance-contract.js +28 -1
  164. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  165. package/dist/hooks/triage-config.d.ts +33 -0
  166. package/dist/hooks/triage-config.d.ts.map +1 -0
  167. package/dist/hooks/triage-config.js +87 -0
  168. package/dist/hooks/triage-config.js.map +1 -0
  169. package/dist/hooks/triage-heuristic.d.ts +20 -0
  170. package/dist/hooks/triage-heuristic.d.ts.map +1 -0
  171. package/dist/hooks/triage-heuristic.js +210 -0
  172. package/dist/hooks/triage-heuristic.js.map +1 -0
  173. package/dist/hooks/triage-state.d.ts +63 -0
  174. package/dist/hooks/triage-state.d.ts.map +1 -0
  175. package/dist/hooks/triage-state.js +138 -0
  176. package/dist/hooks/triage-state.js.map +1 -0
  177. package/dist/hud/__tests__/reconcile.test.js +20 -0
  178. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  179. package/dist/hud/reconcile.d.ts +1 -0
  180. package/dist/hud/reconcile.d.ts.map +1 -1
  181. package/dist/hud/reconcile.js +2 -1
  182. package/dist/hud/reconcile.js.map +1 -1
  183. package/dist/mcp/__tests__/bootstrap.test.js +5 -24
  184. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  185. package/dist/mcp/__tests__/state-server.test.js +127 -0
  186. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  187. package/dist/mcp/bootstrap.d.ts +1 -1
  188. package/dist/mcp/bootstrap.d.ts.map +1 -1
  189. package/dist/mcp/bootstrap.js +3 -11
  190. package/dist/mcp/bootstrap.js.map +1 -1
  191. package/dist/mcp/state-server.d.ts +25 -0
  192. package/dist/mcp/state-server.d.ts.map +1 -1
  193. package/dist/mcp/state-server.js +41 -0
  194. package/dist/mcp/state-server.js.map +1 -1
  195. package/dist/modes/__tests__/base-ralph-contract.test.js +15 -0
  196. package/dist/modes/__tests__/base-ralph-contract.test.js.map +1 -1
  197. package/dist/modes/base.d.ts +1 -0
  198. package/dist/modes/base.d.ts.map +1 -1
  199. package/dist/modes/base.js +22 -6
  200. package/dist/modes/base.js.map +1 -1
  201. package/dist/notifications/__tests__/index.test.js +75 -0
  202. package/dist/notifications/__tests__/index.test.js.map +1 -1
  203. package/dist/notifications/__tests__/session-status.test.js +90 -0
  204. package/dist/notifications/__tests__/session-status.test.js.map +1 -1
  205. package/dist/notifications/index.d.ts.map +1 -1
  206. package/dist/notifications/index.js +39 -22
  207. package/dist/notifications/index.js.map +1 -1
  208. package/dist/notifications/session-status.d.ts +2 -0
  209. package/dist/notifications/session-status.d.ts.map +1 -1
  210. package/dist/notifications/session-status.js +19 -4
  211. package/dist/notifications/session-status.js.map +1 -1
  212. package/dist/openclaw/index.d.ts +5 -3
  213. package/dist/openclaw/index.d.ts.map +1 -1
  214. package/dist/openclaw/index.js +5 -3
  215. package/dist/openclaw/index.js.map +1 -1
  216. package/dist/question/__tests__/client.test.d.ts +2 -0
  217. package/dist/question/__tests__/client.test.d.ts.map +1 -0
  218. package/dist/question/__tests__/client.test.js +70 -0
  219. package/dist/question/__tests__/client.test.js.map +1 -0
  220. package/dist/question/__tests__/deep-interview.test.d.ts +2 -0
  221. package/dist/question/__tests__/deep-interview.test.d.ts.map +1 -0
  222. package/dist/question/__tests__/deep-interview.test.js +118 -0
  223. package/dist/question/__tests__/deep-interview.test.js.map +1 -0
  224. package/dist/question/__tests__/policy.test.d.ts +2 -0
  225. package/dist/question/__tests__/policy.test.d.ts.map +1 -0
  226. package/dist/question/__tests__/policy.test.js +107 -0
  227. package/dist/question/__tests__/policy.test.js.map +1 -0
  228. package/dist/question/__tests__/renderer.test.d.ts +2 -0
  229. package/dist/question/__tests__/renderer.test.d.ts.map +1 -0
  230. package/dist/question/__tests__/renderer.test.js +238 -0
  231. package/dist/question/__tests__/renderer.test.js.map +1 -0
  232. package/dist/question/__tests__/state.test.d.ts +2 -0
  233. package/dist/question/__tests__/state.test.d.ts.map +1 -0
  234. package/dist/question/__tests__/state.test.js +75 -0
  235. package/dist/question/__tests__/state.test.js.map +1 -0
  236. package/dist/question/__tests__/types.test.d.ts +2 -0
  237. package/dist/question/__tests__/types.test.d.ts.map +1 -0
  238. package/dist/question/__tests__/types.test.js +44 -0
  239. package/dist/question/__tests__/types.test.js.map +1 -0
  240. package/dist/question/__tests__/ui.test.d.ts +2 -0
  241. package/dist/question/__tests__/ui.test.d.ts.map +1 -0
  242. package/dist/question/__tests__/ui.test.js +169 -0
  243. package/dist/question/__tests__/ui.test.js.map +1 -0
  244. package/dist/question/client.d.ts +54 -0
  245. package/dist/question/client.d.ts.map +1 -0
  246. package/dist/question/client.js +77 -0
  247. package/dist/question/client.js.map +1 -0
  248. package/dist/question/deep-interview.d.ts +30 -0
  249. package/dist/question/deep-interview.d.ts.map +1 -0
  250. package/dist/question/deep-interview.js +118 -0
  251. package/dist/question/deep-interview.js.map +1 -0
  252. package/dist/question/policy.d.ts +18 -0
  253. package/dist/question/policy.d.ts.map +1 -0
  254. package/dist/question/policy.js +77 -0
  255. package/dist/question/policy.js.map +1 -0
  256. package/dist/question/renderer.d.ts +20 -0
  257. package/dist/question/renderer.d.ts.map +1 -0
  258. package/dist/question/renderer.js +190 -0
  259. package/dist/question/renderer.js.map +1 -0
  260. package/dist/question/state.d.ts +19 -0
  261. package/dist/question/state.d.ts.map +1 -0
  262. package/dist/question/state.js +108 -0
  263. package/dist/question/state.js.map +1 -0
  264. package/dist/question/types.d.ts +66 -0
  265. package/dist/question/types.d.ts.map +1 -0
  266. package/dist/question/types.js +82 -0
  267. package/dist/question/types.js.map +1 -0
  268. package/dist/question/ui.d.ts +38 -0
  269. package/dist/question/ui.d.ts.map +1 -0
  270. package/dist/question/ui.js +321 -0
  271. package/dist/question/ui.js.map +1 -0
  272. package/dist/ralph/contract.d.ts +1 -1
  273. package/dist/ralph/contract.d.ts.map +1 -1
  274. package/dist/ralph/contract.js +4 -1
  275. package/dist/ralph/contract.js.map +1 -1
  276. package/dist/ralplan/runtime.js +1 -1
  277. package/dist/ralplan/runtime.js.map +1 -1
  278. package/dist/runtime/__tests__/run-loop.test.d.ts +2 -0
  279. package/dist/runtime/__tests__/run-loop.test.d.ts.map +1 -0
  280. package/dist/runtime/__tests__/run-loop.test.js +35 -0
  281. package/dist/runtime/__tests__/run-loop.test.js.map +1 -0
  282. package/dist/runtime/__tests__/run-outcome.test.d.ts +2 -0
  283. package/dist/runtime/__tests__/run-outcome.test.d.ts.map +1 -0
  284. package/dist/runtime/__tests__/run-outcome.test.js +102 -0
  285. package/dist/runtime/__tests__/run-outcome.test.js.map +1 -0
  286. package/dist/runtime/__tests__/run-state.test.d.ts +2 -0
  287. package/dist/runtime/__tests__/run-state.test.d.ts.map +1 -0
  288. package/dist/runtime/__tests__/run-state.test.js +37 -0
  289. package/dist/runtime/__tests__/run-state.test.js.map +1 -0
  290. package/dist/runtime/run-loop.d.ts +45 -0
  291. package/dist/runtime/run-loop.d.ts.map +1 -0
  292. package/dist/runtime/run-loop.js +51 -0
  293. package/dist/runtime/run-loop.js.map +1 -0
  294. package/dist/runtime/run-outcome.d.ts +46 -0
  295. package/dist/runtime/run-outcome.d.ts.map +1 -0
  296. package/dist/runtime/run-outcome.js +285 -0
  297. package/dist/runtime/run-outcome.js.map +1 -0
  298. package/dist/runtime/run-state.d.ts +40 -0
  299. package/dist/runtime/run-state.d.ts.map +1 -0
  300. package/dist/runtime/run-state.js +120 -0
  301. package/dist/runtime/run-state.js.map +1 -0
  302. package/dist/runtime/terminal-lifecycle.d.ts +11 -0
  303. package/dist/runtime/terminal-lifecycle.d.ts.map +1 -0
  304. package/dist/runtime/terminal-lifecycle.js +52 -0
  305. package/dist/runtime/terminal-lifecycle.js.map +1 -0
  306. package/dist/scripts/__tests__/codex-native-hook.test.js +1459 -126
  307. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  308. package/dist/scripts/__tests__/postinstall.test.d.ts +2 -0
  309. package/dist/scripts/__tests__/postinstall.test.d.ts.map +1 -0
  310. package/dist/scripts/__tests__/postinstall.test.js +178 -0
  311. package/dist/scripts/__tests__/postinstall.test.js.map +1 -0
  312. package/dist/scripts/codex-native-hook.d.ts +3 -0
  313. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  314. package/dist/scripts/codex-native-hook.js +308 -61
  315. package/dist/scripts/codex-native-hook.js.map +1 -1
  316. package/dist/scripts/notify-fallback-watcher.js +81 -2
  317. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  318. package/dist/scripts/notify-hook/auto-nudge.d.ts +27 -0
  319. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  320. package/dist/scripts/notify-hook/auto-nudge.js +83 -20
  321. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  322. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  323. package/dist/scripts/notify-hook/managed-tmux.js +64 -38
  324. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  325. package/dist/scripts/notify-hook/ralph-session-resume.js +1 -1
  326. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  327. package/dist/scripts/notify-hook.js +15 -5
  328. package/dist/scripts/notify-hook.js.map +1 -1
  329. package/dist/scripts/postinstall.d.ts +22 -0
  330. package/dist/scripts/postinstall.d.ts.map +1 -0
  331. package/dist/scripts/postinstall.js +105 -0
  332. package/dist/scripts/postinstall.js.map +1 -0
  333. package/dist/scripts/sync-prompt-guidance-fragments.js +5 -0
  334. package/dist/scripts/sync-prompt-guidance-fragments.js.map +1 -1
  335. package/dist/state/__tests__/operations-ralph-phase.test.js +21 -0
  336. package/dist/state/__tests__/operations-ralph-phase.test.js.map +1 -1
  337. package/dist/state/__tests__/operations.test.js +18 -0
  338. package/dist/state/__tests__/operations.test.js.map +1 -1
  339. package/dist/state/__tests__/workflow-transition.test.js +11 -0
  340. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  341. package/dist/state/operations.d.ts.map +1 -1
  342. package/dist/state/operations.js +15 -0
  343. package/dist/state/operations.js.map +1 -1
  344. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  345. package/dist/state/workflow-transition-reconcile.js +14 -1
  346. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  347. package/dist/state/workflow-transition.d.ts.map +1 -1
  348. package/dist/state/workflow-transition.js +3 -1
  349. package/dist/state/workflow-transition.js.map +1 -1
  350. package/dist/team/__tests__/followup-planner.test.js +15 -0
  351. package/dist/team/__tests__/followup-planner.test.js.map +1 -1
  352. package/dist/team/__tests__/role-router.test.js +47 -0
  353. package/dist/team/__tests__/role-router.test.js.map +1 -1
  354. package/dist/team/__tests__/runtime.test.js +108 -2
  355. package/dist/team/__tests__/runtime.test.js.map +1 -1
  356. package/dist/team/followup-planner.d.ts.map +1 -1
  357. package/dist/team/followup-planner.js +31 -9
  358. package/dist/team/followup-planner.js.map +1 -1
  359. package/dist/team/role-router.d.ts.map +1 -1
  360. package/dist/team/role-router.js +73 -0
  361. package/dist/team/role-router.js.map +1 -1
  362. package/dist/team/runtime.d.ts.map +1 -1
  363. package/dist/team/runtime.js +18 -4
  364. package/dist/team/runtime.js.map +1 -1
  365. package/dist/utils/__tests__/dep-versions.test.js +25 -8
  366. package/dist/utils/__tests__/dep-versions.test.js.map +1 -1
  367. package/dist/utils/__tests__/paths.test.js +45 -0
  368. package/dist/utils/__tests__/paths.test.js.map +1 -1
  369. package/dist/utils/paths.d.ts +2 -0
  370. package/dist/utils/paths.d.ts.map +1 -1
  371. package/dist/utils/paths.js +22 -7
  372. package/dist/utils/paths.js.map +1 -1
  373. package/dist/verification/__tests__/ci-rust-gates.test.js +1 -1
  374. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  375. package/package.json +4 -2
  376. package/prompts/architect.md +4 -0
  377. package/prompts/code-reviewer.md +3 -0
  378. package/prompts/dependency-expert.md +3 -0
  379. package/prompts/executor.md +5 -0
  380. package/prompts/explore.md +2 -0
  381. package/prompts/planner.md +5 -0
  382. package/prompts/product-analyst.md +8 -8
  383. package/prompts/researcher.md +78 -30
  384. package/prompts/verifier.md +4 -0
  385. package/skills/autoresearch/SKILL.md +68 -0
  386. package/skills/code-review/SKILL.md +94 -28
  387. package/skills/deep-interview/SKILL.md +100 -9
  388. package/skills/help/SKILL.md +3 -1
  389. package/skills/ralplan/SKILL.md +1 -0
  390. package/skills/team/SKILL.md +1 -0
  391. package/skills/ultrawork/SKILL.md +1 -0
  392. package/src/scripts/__tests__/codex-native-hook.test.ts +2373 -692
  393. package/src/scripts/__tests__/postinstall.test.ts +210 -0
  394. package/src/scripts/codex-native-hook.ts +365 -66
  395. package/src/scripts/notify-fallback-watcher.ts +92 -2
  396. package/src/scripts/notify-hook/auto-nudge.ts +89 -20
  397. package/src/scripts/notify-hook/managed-tmux.ts +70 -31
  398. package/src/scripts/notify-hook/ralph-session-resume.ts +1 -1
  399. package/src/scripts/notify-hook.ts +23 -5
  400. package/src/scripts/postinstall-bootstrap.js +23 -0
  401. package/src/scripts/postinstall.ts +161 -0
  402. package/src/scripts/sync-prompt-guidance-fragments.ts +4 -0
  403. package/templates/AGENTS.md +48 -37
  404. package/templates/catalog-manifest.json +7 -0
  405. package/templates/model-instructions/explore-lightweight-AGENTS.md +11 -0
  406. package/templates/model-instructions/sparkshell-lightweight-AGENTS.md +10 -0
@@ -25,6 +25,7 @@ import {
25
25
  maybeNudgeTeamLeader,
26
26
  resolveLeaderStalenessThresholdMs,
27
27
  } from './notify-hook/team-leader-nudge.js';
28
+ import { resolveManagedPaneFromAnchor, resolveManagedSessionPane } from './notify-hook/managed-tmux.js';
28
29
  import { DEFAULT_MARKER } from './tmux-hook-engine.js';
29
30
  import { isTerminalPhase } from './notify-hook/utils.js';
30
31
  import { isSessionStale, isSessionStateAuthoritativeForCwd, readSessionState } from '../hooks/session.js';
@@ -36,6 +37,7 @@ import { listNotifyCanonicalActiveTeams } from './notify-hook/active-team.js';
36
37
  import { sameFilePath } from '../utils/paths.js';
37
38
  import { validateSessionId } from '../mcp/state-paths.js';
38
39
  import { TEAM_NAME_SAFE_PATTERN } from '../team/contracts.js';
40
+ import { shouldContinueRun } from '../runtime/run-loop.js';
39
41
 
40
42
  function argValue(name: string, fallback = ''): string {
41
43
  const idx = process.argv.indexOf(name);
@@ -145,7 +147,7 @@ const watcherOwnerToken = `${process.pid}-${startedAt}-${Math.random().toString(
145
147
  const RALPH_CONTINUE_TEXT = 'Ralph loop active continue';
146
148
  const RALPH_CONTINUE_CADENCE_MS = 60_000;
147
149
  const RALPH_STEER_LOCK_STALE_MS = 30_000;
148
- const RALPH_TERMINAL_PHASES = new Set(['complete', 'failed', 'cancelled']);
150
+ const RALPH_TERMINAL_PHASES = new Set(['blocked_on_user', 'complete', 'failed', 'cancelled']);
149
151
  const RALPH_STARTING_PHASE_TIMEOUT_MS = RALPH_CONTINUE_CADENCE_MS * 2;
150
152
  const QUIET_ONCE_EVENT_TYPES = new Set(['watcher_start', 'watcher_once_complete']);
151
153
 
@@ -474,6 +476,7 @@ function normalizeRalphContinueSteerState(raw: Record<string, unknown> | null |
474
476
  function hasRalphTerminalState(raw: Record<string, unknown> | null | undefined): boolean {
475
477
  if (!raw || typeof raw !== 'object') return true;
476
478
  if (raw.active !== true) return true;
479
+ if (!shouldContinueRun(raw)) return true;
477
480
  const phase = safeString(raw.current_phase).trim().toLowerCase();
478
481
  if (phase && RALPH_TERMINAL_PHASES.has(phase)) return true;
479
482
  if (isStaleRalphStartingPhase(raw)) return true;
@@ -1029,6 +1032,90 @@ async function writePidFileRecord(): Promise<void> {
1029
1032
  await writeFile(pidFilePath, JSON.stringify(nextRecord, null, 2)).catch(() => {});
1030
1033
  }
1031
1034
 
1035
+ async function buildWatcherManagedPayload(): Promise<Record<string, string> | null> {
1036
+ const session = await readSessionState(cwd).catch(() => null);
1037
+ const sessionId = safeString(session?.session_id).trim();
1038
+ if (!sessionId || !session || isSessionStale(session)) return null;
1039
+ return { session_id: sessionId };
1040
+ }
1041
+
1042
+ async function persistReboundRalphPaneState(
1043
+ statePath: string,
1044
+ state: Record<string, unknown> | null,
1045
+ paneId: string,
1046
+ nowIso: string,
1047
+ ): Promise<Record<string, unknown>> {
1048
+ const latestState = await readFile(statePath, 'utf-8')
1049
+ .then((content) => JSON.parse(content) as Record<string, unknown>)
1050
+ .catch(() => null);
1051
+ const nextState = {
1052
+ ...((latestState && typeof latestState === 'object') ? latestState : (state || {})),
1053
+ tmux_pane_id: paneId,
1054
+ tmux_pane_set_at: nowIso,
1055
+ };
1056
+ const tmpPath = `${statePath}.tmp.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}`;
1057
+ await writeFile(tmpPath, JSON.stringify(nextState, null, 2));
1058
+ try {
1059
+ await rename(tmpPath, statePath);
1060
+ } catch (error) {
1061
+ await unlink(tmpPath).catch(() => {});
1062
+ throw error;
1063
+ }
1064
+ return nextState;
1065
+ }
1066
+
1067
+ async function resolveRalphContinuePaneTarget(
1068
+ activeRalph: ActiveModeResult,
1069
+ nowIso: string,
1070
+ ): Promise<{ paneId: string; state: Record<string, unknown> | null; reboundFrom: string }> {
1071
+ const currentState = activeRalph.state && typeof activeRalph.state === 'object'
1072
+ ? activeRalph.state as Record<string, unknown>
1073
+ : null;
1074
+ const anchorPaneId = safeString(currentState?.tmux_pane_id).trim();
1075
+ if (!anchorPaneId) {
1076
+ return {
1077
+ paneId: '',
1078
+ state: currentState,
1079
+ reboundFrom: '',
1080
+ };
1081
+ }
1082
+
1083
+ const managedPayload = await buildWatcherManagedPayload();
1084
+ if (!managedPayload) {
1085
+ return {
1086
+ paneId: anchorPaneId,
1087
+ state: currentState,
1088
+ reboundFrom: '',
1089
+ };
1090
+ }
1091
+
1092
+ let resolvedPaneId = await resolveManagedPaneFromAnchor(anchorPaneId, cwd, managedPayload, { allowTeamWorker: false });
1093
+ if (!resolvedPaneId) {
1094
+ resolvedPaneId = await resolveManagedSessionPane(cwd, managedPayload);
1095
+ }
1096
+ if (!resolvedPaneId) {
1097
+ return {
1098
+ paneId: '',
1099
+ state: currentState,
1100
+ reboundFrom: '',
1101
+ };
1102
+ }
1103
+ if (resolvedPaneId === anchorPaneId) {
1104
+ return {
1105
+ paneId: resolvedPaneId,
1106
+ state: currentState,
1107
+ reboundFrom: '',
1108
+ };
1109
+ }
1110
+
1111
+ const updatedState = await persistReboundRalphPaneState(activeRalph.path, currentState, resolvedPaneId, nowIso);
1112
+ return {
1113
+ paneId: resolvedPaneId,
1114
+ state: updatedState,
1115
+ reboundFrom: anchorPaneId,
1116
+ };
1117
+ }
1118
+
1032
1119
  async function runRalphContinueSteerTick(): Promise<void> {
1033
1120
  const now = Date.now();
1034
1121
  const nowIso = new Date(now).toISOString();
@@ -1088,7 +1175,9 @@ async function runRalphContinueSteerTick(): Promise<void> {
1088
1175
  return { sent: false, skipped: true };
1089
1176
  }
1090
1177
 
1091
- const paneId = safeString(activeRalph.state?.tmux_pane_id).trim();
1178
+ const resolvedPane = await resolveRalphContinuePaneTarget(activeRalph, nowIso);
1179
+ activeRalph.state = resolvedPane.state;
1180
+ const paneId = resolvedPane.paneId;
1092
1181
  if (!paneId) {
1093
1182
  lastRalphContinueSteer.last_reason = 'pane_missing';
1094
1183
  lastRalphContinueSteer.pane_id = '';
@@ -1113,6 +1202,7 @@ async function runRalphContinueSteerTick(): Promise<void> {
1113
1202
  type: 'ralph_continue_steer',
1114
1203
  reason: 'sent',
1115
1204
  pane_id: paneId,
1205
+ rebound_from: resolvedPane.reboundFrom || null,
1116
1206
  state_path: activeRalph.path,
1117
1207
  current_phase: safeString(activeRalph.state?.current_phase) || null,
1118
1208
  cadence_ms: RALPH_CONTINUE_CADENCE_MS,
@@ -21,6 +21,8 @@ import { logTmuxHookEvent } from './log.js';
21
21
  import { evaluatePaneInjectionReadiness, mapPaneInjectionReadinessReason, sendPaneInput } from './team-tmux-guard.js';
22
22
  import { stripOrchestrationIntentTags } from './orchestration-intent.js';
23
23
  import { buildCapturePaneArgv, DEFAULT_MARKER, tmuxHookExplicitlyDisablesInjection } from '../tmux-hook-engine.js';
24
+ import { readAutoresearchCompletionStatus } from '../../autoresearch/skill-validation.js';
25
+ import { persistDeepInterviewModeState } from '../../hooks/keyword-detector.js';
24
26
  import {
25
27
  isManagedOmxSession,
26
28
  resolveManagedCurrentPane,
@@ -36,6 +38,7 @@ export const DEEP_INTERVIEW_INPUT_LOCK_MESSAGE = 'Deep interview is active; auto
36
38
  export const DEFAULT_AUTO_NUDGE_RESPONSE = 'continue with the current task only if it is already authorized';
37
39
  const DEEP_INTERVIEW_ERROR_PATTERNS = [' error', ' failed', ' failure', ' exception', 'unable to continue', 'cannot continue', 'could not continue'];
38
40
  const DEEP_INTERVIEW_ABORT_PATTERNS = ['aborted', 'cancelled', 'canceled'];
41
+ const DEEP_INTERVIEW_SUCCESS_PATTERNS = ['interview completed', 'interview complete', 'interview finished', 'final summary ready'];
39
42
  const DEEP_INTERVIEW_ABORT_INPUTS = new Set(['abort', 'cancel', 'stop']);
40
43
  const DEEP_INTERVIEW_BLOCKED_APPROVAL_PREFIXES = new Set(['next i should']);
41
44
  const SKILL_PHASES = new Set(['planning', 'executing', 'reviewing', 'completing']);
@@ -99,6 +102,10 @@ function hasAnySubstring(text, patterns) {
99
102
  return patterns.some((pattern) => lower.includes(pattern));
100
103
  }
101
104
 
105
+ function looksLikeDeepInterviewSuccess(text) {
106
+ return hasAnySubstring(text, DEEP_INTERVIEW_SUCCESS_PATTERNS);
107
+ }
108
+
102
109
  export function isDeepInterviewAutoApprovalLocked(skillState) {
103
110
  return Boolean(
104
111
  skillState
@@ -188,6 +195,78 @@ async function persistSkillActiveState(stateDir, sessionId, state) {
188
195
  await writeScopedJson(stateDir, SKILL_ACTIVE_STATE_FILE, sessionId, state).catch(() => {});
189
196
  }
190
197
 
198
+ function cloneSkillActiveState(state) {
199
+ if (!state || typeof state !== 'object') return null;
200
+ return {
201
+ ...state,
202
+ input_lock: state.input_lock
203
+ ? {
204
+ ...state.input_lock,
205
+ blocked_inputs: Array.isArray(state.input_lock.blocked_inputs)
206
+ ? [...state.input_lock.blocked_inputs]
207
+ : state.input_lock.blocked_inputs,
208
+ }
209
+ : state.input_lock,
210
+ };
211
+ }
212
+
213
+ export async function syncSkillStateFromTurn(stateDir, payload) {
214
+ const lastMessage = safeString(payload['last-assistant-message'] || payload.last_assistant_message || '');
215
+ const latestUserInput = latestUserInputFromPayload(payload);
216
+ const invocationSessionId = resolveInvocationSessionId(payload);
217
+ let skillState = await loadSkillActiveState(stateDir, invocationSessionId);
218
+ let releaseReason = null;
219
+
220
+ if (!skillState) {
221
+ return { invocationSessionId, skillState: null, releaseReason };
222
+ }
223
+
224
+ const previousSkillState = cloneSkillActiveState(skillState);
225
+ const previousPhase = normalizeSkillPhase(skillState.phase);
226
+ const inferredPhase = inferSkillPhaseFromText(lastMessage, previousPhase);
227
+ const explicitDeepInterviewSuccess = skillState.skill === 'deep-interview' && looksLikeDeepInterviewSuccess(lastMessage);
228
+ const nextPhase = skillState.skill === 'deep-interview'
229
+ && inferredPhase === 'completing'
230
+ && previousPhase !== 'completing'
231
+ && !explicitDeepInterviewSuccess
232
+ ? previousPhase
233
+ : inferredPhase;
234
+ skillState.phase = nextPhase;
235
+ skillState.active = nextPhase !== 'completing';
236
+
237
+ if (skillState.skill === 'autoresearch') {
238
+ const completion = await readAutoresearchCompletionStatus(payload.cwd || process.cwd(), invocationSessionId);
239
+ skillState.validation_mode = completion.validationMode;
240
+ skillState.autoresearch_completion_reason = completion.reason;
241
+ skillState.completion_artifact_path = completion.artifactPath;
242
+ if (completion.complete) {
243
+ skillState.phase = 'completing';
244
+ skillState.active = false;
245
+ } else if (inferredPhase === 'completing') {
246
+ skillState.phase = previousPhase === 'completing' ? 'reviewing' : previousPhase;
247
+ skillState.active = true;
248
+ }
249
+ }
250
+
251
+ const nowIso = new Date().toISOString();
252
+ skillState.updated_at = nowIso;
253
+ releaseReason = inferDeepInterviewReleaseReason({ skillState, latestUserInput, lastMessage });
254
+ if (releaseReason && isDeepInterviewAutoApprovalLocked(skillState)) {
255
+ releaseDeepInterviewInputLock(skillState, releaseReason, nowIso);
256
+ }
257
+ await persistSkillActiveState(stateDir, invocationSessionId, skillState);
258
+
259
+ if (skillState.skill === 'deep-interview' || previousSkillState?.skill === 'deep-interview') {
260
+ await persistDeepInterviewModeState(stateDir, skillState, nowIso, previousSkillState, {
261
+ sessionId: invocationSessionId,
262
+ threadId: safeString(payload?.['thread-id'] || payload?.thread_id || ''),
263
+ turnId: safeString(payload?.['turn-id'] || payload?.turn_id || ''),
264
+ });
265
+ }
266
+
267
+ return { invocationSessionId, skillState, releaseReason };
268
+ }
269
+
191
270
 
192
271
  export async function isDeepInterviewStateActive(stateDir, sessionId) {
193
272
  const modeState = await readScopedJsonIfExists(stateDir, 'deep-interview-state.json', sessionId, null);
@@ -479,8 +558,10 @@ export async function resolveNudgePaneTarget(stateDir: any, cwd = '', payload: a
479
558
  if (!anchoredPane) continue;
480
559
  const managedPane = await resolveManagedPaneFromAnchor(anchoredPane, cwd, payload, { allowTeamWorker });
481
560
  if (managedPane) return managedPane;
482
- const verdict = await verifyManagedPaneTarget(anchoredPane, cwd, payload, { allowTeamWorker });
483
- if (verdict.ok) return anchoredPane;
561
+ if (allowTeamWorker) {
562
+ const verdict = await verifyManagedPaneTarget(anchoredPane, cwd, payload, { allowTeamWorker });
563
+ if (verdict.ok) return anchoredPane;
564
+ }
484
565
  } catch {
485
566
  // skip malformed state
486
567
  }
@@ -516,21 +597,9 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
516
597
  }
517
598
 
518
599
  const lastMessage = safeString(payload['last-assistant-message'] || payload.last_assistant_message || '');
519
- const latestUserInput = latestUserInputFromPayload(payload);
520
- const invocationSessionId = resolveInvocationSessionId(payload);
521
- let skillState = await loadSkillActiveState(stateDir, invocationSessionId);
522
- let releaseReason = null;
600
+ const { invocationSessionId, skillState, releaseReason } = await syncSkillStateFromTurn(stateDir, payload);
523
601
 
524
602
  try {
525
- if (skillState) {
526
- const inferredPhase = inferSkillPhaseFromText(lastMessage, skillState.phase);
527
- skillState.phase = inferredPhase;
528
- skillState.active = inferredPhase !== 'completing';
529
- skillState.updated_at = new Date().toISOString();
530
- releaseReason = inferDeepInterviewReleaseReason({ skillState, latestUserInput, lastMessage });
531
- await persistSkillActiveState(stateDir, invocationSessionId, skillState);
532
- }
533
-
534
603
  const nudgeStatePath = await getScopedStatePath(stateDir, 'auto-nudge-state.json', invocationSessionId);
535
604
  let nudgeState = await readScopedJsonIfExists(stateDir, 'auto-nudge-state.json', invocationSessionId, null);
536
605
  if (!nudgeState || typeof nudgeState !== 'object') {
@@ -618,8 +687,10 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
618
687
  return;
619
688
  }
620
689
 
621
- const deepInterviewLockActive = isDeepInterviewAutoApprovalLocked(skillState) && !releaseReason;
622
- if (deepInterviewLockActive) {
690
+ const blockedAutoApproval = isDeepInterviewAutoApprovalLocked(skillState)
691
+ && !releaseReason
692
+ && isBlockedAutoApprovalInput(effectiveResponse, skillState.input_lock?.blocked_inputs);
693
+ if (blockedAutoApproval) {
623
694
  const blockedMessage = skillState.input_lock?.message || DEEP_INTERVIEW_INPUT_LOCK_MESSAGE;
624
695
  await logTmuxHookEvent(logsDir, {
625
696
  timestamp: new Date().toISOString(),
@@ -628,9 +699,7 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
628
699
  response: effectiveResponse,
629
700
  source,
630
701
  blocked_by: 'deep-interview-lock',
631
- block_kind: isBlockedAutoApprovalInput(effectiveResponse, skillState.input_lock?.blocked_inputs)
632
- ? 'blocked-auto-approval'
633
- : 'input-lock-active',
702
+ block_kind: 'blocked-auto-approval',
634
703
  message: blockedMessage,
635
704
  suppressed: true,
636
705
  }).catch(() => {});
@@ -248,7 +248,7 @@ export async function verifyManagedPaneTarget(paneId: string, cwd: string, paylo
248
248
  }
249
249
 
250
250
 
251
- async function readManagedPaneCommandState(paneTarget: string): Promise<{ currentCommand: string; startCommand: string }> {
251
+ async function readManagedPaneCommandState(paneTarget: string): Promise<{ currentCommand: string; startCommand: string; lookupFailed: boolean }> {
252
252
  try {
253
253
  const [currentResult, startResult] = await Promise.all([
254
254
  runProcess('tmux', ['display-message', '-p', '-t', paneTarget, '#{pane_current_command}'], 2000),
@@ -257,9 +257,10 @@ async function readManagedPaneCommandState(paneTarget: string): Promise<{ curren
257
257
  return {
258
258
  currentCommand: safeString(currentResult.stdout).trim().toLowerCase(),
259
259
  startCommand: safeString(startResult.stdout).trim().toLowerCase(),
260
+ lookupFailed: false,
260
261
  };
261
262
  } catch {
262
- return { currentCommand: '', startCommand: '' };
263
+ return { currentCommand: '', startCommand: '', lookupFailed: true };
263
264
  }
264
265
  }
265
266
 
@@ -268,6 +269,59 @@ function paneLooksLikeManagedAgent({ currentCommand, startCommand }: { currentCo
268
269
  if (startCommand.includes('codex')) return true;
269
270
  return currentCommand === 'codex' || currentCommand === 'node' || currentCommand === 'npx';
270
271
  }
272
+
273
+ function paneLooksLikeRetainableManagedAnchor({ currentCommand, startCommand }: { currentCommand: string; startCommand: string }): boolean {
274
+ if (/\bomx\b.*\bhud\b.*--watch/i.test(startCommand)) return false;
275
+ if (currentCommand === 'codex') return true;
276
+ if ((currentCommand === 'node' || currentCommand === 'npx') && startCommand.includes('codex')) return true;
277
+ return false;
278
+ }
279
+
280
+ function paneLooksLikeDetachedManagedWrapperFallback({ currentCommand, startCommand }: { currentCommand: string; startCommand: string }): boolean {
281
+ if (/\bomx\b.*\bhud\b.*--watch/i.test(startCommand)) return false;
282
+ return currentCommand === 'node' || currentCommand === 'npx';
283
+ }
284
+
285
+ interface ManagedSessionPaneRow {
286
+ paneId: string;
287
+ active: boolean;
288
+ currentCommand: string;
289
+ startCommand: string;
290
+ }
291
+
292
+ function parseManagedSessionPaneRows(stdout: string): ManagedSessionPaneRow[] {
293
+ return safeString(stdout)
294
+ .trim()
295
+ .split('\n')
296
+ .filter(Boolean)
297
+ .map((line) => {
298
+ const [paneId = '', activeRaw = '0', rawCurrentCommand = '', rawStartCommand = ''] = line.split('\t');
299
+ return {
300
+ paneId: safeString(paneId).trim(),
301
+ active: safeString(activeRaw).trim() === '1',
302
+ currentCommand: safeString(rawCurrentCommand).trim().toLowerCase(),
303
+ startCommand: safeString(rawStartCommand).trim().toLowerCase(),
304
+ };
305
+ })
306
+ .filter((row) => row.paneId !== '');
307
+ }
308
+
309
+ function selectManagedSessionPane(
310
+ rows: ManagedSessionPaneRow[],
311
+ { allowWrapperFallback = false }: { allowWrapperFallback?: boolean } = {},
312
+ ): string {
313
+ const nonHudRows = rows.filter((row) => !/\bomx\b.*\bhud\b.*--watch/i.test(row.startCommand));
314
+ const canonicalRows = nonHudRows.filter((row) => paneLooksLikeRetainableManagedAnchor(row));
315
+ const activeCanonical = canonicalRows.find((row) => row.active);
316
+ if (activeCanonical) return activeCanonical.paneId;
317
+ if (canonicalRows[0]?.paneId) return canonicalRows[0].paneId;
318
+ if (!allowWrapperFallback) return '';
319
+
320
+ const wrapperFallbackRows = nonHudRows.filter((row) => paneLooksLikeDetachedManagedWrapperFallback(row));
321
+ const activeWrapperFallback = wrapperFallbackRows.find((row) => row.active);
322
+ if (activeWrapperFallback) return activeWrapperFallback.paneId;
323
+ return wrapperFallbackRows[0]?.paneId || '';
324
+ }
271
325
  export async function resolveManagedCurrentPane(cwd: string, payload: any, { allowTeamWorker = false } = {}): Promise<string> {
272
326
  const paneTarget = safeString(process.env.TMUX_PANE || '').trim();
273
327
  if (!paneTarget) return '';
@@ -286,22 +340,10 @@ export async function resolveManagedSessionPane(cwd: string, payload: any): Prom
286
340
  try {
287
341
  const panesResult = await runProcess(
288
342
  'tmux',
289
- ['list-panes', '-s', '-t', expectedSession, '-F', '#{pane_id}\t#{pane_current_command}\t#{pane_start_command}'],
343
+ ['list-panes', '-s', '-t', expectedSession, '-F', '#{pane_id}\t#{pane_active}\t#{pane_current_command}\t#{pane_start_command}'],
290
344
  2000,
291
345
  );
292
- const panes = safeString(panesResult.stdout)
293
- .trim()
294
- .split('\n')
295
- .filter(Boolean);
296
- for (const line of panes) {
297
- const [candidatePaneId, rawCurrentCommand = '', rawStartCommand = ''] = line.split('\t');
298
- const startCommand = safeString(rawStartCommand).toLowerCase();
299
- const currentCommand = safeString(rawCurrentCommand).trim().toLowerCase();
300
- if (!candidatePaneId) continue;
301
- if (/\bomx\b.*\bhud\b.*--watch/i.test(startCommand)) continue;
302
- if (startCommand.includes('codex')) return candidatePaneId;
303
- if (currentCommand === 'codex') return candidatePaneId;
304
- }
346
+ return selectManagedSessionPane(parseManagedSessionPaneRows(panesResult.stdout));
305
347
  } catch {
306
348
  // best effort only
307
349
  }
@@ -315,29 +357,26 @@ export async function resolveManagedPaneFromAnchor(anchorPane: string, cwd: stri
315
357
  const verdict = await verifyManagedPaneTarget(paneTarget, cwd, payload, { allowTeamWorker });
316
358
  if (!verdict.ok) return '';
317
359
 
360
+ const commandState = await readManagedPaneCommandState(paneTarget);
361
+ if (commandState.lookupFailed) return paneTarget;
362
+ if (paneLooksLikeRetainableManagedAnchor(commandState)) return paneTarget;
363
+
318
364
  try {
319
- const sessionResult = await runProcess('tmux', ['display-message', '-p', '-t', paneTarget, '#S'], 2000);
320
- const sessionName = safeString(sessionResult.stdout).trim();
321
- if (!sessionName) return paneTarget;
365
+ const sessionName = safeString(verdict.paneSessionName || verdict.managedContext?.expectedTmuxSessionName).trim();
366
+ if (!sessionName) return '';
322
367
 
323
368
  const panesResult = await runProcess(
324
369
  'tmux',
325
- ['list-panes', '-s', '-t', sessionName, '-F', '#{pane_id}\t#{pane_current_command}\t#{pane_start_command}'],
370
+ ['list-panes', '-s', '-t', sessionName, '-F', '#{pane_id}\t#{pane_active}\t#{pane_current_command}\t#{pane_start_command}'],
326
371
  2000,
327
372
  );
328
- const panes = safeString(panesResult.stdout).trim().split('\n').filter(Boolean);
329
- for (const line of panes) {
330
- const [candidatePaneId, rawCurrentCommand = '', rawStartCommand = ''] = line.split('\t');
331
- const startCommand = safeString(rawStartCommand).toLowerCase();
332
- const currentCommand = safeString(rawCurrentCommand).trim().toLowerCase();
333
- if (!candidatePaneId) continue;
334
- if (/\bomx\b.*\bhud\b.*--watch/i.test(startCommand)) continue;
335
- if (startCommand.includes('codex')) return candidatePaneId;
336
- if (currentCommand === 'codex') return candidatePaneId;
337
- }
373
+ const selectedPane = selectManagedSessionPane(parseManagedSessionPaneRows(panesResult.stdout), {
374
+ allowWrapperFallback: paneLooksLikeDetachedManagedWrapperFallback(commandState),
375
+ });
376
+ if (selectedPane) return selectedPane;
338
377
  } catch {
339
378
  // best effort only
340
379
  }
341
380
 
342
- return paneTarget;
381
+ return '';
343
382
  }
@@ -7,7 +7,7 @@ import { resolveCodexPane } from '../tmux-hook-engine.js';
7
7
  import { safeString } from './utils.js';
8
8
 
9
9
  const SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
10
- const RALPH_TERMINAL_PHASES = new Set(['complete', 'failed', 'cancelled']);
10
+ const RALPH_TERMINAL_PHASES = new Set(['blocked_on_user', 'complete', 'failed', 'cancelled']);
11
11
  const RALPH_RESUME_LOCK_STALE_MS = 10_000;
12
12
  const RALPH_RESUME_LOCK_TIMEOUT_MS = 5_000;
13
13
  const RALPH_RESUME_LOCK_RETRY_MS = 25;
@@ -40,7 +40,13 @@ import {
40
40
  import { isLeaderStale, resolveLeaderStalenessThresholdMs, maybeNudgeTeamLeader } from './notify-hook/team-leader-nudge.js';
41
41
  import { drainPendingTeamDispatch } from './notify-hook/team-dispatch.js';
42
42
  import { handleTmuxInjection } from './notify-hook/tmux-injection.js';
43
- import { maybeAutoNudge, resolveNudgePaneTarget, isDeepInterviewStateActive } from './notify-hook/auto-nudge.js';
43
+ import {
44
+ maybeAutoNudge,
45
+ resolveNudgePaneTarget,
46
+ isDeepInterviewStateActive,
47
+ isDeepInterviewInputLockActive,
48
+ syncSkillStateFromTurn,
49
+ } from './notify-hook/auto-nudge.js';
44
50
  import { isManagedOmxSession } from './notify-hook/managed-tmux.js';
45
51
  import { logNotifyHookEvent } from './notify-hook/log.js';
46
52
  import { reconcileRalphSessionResume } from './notify-hook/ralph-session-resume.js';
@@ -238,14 +244,19 @@ async function main() {
238
244
  }
239
245
 
240
246
  // 1. Log the turn
247
+ const normalizedInputMessages = normalizeInputMessages(payload);
248
+ const latestInputPreview = safeString(
249
+ normalizedInputMessages.length > 0
250
+ ? normalizedInputMessages[normalizedInputMessages.length - 1]
251
+ : '',
252
+ ).slice(0, 200);
241
253
  const logEntry = {
242
254
  timestamp: new Date().toISOString(),
243
255
  type: payload.type || 'agent-turn-complete',
244
256
  thread_id: payload['thread-id'] || payload.thread_id,
245
257
  turn_id: payload['turn-id'] || payload.turn_id,
246
- input_preview: (payload['input-messages'] || payload.input_messages || [])
247
- .map((m: any) => m.slice(0, 100))
248
- .join('; '),
258
+ input_preview: latestInputPreview,
259
+ input_message_count: normalizedInputMessages.length,
249
260
  output_preview: (payload['last-assistant-message'] || payload.last_assistant_message || '')
250
261
  .slice(0, 200),
251
262
  };
@@ -473,7 +484,14 @@ async function main() {
473
484
  // Non-fatal: keyword detector module may not be built yet
474
485
  }
475
486
 
487
+ try {
488
+ await syncSkillStateFromTurn(stateDir, payload);
489
+ } catch {
490
+ // Non-fatal: lifecycle sync should not block the hook
491
+ }
492
+
476
493
  const deepInterviewStateActive = await isDeepInterviewStateActive(stateDir, getEffectiveSessionId());
494
+ const deepInterviewInputLockActive = await isDeepInterviewInputLockActive(stateDir, getEffectiveSessionId());
477
495
 
478
496
  // 4.55. Notify leader when individual worker transitions to idle (worker session only)
479
497
  if (isTeamWorker && parsedTeamWorker && !deepInterviewStateActive) {
@@ -642,7 +660,7 @@ async function main() {
642
660
 
643
661
  // 9. Auto-nudge: detect Codex stall patterns and automatically send a continuation prompt.
644
662
  // Works for both leader and worker contexts.
645
- if (!deepInterviewStateActive) {
663
+ if (!deepInterviewStateActive || deepInterviewInputLockActive) {
646
664
  try {
647
665
  await maybeAutoNudge({ cwd, stateDir, logsDir, payload });
648
666
  } catch {
@@ -0,0 +1,23 @@
1
+ import { existsSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath, pathToFileURL } from "node:url";
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = dirname(__filename);
7
+ const distScriptPath = join(__dirname, "..", "..", "dist", "scripts", "postinstall.js");
8
+
9
+ if (!existsSync(distScriptPath)) {
10
+ process.exit(0);
11
+ }
12
+
13
+ const moduleUrl = pathToFileURL(distScriptPath).href;
14
+ try {
15
+ const postinstallModule = await import(moduleUrl);
16
+ if (typeof postinstallModule.main === "function") {
17
+ await postinstallModule.main();
18
+ }
19
+ } catch (error) {
20
+ console.warn(
21
+ `[omx] Postinstall bootstrap skipped after a non-fatal error: ${error instanceof Error ? error.message : String(error)}`,
22
+ );
23
+ }