oh-my-codex 0.6.4 → 0.7.0

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 (416) hide show
  1. package/README.md +19 -9
  2. package/bin/omx.js +3 -5
  3. package/dist/agents/__tests__/definitions.test.d.ts +2 -0
  4. package/dist/agents/__tests__/definitions.test.d.ts.map +1 -0
  5. package/dist/agents/__tests__/definitions.test.js +35 -0
  6. package/dist/agents/__tests__/definitions.test.js.map +1 -0
  7. package/dist/agents/__tests__/native-config.test.d.ts +2 -0
  8. package/dist/agents/__tests__/native-config.test.d.ts.map +1 -0
  9. package/dist/agents/__tests__/native-config.test.js +48 -0
  10. package/dist/agents/__tests__/native-config.test.js.map +1 -0
  11. package/dist/catalog/__tests__/schema.test.js +15 -0
  12. package/dist/catalog/__tests__/schema.test.js.map +1 -1
  13. package/dist/catalog/schema.d.ts.map +1 -1
  14. package/dist/catalog/schema.js +6 -0
  15. package/dist/catalog/schema.js.map +1 -1
  16. package/dist/cli/__tests__/catalog-contract.test.d.ts +2 -0
  17. package/dist/cli/__tests__/catalog-contract.test.d.ts.map +1 -0
  18. package/dist/cli/__tests__/catalog-contract.test.js +18 -0
  19. package/dist/cli/__tests__/catalog-contract.test.js.map +1 -0
  20. package/dist/cli/__tests__/doctor-team.test.js +3 -2
  21. package/dist/cli/__tests__/doctor-team.test.js.map +1 -1
  22. package/dist/cli/__tests__/error-handling-warnings.test.d.ts +2 -0
  23. package/dist/cli/__tests__/error-handling-warnings.test.d.ts.map +1 -0
  24. package/dist/cli/__tests__/error-handling-warnings.test.js +35 -0
  25. package/dist/cli/__tests__/error-handling-warnings.test.js.map +1 -0
  26. package/dist/cli/__tests__/index.test.js +81 -8
  27. package/dist/cli/__tests__/index.test.js.map +1 -1
  28. package/dist/cli/__tests__/setup-agents-overwrite.test.d.ts +2 -0
  29. package/dist/cli/__tests__/setup-agents-overwrite.test.d.ts.map +1 -0
  30. package/dist/cli/__tests__/setup-agents-overwrite.test.js +124 -0
  31. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -0
  32. package/dist/cli/__tests__/setup-scope.test.js +79 -21
  33. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  34. package/dist/cli/__tests__/setup-skills-overwrite.test.d.ts +2 -0
  35. package/dist/cli/__tests__/setup-skills-overwrite.test.d.ts.map +1 -0
  36. package/dist/cli/__tests__/setup-skills-overwrite.test.js +32 -0
  37. package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -0
  38. package/dist/cli/__tests__/star-prompt.test.js +74 -0
  39. package/dist/cli/__tests__/star-prompt.test.js.map +1 -1
  40. package/dist/cli/__tests__/team.test.js +8 -0
  41. package/dist/cli/__tests__/team.test.js.map +1 -1
  42. package/dist/cli/doctor.d.ts.map +1 -1
  43. package/dist/cli/doctor.js +75 -18
  44. package/dist/cli/doctor.js.map +1 -1
  45. package/dist/cli/index.d.ts +10 -1
  46. package/dist/cli/index.d.ts.map +1 -1
  47. package/dist/cli/index.js +153 -45
  48. package/dist/cli/index.js.map +1 -1
  49. package/dist/cli/setup.d.ts +2 -1
  50. package/dist/cli/setup.d.ts.map +1 -1
  51. package/dist/cli/setup.js +104 -60
  52. package/dist/cli/setup.js.map +1 -1
  53. package/dist/cli/star-prompt.d.ts +21 -1
  54. package/dist/cli/star-prompt.d.ts.map +1 -1
  55. package/dist/cli/star-prompt.js +34 -13
  56. package/dist/cli/star-prompt.js.map +1 -1
  57. package/dist/cli/team.d.ts.map +1 -1
  58. package/dist/cli/team.js +10 -3
  59. package/dist/cli/team.js.map +1 -1
  60. package/dist/cli/update.d.ts.map +1 -1
  61. package/dist/cli/update.js.map +1 -1
  62. package/dist/config/__tests__/generator-notify.test.js +16 -0
  63. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  64. package/dist/config/__tests__/models.test.js +9 -1
  65. package/dist/config/__tests__/models.test.js.map +1 -1
  66. package/dist/config/generator.js +9 -10
  67. package/dist/config/generator.js.map +1 -1
  68. package/dist/config/models.d.ts +8 -1
  69. package/dist/config/models.d.ts.map +1 -1
  70. package/dist/config/models.js +27 -5
  71. package/dist/config/models.js.map +1 -1
  72. package/dist/hooks/__tests__/agents-overlay.test.js +24 -0
  73. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  74. package/dist/hooks/__tests__/consensus-execution-handoff.test.d.ts +18 -0
  75. package/dist/hooks/__tests__/consensus-execution-handoff.test.d.ts.map +1 -0
  76. package/dist/hooks/__tests__/consensus-execution-handoff.test.js +204 -0
  77. package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -0
  78. package/dist/hooks/__tests__/emulator.test.d.ts +2 -0
  79. package/dist/hooks/__tests__/emulator.test.d.ts.map +1 -0
  80. package/dist/hooks/__tests__/emulator.test.js +47 -0
  81. package/dist/hooks/__tests__/emulator.test.js.map +1 -0
  82. package/dist/hooks/__tests__/keyword-detector.test.js +330 -4
  83. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  84. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +101 -0
  85. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  86. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +13 -7
  87. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
  88. package/dist/hooks/__tests__/notify-hook-modules.test.js +61 -0
  89. package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
  90. package/dist/hooks/__tests__/notify-hook-session-scope.test.js +47 -0
  91. package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
  92. package/dist/hooks/__tests__/notify-hook-worker-idle.test.d.ts +2 -0
  93. package/dist/hooks/__tests__/notify-hook-worker-idle.test.d.ts.map +1 -0
  94. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +560 -0
  95. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -0
  96. package/dist/hooks/__tests__/session.test.d.ts +2 -0
  97. package/dist/hooks/__tests__/session.test.d.ts.map +1 -0
  98. package/dist/hooks/__tests__/session.test.js +161 -0
  99. package/dist/hooks/__tests__/session.test.js.map +1 -0
  100. package/dist/hooks/__tests__/task-size-detector.test.d.ts +2 -0
  101. package/dist/hooks/__tests__/task-size-detector.test.d.ts.map +1 -0
  102. package/dist/hooks/__tests__/task-size-detector.test.js +336 -0
  103. package/dist/hooks/__tests__/task-size-detector.test.js.map +1 -0
  104. package/dist/hooks/__tests__/tmux-hook-engine-types-sync.test.d.ts +2 -0
  105. package/dist/hooks/__tests__/tmux-hook-engine-types-sync.test.d.ts.map +1 -0
  106. package/dist/hooks/__tests__/tmux-hook-engine-types-sync.test.js +24 -0
  107. package/dist/hooks/__tests__/tmux-hook-engine-types-sync.test.js.map +1 -0
  108. package/dist/hooks/agents-overlay.d.ts.map +1 -1
  109. package/dist/hooks/agents-overlay.js +46 -2
  110. package/dist/hooks/agents-overlay.js.map +1 -1
  111. package/dist/hooks/code-simplifier/__tests__/index.test.js +67 -15
  112. package/dist/hooks/code-simplifier/__tests__/index.test.js.map +1 -1
  113. package/dist/hooks/code-simplifier/index.d.ts +10 -4
  114. package/dist/hooks/code-simplifier/index.d.ts.map +1 -1
  115. package/dist/hooks/code-simplifier/index.js +38 -12
  116. package/dist/hooks/code-simplifier/index.js.map +1 -1
  117. package/dist/hooks/codebase-map.d.ts.map +1 -1
  118. package/dist/hooks/codebase-map.js +5 -32
  119. package/dist/hooks/codebase-map.js.map +1 -1
  120. package/dist/hooks/emulator.d.ts.map +1 -1
  121. package/dist/hooks/emulator.js +11 -18
  122. package/dist/hooks/emulator.js.map +1 -1
  123. package/dist/hooks/extensibility/__tests__/dispatcher.test.js +59 -1
  124. package/dist/hooks/extensibility/__tests__/dispatcher.test.js.map +1 -1
  125. package/dist/hooks/extensibility/__tests__/loader.test.js +19 -0
  126. package/dist/hooks/extensibility/__tests__/loader.test.js.map +1 -1
  127. package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
  128. package/dist/hooks/extensibility/dispatcher.js +51 -39
  129. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  130. package/dist/hooks/extensibility/loader.d.ts.map +1 -1
  131. package/dist/hooks/extensibility/loader.js +25 -13
  132. package/dist/hooks/extensibility/loader.js.map +1 -1
  133. package/dist/hooks/extensibility/logging.d.ts.map +1 -1
  134. package/dist/hooks/extensibility/logging.js +6 -1
  135. package/dist/hooks/extensibility/logging.js.map +1 -1
  136. package/dist/hooks/extensibility/sdk.js.map +1 -1
  137. package/dist/hooks/keyword-detector.d.ts +87 -0
  138. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  139. package/dist/hooks/keyword-detector.js +235 -23
  140. package/dist/hooks/keyword-detector.js.map +1 -1
  141. package/dist/hooks/keyword-registry.d.ts +15 -0
  142. package/dist/hooks/keyword-registry.d.ts.map +1 -0
  143. package/dist/hooks/keyword-registry.js +41 -0
  144. package/dist/hooks/keyword-registry.js.map +1 -0
  145. package/dist/hooks/session.d.ts +18 -2
  146. package/dist/hooks/session.d.ts.map +1 -1
  147. package/dist/hooks/session.js +84 -11
  148. package/dist/hooks/session.js.map +1 -1
  149. package/dist/hooks/task-size-detector.d.ts +72 -0
  150. package/dist/hooks/task-size-detector.d.ts.map +1 -0
  151. package/dist/hooks/task-size-detector.js +204 -0
  152. package/dist/hooks/task-size-detector.js.map +1 -0
  153. package/dist/hud/__tests__/colors.test.js +1 -103
  154. package/dist/hud/__tests__/colors.test.js.map +1 -1
  155. package/dist/hud/__tests__/index.test.d.ts +2 -0
  156. package/dist/hud/__tests__/index.test.d.ts.map +1 -0
  157. package/dist/hud/__tests__/index.test.js +131 -0
  158. package/dist/hud/__tests__/index.test.js.map +1 -0
  159. package/dist/hud/__tests__/render.test.js +53 -0
  160. package/dist/hud/__tests__/render.test.js.map +1 -1
  161. package/dist/hud/__tests__/watch.test.d.ts +2 -0
  162. package/dist/hud/__tests__/watch.test.d.ts.map +1 -0
  163. package/dist/hud/__tests__/watch.test.js +63 -0
  164. package/dist/hud/__tests__/watch.test.js.map +1 -0
  165. package/dist/hud/colors.d.ts +2 -9
  166. package/dist/hud/colors.d.ts.map +1 -1
  167. package/dist/hud/colors.js +19 -34
  168. package/dist/hud/colors.js.map +1 -1
  169. package/dist/hud/constants.d.ts +1 -0
  170. package/dist/hud/constants.d.ts.map +1 -1
  171. package/dist/hud/constants.js +1 -0
  172. package/dist/hud/constants.js.map +1 -1
  173. package/dist/hud/index.d.ts +27 -0
  174. package/dist/hud/index.d.ts.map +1 -1
  175. package/dist/hud/index.js +149 -9
  176. package/dist/hud/index.js.map +1 -1
  177. package/dist/hud/render.d.ts.map +1 -1
  178. package/dist/hud/render.js +20 -7
  179. package/dist/hud/render.js.map +1 -1
  180. package/dist/mcp/__tests__/bootstrap.test.d.ts +2 -0
  181. package/dist/mcp/__tests__/bootstrap.test.d.ts.map +1 -0
  182. package/dist/mcp/__tests__/bootstrap.test.js +25 -0
  183. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -0
  184. package/dist/mcp/__tests__/code-intel-server.test.d.ts +2 -0
  185. package/dist/mcp/__tests__/code-intel-server.test.d.ts.map +1 -0
  186. package/dist/mcp/__tests__/code-intel-server.test.js +43 -0
  187. package/dist/mcp/__tests__/code-intel-server.test.js.map +1 -0
  188. package/dist/mcp/__tests__/memory-server.test.d.ts +2 -0
  189. package/dist/mcp/__tests__/memory-server.test.d.ts.map +1 -0
  190. package/dist/mcp/__tests__/memory-server.test.js +34 -0
  191. package/dist/mcp/__tests__/memory-server.test.js.map +1 -0
  192. package/dist/mcp/__tests__/memory-validation.test.d.ts +2 -0
  193. package/dist/mcp/__tests__/memory-validation.test.d.ts.map +1 -0
  194. package/dist/mcp/__tests__/memory-validation.test.js +29 -0
  195. package/dist/mcp/__tests__/memory-validation.test.js.map +1 -0
  196. package/dist/mcp/__tests__/path-traversal.test.js +55 -0
  197. package/dist/mcp/__tests__/path-traversal.test.js.map +1 -1
  198. package/dist/mcp/__tests__/state-paths.test.js +43 -6
  199. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  200. package/dist/mcp/__tests__/state-server-ralph-phase.test.js +50 -0
  201. package/dist/mcp/__tests__/state-server-ralph-phase.test.js.map +1 -1
  202. package/dist/mcp/__tests__/state-server-schema.test.js +3 -7
  203. package/dist/mcp/__tests__/state-server-schema.test.js.map +1 -1
  204. package/dist/mcp/__tests__/state-server.test.js +30 -1
  205. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  206. package/dist/mcp/__tests__/trace-server.test.js +58 -0
  207. package/dist/mcp/__tests__/trace-server.test.js.map +1 -1
  208. package/dist/mcp/bootstrap.d.ts +3 -0
  209. package/dist/mcp/bootstrap.d.ts.map +1 -0
  210. package/dist/mcp/bootstrap.js +13 -0
  211. package/dist/mcp/bootstrap.js.map +1 -0
  212. package/dist/mcp/code-intel-server.d.ts +8 -0
  213. package/dist/mcp/code-intel-server.d.ts.map +1 -1
  214. package/dist/mcp/code-intel-server.js +50 -24
  215. package/dist/mcp/code-intel-server.js.map +1 -1
  216. package/dist/mcp/memory-server.js +34 -13
  217. package/dist/mcp/memory-server.js.map +1 -1
  218. package/dist/mcp/memory-validation.d.ts +9 -0
  219. package/dist/mcp/memory-validation.d.ts.map +1 -0
  220. package/dist/mcp/memory-validation.js +11 -0
  221. package/dist/mcp/memory-validation.js.map +1 -0
  222. package/dist/mcp/state-paths.d.ts +2 -0
  223. package/dist/mcp/state-paths.d.ts.map +1 -1
  224. package/dist/mcp/state-paths.js +83 -12
  225. package/dist/mcp/state-paths.js.map +1 -1
  226. package/dist/mcp/state-server.d.ts.map +1 -1
  227. package/dist/mcp/state-server.js +85 -47
  228. package/dist/mcp/state-server.js.map +1 -1
  229. package/dist/mcp/trace-server.d.ts +16 -0
  230. package/dist/mcp/trace-server.d.ts.map +1 -1
  231. package/dist/mcp/trace-server.js +84 -24
  232. package/dist/mcp/trace-server.js.map +1 -1
  233. package/dist/modes/__tests__/base-ralph-contract.test.d.ts +2 -0
  234. package/dist/modes/__tests__/base-ralph-contract.test.d.ts.map +1 -0
  235. package/dist/modes/__tests__/base-ralph-contract.test.js +49 -0
  236. package/dist/modes/__tests__/base-ralph-contract.test.js.map +1 -0
  237. package/dist/modes/__tests__/base-tmux-pane.test.js +13 -1
  238. package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
  239. package/dist/modes/base.d.ts +0 -4
  240. package/dist/modes/base.d.ts.map +1 -1
  241. package/dist/modes/base.js +31 -11
  242. package/dist/modes/base.js.map +1 -1
  243. package/dist/notifications/__tests__/config.test.js +47 -1
  244. package/dist/notifications/__tests__/config.test.js.map +1 -1
  245. package/dist/notifications/__tests__/formatter.test.js +54 -2
  246. package/dist/notifications/__tests__/formatter.test.js.map +1 -1
  247. package/dist/notifications/__tests__/hook-config.test.d.ts +5 -0
  248. package/dist/notifications/__tests__/hook-config.test.d.ts.map +1 -0
  249. package/dist/notifications/__tests__/hook-config.test.js +139 -0
  250. package/dist/notifications/__tests__/hook-config.test.js.map +1 -0
  251. package/dist/notifications/__tests__/idle-cooldown.test.d.ts +5 -0
  252. package/dist/notifications/__tests__/idle-cooldown.test.d.ts.map +1 -0
  253. package/dist/notifications/__tests__/idle-cooldown.test.js +100 -0
  254. package/dist/notifications/__tests__/idle-cooldown.test.js.map +1 -0
  255. package/dist/notifications/__tests__/notifier.test.js +89 -1
  256. package/dist/notifications/__tests__/notifier.test.js.map +1 -1
  257. package/dist/notifications/__tests__/reply-config.test.d.ts +2 -0
  258. package/dist/notifications/__tests__/reply-config.test.d.ts.map +1 -0
  259. package/dist/notifications/__tests__/reply-config.test.js +79 -0
  260. package/dist/notifications/__tests__/reply-config.test.js.map +1 -0
  261. package/dist/notifications/__tests__/reply-listener.test.js +35 -1
  262. package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
  263. package/dist/notifications/__tests__/session-registry.test.js +40 -0
  264. package/dist/notifications/__tests__/session-registry.test.js.map +1 -1
  265. package/dist/notifications/__tests__/template-engine.test.d.ts +5 -0
  266. package/dist/notifications/__tests__/template-engine.test.d.ts.map +1 -0
  267. package/dist/notifications/__tests__/template-engine.test.js +147 -0
  268. package/dist/notifications/__tests__/template-engine.test.js.map +1 -0
  269. package/dist/notifications/config.d.ts +8 -0
  270. package/dist/notifications/config.d.ts.map +1 -1
  271. package/dist/notifications/config.js +110 -19
  272. package/dist/notifications/config.js.map +1 -1
  273. package/dist/notifications/formatter.d.ts +5 -0
  274. package/dist/notifications/formatter.d.ts.map +1 -1
  275. package/dist/notifications/formatter.js +45 -10
  276. package/dist/notifications/formatter.js.map +1 -1
  277. package/dist/notifications/hook-config-types.d.ts +43 -0
  278. package/dist/notifications/hook-config-types.d.ts.map +1 -0
  279. package/dist/notifications/hook-config-types.js +8 -0
  280. package/dist/notifications/hook-config-types.js.map +1 -0
  281. package/dist/notifications/hook-config.d.ts +40 -0
  282. package/dist/notifications/hook-config.d.ts.map +1 -0
  283. package/dist/notifications/hook-config.js +127 -0
  284. package/dist/notifications/hook-config.js.map +1 -0
  285. package/dist/notifications/idle-cooldown.d.ts +35 -0
  286. package/dist/notifications/idle-cooldown.d.ts.map +1 -0
  287. package/dist/notifications/idle-cooldown.js +108 -0
  288. package/dist/notifications/idle-cooldown.js.map +1 -0
  289. package/dist/notifications/index.d.ts +3 -0
  290. package/dist/notifications/index.d.ts.map +1 -1
  291. package/dist/notifications/index.js +43 -1
  292. package/dist/notifications/index.js.map +1 -1
  293. package/dist/notifications/notifier.d.ts +9 -0
  294. package/dist/notifications/notifier.d.ts.map +1 -1
  295. package/dist/notifications/notifier.js +36 -30
  296. package/dist/notifications/notifier.js.map +1 -1
  297. package/dist/notifications/reply-listener.d.ts +3 -0
  298. package/dist/notifications/reply-listener.d.ts.map +1 -1
  299. package/dist/notifications/reply-listener.js +61 -7
  300. package/dist/notifications/reply-listener.js.map +1 -1
  301. package/dist/notifications/session-registry.d.ts +1 -1
  302. package/dist/notifications/session-registry.d.ts.map +1 -1
  303. package/dist/notifications/session-registry.js +18 -6
  304. package/dist/notifications/session-registry.js.map +1 -1
  305. package/dist/notifications/template-engine.d.ts +34 -0
  306. package/dist/notifications/template-engine.d.ts.map +1 -0
  307. package/dist/notifications/template-engine.js +246 -0
  308. package/dist/notifications/template-engine.js.map +1 -0
  309. package/dist/notifications/types.d.ts +6 -0
  310. package/dist/notifications/types.d.ts.map +1 -1
  311. package/dist/openclaw/__tests__/config.test.d.ts +6 -0
  312. package/dist/openclaw/__tests__/config.test.d.ts.map +1 -0
  313. package/dist/openclaw/__tests__/config.test.js +174 -0
  314. package/dist/openclaw/__tests__/config.test.js.map +1 -0
  315. package/dist/openclaw/__tests__/dispatcher.test.d.ts +5 -0
  316. package/dist/openclaw/__tests__/dispatcher.test.d.ts.map +1 -0
  317. package/dist/openclaw/__tests__/dispatcher.test.js +104 -0
  318. package/dist/openclaw/__tests__/dispatcher.test.js.map +1 -0
  319. package/dist/openclaw/__tests__/index.test.d.ts +6 -0
  320. package/dist/openclaw/__tests__/index.test.d.ts.map +1 -0
  321. package/dist/openclaw/__tests__/index.test.js +131 -0
  322. package/dist/openclaw/__tests__/index.test.js.map +1 -0
  323. package/dist/openclaw/config.d.ts +37 -0
  324. package/dist/openclaw/config.d.ts.map +1 -0
  325. package/dist/openclaw/config.js +106 -0
  326. package/dist/openclaw/config.js.map +1 -0
  327. package/dist/openclaw/dispatcher.d.ts +63 -0
  328. package/dist/openclaw/dispatcher.d.ts.map +1 -0
  329. package/dist/openclaw/dispatcher.js +223 -0
  330. package/dist/openclaw/dispatcher.js.map +1 -0
  331. package/dist/openclaw/index.d.ts +27 -0
  332. package/dist/openclaw/index.d.ts.map +1 -0
  333. package/dist/openclaw/index.js +130 -0
  334. package/dist/openclaw/index.js.map +1 -0
  335. package/dist/openclaw/types.d.ts +105 -0
  336. package/dist/openclaw/types.d.ts.map +1 -0
  337. package/dist/openclaw/types.js +12 -0
  338. package/dist/openclaw/types.js.map +1 -0
  339. package/dist/ralph/contract.d.ts.map +1 -1
  340. package/dist/ralph/contract.js +13 -4
  341. package/dist/ralph/contract.js.map +1 -1
  342. package/dist/team/__tests__/phase-controller.test.js +14 -0
  343. package/dist/team/__tests__/phase-controller.test.js.map +1 -1
  344. package/dist/team/__tests__/runtime.test.js +328 -1
  345. package/dist/team/__tests__/runtime.test.js.map +1 -1
  346. package/dist/team/__tests__/scaling.test.d.ts +2 -0
  347. package/dist/team/__tests__/scaling.test.d.ts.map +1 -0
  348. package/dist/team/__tests__/scaling.test.js +295 -0
  349. package/dist/team/__tests__/scaling.test.js.map +1 -0
  350. package/dist/team/__tests__/state.test.js +62 -1
  351. package/dist/team/__tests__/state.test.js.map +1 -1
  352. package/dist/team/__tests__/tmux-session.test.js +156 -8
  353. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  354. package/dist/team/__tests__/worker-bootstrap.test.js +4 -0
  355. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  356. package/dist/team/contracts.d.ts +14 -0
  357. package/dist/team/contracts.d.ts.map +1 -0
  358. package/dist/team/contracts.js +30 -0
  359. package/dist/team/contracts.js.map +1 -0
  360. package/dist/team/model-contract.d.ts +1 -0
  361. package/dist/team/model-contract.d.ts.map +1 -1
  362. package/dist/team/model-contract.js +5 -1
  363. package/dist/team/model-contract.js.map +1 -1
  364. package/dist/team/phase-controller.d.ts +2 -0
  365. package/dist/team/phase-controller.d.ts.map +1 -1
  366. package/dist/team/phase-controller.js +16 -2
  367. package/dist/team/phase-controller.js.map +1 -1
  368. package/dist/team/runtime.d.ts.map +1 -1
  369. package/dist/team/runtime.js +353 -66
  370. package/dist/team/runtime.js.map +1 -1
  371. package/dist/team/scaling.d.ts +58 -0
  372. package/dist/team/scaling.d.ts.map +1 -0
  373. package/dist/team/scaling.js +319 -0
  374. package/dist/team/scaling.js.map +1 -0
  375. package/dist/team/state.d.ts +10 -2
  376. package/dist/team/state.d.ts.map +1 -1
  377. package/dist/team/state.js +97 -27
  378. package/dist/team/state.js.map +1 -1
  379. package/dist/team/team-ops.d.ts +2 -0
  380. package/dist/team/team-ops.d.ts.map +1 -1
  381. package/dist/team/team-ops.js +4 -0
  382. package/dist/team/team-ops.js.map +1 -1
  383. package/dist/team/tmux-session.d.ts +18 -4
  384. package/dist/team/tmux-session.d.ts.map +1 -1
  385. package/dist/team/tmux-session.js +110 -32
  386. package/dist/team/tmux-session.js.map +1 -1
  387. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  388. package/dist/team/worker-bootstrap.js +20 -0
  389. package/dist/team/worker-bootstrap.js.map +1 -1
  390. package/dist/utils/__tests__/paths.test.js +8 -1
  391. package/dist/utils/__tests__/paths.test.js.map +1 -1
  392. package/dist/utils/paths.d.ts.map +1 -1
  393. package/dist/utils/paths.js +14 -6
  394. package/dist/utils/paths.js.map +1 -1
  395. package/dist/verification/__tests__/verifier.test.js +20 -1
  396. package/dist/verification/__tests__/verifier.test.js.map +1 -1
  397. package/dist/verification/verifier.d.ts +5 -0
  398. package/dist/verification/verifier.d.ts.map +1 -1
  399. package/dist/verification/verifier.js +19 -0
  400. package/dist/verification/verifier.js.map +1 -1
  401. package/package.json +2 -1
  402. package/prompts/architect.md +11 -0
  403. package/prompts/critic.md +14 -1
  404. package/prompts/planner.md +21 -0
  405. package/scripts/notify-hook/auto-nudge.js +80 -1
  406. package/scripts/notify-hook/payload-parser.js +21 -0
  407. package/scripts/notify-hook/team-worker.js +142 -0
  408. package/scripts/notify-hook/tmux-injection.js +3 -3
  409. package/scripts/notify-hook.js +55 -4
  410. package/skills/configure-notifications/SKILL.md +278 -0
  411. package/skills/configure-openclaw/SKILL.md +267 -0
  412. package/skills/configure-slack/SKILL.md +226 -0
  413. package/skills/omx-setup/SKILL.md +14 -19
  414. package/skills/plan/SKILL.md +57 -33
  415. package/skills/ralplan/SKILL.md +107 -21
  416. package/templates/AGENTS.md +11 -3
