oh-my-codex 0.6.3 → 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 (417) 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 +219 -14
  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 +356 -65
  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 +11 -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 +34 -5
  384. package/dist/team/tmux-session.d.ts.map +1 -1
  385. package/dist/team/tmux-session.js +177 -49
  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/skills/team/SKILL.md +10 -1
  417. 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, 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);
@@ -167,11 +308,12 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
167
308
  let createdLeaderPaneId;
168
309
  let config = null;
169
310
  const workerLaunchArgs = resolveWorkerLaunchArgsFromEnv(process.env, agentType);
311
+ const workerCliPlan = resolveTeamWorkerCliPlan(workerCount, workerLaunchArgs, process.env);
170
312
  const workerReadyTimeoutMs = resolveWorkerReadyTimeoutMs(process.env);
171
313
  const skipWorkerReadyWait = shouldSkipWorkerReadyWait(process.env);
172
314
  try {
173
315
  // 3. Init state directory + config
174
- 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 }, {
175
317
  leader_cwd: leaderCwd,
176
318
  team_state_root: teamStateRoot,
177
319
  workspace_mode: workspaceMode,
@@ -216,23 +358,44 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
216
358
  env,
217
359
  };
218
360
  });
219
- // 6. Create tmux session with workers
220
- const createdSession = createTeamSession(sanitized, workerCount, leaderCwd, workerLaunchArgs, workerStartups);
221
- sessionName = createdSession.name;
222
- sessionCreated = true;
223
- createdWorkerPaneIds.push(...createdSession.workerPaneIds);
224
- createdLeaderPaneId = createdSession.leaderPaneId;
225
- config.tmux_session = sessionName;
226
- config.leader_pane_id = createdSession.leaderPaneId;
227
- config.hud_pane_id = createdSession.hudPaneId;
228
- config.resize_hook_name = createdSession.resizeHookName;
229
- 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
+ }
230
393
  await saveTeamConfig(config, leaderCwd);
231
- // 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
232
395
  const allTasks = await listTasks(sanitized, leaderCwd);
233
396
  for (let i = 1; i <= workerCount; i++) {
234
397
  const workerName = `worker-${i}`;
235
- const paneId = createdSession.workerPaneIds[i - 1];
398
+ const paneId = workerPaneIds[i - 1];
236
399
  const workerWorkspace = workerWorkspaceByName.get(workerName) ?? { cwd: leaderCwd };
237
400
  // Get tasks assigned to this worker
238
401
  const workerTasks = allTasks.filter(t => t.owner === workerName);
@@ -241,6 +404,7 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
241
404
  name: workerName,
242
405
  index: i,
243
406
  role: agentType,
407
+ worker_cli: workerCliPlan[i - 1],
244
408
  assigned_tasks: workerTasks.map(t => t.id),
245
409
  working_dir: workerWorkspace.cwd,
246
410
  worktree_path: workerWorkspace.worktreePath,
@@ -248,14 +412,20 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
248
412
  worktree_detached: workerWorkspace.worktreeDetached,
249
413
  team_state_root: teamStateRoot,
250
414
  };
251
- // Get pane PID and store it
252
- const panePid = getWorkerPanePid(sessionName, i);
253
- if (panePid)
254
- 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
+ }
255
424
  if (paneId)
256
425
  identity.pane_id = paneId;
257
426
  if (config.workers[i - 1]) {
258
427
  config.workers[i - 1].pane_id = paneId;
428
+ config.workers[i - 1].worker_cli = workerCliPlan[i - 1];
259
429
  config.workers[i - 1].working_dir = workerWorkspace.cwd;
260
430
  config.workers[i - 1].worktree_path = workerWorkspace.worktreePath;
261
431
  config.workers[i - 1].worktree_branch = workerWorkspace.worktreeBranch;
@@ -264,7 +434,7 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
264
434
  }
265
435
  await writeWorkerIdentity(sanitized, workerName, identity, leaderCwd);
266
436
  // Wait for worker readiness
