botmux 2.47.0 → 2.47.2

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 (472) hide show
  1. package/README.en.md +10 -5
  2. package/README.md +10 -5
  3. package/dist/adapters/adopt-route.d.ts +63 -0
  4. package/dist/adapters/adopt-route.d.ts.map +1 -0
  5. package/dist/adapters/adopt-route.js +195 -0
  6. package/dist/adapters/adopt-route.js.map +1 -0
  7. package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
  8. package/dist/adapters/backend/tmux-backend.js +11 -0
  9. package/dist/adapters/backend/tmux-backend.js.map +1 -1
  10. package/dist/adapters/backend/tmux-pipe-backend.d.ts +11 -0
  11. package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -1
  12. package/dist/adapters/backend/tmux-pipe-backend.js +17 -1
  13. package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
  14. package/dist/adapters/cli/claude-code.d.ts.map +1 -1
  15. package/dist/adapters/cli/claude-code.js +36 -9
  16. package/dist/adapters/cli/claude-code.js.map +1 -1
  17. package/dist/adapters/cli/coco.d.ts.map +1 -1
  18. package/dist/adapters/cli/coco.js +26 -1
  19. package/dist/adapters/cli/coco.js.map +1 -1
  20. package/dist/adapters/cli/codex-app.d.ts +4 -0
  21. package/dist/adapters/cli/codex-app.d.ts.map +1 -0
  22. package/dist/adapters/cli/codex-app.js +72 -0
  23. package/dist/adapters/cli/codex-app.js.map +1 -0
  24. package/dist/adapters/cli/codex.d.ts.map +1 -1
  25. package/dist/adapters/cli/codex.js +34 -17
  26. package/dist/adapters/cli/codex.js.map +1 -1
  27. package/dist/adapters/cli/cursor.d.ts.map +1 -1
  28. package/dist/adapters/cli/cursor.js +58 -12
  29. package/dist/adapters/cli/cursor.js.map +1 -1
  30. package/dist/adapters/cli/gemini.d.ts.map +1 -1
  31. package/dist/adapters/cli/gemini.js +5 -1
  32. package/dist/adapters/cli/gemini.js.map +1 -1
  33. package/dist/adapters/cli/hermes.d.ts +4 -0
  34. package/dist/adapters/cli/hermes.d.ts.map +1 -0
  35. package/dist/adapters/cli/hermes.js +40 -0
  36. package/dist/adapters/cli/hermes.js.map +1 -0
  37. package/dist/adapters/cli/mira.d.ts +4 -0
  38. package/dist/adapters/cli/mira.d.ts.map +1 -0
  39. package/dist/adapters/cli/mira.js +67 -0
  40. package/dist/adapters/cli/mira.js.map +1 -0
  41. package/dist/adapters/cli/mtr.d.ts +5 -0
  42. package/dist/adapters/cli/mtr.d.ts.map +1 -0
  43. package/dist/adapters/cli/mtr.js +62 -0
  44. package/dist/adapters/cli/mtr.js.map +1 -0
  45. package/dist/adapters/cli/opencode.d.ts.map +1 -1
  46. package/dist/adapters/cli/opencode.js +19 -1
  47. package/dist/adapters/cli/opencode.js.map +1 -1
  48. package/dist/adapters/cli/registry.d.ts +5 -1
  49. package/dist/adapters/cli/registry.d.ts.map +1 -1
  50. package/dist/adapters/cli/registry.js +22 -2
  51. package/dist/adapters/cli/registry.js.map +1 -1
  52. package/dist/adapters/cli/shared-hints.d.ts +1 -1
  53. package/dist/adapters/cli/shared-hints.d.ts.map +1 -1
  54. package/dist/adapters/cli/shared-hints.js +2 -1
  55. package/dist/adapters/cli/shared-hints.js.map +1 -1
  56. package/dist/adapters/cli/types.d.ts +35 -2
  57. package/dist/adapters/cli/types.d.ts.map +1 -1
  58. package/dist/adapters/hook-command.d.ts +18 -0
  59. package/dist/adapters/hook-command.d.ts.map +1 -0
  60. package/dist/adapters/hook-command.js +38 -0
  61. package/dist/adapters/hook-command.js.map +1 -0
  62. package/dist/adapters/hook-installer.d.ts +14 -0
  63. package/dist/adapters/hook-installer.d.ts.map +1 -0
  64. package/dist/adapters/hook-installer.js +192 -0
  65. package/dist/adapters/hook-installer.js.map +1 -0
  66. package/dist/bot-registry.d.ts +59 -0
  67. package/dist/bot-registry.d.ts.map +1 -1
  68. package/dist/bot-registry.js +67 -0
  69. package/dist/bot-registry.js.map +1 -1
  70. package/dist/cli/bots-list-output.d.ts +8 -0
  71. package/dist/cli/bots-list-output.d.ts.map +1 -1
  72. package/dist/cli/bots-list-output.js +9 -0
  73. package/dist/cli/bots-list-output.js.map +1 -1
  74. package/dist/cli.d.ts +15 -1
  75. package/dist/cli.d.ts.map +1 -1
  76. package/dist/cli.js +603 -106
  77. package/dist/cli.js.map +1 -1
  78. package/dist/codex-app-runner.d.ts +3 -0
  79. package/dist/codex-app-runner.d.ts.map +1 -0
  80. package/dist/codex-app-runner.js +512 -0
  81. package/dist/codex-app-runner.js.map +1 -0
  82. package/dist/config.d.ts +11 -2
  83. package/dist/config.d.ts.map +1 -1
  84. package/dist/config.js +17 -4
  85. package/dist/config.js.map +1 -1
  86. package/dist/core/ask-api.d.ts +47 -0
  87. package/dist/core/ask-api.d.ts.map +1 -0
  88. package/dist/core/ask-api.js +139 -0
  89. package/dist/core/ask-api.js.map +1 -0
  90. package/dist/core/ask-args.d.ts +53 -0
  91. package/dist/core/ask-args.d.ts.map +1 -0
  92. package/dist/core/ask-args.js +122 -0
  93. package/dist/core/ask-args.js.map +1 -0
  94. package/dist/core/ask-broker.d.ts +98 -0
  95. package/dist/core/ask-broker.d.ts.map +1 -0
  96. package/dist/core/ask-broker.js +329 -0
  97. package/dist/core/ask-broker.js.map +1 -0
  98. package/dist/core/ask-hook/claude-code.d.ts +50 -0
  99. package/dist/core/ask-hook/claude-code.d.ts.map +1 -0
  100. package/dist/core/ask-hook/claude-code.js +145 -0
  101. package/dist/core/ask-hook/claude-code.js.map +1 -0
  102. package/dist/core/ask-hook/codex.d.ts +43 -0
  103. package/dist/core/ask-hook/codex.d.ts.map +1 -0
  104. package/dist/core/ask-hook/codex.js +69 -0
  105. package/dist/core/ask-hook/codex.js.map +1 -0
  106. package/dist/core/ask-hook/opencode.d.ts +41 -0
  107. package/dist/core/ask-hook/opencode.d.ts.map +1 -0
  108. package/dist/core/ask-hook/opencode.js +108 -0
  109. package/dist/core/ask-hook/opencode.js.map +1 -0
  110. package/dist/core/ask-hook/registry.d.ts +3 -0
  111. package/dist/core/ask-hook/registry.d.ts.map +1 -0
  112. package/dist/core/ask-hook/registry.js +12 -0
  113. package/dist/core/ask-hook/registry.js.map +1 -0
  114. package/dist/core/ask-hook/types.d.ts +26 -0
  115. package/dist/core/ask-hook/types.d.ts.map +1 -0
  116. package/dist/core/ask-hook/types.js +2 -0
  117. package/dist/core/ask-hook/types.js.map +1 -0
  118. package/dist/core/ask-types.d.ts +146 -0
  119. package/dist/core/ask-types.d.ts.map +1 -0
  120. package/dist/core/ask-types.js +18 -0
  121. package/dist/core/ask-types.js.map +1 -0
  122. package/dist/core/command-handler.d.ts +29 -0
  123. package/dist/core/command-handler.d.ts.map +1 -1
  124. package/dist/core/command-handler.js +787 -312
  125. package/dist/core/command-handler.js.map +1 -1
  126. package/dist/core/dashboard-ipc-server.d.ts +2 -0
  127. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  128. package/dist/core/dashboard-ipc-server.js +222 -2
  129. package/dist/core/dashboard-ipc-server.js.map +1 -1
  130. package/dist/core/role-resolver.d.ts +17 -1
  131. package/dist/core/role-resolver.d.ts.map +1 -1
  132. package/dist/core/role-resolver.js +64 -10
  133. package/dist/core/role-resolver.js.map +1 -1
  134. package/dist/core/session-discovery.d.ts.map +1 -1
  135. package/dist/core/session-discovery.js +19 -5
  136. package/dist/core/session-discovery.js.map +1 -1
  137. package/dist/core/session-manager.d.ts +1 -1
  138. package/dist/core/session-manager.d.ts.map +1 -1
  139. package/dist/core/session-manager.js +37 -20
  140. package/dist/core/session-manager.js.map +1 -1
  141. package/dist/core/trigger-session.d.ts +9 -0
  142. package/dist/core/trigger-session.d.ts.map +1 -0
  143. package/dist/core/trigger-session.js +158 -0
  144. package/dist/core/trigger-session.js.map +1 -0
  145. package/dist/core/types.d.ts +5 -0
  146. package/dist/core/types.d.ts.map +1 -1
  147. package/dist/core/types.js.map +1 -1
  148. package/dist/core/worker-pool.d.ts +141 -0
  149. package/dist/core/worker-pool.d.ts.map +1 -1
  150. package/dist/core/worker-pool.js +543 -24
  151. package/dist/core/worker-pool.js.map +1 -1
  152. package/dist/daemon.d.ts.map +1 -1
  153. package/dist/daemon.js +224 -60
  154. package/dist/daemon.js.map +1 -1
  155. package/dist/dashboard/auth.d.ts +6 -1
  156. package/dist/dashboard/auth.d.ts.map +1 -1
  157. package/dist/dashboard/auth.js +9 -1
  158. package/dist/dashboard/auth.js.map +1 -1
  159. package/dist/dashboard/connector-api.d.ts +3 -0
  160. package/dist/dashboard/connector-api.d.ts.map +1 -0
  161. package/dist/dashboard/connector-api.js +351 -0
  162. package/dist/dashboard/connector-api.js.map +1 -0
  163. package/dist/dashboard/federated-group-core.d.ts +54 -0
  164. package/dist/dashboard/federated-group-core.d.ts.map +1 -0
  165. package/dist/dashboard/federated-group-core.js +165 -0
  166. package/dist/dashboard/federated-group-core.js.map +1 -0
  167. package/dist/dashboard/federation-api.d.ts +42 -0
  168. package/dist/dashboard/federation-api.d.ts.map +1 -0
  169. package/dist/dashboard/federation-api.js +408 -0
  170. package/dist/dashboard/federation-api.js.map +1 -0
  171. package/dist/dashboard/federation-spoke-api.d.ts +76 -0
  172. package/dist/dashboard/federation-spoke-api.d.ts.map +1 -0
  173. package/dist/dashboard/federation-spoke-api.js +618 -0
  174. package/dist/dashboard/federation-spoke-api.js.map +1 -0
  175. package/dist/dashboard/team-group.d.ts +18 -0
  176. package/dist/dashboard/team-group.d.ts.map +1 -0
  177. package/dist/dashboard/team-group.js +7 -0
  178. package/dist/dashboard/team-group.js.map +1 -0
  179. package/dist/dashboard/trigger-api.d.ts +13 -0
  180. package/dist/dashboard/trigger-api.d.ts.map +1 -0
  181. package/dist/dashboard/trigger-api.js +77 -0
  182. package/dist/dashboard/trigger-api.js.map +1 -0
  183. package/dist/dashboard/web/app.js +8 -0
  184. package/dist/dashboard/web/app.js.map +1 -1
  185. package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
  186. package/dist/dashboard/web/bot-defaults.js +205 -21
  187. package/dist/dashboard/web/bot-defaults.js.map +1 -1
  188. package/dist/dashboard/web/connectors.d.ts +2 -0
  189. package/dist/dashboard/web/connectors.d.ts.map +1 -0
  190. package/dist/dashboard/web/connectors.js +187 -0
  191. package/dist/dashboard/web/connectors.js.map +1 -0
  192. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  193. package/dist/dashboard/web/i18n.js +43 -5
  194. package/dist/dashboard/web/i18n.js.map +1 -1
  195. package/dist/dashboard/web/sessions.d.ts.map +1 -1
  196. package/dist/dashboard/web/sessions.js +4 -0
  197. package/dist/dashboard/web/sessions.js.map +1 -1
  198. package/dist/dashboard/web/team-federation.d.ts +3 -0
  199. package/dist/dashboard/web/team-federation.d.ts.map +1 -0
  200. package/dist/dashboard/web/team-federation.js +487 -0
  201. package/dist/dashboard/web/team-federation.js.map +1 -0
  202. package/dist/dashboard/web/workflows.js +3 -3
  203. package/dist/dashboard/web/workflows.js.map +1 -1
  204. package/dist/dashboard/webhook-routes.d.ts +19 -0
  205. package/dist/dashboard/webhook-routes.d.ts.map +1 -0
  206. package/dist/dashboard/webhook-routes.js +321 -0
  207. package/dist/dashboard/webhook-routes.js.map +1 -0
  208. package/dist/dashboard/workflow-api.d.ts +8 -1
  209. package/dist/dashboard/workflow-api.d.ts.map +1 -1
  210. package/dist/dashboard/workflow-api.js +19 -4
  211. package/dist/dashboard/workflow-api.js.map +1 -1
  212. package/dist/dashboard-web/app.js +539 -375
  213. package/dist/dashboard-web/index.html +3 -1
  214. package/dist/dashboard-web/style.css +22 -0
  215. package/dist/dashboard.js +199 -2
  216. package/dist/dashboard.js.map +1 -1
  217. package/dist/i18n/en.d.ts.map +1 -1
  218. package/dist/i18n/en.js +104 -11
  219. package/dist/i18n/en.js.map +1 -1
  220. package/dist/i18n/zh.d.ts.map +1 -1
  221. package/dist/i18n/zh.js +104 -11
  222. package/dist/i18n/zh.js.map +1 -1
  223. package/dist/im/lark/ask-card.d.ts +55 -0
  224. package/dist/im/lark/ask-card.d.ts.map +1 -0
  225. package/dist/im/lark/ask-card.js +328 -0
  226. package/dist/im/lark/ask-card.js.map +1 -0
  227. package/dist/im/lark/card-builder.d.ts +108 -3
  228. package/dist/im/lark/card-builder.d.ts.map +1 -1
  229. package/dist/im/lark/card-builder.js +480 -50
  230. package/dist/im/lark/card-builder.js.map +1 -1
  231. package/dist/im/lark/card-handler.d.ts.map +1 -1
  232. package/dist/im/lark/card-handler.js +241 -18
  233. package/dist/im/lark/card-handler.js.map +1 -1
  234. package/dist/im/lark/client.d.ts +83 -0
  235. package/dist/im/lark/client.d.ts.map +1 -1
  236. package/dist/im/lark/client.js +286 -70
  237. package/dist/im/lark/client.js.map +1 -1
  238. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  239. package/dist/im/lark/event-dispatcher.js +29 -4
  240. package/dist/im/lark/event-dispatcher.js.map +1 -1
  241. package/dist/im/lark/grant-command.d.ts +2 -1
  242. package/dist/im/lark/grant-command.d.ts.map +1 -1
  243. package/dist/im/lark/grant-command.js +3 -2
  244. package/dist/im/lark/grant-command.js.map +1 -1
  245. package/dist/im/lark/identity-cache.d.ts.map +1 -1
  246. package/dist/im/lark/identity-cache.js +3 -3
  247. package/dist/im/lark/identity-cache.js.map +1 -1
  248. package/dist/im/lark/md-card.d.ts +20 -2
  249. package/dist/im/lark/md-card.d.ts.map +1 -1
  250. package/dist/im/lark/md-card.js +49 -17
  251. package/dist/im/lark/md-card.js.map +1 -1
  252. package/dist/im/lark/message-parser.d.ts.map +1 -1
  253. package/dist/im/lark/message-parser.js +87 -31
  254. package/dist/im/lark/message-parser.js.map +1 -1
  255. package/dist/im/lark/workflow-card-handler.d.ts +2 -2
  256. package/dist/im/lark/workflow-card-handler.d.ts.map +1 -1
  257. package/dist/im/lark/workflow-card-handler.js +12 -1
  258. package/dist/im/lark/workflow-card-handler.js.map +1 -1
  259. package/dist/im/lark/workflow-progress-card.d.ts.map +1 -1
  260. package/dist/im/lark/workflow-progress-card.js +53 -0
  261. package/dist/im/lark/workflow-progress-card.js.map +1 -1
  262. package/dist/mira-output.d.ts +3 -0
  263. package/dist/mira-output.d.ts.map +1 -0
  264. package/dist/mira-output.js +136 -0
  265. package/dist/mira-output.js.map +1 -0
  266. package/dist/mira-runner.d.ts +3 -0
  267. package/dist/mira-runner.d.ts.map +1 -0
  268. package/dist/mira-runner.js +534 -0
  269. package/dist/mira-runner.js.map +1 -0
  270. package/dist/services/bot-owner-store.d.ts +28 -0
  271. package/dist/services/bot-owner-store.d.ts.map +1 -0
  272. package/dist/services/bot-owner-store.js +82 -0
  273. package/dist/services/bot-owner-store.js.map +1 -0
  274. package/dist/services/bot-profile-store.d.ts +16 -0
  275. package/dist/services/bot-profile-store.d.ts.map +1 -0
  276. package/dist/services/bot-profile-store.js +98 -0
  277. package/dist/services/bot-profile-store.js.map +1 -0
  278. package/dist/services/brand-store.d.ts +15 -0
  279. package/dist/services/brand-store.d.ts.map +1 -0
  280. package/dist/services/brand-store.js +47 -0
  281. package/dist/services/brand-store.js.map +1 -0
  282. package/dist/services/card-prefs-store.d.ts +20 -0
  283. package/dist/services/card-prefs-store.d.ts.map +1 -0
  284. package/dist/services/card-prefs-store.js +82 -0
  285. package/dist/services/card-prefs-store.js.map +1 -0
  286. package/dist/services/codex-bridge-queue.d.ts +1 -0
  287. package/dist/services/codex-bridge-queue.d.ts.map +1 -1
  288. package/dist/services/codex-bridge-queue.js +23 -0
  289. package/dist/services/codex-bridge-queue.js.map +1 -1
  290. package/dist/services/codex-transcript.d.ts +1 -0
  291. package/dist/services/codex-transcript.d.ts.map +1 -1
  292. package/dist/services/codex-transcript.js.map +1 -1
  293. package/dist/services/connector-store.d.ts +58 -0
  294. package/dist/services/connector-store.d.ts.map +1 -0
  295. package/dist/services/connector-store.js +79 -0
  296. package/dist/services/connector-store.js.map +1 -0
  297. package/dist/services/deployment-identity.d.ts +22 -0
  298. package/dist/services/deployment-identity.d.ts.map +1 -0
  299. package/dist/services/deployment-identity.js +67 -0
  300. package/dist/services/deployment-identity.js.map +1 -0
  301. package/dist/services/federation-membership-store.d.ts +23 -0
  302. package/dist/services/federation-membership-store.d.ts.map +1 -0
  303. package/dist/services/federation-membership-store.js +66 -0
  304. package/dist/services/federation-membership-store.js.map +1 -0
  305. package/dist/services/federation-roster.d.ts +54 -0
  306. package/dist/services/federation-roster.d.ts.map +1 -0
  307. package/dist/services/federation-roster.js +51 -0
  308. package/dist/services/federation-roster.js.map +1 -0
  309. package/dist/services/federation-store.d.ts +76 -0
  310. package/dist/services/federation-store.d.ts.map +1 -0
  311. package/dist/services/federation-store.js +133 -0
  312. package/dist/services/federation-store.js.map +1 -0
  313. package/dist/services/grant-store.d.ts +12 -2
  314. package/dist/services/grant-store.d.ts.map +1 -1
  315. package/dist/services/grant-store.js +51 -4
  316. package/dist/services/grant-store.js.map +1 -1
  317. package/dist/services/group-creator.d.ts +10 -0
  318. package/dist/services/group-creator.d.ts.map +1 -1
  319. package/dist/services/group-creator.js +26 -1
  320. package/dist/services/group-creator.js.map +1 -1
  321. package/dist/services/groups-store.d.ts +30 -0
  322. package/dist/services/groups-store.d.ts.map +1 -1
  323. package/dist/services/groups-store.js +85 -12
  324. package/dist/services/groups-store.js.map +1 -1
  325. package/dist/services/hermes-transcript.d.ts +7 -0
  326. package/dist/services/hermes-transcript.d.ts.map +1 -0
  327. package/dist/services/hermes-transcript.js +117 -0
  328. package/dist/services/hermes-transcript.js.map +1 -0
  329. package/dist/services/invite-store.d.ts +28 -0
  330. package/dist/services/invite-store.d.ts.map +1 -0
  331. package/dist/services/invite-store.js +85 -0
  332. package/dist/services/invite-store.js.map +1 -0
  333. package/dist/services/pairing-store.d.ts +47 -0
  334. package/dist/services/pairing-store.d.ts.map +1 -0
  335. package/dist/services/pairing-store.js +132 -0
  336. package/dist/services/pairing-store.js.map +1 -0
  337. package/dist/services/project-scanner.d.ts +10 -0
  338. package/dist/services/project-scanner.d.ts.map +1 -1
  339. package/dist/services/project-scanner.js +11 -0
  340. package/dist/services/project-scanner.js.map +1 -1
  341. package/dist/services/relay-picker.d.ts +22 -0
  342. package/dist/services/relay-picker.d.ts.map +1 -0
  343. package/dist/services/relay-picker.js +62 -0
  344. package/dist/services/relay-picker.js.map +1 -0
  345. package/dist/services/send-policy.d.ts +55 -0
  346. package/dist/services/send-policy.d.ts.map +1 -0
  347. package/dist/services/send-policy.js +47 -0
  348. package/dist/services/send-policy.js.map +1 -0
  349. package/dist/services/session-store.js +1 -1
  350. package/dist/services/session-store.js.map +1 -1
  351. package/dist/services/team-roster.d.ts +38 -0
  352. package/dist/services/team-roster.d.ts.map +1 -0
  353. package/dist/services/team-roster.js +82 -0
  354. package/dist/services/team-roster.js.map +1 -0
  355. package/dist/services/team-store.d.ts +54 -0
  356. package/dist/services/team-store.d.ts.map +1 -0
  357. package/dist/services/team-store.js +156 -0
  358. package/dist/services/team-store.js.map +1 -0
  359. package/dist/services/trigger-log-store.d.ts +46 -0
  360. package/dist/services/trigger-log-store.d.ts.map +1 -0
  361. package/dist/services/trigger-log-store.js +132 -0
  362. package/dist/services/trigger-log-store.js.map +1 -0
  363. package/dist/services/trigger-types.d.ts +57 -0
  364. package/dist/services/trigger-types.d.ts.map +1 -0
  365. package/dist/services/trigger-types.js +28 -0
  366. package/dist/services/trigger-types.js.map +1 -0
  367. package/dist/services/webhook-key.d.ts +16 -0
  368. package/dist/services/webhook-key.d.ts.map +1 -0
  369. package/dist/services/webhook-key.js +123 -0
  370. package/dist/services/webhook-key.js.map +1 -0
  371. package/dist/services/webhook-lifecycle-extractors.d.ts +15 -0
  372. package/dist/services/webhook-lifecycle-extractors.d.ts.map +1 -0
  373. package/dist/services/webhook-lifecycle-extractors.js +59 -0
  374. package/dist/services/webhook-lifecycle-extractors.js.map +1 -0
  375. package/dist/services/webhook-lifecycle-store.d.ts +45 -0
  376. package/dist/services/webhook-lifecycle-store.d.ts.map +1 -0
  377. package/dist/services/webhook-lifecycle-store.js +159 -0
  378. package/dist/services/webhook-lifecycle-store.js.map +1 -0
  379. package/dist/setup/bot-config-editor.d.ts +8 -1
  380. package/dist/setup/bot-config-editor.d.ts.map +1 -1
  381. package/dist/setup/bot-config-editor.js +20 -2
  382. package/dist/setup/bot-config-editor.js.map +1 -1
  383. package/dist/setup/ensure-tmux.d.ts +0 -22
  384. package/dist/setup/ensure-tmux.d.ts.map +1 -1
  385. package/dist/setup/ensure-tmux.js +25 -1
  386. package/dist/setup/ensure-tmux.js.map +1 -1
  387. package/dist/setup/verify-permissions.d.ts.map +1 -1
  388. package/dist/setup/verify-permissions.js +15 -1
  389. package/dist/setup/verify-permissions.js.map +1 -1
  390. package/dist/skills/definitions.d.ts +2 -0
  391. package/dist/skills/definitions.d.ts.map +1 -1
  392. package/dist/skills/definitions.js +178 -12
  393. package/dist/skills/definitions.js.map +1 -1
  394. package/dist/skills/installer.d.ts +34 -0
  395. package/dist/skills/installer.d.ts.map +1 -1
  396. package/dist/skills/installer.js +119 -2
  397. package/dist/skills/installer.js.map +1 -1
  398. package/dist/types.d.ts +29 -0
  399. package/dist/types.d.ts.map +1 -1
  400. package/dist/utils/bot-routing.d.ts +50 -0
  401. package/dist/utils/bot-routing.d.ts.map +1 -1
  402. package/dist/utils/bot-routing.js +83 -0
  403. package/dist/utils/bot-routing.js.map +1 -1
  404. package/dist/utils/daemon-discovery.d.ts +11 -0
  405. package/dist/utils/daemon-discovery.d.ts.map +1 -0
  406. package/dist/utils/daemon-discovery.js +59 -0
  407. package/dist/utils/daemon-discovery.js.map +1 -0
  408. package/dist/utils/user-token.d.ts.map +1 -1
  409. package/dist/utils/user-token.js +0 -2
  410. package/dist/utils/user-token.js.map +1 -1
  411. package/dist/worker.js +233 -51
  412. package/dist/worker.js.map +1 -1
  413. package/dist/workflows/attempt-resume.d.ts.map +1 -1
  414. package/dist/workflows/attempt-resume.js +2 -2
  415. package/dist/workflows/attempt-resume.js.map +1 -1
  416. package/dist/workflows/definition.d.ts +412 -9
  417. package/dist/workflows/definition.d.ts.map +1 -1
  418. package/dist/workflows/definition.js +238 -3
  419. package/dist/workflows/definition.js.map +1 -1
  420. package/dist/workflows/events/payloads.d.ts +114 -11
  421. package/dist/workflows/events/payloads.d.ts.map +1 -1
  422. package/dist/workflows/events/payloads.js +46 -0
  423. package/dist/workflows/events/payloads.js.map +1 -1
  424. package/dist/workflows/events/replay.d.ts +21 -0
  425. package/dist/workflows/events/replay.d.ts.map +1 -1
  426. package/dist/workflows/events/replay.js +103 -0
  427. package/dist/workflows/events/replay.js.map +1 -1
  428. package/dist/workflows/events/schema.d.ts +1301 -606
  429. package/dist/workflows/events/schema.d.ts.map +1 -1
  430. package/dist/workflows/events/schema.js +37 -1
  431. package/dist/workflows/events/schema.js.map +1 -1
  432. package/dist/workflows/events/types.d.ts +5 -1
  433. package/dist/workflows/events/types.d.ts.map +1 -1
  434. package/dist/workflows/loader.d.ts +14 -0
  435. package/dist/workflows/loader.d.ts.map +1 -1
  436. package/dist/workflows/loader.js +27 -0
  437. package/dist/workflows/loader.js.map +1 -1
  438. package/dist/workflows/loop.js +58 -0
  439. package/dist/workflows/loop.js.map +1 -1
  440. package/dist/workflows/ops-projection.d.ts +58 -0
  441. package/dist/workflows/ops-projection.d.ts.map +1 -1
  442. package/dist/workflows/ops-projection.js +74 -0
  443. package/dist/workflows/ops-projection.js.map +1 -1
  444. package/dist/workflows/orchestrator.d.ts +65 -1
  445. package/dist/workflows/orchestrator.d.ts.map +1 -1
  446. package/dist/workflows/orchestrator.js +486 -74
  447. package/dist/workflows/orchestrator.js.map +1 -1
  448. package/dist/workflows/output-binding.d.ts +8 -1
  449. package/dist/workflows/output-binding.d.ts.map +1 -1
  450. package/dist/workflows/output-binding.js +75 -11
  451. package/dist/workflows/output-binding.js.map +1 -1
  452. package/dist/workflows/runtime.d.ts +1 -1
  453. package/dist/workflows/runtime.d.ts.map +1 -1
  454. package/dist/workflows/runtime.js +39 -4
  455. package/dist/workflows/runtime.js.map +1 -1
  456. package/dist/workflows/trigger-from-envelope.d.ts +13 -0
  457. package/dist/workflows/trigger-from-envelope.d.ts.map +1 -0
  458. package/dist/workflows/trigger-from-envelope.js +67 -0
  459. package/dist/workflows/trigger-from-envelope.js.map +1 -0
  460. package/dist/workflows/wait.d.ts +23 -2
  461. package/dist/workflows/wait.d.ts.map +1 -1
  462. package/dist/workflows/wait.js +39 -17
  463. package/dist/workflows/wait.js.map +1 -1
  464. package/package.json +1 -1
  465. package/dist/services/feishu-task-client.d.ts +0 -28
  466. package/dist/services/feishu-task-client.d.ts.map +0 -1
  467. package/dist/services/feishu-task-client.js +0 -123
  468. package/dist/services/feishu-task-client.js.map +0 -1
  469. package/dist/services/task-store.d.ts +0 -37
  470. package/dist/services/task-store.d.ts.map +0 -1
  471. package/dist/services/task-store.js +0 -115
  472. package/dist/services/task-store.js.map +0 -1