@@ -2,18 +2,24 @@ import { join, resolve } from 'path';
2
2
  import { existsSync } from 'fs';
3
3
  import { readdir, readFile } from 'fs/promises';
4
4
  import { performance } from 'perf_hooks';
5
- import { sanitizeTeamName, isTmuxAvailable, createTeamSession, resolveTeamWorkerCli, resolveTeamWorkerCliPlan, waitForWorkerReady, sendToWorker, notifyLeaderStatus, isWorkerAlive, getWorkerPanePid, killWorker, killWorkerByPaneId, unregisterResizeHook, destroyTeamSession, listTeamSessions, } from './tmux-session.js';
5
+ import { spawn } from 'child_process';
6
+ import { sanitizeTeamName, isTmuxAvailable, createTeamSession, buildWorkerProcessLaunchSpec, resolveTeamWorkerCli, resolveTeamWorkerCliPlan, resolveTeamWorkerLaunchMode, waitForWorkerReady, sendToWorker, sendToWorkerStdin, notifyLeaderStatus, isWorkerAlive, getWorkerPanePid, killWorker, killWorkerByPaneId, unregisterResizeHook, destroyTeamSession, listTeamSessions, } from './tmux-session.js';
6
7
  import { teamInit as initTeamState, DEFAULT_MAX_WORKERS, teamReadConfig as readTeamConfig, teamWriteWorkerIdentity as writeWorkerIdentity, teamReadWorkerHeartbeat as readWorkerHeartbeat, teamReadWorkerStatus as readWorkerStatus, teamWriteWorkerInbox as writeWorkerInbox, teamCreateTask as createStateTask, teamReadTask as readTask, teamListTasks as listTasks, teamReadManifest as readTeamManifestV2, teamClaimTask as claimTask, teamReleaseTaskClaim as releaseTaskClaim, teamAppendEvent as appendTeamEvent, teamReadTaskApproval as readTaskApproval, teamListMailbox as listMailboxMessages, teamMarkMessageNotified as markMessageNotified, teamCleanup as cleanupTeamState, teamSaveConfig as saveTeamConfig, teamWriteShutdownRequest as writeShutdownRequest, teamReadShutdownAck as readShutdownAck, teamReadMonitorSnapshot as readMonitorSnapshot, teamWriteMonitorSnapshot as writeMonitorSnapshot, teamReadPhase as readTeamPhaseState, teamWritePhase as writeTeamPhaseState, } from './team-ops.js';