267
- if (!skipWorkerReadyWait) {
437
+ if (workerLaunchMode === 'interactive' && !skipWorkerReadyWait) {
268
438
  const ready = waitForWorkerReady(sessionName, i, workerReadyTimeoutMs, paneId);
269
439
  if (!ready) {
270
440
  throw new Error(`Worker ${workerName} did not become ready in tmux session ${sessionName}`);
@@ -347,6 +517,18 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
347
517
  }
348
518
  }
349
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
+ }
350
532
  if (workerInstructionsPath) {
351
533
  try {
352
534
  await removeTeamWorkerInstructionsFile(sanitized, leaderCwd);
@@ -406,7 +588,9 @@ export async function monitorTeam(teamName, cwd) {
406
588
  const recommendations = [];
407
589
  const workerScanStartMs = performance.now();
408
590
  const workerSignals = await Promise.all(config.workers.map(async (worker) => {
409
- 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);
410
594
  const [status, heartbeat] = await Promise.all([
411
595
  readWorkerStatus(sanitized, worker.name, cwd),
412
596
  readWorkerHeartbeat(sanitized, worker.name, cwd),
@@ -458,9 +642,19 @@ export async function monitorTeam(teamName, cwd) {
458
642
  completed: allTasks.filter(t => t.status === 'completed').length,
459
643
  failed: allTasks.filter(t => t.status === 'failed').length,
460
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
+ }
461
653
  const allTasksTerminal = taskCounts.pending === 0 && taskCounts.blocked === 0 && taskCounts.in_progress === 0;
462
654
  const persistedPhase = await readTeamPhaseState(sanitized, cwd);
463
- const targetPhase = inferPhaseTargetFromTaskCounts(taskCounts);
655
+ const targetPhase = inferPhaseTargetFromTaskCounts(taskCounts, {
656
+ verificationPending: verificationPendingTasks.length > 0,
657
+ });
464
658
  const phaseState = reconcilePhaseStateForMonitor(persistedPhase, targetPhase);
465
659
  await writeTeamPhaseState(sanitized, phaseState, cwd);
466
660
  const phase = phaseState.current_phase;
@@ -597,6 +791,27 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
597
791
  restoreTeamModelInstructionsFile(sanitized);
598
792
  return;
599
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
+ }
600
815
  const sessionName = config.tmux_session;
601
816
  const shutdownRequestTimes = new Map();
602
817
  // 1. Send shutdown inbox to each worker
@@ -646,13 +861,17 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
646
861
  const detail = rejected.map(r => `${r.worker}:${r.reason}`).join(',');
647
862
  throw new Error(`shutdown_rejected:${detail}`);
648
863
  }
649
- 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)));
650
867
  if (!anyAlive)
651
868
  break;
652
869
  // Sleep 2s
653
870
  await new Promise(resolve => setTimeout(resolve, 2000));
654
871
  }
655
- 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)));
656
875
  if (anyAliveAfterWait && !force) {
657
876
  // Workers may have accepted shutdown but not exited (Codex TUI requires explicit exit).
658
877
  // In this case, proceed to force kill panes (next step) rather than failing and leaving state around.
@@ -660,38 +879,57 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
660
879
  // 3. Force kill remaining workers
661
880
  const leaderPaneId = config.leader_pane_id;
662
881
  const hudPaneId = config.hud_pane_id;
663
- if (config.resize_hook_name && config.resize_hook_target) {
664
- const unregistered = unregisterResizeHook(config.resize_hook_target, config.resize_hook_name);
665
- if (!unregistered && isTmuxAvailable()) {
666
- const baseSession = sessionName.split(':')[0];
667
- const sessionStillActive = listTeamSessions().includes(baseSession);
668
- if (sessionStillActive) {
669
- 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
+ }
670
893
  }
671
894
  }
672
- }
673
- config.resize_hook_name = null;
674
- config.resize_hook_target = null;
675
- await saveTeamConfig(config, cwd);
676
- for (const w of config.workers) {
677
- try {
678
- // Guard: never kill the leader's own pane or the HUD pane.
679
- if (leaderPaneId && w.pane_id === leaderPaneId)
680
- continue;
681
- if (hudPaneId && w.pane_id === hudPaneId)
682
- continue;
683
- if (isWorkerAlive(sessionName, w.index, w.pane_id)) {
684
- 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
+ }
685
911
  }
912
+ catch { /* ignore */ }
913
+ }
914
+ // 4. Destroy tmux session
915
+ if (!sessionName.includes(':')) {
916
+ try {
917
+ destroyTeamSession(sessionName);
918
+ }
919
+ catch { /* ignore */ }
686
920
  }
687
- catch { /* ignore */ }
688
921
  }
689
- // 4. Destroy tmux session
690
- if (!sessionName.includes(':')) {
691
- try {
692
- 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(',')}`);
693
932
  }
694
- catch { /* ignore */ }
695
933
  }
696
934
  // 5. Remove team-scoped worker instructions file (no mutation of project AGENTS.md)
697
935
  try {
@@ -710,11 +948,40 @@ export async function resumeTeam(teamName, cwd) {
710
948
  const config = await readTeamConfig(sanitized, cwd);
711
949
  if (!config)
712
950
  return null;
713
- // Check if tmux session still exists
714
- const baseSession = config.tmux_session.split(':')[0];
715
- const teamSessions = getTeamTmuxSessions(sanitized);
716
- if (!teamSessions.includes(baseSession))
717
- 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
+ }
718
985
  return {
719
986
  teamName: sanitized,
720
987
  sanitizedName: sanitized,
@@ -738,12 +1005,21 @@ async function findActiveTeams(cwd, leaderSessionId) {
738
1005
  const manifest = await readTeamManifestV2(teamName, cwd);
739
1006
  if (manifest?.policy?.one_team_per_leader_session === false)
740
1007
  continue;
1008
+ const workerLaunchMode = cfg?.worker_launch_mode
1009
+ ?? manifest?.policy?.worker_launch_mode
1010
+ ?? 'interactive';
741
1011
  const tmuxSession = (manifest?.tmux_session || cfg?.tmux_session || `omx-team-${teamName}`).split(':')[0];
742
1012
  if (leaderSessionId) {
743
1013
  const ownerSessionId = manifest?.leader?.session_id?.trim() ?? '';
744
1014
  if (ownerSessionId && ownerSessionId !== leaderSessionId)
745
1015
  continue;
746
1016
  }
1017
+ if (workerLaunchMode === 'prompt') {
1018
+ if ((cfg?.workers ?? []).some((worker) => isPromptWorkerAlive(cfg, worker))) {
1019
+ active.push(teamName);
1020
+ }
1021
+ continue;
1022
+ }
747
1023
  if (sessions.has(tmuxSession))
748
1024
  active.push(teamName);
749
1025
  }
@@ -813,10 +1089,25 @@ async function emitMonitorDerivedEvents(teamName, tasks, workers, previous, cwd)
813
1089
  }
814
1090
  }
815
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
+ }
816
1107
  if (!config.tmux_session || !isTmuxAvailable())
817
1108
  return false;
818
1109
  try {
819
- sendToWorker(config.tmux_session, workerIndex, message, workerPaneId);
1110
+ sendToWorker(config.tmux_session, workerIndex, message, workerPaneId, worker.worker_cli);
820
1111
  return true;
821
1112
  }
822
1113
  catch {