@@ -21,6 +21,16 @@
21
21
  *
22
22
  * One node may own at most one gate (before-gate) and one work
23
23
  * activity in v0. After-gates and re-runs are deferred.
24
+ *
25
+ * v0.2 loop body activities are scoped by a `loop::<loopId>.<N>` segment:
26
+ *
27
+ * loop work activity: `<runId>::loop::<loopId>.<N>::work::<bodyNodeId>`
28
+ * loop gate activity: `<runId>::loop::<loopId>.<N>::gate::<bodyNodeId>`
29
+ *
30
+ * `<N>` is the 1-indexed iteration; `<loopId>` is the loop block's
31
+ * nodeId. All segments stay within `SEGMENT_RE` (allows
32
+ * `[A-Za-z0-9._:-]`), so existing `isValidPathSegment` / attempt-
33
+ * sidecar path guards continue to apply without modification.
24
34
  * ────────────────────────────────────────────────────────────────────
25
35
  */
26
36
  import { topologicalOrder, } from './definition.js';
@@ -31,6 +41,58 @@ export function gateActivityId(runId, nodeId) {
31
41
  export function workActivityId(runId, nodeId) {
32
42
  return `${runId}::work::${nodeId}`;
33
43
  }
44
+ // ─── Loop iteration activity IDs (v0.2) ───────────────────────────────────
45
+ //
46
+ // See /tmp/wf-loop-v02.md §4.2 and the top-of-file ASCII spec.
47
+ //
48
+ // Iteration `N` is 1-indexed. We refuse to encode `N < 1` so callers
49
+ // never accidentally emit `loop::foo.0` ids (the iteration counter is a
50
+ // real loop-state position, not a placeholder).
51
+ export function loopWorkActivityId(runId, loopId, iteration, bodyNodeId) {
52
+ if (!Number.isInteger(iteration) || iteration < 1) {
53
+ throw new Error(`loopWorkActivityId: iteration must be a positive integer (got ${iteration})`);
54
+ }
55
+ return `${runId}::loop::${loopId}.${iteration}::work::${bodyNodeId}`;
56
+ }
57
+ export function loopGateActivityId(runId, loopId, iteration, bodyNodeId) {
58
+ if (!Number.isInteger(iteration) || iteration < 1) {
59
+ throw new Error(`loopGateActivityId: iteration must be a positive integer (got ${iteration})`);
60
+ }
61
+ return `${runId}::loop::${loopId}.${iteration}::gate::${bodyNodeId}`;
62
+ }
63
+ const PLAIN_RE = /^([^:]+(?:::?[^:]+)*?)::(work|gate)::([A-Za-z0-9_.-]+)$/;
64
+ const LOOP_RE = /^(.+)::loop::([A-Za-z0-9_.-]+)\.(\d+)::(work|gate)::([A-Za-z0-9_.-]+)$/;
65
+ export function parseActivityId(s) {
66
+ // Loop form first — the `::loop::` segment is unambiguous and would
67
+ // also accidentally satisfy a greedy plain match if we tried plain
68
+ // first (`runId` would absorb `::loop::<id>.<N>::work`).
69
+ const loopMatch = LOOP_RE.exec(s);
70
+ if (loopMatch) {
71
+ const [, runId, loopId, iterStr, activityKind, nodeId] = loopMatch;
72
+ const iteration = Number(iterStr);
73
+ if (!Number.isFinite(iteration) || iteration < 1)
74
+ return undefined;
75
+ return {
76
+ kind: 'loop',
77
+ runId,
78
+ loopId,
79
+ iteration,
80
+ activityKind: activityKind,
81
+ nodeId,
82
+ };
83
+ }
84
+ const plainMatch = PLAIN_RE.exec(s);
85
+ if (plainMatch) {
86
+ const [, runId, activityKind, nodeId] = plainMatch;
87
+ return {
88
+ kind: 'plain',
89
+ runId,
90
+ activityKind: activityKind,
91
+ nodeId,
92
+ };
93
+ }
94
+ return undefined;
95
+ }
34
96
  // ─── Decision function ───────────────────────────────────────────────────
35
97
  /**
36
98
  * Pure decision function. Read-only — never throws on graph cycles
@@ -64,110 +126,309 @@ export function decideNextActions(snapshot, def) {
64
126
  const actions = [];
65
127
  const runId = snapshot.run.runId;
66
128
  const order = topologicalOrder(def);
129
+ // v0.2: compute loop body membership once so the top-level scheduler
130
+ // skips body nodes (they're dispatched by the loop branch only).
131
+ const bodyOwner = buildBodyOwnerMap(def);
67
132
  let failedNodeId;
68
133
  let pendingCount = 0;
134
+ // ─── Loop lifecycle + body dispatch pass (v0.2) ──────────────────────
135
+ //
136
+ // For each loop block in topo order, this pass owns:
137
+ // 1. emitting `startLoop` when the loop's `depends` first go green
138
+ // 2. emitting `startLoopIteration` to open each iteration
139
+ // 3. dispatching body activities under loop-iteration activityIds
140
+ // 4. emitting `finishLoopIteration` once the terminator decision
141
+ // attempt settles (approve or reject)
142
+ // 5. emitting `finishLoop` once iteration loop closes (approved /
143
+ // max-iterations-exceeded / non-terminator body failure / decision
144
+ // humanGate timeout)
145
+ //
146
+ // Per codex round 3: lifecycle actions are returned as the SOLE action
147
+ // of the tick so event ordering — `loopStarted` → `loopIterationStarted`
148
+ // → body attempts — is deterministic and never interleaves with body
149
+ // dispatches. Body dispatches batch up only when no lifecycle action
150
+ // fires this tick.
69
151
  for (const nodeId of order) {
70
152
  const node = def.nodes[nodeId];
71
- const nstatus = snapshot.nodes.get(nodeId)?.status ?? 'idle';
72
- // Already settled at the node level — nothing for us to advance.
73
- if (nstatus === 'succeeded' || nstatus === 'skipped' || nstatus === 'cancelled') {
153
+ if (node.type !== 'loop')
74
154
  continue;
155
+ const loopId = nodeId;
156
+ const loopState = snapshot.loops.get(loopId);
157
+ const depsOk = (node.depends ?? []).every((dep) => snapshot.nodes.get(dep)?.status === 'succeeded');
158
+ // (1) Loop not yet entered.
159
+ if (!loopState) {
160
+ if (!depsOk) {
161
+ pendingCount++;
162
+ continue;
163
+ }
164
+ return [{ kind: 'startLoop', loopId, maxIterations: node.maxIterations }];
75
165
  }
76
- if (nstatus === 'failed') {
77
- failedNodeId = failedNodeId ?? nodeId;
166
+ // Loop already finalized — nothing else to do.
167
+ if (loopState.status !== 'running')
78
168
  continue;
169
+ // (2) Loop started but no iteration opened yet.
170
+ if (loopState.iteration === 0) {
171
+ return [
172
+ { kind: 'startLoopIteration', loopId, iteration: 1, prevResolution: 'initial' },
173
+ ];
79
174
  }
80
- // Dependencies gate dispatch.
81
- const depsOk = (node.depends ?? []).every((dep) => snapshot.nodes.get(dep)?.status === 'succeeded');
82
- if (!depsOk) {
175
+ // Active iteration in progress.
176
+ const currentIter = loopState.iteration;
177
+ const iterState = loopState.iterations[currentIter - 1];
178
+ if (!iterState) {
83
179
  pendingCount++;
84
- continue;
180
+ continue; // anomalous — shouldn't happen, skip safely
85
181
  }
86
- const gateActId = gateActivityId(runId, nodeId);
87
- const workActId = workActivityId(runId, nodeId);
88
- const gateAct = snapshot.activities.get(gateActId);
89
- const workAct = snapshot.activities.get(workActId);
90
- if (node.humanGate) {
91
- if (!gateAct) {
92
- actions.push({
93
- kind: 'dispatchGate',
94
- nodeId,
95
- activityId: gateActId,
96
- humanGate: node.humanGate,
97
- });
98
- pendingCount++;
99
- continue;
182
+ // (4) Current iteration already has a settled resolution:
183
+ if (iterState.status === 'approved') {
184
+ const outputRef = computeLoopOutputRef(snapshot, runId, node, loopId, currentIter);
185
+ return [
186
+ {
187
+ kind: 'finishLoop',
188
+ loopId,
189
+ finalIteration: currentIter,
190
+ resolution: 'approved',
191
+ outputRef,
192
+ },
193
+ ];
194
+ }
195
+ if (iterState.status === 'rejected') {
196
+ if (currentIter < loopState.maxIterations) {
197
+ return [
198
+ {
199
+ kind: 'startLoopIteration',
200
+ loopId,
201
+ iteration: currentIter + 1,
202
+ prevResolution: 'rejected',
203
+ },
204
+ ];
100
205
  }
101
- if (gateAct.status === 'failed') {
102
- actions.push({
103
- kind: 'completeNodeFailed',
104
- nodeId,
105
- lastActivityId: gateActId,
206
+ return [
207
+ {
208
+ kind: 'finishLoop',
209
+ loopId,
210
+ finalIteration: currentIter,
211
+ resolution: 'max-iterations-exceeded',
212
+ errorCode: 'LoopMaxIterationsExceeded',
106
213
  errorClass: 'userFault',
107
- });
214
+ },
215
+ ];
216
+ }
217
+ if (iterState.status === 'failed' || iterState.status === 'cancelled') {
218
+ // Non-terminator failure already finalized this iteration; close
219
+ // the loop block with `body-failed` resolution (codex Step 3
220
+ // review Medium). `cancelled` is reserved for user-initiated
221
+ // cancel; `body-failed` distinguishes "this loop died because a
222
+ // body node failed" from "user clicked cancel".
223
+ return [
224
+ {
225
+ kind: 'finishLoop',
226
+ loopId,
227
+ finalIteration: currentIter,
228
+ resolution: 'body-failed',
229
+ errorCode: 'LoopBodyFailed',
230
+ errorClass: 'fatal',
231
+ },
232
+ ];
233
+ }
234
+ // (3) Iteration is `running` — dispatch body / terminator.
235
+ //
236
+ // Step through body nodes in topo order *within* this iteration.
237
+ // Use a small per-iteration helper map: bodyId → succeeded? so we
238
+ // can resolve intra-body deps without touching the v0.1 node-level
239
+ // status (which never advances for body nodes in v0.2).
240
+ const bodySet = new Set(node.body);
241
+ const bodyDone = new Map();
242
+ let bodyFailureFinish;
243
+ const iterDispatch = [];
244
+ for (const bodyId of orderForBody(order, bodySet)) {
245
+ const bodyNode = def.nodes[bodyId];
246
+ // Intra-body dep check: all body-side deps must have a succeeded
247
+ // activity in *this* iteration; loop-external deps were guaranteed
248
+ // already by the loop block's own `depends` gate.
249
+ const intraDepsOk = (bodyNode.depends ?? [])
250
+ .filter((d) => bodySet.has(d))
251
+ .every((d) => bodyDone.get(d) === true);
252
+ if (!intraDepsOk) {
253
+ pendingCount++;
108
254
  continue;
109
255
  }
110
- if (gateAct.status !== 'succeeded') {
111
- // gate in-flight (acquired / running / waiting) — wait.
112
- pendingCount++;
256
+ const isTerminator = bodyId === node.terminate.node;
257
+ const bodyGateActId = loopGateActivityId(runId, loopId, currentIter, bodyId);
258
+ const bodyWorkActId = loopWorkActivityId(runId, loopId, currentIter, bodyId);
259
+ const advance = decideNodeAdvancement(snapshot, bodyNode, bodyId, bodyGateActId, bodyWorkActId);
260
+ if (advance.isSucceeded) {
261
+ bodyDone.set(bodyId, true);
262
+ if (isTerminator) {
263
+ // Terminator settled — read its wait resolution out of the
264
+ // activity attempt, then return `finishLoopIteration` as the
265
+ // sole action this tick so the iteration advances cleanly.
266
+ const decisionAct = snapshot.activities.get(bodyGateActId);
267
+ const lastAt = decisionAct?.attempts[decisionAct.attempts.length - 1];
268
+ const wait = lastAt?.wait?.resolution;
269
+ if (wait &&
270
+ wait.kind === 'resolved' &&
271
+ (wait.resolution === 'approved' || wait.resolution === 'rejected')) {
272
+ return [
273
+ {
274
+ kind: 'finishLoopIteration',
275
+ loopId,
276
+ iteration: currentIter,
277
+ resolution: wait.resolution,
278
+ decisionActivityId: bodyGateActId,
279
+ waitResolvedEventId: wait.eventId,
280
+ by: wait.by,
281
+ comment: wait.comment,
282
+ },
283
+ ];
284
+ }
285
+ if (wait && wait.kind === 'deadlineExceeded') {
286
+ return [
287
+ {
288
+ kind: 'finishLoopIteration',
289
+ loopId,
290
+ iteration: currentIter,
291
+ resolution: 'rejected',
292
+ decisionActivityId: bodyGateActId,
293
+ waitResolvedEventId: wait.eventId,
294
+ by: 'system',
295
+ timedOut: true,
296
+ },
297
+ ];
298
+ }
299
+ // Decision attempt succeeded but no resolution found — defer.
300
+ pendingCount++;
301
+ }
113
302
  continue;
114
303
  }
115
- // gate cleared → fall through to work dispatch / advancement
304
+ if (advance.isFailed) {
305
+ if (isTerminator) {
306
+ // Terminator humanGate timed out / failed → finish the loop
307
+ // with `timeout` + WaitDeadlineExceeded (codex Step 3 review
308
+ // Medium — schema invariant requires this errorCode pairing).
309
+ bodyFailureFinish = {
310
+ kind: 'finishLoop',
311
+ loopId,
312
+ finalIteration: currentIter,
313
+ resolution: 'timeout',
314
+ errorCode: 'WaitDeadlineExceeded',
315
+ errorClass: 'userFault',
316
+ };
317
+ }
318
+ else {
319
+ // Non-terminator body node failed (subagent crash / hostExecutor
320
+ // error / non-terminator humanGate.reject = fail-run per
321
+ // /tmp/wf-loop-v02.md §10.8) → close as `body-failed`.
322
+ bodyFailureFinish = {
323
+ kind: 'finishLoop',
324
+ loopId,
325
+ finalIteration: currentIter,
326
+ resolution: 'body-failed',
327
+ errorCode: 'LoopBodyFailed',
328
+ errorClass: 'fatal',
329
+ };
330
+ }
331
+ break; // stop iterating body; we'll close the loop instead
332
+ }
333
+ // Not yet succeeded / failed — accumulate dispatch actions.
334
+ iterDispatch.push(...advance.actions);
335
+ pendingCount += advance.actions.length === 0 ? 1 : 0;
116
336
  }
117
- if (!workAct) {
118
- actions.push({
119
- kind: 'dispatchWork',
120
- nodeId,
121
- activityId: workActId,
122
- node,
123
- });
124
- pendingCount++;
337
+ if (bodyFailureFinish) {
338
+ return [bodyFailureFinish];
339
+ }
340
+ // Body dispatches accumulate into the outer `actions` array; the
341
+ // legacy plain-node pass below will also push into it, so a single
342
+ // tick can dispatch multiple body nodes + plain nodes together.
343
+ // Lifecycle actions never reach this branch (they early-return
344
+ // above).
345
+ for (const a of iterDispatch)
346
+ actions.push(a);
347
+ }
348
+ // ─── Loop terminal propagation ────────────────────────────────────────
349
+ //
350
+ // After the loop branch may have emitted `finishLoop`, the next tick
351
+ // sees `loopState.status` settled but the legacy node-level state
352
+ // machine doesn't know about it. Propagate failed / cancelled loop
353
+ // blocks into `failedNodeId` so the actions.length===0 sweep below
354
+ // can emit `completeRunFailed`. Succeeded loop blocks expose their
355
+ // projected output via `outputs[workActivityId(runId, loopId)]` and
356
+ // are handled naturally by the sink sweep.
357
+ for (const [loopId, loopState] of snapshot.loops) {
358
+ if (loopState.status === 'failed' || loopState.status === 'cancelled') {
359
+ failedNodeId = failedNodeId ?? loopId;
360
+ }
361
+ }
362
+ // ─── Plain (non-loop, non-body) node pass ────────────────────────────
363
+ //
364
+ // Same logic as v0.1: dispatch gate → dispatch work → settle node.
365
+ // Skips loop blocks (already handled) + decision nodes (only valid in
366
+ // loop body) + body nodes (dispatched only by the loop branch above).
367
+ for (const nodeId of order) {
368
+ const node = def.nodes[nodeId];
369
+ if (node.type === 'loop')
370
+ continue;
371
+ if (node.type === 'decision')
372
+ continue; // validateLoopBlocks guarantees in-body
373
+ if (bodyOwner.has(nodeId))
374
+ continue; // dispatched by loop branch
375
+ const nstatus = snapshot.nodes.get(nodeId)?.status ?? 'idle';
376
+ if (nstatus === 'succeeded' || nstatus === 'skipped' || nstatus === 'cancelled') {
125
377
  continue;
126
378
  }
127
- if (workAct.status === 'succeeded') {
128
- const output = snapshot.outputs.get(workActId);
129
- if (output) {
130
- actions.push({
131
- kind: 'completeNodeSucceeded',
132
- nodeId,
133
- lastActivityId: workActId,
134
- outputRef: output,
135
- });
136
- }
137
- else {
138
- pendingCount++;
379
+ if (nstatus === 'failed') {
380
+ failedNodeId = failedNodeId ?? nodeId;
381
+ continue;
382
+ }
383
+ // Dependencies gate dispatch. Loop blocks are treated as "succeeded"
384
+ // for the purpose of downstream deps when their snapshot status is
385
+ // 'succeeded'. This is how `${review-loop.output.x}` and direct
386
+ // body-node refs become resolvable for downstream plain nodes.
387
+ const depsOk = (node.depends ?? []).every((dep) => {
388
+ const depNode = def.nodes[dep];
389
+ if (depNode?.type === 'loop') {
390
+ return snapshot.loops.get(dep)?.status === 'succeeded';
139
391
  }
392
+ return snapshot.nodes.get(dep)?.status === 'succeeded';
393
+ });
394
+ if (!depsOk) {
395
+ pendingCount++;
140
396
  continue;
141
397
  }
142
- if (workAct.status === 'failed' || workAct.status === 'timedOut') {
143
- // activityTimedOut payload pins errorClass='retryable' (spec §2.1),
144
- // but replay doesn't propagate it onto the attempt's `error` field —
145
- // it only records `runningMs`. Special-case here so the orchestrator
146
- // doesn't silently upgrade the class to fatal via `deriveErrorClass`.
147
- const errorClass = workAct.status === 'timedOut' ? 'retryable' : deriveErrorClass(workAct);
148
- actions.push({
149
- kind: 'completeNodeFailed',
150
- nodeId,
151
- lastActivityId: workActId,
152
- errorClass,
153
- });
398
+ const gateActId = gateActivityId(runId, nodeId);
399
+ const workActId = workActivityId(runId, nodeId);
400
+ const advance = decideNodeAdvancement(snapshot, node, nodeId, gateActId, workActId);
401
+ if (advance.isSucceeded) {
402
+ for (const a of advance.actions)
403
+ actions.push(a);
154
404
  continue;
155
405
  }
156
- // running / waiting / acquired / effectAttempting — pending.
157
- pendingCount++;
406
+ if (advance.isFailed) {
407
+ for (const a of advance.actions)
408
+ actions.push(a);
409
+ // completeNodeFailed action records nodeId in actions; the
410
+ // downstream sweep uses node.status='failed' from the next
411
+ // replay to fail the run.
412
+ continue;
413
+ }
414
+ for (const a of advance.actions)
415
+ actions.push(a);
416
+ if (advance.actions.length === 0)
417
+ pendingCount++;
158
418
  }
159
419
  if (actions.length === 0) {
160
420
  if (failedNodeId) {
161
- // Fail-fast: a node terminal-failed. Any pending downstream nodes
162
- // are stuck on the failed dep and will never advance; in-flight
163
- // peer nodes are caller's problem to cancel-fanout if they want.
164
421
  actions.push({ kind: 'completeRunFailed', failedNodeId });
165
422
  }
166
423
  else if (pendingCount === 0) {
167
424
  const sinks = findSinks(def);
168
425
  if (sinks.length === 1) {
169
426
  const sinkId = sinks[0];
170
- const sinkOutput = snapshot.outputs.get(workActivityId(runId, sinkId));
427
+ const sinkNode = def.nodes[sinkId];
428
+ const sinkActId = workActivityId(runId, sinkId);
429
+ const sinkOutput = sinkNode?.type === 'loop'
430
+ ? snapshot.outputs.get(sinkActId) // loopFinished projection (codex Step 2)
431
+ : snapshot.outputs.get(sinkActId);
171
432
  if (sinkOutput) {
172
433
  actions.push({
173
434
  kind: 'completeRunSucceeded',
@@ -176,22 +437,173 @@ export function decideNextActions(snapshot, def) {
176
437
  });
177
438
  }
178
439
  }
179
- // Multi-sink: caller composes the run output (out of v0 scope).
180
440
  }
181
441
  }
182
442
  return actions;
183
443
  }
444
+ /**
445
+ * Per-node advancement decision used by both the top-level scheduler
446
+ * (plain nodes) and the v0.2 loop branch (body / decision nodes). The
447
+ * caller supplies the gate and work activityIds so the same logic
448
+ * applies to plain `runId::work::node` ids and loop iteration
449
+ * `runId::loop::loopId.N::work::node` ids without leaking the
450
+ * activityId convention into this helper.
451
+ *
452
+ * Returns the actions the caller should emit plus a small status
453
+ * triplet — exactly one of `isSucceeded` / `isFailed` is true when
454
+ * the node has reached its terminal; otherwise the call is "pending"
455
+ * (in-flight) and the returned actions are dispatch instructions for
456
+ * gate/work that haven't yet been emitted.
457
+ */
458
+ function decideNodeAdvancement(snapshot, node, nodeId, gateActId, workActId) {
459
+ const actions = [];
460
+ // Decision node: gate-only. Both reject and approve resolve to
461
+ // `activitySucceeded` per wait.ts decision-mode (see N2 in
462
+ // /tmp/wf-loop-v02.md §4.3); only humanGate timeout produces a
463
+ // failed activity.
464
+ if (node.type === 'decision') {
465
+ const gateAct = snapshot.activities.get(gateActId);
466
+ if (!gateAct) {
467
+ actions.push({
468
+ kind: 'dispatchGate',
469
+ nodeId,
470
+ activityId: gateActId,
471
+ humanGate: node.humanGate,
472
+ });
473
+ return { actions, isSucceeded: false, isFailed: false };
474
+ }
475
+ if (gateAct.status === 'succeeded') {
476
+ return { actions, isSucceeded: true, isFailed: false };
477
+ }
478
+ if (gateAct.status === 'failed' || gateAct.status === 'timedOut') {
479
+ return { actions, isSucceeded: false, isFailed: true };
480
+ }
481
+ return { actions, isSucceeded: false, isFailed: false };
482
+ }
483
+ // Loop blocks are not handled by this helper.
484
+ if (node.type === 'loop') {
485
+ return { actions, isSucceeded: false, isFailed: false };
486
+ }
487
+ const gateAct = snapshot.activities.get(gateActId);
488
+ const workAct = snapshot.activities.get(workActId);
489
+ if (node.humanGate) {
490
+ if (!gateAct) {
491
+ actions.push({
492
+ kind: 'dispatchGate',
493
+ nodeId,
494
+ activityId: gateActId,
495
+ humanGate: node.humanGate,
496
+ });
497
+ return { actions, isSucceeded: false, isFailed: false };
498
+ }
499
+ if (gateAct.status === 'failed' || gateAct.status === 'timedOut') {
500
+ actions.push({
501
+ kind: 'completeNodeFailed',
502
+ nodeId,
503
+ lastActivityId: gateActId,
504
+ errorClass: 'userFault',
505
+ });
506
+ return { actions, isSucceeded: false, isFailed: true };
507
+ }
508
+ if (gateAct.status !== 'succeeded') {
509
+ // gate in-flight — caller treats this as pending
510
+ return { actions, isSucceeded: false, isFailed: false };
511
+ }
512
+ // gate cleared → fall through to work
513
+ }
514
+ if (!workAct) {
515
+ actions.push({
516
+ kind: 'dispatchWork',
517
+ nodeId,
518
+ activityId: workActId,
519
+ node,
520
+ });
521
+ return { actions, isSucceeded: false, isFailed: false };
522
+ }
523
+ if (workAct.status === 'succeeded') {
524
+ const output = snapshot.outputs.get(workActId);
525
+ if (output) {
526
+ actions.push({
527
+ kind: 'completeNodeSucceeded',
528
+ nodeId,
529
+ lastActivityId: workActId,
530
+ outputRef: output,
531
+ });
532
+ return { actions, isSucceeded: true, isFailed: false };
533
+ }
534
+ return { actions, isSucceeded: false, isFailed: false };
535
+ }
536
+ if (workAct.status === 'failed' || workAct.status === 'timedOut') {
537
+ const errorClass = workAct.status === 'timedOut' ? 'retryable' : deriveErrorClass(workAct);
538
+ actions.push({
539
+ kind: 'completeNodeFailed',
540
+ nodeId,
541
+ lastActivityId: workActId,
542
+ errorClass,
543
+ });
544
+ return { actions, isSucceeded: false, isFailed: true };
545
+ }
546
+ // running / waiting / acquired / effectAttempting — pending.
547
+ return { actions, isSucceeded: false, isFailed: false };
548
+ }
549
+ /**
550
+ * Build `bodyNodeId → owningLoopId` map for fast carve-out lookups.
551
+ * Mirrors `validateLoopBlocks`'s body collection but built every tick
552
+ * because the orchestrator is a pure function and doesn't carry state.
553
+ */
554
+ function buildBodyOwnerMap(def) {
555
+ const owner = new Map();
556
+ for (const [id, node] of Object.entries(def.nodes)) {
557
+ if (node.type !== 'loop')
558
+ continue;
559
+ for (const bodyId of node.body)
560
+ owner.set(bodyId, id);
561
+ }
562
+ return owner;
563
+ }
564
+ /**
565
+ * Filter a workflow-level topological order down to body nodes only,
566
+ * preserving the global order. Used to dispatch body nodes inside an
567
+ * active loop iteration in deterministic order.
568
+ */
569
+ function orderForBody(allOrder, bodySet) {
570
+ return allOrder.filter((id) => bodySet.has(id));
571
+ }
572
+ /**
573
+ * Resolve `loop.output.from` to the OutputRef of the body node's
574
+ * latest successful iteration so `finishLoop` can carry it forward.
575
+ * Returns `undefined` if the loop did not declare `output.from`, or if
576
+ * the body node hasn't produced a succeeded output in `iteration` yet
577
+ * (which shouldn't happen if the terminator already approved — but
578
+ * defensive against partial replay state).
579
+ */
580
+ function computeLoopOutputRef(snapshot, runId, loopNode, loopId, iteration) {
581
+ const from = loopNode.output?.from;
582
+ if (!from)
583
+ return undefined;
584
+ const bodyWorkActId = loopWorkActivityId(runId, loopId, iteration, from);
585
+ return snapshot.outputs.get(bodyWorkActId);
586
+ }
184
587
  // ─── Helpers ─────────────────────────────────────────────────────────────
185
588
  function deriveErrorClass(activity) {
186
589
  const last = activity.attempts[activity.attempts.length - 1];
187
590
  return last?.error?.errorClass ?? 'fatal';
188
591
  }
189
592
  function findSinks(def) {
593
+ const bodyOwner = buildBodyOwnerMap(def);
594
+ // Only consider top-level (non-body) nodes when computing sinks. Body
595
+ // nodes are dispatched by their owning loop block; the outside view of
596
+ // a loop is "the loop block itself produces output" so body nodes must
597
+ // never count as workflow-level sinks even if no peer depends on them
598
+ // directly. (`reviewDecision` is the canonical example — nothing
599
+ // outside the loop depends on it, but it's structurally internal.)
190
600
  const referenced = new Set();
191
- for (const node of Object.values(def.nodes)) {
601
+ for (const [id, node] of Object.entries(def.nodes)) {
602
+ if (bodyOwner.has(id))
603
+ continue;
192
604
  for (const dep of node.depends ?? [])
193
605
  referenced.add(dep);
194
606
  }
195
- return Object.keys(def.nodes).filter((id) => !referenced.has(id));
607
+ return Object.keys(def.nodes).filter((id) => !bodyOwner.has(id) && !referenced.has(id));
196
608
  }
197
609
  //# sourceMappingURL=orchestrator.js.map