7
8
  import { queueInboxInstruction, queueDirectMailboxMessage, queueBroadcastMailboxMessage, } from './mcp-comm.js';
8
9
  import { generateWorkerOverlay, writeTeamWorkerInstructionsFile, removeTeamWorkerInstructionsFile, generateInitialInbox, generateTaskAssignmentInbox, generateShutdownInbox, generateTriggerMessage, generateMailboxTriggerMessage, } from './worker-bootstrap.js';
9
- import { isLowComplexityAgentType, resolveTeamWorkerLaunchArgs, TEAM_LOW_COMPLEXITY_DEFAULT_MODEL, parseTeamWorkerLaunchArgs, splitWorkerLaunchArgs, } from './model-contract.js';
10
+ import { isLowComplexityAgentType, resolveTeamWorkerLaunchArgs, TEAM_LOW_COMPLEXITY_DEFAULT_MODEL, resolveTeamLowComplexityDefaultModel, parseTeamWorkerLaunchArgs, splitWorkerLaunchArgs, } from './model-contract.js';
10
11
  import { inferPhaseTargetFromTaskCounts, reconcilePhaseStateForMonitor } from './phase-controller.js';
11
12
  import { getTeamTmuxSessions } from '../notifications/tmux.js';
13
+ import { hasStructuredVerificationEvidence } from '../verification/verifier.js';
12
14
  import { ensureWorktree, planWorktreeTarget, rollbackProvisionedWorktrees, } from './worktree.js';
13
15
  const MODEL_INSTRUCTIONS_FILE_ENV = 'OMX_MODEL_INSTRUCTIONS_FILE';
14
16
  const TEAM_STATE_ROOT_ENV = 'OMX_TEAM_STATE_ROOT';
15
17
  const TEAM_LEADER_CWD_ENV = 'OMX_TEAM_LEADER_CWD';
18
+ const promptWorkerRegistry = new Map();
16
19
  const previousModelInstructionsFileByTeam = new Map();
20
+ const PROMPT_WORKER_SIGTERM_WAIT_MS = 3_000;
21
+ const PROMPT_WORKER_SIGKILL_WAIT_MS = 2_000;
22
+ const PROMPT_WORKER_EXIT_POLL_MS = 100;
17
23
  function resolveWorkerReadyTimeoutMs(env) {
18
24
  const raw = env.OMX_TEAM_READY_TIMEOUT_MS;
19
25
  const parsed = Number.parseInt(String(raw ?? ''), 10);
@@ -41,16 +47,149 @@ function restoreTeamModelInstructionsFile(teamName) {
41
47
  }
42
48
  delete process.env[MODEL_INSTRUCTIONS_FILE_ENV];
43
49
  }
50
+ function registerPromptWorkerHandle(teamName, workerName, child) {
51
+ const { pid } = child;
52
+ if (!Number.isFinite(pid) || (pid ?? 0) < 1) {
53
+ throw new Error(`failed to spawn prompt worker process for ${workerName}`);
54
+ }
55
+ const processPid = pid;
56
+ const existingTeamHandles = promptWorkerRegistry.get(teamName) ?? new Map();
57
+ existingTeamHandles.set(workerName, { child, pid: processPid });
58
+ promptWorkerRegistry.set(teamName, existingTeamHandles);
59
+ child.on('exit', () => {
60
+ const teamHandles = promptWorkerRegistry.get(teamName);
61
+ if (!teamHandles)
62
+ return;
63
+ teamHandles.delete(workerName);
64
+ if (teamHandles.size === 0)
65
+ promptWorkerRegistry.delete(teamName);
66
+ });
67
+ }
68
+ function getPromptWorkerHandle(teamName, workerName) {
69
+ return promptWorkerRegistry.get(teamName)?.get(workerName) ?? null;
70
+ }
71
+ function removePromptWorkerHandle(teamName, workerName) {
72
+ const teamHandles = promptWorkerRegistry.get(teamName);
73
+ if (!teamHandles)
74
+ return;
75
+ teamHandles.delete(workerName);
76
+ if (teamHandles.size === 0)
77
+ promptWorkerRegistry.delete(teamName);
78
+ }
79
+ function isPidAlive(pid) {
80
+ if (!Number.isFinite(pid) || pid <= 0)
81
+ return false;
82
+ try {
83
+ process.kill(pid, 0);
84
+ return true;
85
+ }
86
+ catch {
87
+ return false;
88
+ }
89
+ }
90
+ async function waitForPidExit(pid, timeoutMs) {
91
+ if (!isPidAlive(pid))
92
+ return true;
93
+ const deadline = Date.now() + Math.max(0, timeoutMs);
94
+ while (Date.now() < deadline) {
95
+ await new Promise((resolve) => setTimeout(resolve, PROMPT_WORKER_EXIT_POLL_MS));
96
+ if (!isPidAlive(pid))
97
+ return true;
98
+ }
99
+ return !isPidAlive(pid);
100
+ }
101
+ async function teardownPromptWorker(teamName, workerName, fallbackPid, cwd, context) {
102
+ const handle = getPromptWorkerHandle(teamName, workerName);
103
+ const pid = Number.isFinite(handle?.pid)
104
+ ? handle.pid
105
+ : (Number.isFinite(fallbackPid) && (fallbackPid ?? 0) > 0 ? fallbackPid : null);
106
+ if (pid === null) {
107
+ removePromptWorkerHandle(teamName, workerName);
108
+ return { terminated: true, forcedKill: false, pid: null };
109
+ }
110
+ try {
111
+ if (handle && handle.child.exitCode === null && !handle.child.killed) {
112
+ handle.child.kill('SIGTERM');
113
+ }
114
+ else {
115
+ process.kill(pid, 'SIGTERM');
116
+ }
117
+ }
118
+ catch {
119
+ // Best effort.
120
+ }
121
+ const exitedOnTerm = await waitForPidExit(pid, PROMPT_WORKER_SIGTERM_WAIT_MS);
122
+ if (exitedOnTerm) {
123
+ removePromptWorkerHandle(teamName, workerName);
124
+ return { terminated: true, forcedKill: false, pid };
125
+ }
126
+ await appendTeamEvent(teamName, {
127
+ type: 'worker_stopped',
128
+ worker: workerName,
129
+ reason: `prompt_force_kill:${context}:pid=${pid}`,
130
+ }, cwd).catch(() => { });
131
+ try {
132
+ if (handle && handle.child.exitCode === null) {
133
+ handle.child.kill('SIGKILL');
134
+ }
135
+ else {
136
+ process.kill(pid, 'SIGKILL');
137
+ }
138
+ }
139
+ catch {
140
+ // Best effort.
141
+ }
142
+ const exitedOnKill = await waitForPidExit(pid, PROMPT_WORKER_SIGKILL_WAIT_MS);
143
+ if (!exitedOnKill) {
144
+ await appendTeamEvent(teamName, {
145
+ type: 'worker_stopped',
146
+ worker: workerName,
147
+ reason: `prompt_teardown_failed:${context}:pid=${pid}`,
148
+ }, cwd).catch(() => { });
149
+ return {
150
+ terminated: false,
151
+ forcedKill: true,
152
+ pid,
153
+ error: 'still_alive_after_sigkill',
154
+ };
155
+ }
156
+ removePromptWorkerHandle(teamName, workerName);
157
+ return { terminated: true, forcedKill: true, pid };
158
+ }
159
+ function isPromptWorkerAlive(config, worker) {
160
+ const handle = getPromptWorkerHandle(config.name, worker.name);
161
+ if (handle?.child.exitCode === null && !handle.child.killed)
162
+ return true;
163
+ if (!Number.isFinite(worker.pid) || (worker.pid ?? 0) <= 0)
164
+ return false;
165
+ try {
166
+ process.kill(worker.pid, 0);
167
+ return true;
168
+ }
169
+ catch {
170
+ return false;
171
+ }
172
+ }
44
173
  export { TEAM_LOW_COMPLEXITY_DEFAULT_MODEL };
45
174
  export function resolveCanonicalTeamStateRoot(leaderCwd) {
46
175
  return resolve(join(leaderCwd, '.omx', 'state'));
47
176
  }
177
+ function spawnPromptWorker(teamName, workerName, workerIndex, workerCwd, launchArgs, workerEnv, workerCli) {
178
+ const processSpec = buildWorkerProcessLaunchSpec(teamName, workerIndex, launchArgs, workerCwd, workerEnv, workerCli);
179
+ const child = spawn(processSpec.command, processSpec.args, {
180
+ cwd: workerCwd,
181
+ env: { ...process.env, ...processSpec.env },
182
+ stdio: ['pipe', 'ignore', 'ignore'],
183
+ });
184
+ registerPromptWorkerHandle(teamName, workerName, child);
185
+ return child;
186
+ }
48
187
  export function resolveWorkerLaunchArgsFromEnv(env, agentType, inheritedLeaderModel) {
49
188
  const inheritedArgs = (typeof inheritedLeaderModel === 'string' && inheritedLeaderModel.trim() !== '')
50
189
  ? ['--model', inheritedLeaderModel.trim()]
51
190
  : [];
52
191
  const fallbackModel = isLowComplexityAgentType(agentType)
53
- ? TEAM_LOW_COMPLEXITY_DEFAULT_MODEL
192
+ ? resolveTeamLowComplexityDefaultModel(env.CODEX_HOME)
54
193
  : undefined;
55
194
  // Detect if an explicit reasoning override exists before resolving (for log source labelling)
56
195
  const preEnvArgs = splitWorkerLaunchArgs(env.OMX_TEAM_WORKER_LAUNCH_ARGS);
@@ -110,13 +249,15 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
110
249
  if (process.env.OMX_TEAM_WORKER) {
111
250
  throw new Error('nested_team_disallowed');
112
251
  }
113
- // tmux-only runtime
114
- if (!isTmuxAvailable()) {
115
- throw new Error('Team mode requires tmux. Install with: apt install tmux / brew install tmux');
116
- }
117
- const displayMode = 'split_pane';
118
- if (!process.env.TMUX) {
119
- throw new Error('Team mode requires running inside tmux current leader pane');
252
+ const workerLaunchMode = resolveTeamWorkerLaunchMode(process.env);
253
+ const displayMode = workerLaunchMode === 'interactive' ? 'split_pane' : 'auto';
254
+ if (workerLaunchMode === 'interactive') {
255
+ if (!isTmuxAvailable()) {
256
+ throw new Error('Team mode requires tmux. Install with: apt install tmux / brew install tmux');
257
+ }
258
+ if (!process.env.TMUX) {
259
+ throw new Error('Team mode requires running inside tmux current leader pane');
260
+ }
120
261
  }
121
262
  const leaderCwd = resolve(cwd);
122
263
  const sanitized = sanitizeTeamName(teamName);
@@ -172,7 +313,7 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
172
313
  const skipWorkerReadyWait = shouldSkipWorkerReadyWait(process.env);
173
314
  try {
174
315
  // 3. Init state directory + config
175
- config = await initTeamState(sanitized, task, agentType, workerCount, leaderCwd, DEFAULT_MAX_WORKERS, { ...process.env, OMX_TEAM_DISPLAY_MODE: displayMode }, {
316
+ config = await initTeamState(sanitized, task, agentType, workerCount, leaderCwd, DEFAULT_MAX_WORKERS, { ...process.env, OMX_TEAM_DISPLAY_MODE: displayMode, OMX_TEAM_WORKER_LAUNCH_MODE: workerLaunchMode }, {
176
317
  leader_cwd: leaderCwd,
177
318
  team_state_root: teamStateRoot,
178
319
  workspace_mode: workspaceMode,
@@ -217,23 +358,44 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
217
358
  env,
218
359
  };
219
360
  });
220
- // 6. Create tmux session with workers
221
- const createdSession = createTeamSession(sanitized, workerCount, leaderCwd, workerLaunchArgs, workerStartups);
222
- sessionName = createdSession.name;
223
- sessionCreated = true;
224
- createdWorkerPaneIds.push(...createdSession.workerPaneIds);
225
- createdLeaderPaneId = createdSession.leaderPaneId;
226
- config.tmux_session = sessionName;
227
- config.leader_pane_id = createdSession.leaderPaneId;
228
- config.hud_pane_id = createdSession.hudPaneId;
229
- config.resize_hook_name = createdSession.resizeHookName;
230
- config.resize_hook_target = createdSession.resizeHookTarget;
361
+ const workerPaneIds = Array.from({ length: workerCount }, () => undefined);
362
+ // 6. Create worker runtime (interactive tmux panes or prompt-mode child processes)
363
+ if (workerLaunchMode === 'interactive') {
364
+ const createdSession = createTeamSession(sanitized, workerCount, leaderCwd, workerLaunchArgs, workerStartups);
365
+ sessionName = createdSession.name;
366
+ sessionCreated = true;
367
+ createdWorkerPaneIds.push(...createdSession.workerPaneIds);
368
+ createdLeaderPaneId = createdSession.leaderPaneId;
369
+ config.tmux_session = sessionName;
370
+ config.leader_pane_id = createdSession.leaderPaneId;
371
+ config.hud_pane_id = createdSession.hudPaneId;
372
+ config.resize_hook_name = createdSession.resizeHookName;
373
+ config.resize_hook_target = createdSession.resizeHookTarget;
374
+ for (let i = 0; i < createdSession.workerPaneIds.length; i++) {
375
+ workerPaneIds[i] = createdSession.workerPaneIds[i];
376
+ }
377
+ }
378
+ else {
379
+ config.tmux_session = `prompt-${sanitized}`;
380
+ config.leader_pane_id = null;
381
+ config.hud_pane_id = null;
382
+ config.resize_hook_name = null;
383
+ config.resize_hook_target = null;
384
+ for (let i = 1; i <= workerCount; i++) {
385
+ const startup = workerStartups[i - 1] || {};
386
+ const workerName = `worker-${i}`;
387
+ const child = spawnPromptWorker(sanitized, workerName, i, startup.cwd || leaderCwd, workerLaunchArgs, startup.env || {}, workerCliPlan[i - 1]);
388
+ if (config.workers[i - 1]) {
389
+ config.workers[i - 1].pid = child.pid;
390
+ }
391
+ }
392
+ }
231
393
  await saveTeamConfig(config, leaderCwd);
232
- // 7. Wait for all workers to be ready, then bootstrap them
394
+ // 7. Wait for all workers to be ready (interactive mode), then bootstrap them
233
395
  const allTasks = await listTasks(sanitized, leaderCwd);
234
396
  for (let i = 1; i <= workerCount; i++) {
235
397
  const workerName = `worker-${i}`;
236
- const paneId = createdSession.workerPaneIds[i - 1];
398
+ const paneId = workerPaneIds[i - 1];
237
399
  const workerWorkspace = workerWorkspaceByName.get(workerName) ?? { cwd: leaderCwd };
238
400
  // Get tasks assigned to this worker
239
401
  const workerTasks = allTasks.filter(t => t.owner === workerName);
@@ -250,10 +412,15 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
250
412
  worktree_detached: workerWorkspace.worktreeDetached,
251
413
  team_state_root: teamStateRoot,
252
414
  };
253
- // Get pane PID and store it
254
- const panePid = getWorkerPanePid(sessionName, i);
255
- if (panePid)
256
- identity.pid = panePid;
415
+ // Get pane PID and store it (interactive mode) or process PID (prompt mode)
416
+ if (workerLaunchMode === 'interactive') {
417
+ const panePid = getWorkerPanePid(sessionName, i);
418
+ if (panePid)
419
+ identity.pid = panePid;
420
+ }
421
+ else if (config.workers[i - 1]?.pid) {
422
+ identity.pid = config.workers[i - 1].pid;
423
+ }
257
424
  if (paneId)
258
425
  identity.pane_id = paneId;
259
426
  if (config.workers[i - 1]) {
@@ -267,7 +434,7 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
267
434
  }
268
435
  await writeWorkerIdentity(sanitized, workerName, identity, leaderCwd);
269
436
  // Wait for worker readiness
270
- if (!skipWorkerReadyWait) {
437
+ if (workerLaunchMode === 'interactive' && !skipWorkerReadyWait) {
271
438
  const ready = waitForWorkerReady(sessionName, i, workerReadyTimeoutMs, paneId);
272
439
  if (!ready) {
273
440
  throw new Error(`Worker ${workerName} did not become ready in tmux session ${sessionName}`);
@@ -350,6 +517,18 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
350
517
  }
351
518
  }
352
519
  }
520
+ if (workerLaunchMode === 'prompt' && config) {
521
+ const promptTeardownFailures = [];
522
+ for (const worker of config.workers) {
523
+ const teardown = await teardownPromptWorker(sanitized, worker.name, worker.pid, leaderCwd, 'startup_rollback');
524
+ if (!teardown.terminated) {
525
+ promptTeardownFailures.push(`${worker.name}:${teardown.error || 'unknown_error'}`);
526
+ }
527
+ }
528
+ if (promptTeardownFailures.length > 0) {
529
+ rollbackErrors.push(`promptTeardown:${promptTeardownFailures.join(',')}`);
530
+ }
531
+ }
353
532
  if (workerInstructionsPath) {
354
533
  try {
355
534
  await removeTeamWorkerInstructionsFile(sanitized, leaderCwd);
@@ -409,7 +588,9 @@ export async function monitorTeam(teamName, cwd) {
409
588
  const recommendations = [];
410
589
  const workerScanStartMs = performance.now();
411
590
  const workerSignals = await Promise.all(config.workers.map(async (worker) => {
412
- const alive = isWorkerAlive(sessionName, worker.index, worker.pane_id);
591
+ const alive = config.worker_launch_mode === 'prompt'
592
+ ? isPromptWorkerAlive(config, worker)
593
+ : isWorkerAlive(sessionName, worker.index, worker.pane_id);
413
594
  const [status, heartbeat] = await Promise.all([
414
595
  readWorkerStatus(sanitized, worker.name, cwd),
415
596
  readWorkerHeartbeat(sanitized, worker.name, cwd),
@@ -461,9 +642,19 @@ export async function monitorTeam(teamName, cwd) {
461
642
  completed: allTasks.filter(t => t.status === 'completed').length,
462
643
  failed: allTasks.filter(t => t.status === 'failed').length,
463
644
  };
645
+ const verificationPendingTasks = allTasks.filter((task) => task.status === 'completed'
646
+ && task.requires_code_change === true
647
+ && !hasStructuredVerificationEvidence(task.result));
648
+ if (verificationPendingTasks.length > 0) {
649
+ for (const task of verificationPendingTasks) {
650
+ recommendations.push(`Verification evidence missing for task-${task.id}; require structured PASS/FAIL evidence before terminal success`);
651
+ }
652
+ }
464
653
  const allTasksTerminal = taskCounts.pending === 0 && taskCounts.blocked === 0 && taskCounts.in_progress === 0;
465
654
  const persistedPhase = await readTeamPhaseState(sanitized, cwd);
466
- const targetPhase = inferPhaseTargetFromTaskCounts(taskCounts);
655
+ const targetPhase = inferPhaseTargetFromTaskCounts(taskCounts, {
656
+ verificationPending: verificationPendingTasks.length > 0,
657
+ });
467
658
  const phaseState = reconcilePhaseStateForMonitor(persistedPhase, targetPhase);
468
659
  await writeTeamPhaseState(sanitized, phaseState, cwd);
469
660
  const phase = phaseState.current_phase;
@@ -600,6 +791,27 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
600
791
  restoreTeamModelInstructionsFile(sanitized);
601
792
  return;
602
793
  }
794
+ if (!force) {
795
+ const allTasks = await listTasks(sanitized, cwd);
796
+ const gate = {
797
+ total: allTasks.length,
798
+ pending: allTasks.filter((t) => t.status === 'pending').length,
799
+ blocked: allTasks.filter((t) => t.status === 'blocked').length,
800
+ in_progress: allTasks.filter((t) => t.status === 'in_progress').length,
801
+ completed: allTasks.filter((t) => t.status === 'completed').length,
802
+ failed: allTasks.filter((t) => t.status === 'failed').length,
803
+ allowed: false,
804
+ };
805
+ gate.allowed = gate.pending === 0 && gate.blocked === 0 && gate.in_progress === 0 && gate.failed === 0;
806
+ await appendTeamEvent(sanitized, {
807
+ type: 'shutdown_gate',
808
+ worker: 'leader-fixed',
809
+ reason: `allowed=${gate.allowed} total=${gate.total} pending=${gate.pending} blocked=${gate.blocked} in_progress=${gate.in_progress} completed=${gate.completed} failed=${gate.failed}`,
810
+ }, cwd).catch(() => { });
811
+ if (!gate.allowed) {
812
+ throw new Error(`shutdown_gate_blocked:pending=${gate.pending},blocked=${gate.blocked},in_progress=${gate.in_progress},failed=${gate.failed}`);
813
+ }
814
+ }
603
815
  const sessionName = config.tmux_session;
604
816
  const shutdownRequestTimes = new Map();
605
817
  // 1. Send shutdown inbox to each worker
@@ -649,13 +861,17 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
649
861
  const detail = rejected.map(r => `${r.worker}:${r.reason}`).join(',');
650
862
  throw new Error(`shutdown_rejected:${detail}`);
651
863
  }
652
- const anyAlive = config.workers.some(w => isWorkerAlive(sessionName, w.index, w.pane_id));
864
+ const anyAlive = config.workers.some((w) => (config.worker_launch_mode === 'prompt'
865
+ ? isPromptWorkerAlive(config, w)
866
+ : isWorkerAlive(sessionName, w.index, w.pane_id)));
653
867
  if (!anyAlive)
654
868
  break;
655
869
  // Sleep 2s
656
870
  await new Promise(resolve => setTimeout(resolve, 2000));
657
871
  }
658
- const anyAliveAfterWait = config.workers.some(w => isWorkerAlive(sessionName, w.index, w.pane_id));
872
+ const anyAliveAfterWait = config.workers.some((w) => (config.worker_launch_mode === 'prompt'
873
+ ? isPromptWorkerAlive(config, w)
874
+ : isWorkerAlive(sessionName, w.index, w.pane_id)));
659
875
  if (anyAliveAfterWait && !force) {
660
876
  // Workers may have accepted shutdown but not exited (Codex TUI requires explicit exit).
661
877
  // In this case, proceed to force kill panes (next step) rather than failing and leaving state around.
@@ -663,38 +879,57 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
663
879
  // 3. Force kill remaining workers
664
880
  const leaderPaneId = config.leader_pane_id;
665
881
  const hudPaneId = config.hud_pane_id;
666
- if (config.resize_hook_name && config.resize_hook_target) {
667
- const unregistered = unregisterResizeHook(config.resize_hook_target, config.resize_hook_name);
668
- if (!unregistered && isTmuxAvailable()) {
669
- const baseSession = sessionName.split(':')[0];
670
- const sessionStillActive = listTeamSessions().includes(baseSession);
671
- if (sessionStillActive) {
672
- throw new Error(`failed to unregister resize hook ${config.resize_hook_name}`);
882
+ if (config.worker_launch_mode === 'interactive') {
883
+ let resizeHookWarning = null;
884
+ if (config.resize_hook_name && config.resize_hook_target) {
885
+ const resizeHookName = config.resize_hook_name;
886
+ const unregistered = unregisterResizeHook(config.resize_hook_target, resizeHookName);
887
+ if (!unregistered && isTmuxAvailable()) {
888
+ const baseSession = sessionName.split(':')[0];
889
+ const sessionStillActive = listTeamSessions().includes(baseSession);
890
+ if (sessionStillActive) {
891
+ resizeHookWarning = `failed to unregister resize hook ${resizeHookName}`;
892
+ }
673
893
  }
674
894
  }
675
- }
676
- config.resize_hook_name = null;
677
- config.resize_hook_target = null;
678
- await saveTeamConfig(config, cwd);
679
- for (const w of config.workers) {
680
- try {
681
- // Guard: never kill the leader's own pane or the HUD pane.
682
- if (leaderPaneId && w.pane_id === leaderPaneId)
683
- continue;
684
- if (hudPaneId && w.pane_id === hudPaneId)
685
- continue;
686
- if (isWorkerAlive(sessionName, w.index, w.pane_id)) {
687
- killWorker(sessionName, w.index, w.pane_id, leaderPaneId ?? undefined);
895
+ config.resize_hook_name = null;
896
+ config.resize_hook_target = null;
897
+ await saveTeamConfig(config, cwd);
898
+ if (resizeHookWarning) {
899
+ console.warn(`[team shutdown] ${sanitized}: ${resizeHookWarning}; continuing teardown`);
900
+ }
901
+ for (const w of config.workers) {
902
+ try {
903
+ // Guard: never kill the leader's own pane or the HUD pane.
904
+ if (leaderPaneId && w.pane_id === leaderPaneId)
905
+ continue;
906
+ if (hudPaneId && w.pane_id === hudPaneId)
907
+ continue;
908
+ if (isWorkerAlive(sessionName, w.index, w.pane_id)) {
909
+ killWorker(sessionName, w.index, w.pane_id, leaderPaneId ?? undefined);
910
+ }
688
911
  }
912
+ catch { /* ignore */ }
913
+ }
914
+ // 4. Destroy tmux session
915
+ if (!sessionName.includes(':')) {
916
+ try {
917
+ destroyTeamSession(sessionName);
918
+ }
919
+ catch { /* ignore */ }
689
920
  }
690
- catch { /* ignore */ }
691
921
  }
692
- // 4. Destroy tmux session
693
- if (!sessionName.includes(':')) {
694
- try {
695
- destroyTeamSession(sessionName);
922
+ else {
923
+ const promptTeardownFailures = [];
924
+ for (const w of config.workers) {
925
+ const teardown = await teardownPromptWorker(sanitized, w.name, w.pid, cwd, 'shutdown');
926
+ if (!teardown.terminated) {
927
+ promptTeardownFailures.push(`${w.name}:${teardown.error || 'unknown_error'}`);
928
+ }
929
+ }
930
+ if (promptTeardownFailures.length > 0) {
931
+ throw new Error(`shutdown_prompt_teardown_failed:${promptTeardownFailures.join(',')}`);
696
932
  }
697
- catch { /* ignore */ }
698
933
  }
699
934
  // 5. Remove team-scoped worker instructions file (no mutation of project AGENTS.md)
700
935
  try {
@@ -713,11 +948,40 @@ export async function resumeTeam(teamName, cwd) {
713
948
  const config = await readTeamConfig(sanitized, cwd);
714
949
  if (!config)
715
950
  return null;
716
- // Check if tmux session still exists
717
- const baseSession = config.tmux_session.split(':')[0];
718
- const teamSessions = getTeamTmuxSessions(sanitized);
719
- if (!teamSessions.includes(baseSession))
720
- return null;
951
+ if (config.worker_launch_mode === 'prompt') {
952
+ const hasLivePromptWorker = config.workers.some((worker) => isPromptWorkerAlive(config, worker));
953
+ if (!hasLivePromptWorker)
954
+ return null;
955
+ const missingHandles = config.workers
956
+ .filter((worker) => {
957
+ if (!Number.isFinite(worker.pid) || (worker.pid ?? 0) <= 0)
958
+ return false;
959
+ try {
960
+ process.kill(worker.pid, 0);
961
+ return true;
962
+ }
963
+ catch {
964
+ return false;
965
+ }
966
+ })
967
+ .filter((worker) => !getPromptWorkerHandle(sanitized, worker.name));
968
+ if (missingHandles.length > 0) {
969
+ const detail = missingHandles.map((worker) => `${worker.name}:${worker.pid ?? 'unknown'}`).join(',');
970
+ await appendTeamEvent(sanitized, {
971
+ type: 'worker_stopped',
972
+ worker: 'leader-fixed',
973
+ reason: `prompt_resume_unavailable:missing_handle:${detail}`,
974
+ }, cwd).catch(() => { });
975
+ return null;
976
+ }
977
+ }
978
+ else {
979
+ // Check if tmux session still exists
980
+ const baseSession = config.tmux_session.split(':')[0];
981
+ const teamSessions = getTeamTmuxSessions(sanitized);
982
+ if (!teamSessions.includes(baseSession))
983
+ return null;
984
+ }
721
985
  return {
722
986
  teamName: sanitized,
723
987
  sanitizedName: sanitized,
@@ -741,12 +1005,21 @@ async function findActiveTeams(cwd, leaderSessionId) {
741
1005
  const manifest = await readTeamManifestV2(teamName, cwd);
742
1006
  if (manifest?.policy?.one_team_per_leader_session === false)
743
1007
  continue;
1008
+ const workerLaunchMode = cfg?.worker_launch_mode
1009
+ ?? manifest?.policy?.worker_launch_mode
1010
+ ?? 'interactive';
744
1011
  const tmuxSession = (manifest?.tmux_session || cfg?.tmux_session || `omx-team-${teamName}`).split(':')[0];
745
1012
  if (leaderSessionId) {
746
1013
  const ownerSessionId = manifest?.leader?.session_id?.trim() ?? '';
747
1014
  if (ownerSessionId && ownerSessionId !== leaderSessionId)
748
1015
  continue;
749
1016
  }
1017
+ if (workerLaunchMode === 'prompt') {
1018
+ if ((cfg?.workers ?? []).some((worker) => isPromptWorkerAlive(cfg, worker))) {
1019
+ active.push(teamName);
1020
+ }
1021
+ continue;
1022
+ }
750
1023
  if (sessions.has(tmuxSession))
751
1024
  active.push(teamName);
752
1025
  }
@@ -816,11 +1089,25 @@ async function emitMonitorDerivedEvents(teamName, tasks, workers, previous, cwd)
816
1089
  }
817
1090
  }
818
1091
  function notifyWorker(config, workerIndex, message, workerPaneId) {
1092
+ const worker = config.workers.find((candidate) => candidate.index === workerIndex);
1093
+ if (!worker)
1094
+ return false;
1095
+ if (config.worker_launch_mode === 'prompt') {
1096
+ const handle = getPromptWorkerHandle(config.name, worker.name);
1097
+ if (!handle)
1098
+ return false;
1099
+ try {
1100
+ sendToWorkerStdin(handle.child.stdin, message);
1101
+ return true;
1102
+ }
1103
+ catch {
1104
+ return false;
1105
+ }
1106
+ }
819
1107
  if (!config.tmux_session || !isTmuxAvailable())
820
1108
  return false;
821
1109
  try {
822
- const workerCli = config.workers.find((worker) => worker.index === workerIndex)?.worker_cli;
823
- sendToWorker(config.tmux_session, workerIndex, message, workerPaneId, workerCli);
1110
+ sendToWorker(config.tmux_session, workerIndex, message, workerPaneId, worker.worker_cli);
824
1111
  return true;
825
1112
  }
826
1113
  catch {