@runcore-sh/runcore 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (351) hide show
  1. package/dictionary.json +2 -2
  2. package/dist/activity/log.js +2 -2
  3. package/dist/activity/log.js.map +1 -1
  4. package/dist/agents/governed-spawn.d.ts.map +1 -1
  5. package/dist/cli.js +101 -11
  6. package/dist/cli.js.map +1 -1
  7. package/dist/extensions/cache.d.ts +57 -0
  8. package/dist/extensions/cache.d.ts.map +1 -0
  9. package/dist/extensions/cache.js +173 -0
  10. package/dist/extensions/cache.js.map +1 -0
  11. package/dist/extensions/client.d.ts +55 -0
  12. package/dist/extensions/client.d.ts.map +1 -0
  13. package/dist/extensions/client.js +120 -0
  14. package/dist/extensions/client.js.map +1 -0
  15. package/dist/extensions/index.d.ts +13 -0
  16. package/dist/extensions/index.d.ts.map +1 -0
  17. package/dist/extensions/index.js +12 -0
  18. package/dist/extensions/index.js.map +1 -0
  19. package/dist/extensions/loader.d.ts +50 -0
  20. package/dist/extensions/loader.d.ts.map +1 -0
  21. package/dist/extensions/loader.js +166 -0
  22. package/dist/extensions/loader.js.map +1 -0
  23. package/dist/extensions/manifest.d.ts +38 -0
  24. package/dist/extensions/manifest.d.ts.map +1 -0
  25. package/dist/extensions/manifest.js +17 -0
  26. package/dist/extensions/manifest.js.map +1 -0
  27. package/dist/extensions/stubs.d.ts +27 -0
  28. package/dist/extensions/stubs.d.ts.map +1 -0
  29. package/dist/extensions/stubs.js +45 -0
  30. package/dist/extensions/stubs.js.map +1 -0
  31. package/dist/lib/audit.js +2 -2
  32. package/dist/lib/audit.js.map +1 -1
  33. package/dist/lib/brain-migrate.d.ts +21 -0
  34. package/dist/lib/brain-migrate.d.ts.map +1 -0
  35. package/dist/lib/brain-migrate.js +137 -0
  36. package/dist/lib/brain-migrate.js.map +1 -0
  37. package/dist/lib/paths.d.ts +27 -0
  38. package/dist/lib/paths.d.ts.map +1 -1
  39. package/dist/lib/paths.js +65 -0
  40. package/dist/lib/paths.js.map +1 -1
  41. package/dist/llm/call-log.d.ts +40 -0
  42. package/dist/llm/call-log.d.ts.map +1 -0
  43. package/dist/llm/call-log.js +35 -0
  44. package/dist/llm/call-log.js.map +1 -0
  45. package/dist/llm/complete.d.ts +6 -0
  46. package/dist/llm/complete.d.ts.map +1 -1
  47. package/dist/llm/complete.js +27 -0
  48. package/dist/llm/complete.js.map +1 -1
  49. package/dist/mcp-server.js +118 -2
  50. package/dist/mcp-server.js.map +1 -1
  51. package/dist/memory/file-backed.d.ts +4 -0
  52. package/dist/memory/file-backed.d.ts.map +1 -1
  53. package/dist/memory/file-backed.js +4 -0
  54. package/dist/memory/file-backed.js.map +1 -1
  55. package/dist/memory/vector-index.d.ts +4 -12
  56. package/dist/memory/vector-index.d.ts.map +1 -1
  57. package/dist/memory/vector-index.js +11 -93
  58. package/dist/memory/vector-index.js.map +1 -1
  59. package/dist/search/brain-docs.d.ts +17 -7
  60. package/dist/search/brain-docs.d.ts.map +1 -1
  61. package/dist/search/brain-docs.js +170 -52
  62. package/dist/search/brain-docs.js.map +1 -1
  63. package/dist/search/brain-rag.d.ts +45 -0
  64. package/dist/search/brain-rag.d.ts.map +1 -0
  65. package/dist/search/brain-rag.js +275 -0
  66. package/dist/search/brain-rag.js.map +1 -0
  67. package/dist/search/chunker.d.ts +24 -0
  68. package/dist/search/chunker.d.ts.map +1 -0
  69. package/dist/search/chunker.js +95 -0
  70. package/dist/search/chunker.js.map +1 -0
  71. package/dist/search/embedder.d.ts +16 -0
  72. package/dist/search/embedder.d.ts.map +1 -0
  73. package/dist/search/embedder.js +108 -0
  74. package/dist/search/embedder.js.map +1 -0
  75. package/dist/search/file-watcher.d.ts +11 -0
  76. package/dist/search/file-watcher.d.ts.map +1 -0
  77. package/dist/search/file-watcher.js +86 -0
  78. package/dist/search/file-watcher.js.map +1 -0
  79. package/dist/server.d.ts.map +1 -1
  80. package/dist/server.js +814 -472
  81. package/dist/server.js.map +1 -1
  82. package/dist/sessions/store.d.ts +9 -0
  83. package/dist/sessions/store.d.ts.map +1 -1
  84. package/dist/sessions/store.js.map +1 -1
  85. package/dist/settings.d.ts +26 -0
  86. package/dist/settings.d.ts.map +1 -1
  87. package/dist/settings.js +78 -2
  88. package/dist/settings.js.map +1 -1
  89. package/dist/tracing/init.d.ts +1 -1
  90. package/dist/tracing/init.d.ts.map +1 -1
  91. package/dist/utils/logger.js +2 -2
  92. package/dist/utils/logger.js.map +1 -1
  93. package/module-tiers.json +164 -0
  94. package/package.json +9 -13
  95. package/public/avatar/cache/1184385ec5522b57.mp4 +0 -0
  96. package/public/avatar/cache/1f15f6a1ebd7e439.mp4 +0 -0
  97. package/public/avatar/cache/2c7e47ff0bdeb8d1.mp4 +0 -0
  98. package/public/avatar/cache/5f308566f7abb8f2.mp4 +0 -0
  99. package/public/avatar/cache/62f9cfba848d724e.mp4 +0 -0
  100. package/public/avatar/cache/6d64e657e6bf2aab.mp4 +0 -0
  101. package/public/avatar/cache/763ad0349e0b6f26.mp4 +0 -0
  102. package/public/avatar/cache/81a516cfd461b2b9.mp4 +0 -0
  103. package/public/avatar/cache/9366de15fd6910ca.mp4 +0 -0
  104. package/public/avatar/cache/ade41a846b283895.mp4 +0 -0
  105. package/public/avatar/cache/b6066e5c65383eec.mp4 +0 -0
  106. package/public/avatar/cache/edadb75d37891fc7.mp4 +0 -0
  107. package/public/avatar/cache/f0ae159640621dd9.mp4 +0 -0
  108. package/public/avatar/cache/fc2e5419adf29d96.mp4 +0 -0
  109. package/public/index.html +379 -59
  110. package/dist/agents/autonomous.js +0 -749
  111. package/dist/agents/autonomous.js.map +0 -1
  112. package/dist/agents/commit.js +0 -113
  113. package/dist/agents/commit.js.map +0 -1
  114. package/dist/agents/continue.js +0 -158
  115. package/dist/agents/continue.js.map +0 -1
  116. package/dist/agents/cooldown.js +0 -397
  117. package/dist/agents/cooldown.js.map +0 -1
  118. package/dist/agents/dedup-guard.js +0 -131
  119. package/dist/agents/dedup-guard.js.map +0 -1
  120. package/dist/agents/feed.js +0 -176
  121. package/dist/agents/feed.js.map +0 -1
  122. package/dist/agents/governance.js +0 -292
  123. package/dist/agents/governance.js.map +0 -1
  124. package/dist/agents/governed-spawn.js +0 -192
  125. package/dist/agents/governed-spawn.js.map +0 -1
  126. package/dist/agents/heartbeat.js +0 -324
  127. package/dist/agents/heartbeat.js.map +0 -1
  128. package/dist/agents/instance-manager.js +0 -850
  129. package/dist/agents/instance-manager.js.map +0 -1
  130. package/dist/agents/issue-reporter.js +0 -123
  131. package/dist/agents/issue-reporter.js.map +0 -1
  132. package/dist/agents/issues.js +0 -141
  133. package/dist/agents/issues.js.map +0 -1
  134. package/dist/agents/locks.js +0 -234
  135. package/dist/agents/locks.js.map +0 -1
  136. package/dist/agents/memory.js +0 -93
  137. package/dist/agents/memory.js.map +0 -1
  138. package/dist/agents/monitor.js +0 -235
  139. package/dist/agents/monitor.js.map +0 -1
  140. package/dist/agents/orchestration.js +0 -715
  141. package/dist/agents/orchestration.js.map +0 -1
  142. package/dist/agents/recover.js +0 -166
  143. package/dist/agents/recover.js.map +0 -1
  144. package/dist/agents/reflection.js +0 -199
  145. package/dist/agents/reflection.js.map +0 -1
  146. package/dist/agents/runtime/bus.js +0 -174
  147. package/dist/agents/runtime/bus.js.map +0 -1
  148. package/dist/agents/runtime/config.js +0 -101
  149. package/dist/agents/runtime/config.js.map +0 -1
  150. package/dist/agents/runtime/driver.js +0 -214
  151. package/dist/agents/runtime/driver.js.map +0 -1
  152. package/dist/agents/runtime/errors.js +0 -40
  153. package/dist/agents/runtime/errors.js.map +0 -1
  154. package/dist/agents/runtime/index.js +0 -54
  155. package/dist/agents/runtime/index.js.map +0 -1
  156. package/dist/agents/runtime/lifecycle.js +0 -116
  157. package/dist/agents/runtime/lifecycle.js.map +0 -1
  158. package/dist/agents/runtime/manager.js +0 -948
  159. package/dist/agents/runtime/manager.js.map +0 -1
  160. package/dist/agents/runtime/registry.js +0 -195
  161. package/dist/agents/runtime/registry.js.map +0 -1
  162. package/dist/agents/runtime/resources.js +0 -146
  163. package/dist/agents/runtime/resources.js.map +0 -1
  164. package/dist/agents/runtime/types.js +0 -24
  165. package/dist/agents/runtime/types.js.map +0 -1
  166. package/dist/agents/spawn-policy.js +0 -202
  167. package/dist/agents/spawn-policy.js.map +0 -1
  168. package/dist/agents/spawn.js +0 -970
  169. package/dist/agents/spawn.js.map +0 -1
  170. package/dist/agents/triage.js +0 -81
  171. package/dist/agents/triage.js.map +0 -1
  172. package/dist/agents/workflow.js +0 -543
  173. package/dist/agents/workflow.js.map +0 -1
  174. package/dist/avatar/client.js +0 -172
  175. package/dist/avatar/client.js.map +0 -1
  176. package/dist/avatar/sidecar.js +0 -125
  177. package/dist/avatar/sidecar.js.map +0 -1
  178. package/dist/browser/sessions.js +0 -122
  179. package/dist/browser/sessions.js.map +0 -1
  180. package/dist/capabilities/definitions/browser.js +0 -242
  181. package/dist/capabilities/definitions/browser.js.map +0 -1
  182. package/dist/channels/whatsapp.js +0 -200
  183. package/dist/channels/whatsapp.js.map +0 -1
  184. package/dist/credentials/store.js +0 -189
  185. package/dist/credentials/store.js.map +0 -1
  186. package/dist/files/deep-index.js +0 -337
  187. package/dist/files/deep-index.js.map +0 -1
  188. package/dist/files/extract.js +0 -33
  189. package/dist/files/extract.js.map +0 -1
  190. package/dist/files/gdrive.js +0 -246
  191. package/dist/files/gdrive.js.map +0 -1
  192. package/dist/github/client.js +0 -408
  193. package/dist/github/client.js.map +0 -1
  194. package/dist/github/commit-analysis.js +0 -276
  195. package/dist/github/commit-analysis.js.map +0 -1
  196. package/dist/github/contributor-stats.js +0 -119
  197. package/dist/github/contributor-stats.js.map +0 -1
  198. package/dist/github/issue-sla.js +0 -220
  199. package/dist/github/issue-sla.js.map +0 -1
  200. package/dist/github/issue-triage.js +0 -286
  201. package/dist/github/issue-triage.js.map +0 -1
  202. package/dist/github/pr-readiness.js +0 -197
  203. package/dist/github/pr-readiness.js.map +0 -1
  204. package/dist/github/pr-review.js +0 -410
  205. package/dist/github/pr-review.js.map +0 -1
  206. package/dist/github/release-notes.js +0 -227
  207. package/dist/github/release-notes.js.map +0 -1
  208. package/dist/github/repo-health.js +0 -303
  209. package/dist/github/repo-health.js.map +0 -1
  210. package/dist/github/retry.js +0 -117
  211. package/dist/github/retry.js.map +0 -1
  212. package/dist/github/types.js +0 -8
  213. package/dist/github/types.js.map +0 -1
  214. package/dist/github/webhooks.js +0 -153
  215. package/dist/github/webhooks.js.map +0 -1
  216. package/dist/google/auth.js +0 -325
  217. package/dist/google/auth.js.map +0 -1
  218. package/dist/google/calendar-timer.js +0 -91
  219. package/dist/google/calendar-timer.js.map +0 -1
  220. package/dist/google/calendar.js +0 -270
  221. package/dist/google/calendar.js.map +0 -1
  222. package/dist/google/docs.js +0 -309
  223. package/dist/google/docs.js.map +0 -1
  224. package/dist/google/gmail-send.js +0 -219
  225. package/dist/google/gmail-send.js.map +0 -1
  226. package/dist/google/gmail-timer.js +0 -223
  227. package/dist/google/gmail-timer.js.map +0 -1
  228. package/dist/google/gmail.js +0 -470
  229. package/dist/google/gmail.js.map +0 -1
  230. package/dist/google/plugin.js +0 -169
  231. package/dist/google/plugin.js.map +0 -1
  232. package/dist/google/tasks-timer.js +0 -107
  233. package/dist/google/tasks-timer.js.map +0 -1
  234. package/dist/google/tasks.js +0 -331
  235. package/dist/google/tasks.js.map +0 -1
  236. package/dist/google/temporal.js +0 -176
  237. package/dist/google/temporal.js.map +0 -1
  238. package/dist/integrations/gate.js +0 -100
  239. package/dist/integrations/gate.js.map +0 -1
  240. package/dist/integrations/github.js +0 -331
  241. package/dist/integrations/github.js.map +0 -1
  242. package/dist/integrations/google-tasks.js +0 -432
  243. package/dist/integrations/google-tasks.js.map +0 -1
  244. package/dist/mdns.js +0 -110
  245. package/dist/mdns.js.map +0 -1
  246. package/dist/notifications/channel.js +0 -83
  247. package/dist/notifications/channel.js.map +0 -1
  248. package/dist/notifications/channels/adapter.js +0 -55
  249. package/dist/notifications/channels/adapter.js.map +0 -1
  250. package/dist/notifications/channels/index.js +0 -6
  251. package/dist/notifications/channels/index.js.map +0 -1
  252. package/dist/notifications/channels/log.js +0 -29
  253. package/dist/notifications/channels/log.js.map +0 -1
  254. package/dist/notifications/email.js +0 -72
  255. package/dist/notifications/email.js.map +0 -1
  256. package/dist/notifications/engine.js +0 -198
  257. package/dist/notifications/engine.js.map +0 -1
  258. package/dist/notifications/index.js +0 -24
  259. package/dist/notifications/index.js.map +0 -1
  260. package/dist/notifications/phone.js +0 -48
  261. package/dist/notifications/phone.js.map +0 -1
  262. package/dist/notifications/sms.js +0 -65
  263. package/dist/notifications/sms.js.map +0 -1
  264. package/dist/notifications/types.js +0 -14
  265. package/dist/notifications/types.js.map +0 -1
  266. package/dist/notifications/webhook.js +0 -65
  267. package/dist/notifications/webhook.js.map +0 -1
  268. package/dist/resend/inbox.js +0 -199
  269. package/dist/resend/inbox.js.map +0 -1
  270. package/dist/resend/webhooks.js +0 -244
  271. package/dist/resend/webhooks.js.map +0 -1
  272. package/dist/search/browse.js +0 -225
  273. package/dist/search/browse.js.map +0 -1
  274. package/dist/search/perplexity.js +0 -41
  275. package/dist/search/perplexity.js.map +0 -1
  276. package/dist/slack/channels.js +0 -277
  277. package/dist/slack/channels.js.map +0 -1
  278. package/dist/slack/client.js +0 -468
  279. package/dist/slack/client.js.map +0 -1
  280. package/dist/slack/retry.js +0 -100
  281. package/dist/slack/retry.js.map +0 -1
  282. package/dist/slack/types.js +0 -52
  283. package/dist/slack/types.js.map +0 -1
  284. package/dist/slack/webhooks.js +0 -285
  285. package/dist/slack/webhooks.js.map +0 -1
  286. package/dist/stt/client.js +0 -66
  287. package/dist/stt/client.js.map +0 -1
  288. package/dist/stt/sidecar.js +0 -115
  289. package/dist/stt/sidecar.js.map +0 -1
  290. package/dist/tracing/bridge.js +0 -70
  291. package/dist/tracing/bridge.js.map +0 -1
  292. package/dist/tracing/correlation.js +0 -49
  293. package/dist/tracing/correlation.js.map +0 -1
  294. package/dist/tracing/index.js +0 -18
  295. package/dist/tracing/index.js.map +0 -1
  296. package/dist/tracing/init.js +0 -81
  297. package/dist/tracing/init.js.map +0 -1
  298. package/dist/tracing/instrument.js +0 -145
  299. package/dist/tracing/instrument.js.map +0 -1
  300. package/dist/tracing/middleware.js +0 -69
  301. package/dist/tracing/middleware.js.map +0 -1
  302. package/dist/tracing/tracer.js +0 -327
  303. package/dist/tracing/tracer.js.map +0 -1
  304. package/dist/tts/client.js +0 -48
  305. package/dist/tts/client.js.map +0 -1
  306. package/dist/tts/sidecar.js +0 -148
  307. package/dist/tts/sidecar.js.map +0 -1
  308. package/dist/twilio/call.js +0 -79
  309. package/dist/twilio/call.js.map +0 -1
  310. package/dist/vault/matcher.js +0 -197
  311. package/dist/vault/matcher.js.map +0 -1
  312. package/dist/vault/personal.js +0 -163
  313. package/dist/vault/personal.js.map +0 -1
  314. package/dist/vault/policy.js +0 -159
  315. package/dist/vault/policy.js.map +0 -1
  316. package/dist/vault/store.js +0 -122
  317. package/dist/vault/store.js.map +0 -1
  318. package/dist/vault/transfer.js +0 -188
  319. package/dist/vault/transfer.js.map +0 -1
  320. package/dist/volumes/index.js +0 -2
  321. package/dist/volumes/index.js.map +0 -1
  322. package/dist/volumes/manager.js +0 -462
  323. package/dist/volumes/manager.js.map +0 -1
  324. package/dist/volumes/types.js +0 -8
  325. package/dist/volumes/types.js.map +0 -1
  326. package/dist/webhooks/config.js +0 -214
  327. package/dist/webhooks/config.js.map +0 -1
  328. package/dist/webhooks/event-log.js +0 -132
  329. package/dist/webhooks/event-log.js.map +0 -1
  330. package/dist/webhooks/handler.js +0 -103
  331. package/dist/webhooks/handler.js.map +0 -1
  332. package/dist/webhooks/handlers.js +0 -231
  333. package/dist/webhooks/handlers.js.map +0 -1
  334. package/dist/webhooks/index.js +0 -33
  335. package/dist/webhooks/index.js.map +0 -1
  336. package/dist/webhooks/mount.js +0 -400
  337. package/dist/webhooks/mount.js.map +0 -1
  338. package/dist/webhooks/registry.js +0 -143
  339. package/dist/webhooks/registry.js.map +0 -1
  340. package/dist/webhooks/relay.js +0 -53
  341. package/dist/webhooks/relay.js.map +0 -1
  342. package/dist/webhooks/retry.js +0 -270
  343. package/dist/webhooks/retry.js.map +0 -1
  344. package/dist/webhooks/router.js +0 -290
  345. package/dist/webhooks/router.js.map +0 -1
  346. package/dist/webhooks/twilio.js +0 -129
  347. package/dist/webhooks/twilio.js.map +0 -1
  348. package/dist/webhooks/types.js +0 -8
  349. package/dist/webhooks/types.js.map +0 -1
  350. package/dist/webhooks/verify.js +0 -154
  351. package/dist/webhooks/verify.js.map +0 -1
@@ -1,715 +0,0 @@
1
- /**
2
- * Agent Orchestration Engine — Multi-agent coordination, delegation, and result merging.
3
- *
4
- * Sits on top of the AgentPool to provide:
5
- * 1. **Workflows** — Named groups of coordinated agent tasks
6
- * 2. **Task delegation** — Spawn multiple agents for a workflow and track them
7
- * 3. **Result aggregation** — Collect, merge, and synthesize outputs from all agents
8
- * 4. **Conflict resolution** — Preventive file locking + post-hoc detection/resolution
9
- * 5. **Queue management** — Priority-aware workflow scheduling
10
- * 6. **Status tracking** — Real-time workflow status and reporting
11
- * 7. **Context passing** — Dependency mode pipes completed task outputs into dependents
12
- * 8. **Cascading failure limits** — Auto-cancel workflows when failure threshold hit
13
- * 9. **Workflow timeouts** — Wall-clock timeout for entire workflows
14
- * 10. **Bus integration** — Emits workflow lifecycle events for monitoring
15
- *
16
- * Usage:
17
- * const orch = new Orchestrator(pool);
18
- * const wf = orch.createWorkflow({ name: "Feature X", tasks: [...] });
19
- * const result = await orch.execute(wf.id);
20
- * const aggregated = orch.aggregateResults(wf.id);
21
- */
22
- import { randomBytes } from "node:crypto";
23
- import { createLogger } from "../utils/logger.js";
24
- const log = createLogger("orchestrator");
25
- // ---------------------------------------------------------------------------
26
- // Orchestrator
27
- // ---------------------------------------------------------------------------
28
- export class Orchestrator {
29
- pool;
30
- workflows = new Map();
31
- conflicts = new Map();
32
- workflowQueue = [];
33
- activeWorkflowCount = 0;
34
- maxActiveWorkflows;
35
- eventHandlers = new Map();
36
- /** Resolvers for queued workflows waiting for capacity. */
37
- queueResolvers = new Map();
38
- constructor(pool, options) {
39
- this.pool = pool;
40
- this.maxActiveWorkflows = options?.maxActiveWorkflows ?? 3;
41
- }
42
- // -------------------------------------------------------------------------
43
- // Workflow lifecycle
44
- // -------------------------------------------------------------------------
45
- /** Create a workflow (does not start execution). */
46
- createWorkflow(input) {
47
- const id = generateWorkflowId();
48
- const tasks = new Map();
49
- for (const def of input.tasks) {
50
- if (tasks.has(def.key)) {
51
- throw new Error(`Duplicate task key in workflow: ${def.key}`);
52
- }
53
- tasks.set(def.key, {
54
- key: def.key,
55
- label: def.label,
56
- prompt: def.prompt,
57
- cwd: def.cwd,
58
- dependsOn: def.dependsOn ?? [],
59
- priority: def.priority ?? 50,
60
- config: def.config,
61
- tags: def.tags ?? [],
62
- status: "pending",
63
- });
64
- }
65
- // Validate dependency references
66
- if (input.mode === "dependency") {
67
- for (const [, task] of tasks) {
68
- for (const dep of task.dependsOn) {
69
- if (!tasks.has(dep)) {
70
- throw new Error(`Task "${task.key}" depends on unknown task "${dep}"`);
71
- }
72
- }
73
- }
74
- // Check for cycles
75
- this.detectCycles(tasks);
76
- }
77
- const workflow = {
78
- id,
79
- name: input.name,
80
- description: input.description,
81
- status: "pending",
82
- mode: input.mode ?? "parallel",
83
- conflictStrategy: input.conflictStrategy ?? "last-write-wins",
84
- maxConcurrent: input.maxConcurrent ?? 0,
85
- origin: input.origin ?? "ai",
86
- tasks,
87
- createdAt: new Date().toISOString(),
88
- };
89
- this.workflows.set(id, workflow);
90
- this.conflicts.set(id, []);
91
- log.info("Workflow created", {
92
- workflowId: id,
93
- name: input.name,
94
- taskCount: tasks.size,
95
- mode: workflow.mode,
96
- });
97
- return workflow;
98
- }
99
- /**
100
- * Execute a workflow. Spawns agents according to the execution mode.
101
- * Returns a promise that resolves when the workflow completes.
102
- */
103
- async execute(workflowId) {
104
- const workflow = this.requireWorkflow(workflowId);
105
- if (workflow.status !== "pending") {
106
- throw new Error(`Workflow "${workflow.name}" is already ${workflow.status}`);
107
- }
108
- // Check if we can start immediately or need to queue
109
- if (this.activeWorkflowCount >= this.maxActiveWorkflows) {
110
- this.workflowQueue.push(workflowId);
111
- log.info("Workflow queued", { workflowId, queuePosition: this.workflowQueue.length });
112
- // Wait until drainQueue resolves us
113
- await new Promise((resolve) => {
114
- this.queueResolvers.set(workflowId, resolve);
115
- });
116
- }
117
- this.activeWorkflowCount++;
118
- workflow.status = "running";
119
- workflow.startedAt = new Date().toISOString();
120
- log.info("Workflow executing", {
121
- workflowId,
122
- name: workflow.name,
123
- mode: workflow.mode,
124
- });
125
- try {
126
- switch (workflow.mode) {
127
- case "parallel":
128
- await this.executeParallel(workflow);
129
- break;
130
- case "sequential":
131
- await this.executeSequential(workflow);
132
- break;
133
- case "dependency":
134
- await this.executeDependency(workflow);
135
- break;
136
- }
137
- }
138
- catch (err) {
139
- log.error("Workflow execution error", {
140
- workflowId,
141
- error: err instanceof Error ? err.message : String(err),
142
- });
143
- }
144
- // Detect conflicts
145
- const fileConflicts = this.detectFileConflicts(workflow);
146
- this.conflicts.set(workflowId, fileConflicts);
147
- // Resolve conflicts
148
- if (fileConflicts.length > 0) {
149
- this.resolveConflicts(workflow, fileConflicts);
150
- }
151
- // Determine final status
152
- workflow.status = this.computeWorkflowStatus(workflow);
153
- workflow.finishedAt = new Date().toISOString();
154
- this.activeWorkflowCount--;
155
- this.drainQueue();
156
- log.info("Workflow finished", {
157
- workflowId,
158
- name: workflow.name,
159
- status: workflow.status,
160
- conflicts: fileConflicts.length,
161
- });
162
- return this.buildResult(workflow);
163
- }
164
- /** Cancel a running or pending workflow. */
165
- async cancel(workflowId) {
166
- const workflow = this.requireWorkflow(workflowId);
167
- // Remove from queue if pending
168
- const queueIdx = this.workflowQueue.indexOf(workflowId);
169
- if (queueIdx !== -1) {
170
- this.workflowQueue.splice(queueIdx, 1);
171
- }
172
- // Cancel running tasks
173
- for (const [, task] of workflow.tasks) {
174
- if (task.status === "running" && task.instanceId) {
175
- try {
176
- await this.pool.terminate(task.instanceId, "Workflow cancelled");
177
- }
178
- catch {
179
- // Best effort
180
- }
181
- task.status = "cancelled";
182
- task.finishedAt = new Date().toISOString();
183
- }
184
- else if (task.status === "pending" || task.status === "queued") {
185
- task.status = "cancelled";
186
- }
187
- }
188
- const wasRunning = workflow.status === "running";
189
- workflow.status = "cancelled";
190
- workflow.finishedAt = new Date().toISOString();
191
- if (wasRunning) {
192
- this.activeWorkflowCount--;
193
- this.drainQueue();
194
- }
195
- log.info("Workflow cancelled", { workflowId, name: workflow.name });
196
- }
197
- // -------------------------------------------------------------------------
198
- // Execution modes
199
- // -------------------------------------------------------------------------
200
- /** Execute all tasks in parallel (respecting maxConcurrent). */
201
- async executeParallel(workflow) {
202
- const allTasks = [...workflow.tasks.values()];
203
- const maxConcurrent = workflow.maxConcurrent || allTasks.length;
204
- // Spawn in batches respecting concurrency limit
205
- const pending = [...allTasks];
206
- const running = new Map();
207
- const spawnNext = async () => {
208
- while (pending.length > 0 && running.size < maxConcurrent) {
209
- const task = pending.shift();
210
- const promise = this.spawnTask(workflow, task).then(() => {
211
- running.delete(task.key);
212
- });
213
- running.set(task.key, promise);
214
- }
215
- };
216
- await spawnNext();
217
- // Wait for tasks to complete and spawn more as slots free up
218
- while (running.size > 0) {
219
- await Promise.race([...running.values()]);
220
- await spawnNext();
221
- }
222
- }
223
- /** Execute tasks one at a time in definition order. */
224
- async executeSequential(workflow) {
225
- for (const [, task] of workflow.tasks) {
226
- if (workflow.status === "cancelled")
227
- break;
228
- await this.spawnTask(workflow, task);
229
- }
230
- }
231
- /** Execute tasks respecting dependency edges (topological order). */
232
- async executeDependency(workflow) {
233
- const maxConcurrent = workflow.maxConcurrent || workflow.tasks.size;
234
- const completed = new Set();
235
- const failed = new Set();
236
- const running = new Map();
237
- const isReady = (task) => {
238
- if (task.status !== "pending")
239
- return false;
240
- return task.dependsOn.every((dep) => completed.has(dep));
241
- };
242
- const hasFailedDep = (task) => {
243
- return task.dependsOn.some((dep) => failed.has(dep));
244
- };
245
- const scheduleReady = async () => {
246
- for (const [, task] of workflow.tasks) {
247
- if (running.size >= maxConcurrent)
248
- break;
249
- if (workflow.status === "cancelled")
250
- break;
251
- // Skip tasks whose deps have failed
252
- if (hasFailedDep(task)) {
253
- task.status = "skipped";
254
- task.error = "Dependency failed";
255
- continue;
256
- }
257
- if (isReady(task)) {
258
- task.status = "queued";
259
- const promise = this.spawnTask(workflow, task).then(() => {
260
- running.delete(task.key);
261
- if (task.status === "completed") {
262
- completed.add(task.key);
263
- }
264
- else {
265
- failed.add(task.key);
266
- }
267
- });
268
- running.set(task.key, promise);
269
- }
270
- }
271
- };
272
- await scheduleReady();
273
- while (running.size > 0) {
274
- await Promise.race([...running.values()]);
275
- await scheduleReady();
276
- }
277
- // Mark any remaining pending tasks as skipped
278
- for (const [, task] of workflow.tasks) {
279
- if (task.status === "pending") {
280
- task.status = "skipped";
281
- task.error = "Dependency not satisfied";
282
- }
283
- }
284
- }
285
- // -------------------------------------------------------------------------
286
- // Task spawning
287
- // -------------------------------------------------------------------------
288
- /** Spawn a single agent for a workflow task and wait for completion. */
289
- async spawnTask(workflow, task) {
290
- task.status = "running";
291
- task.startedAt = new Date().toISOString();
292
- try {
293
- const request = {
294
- taskId: `orch_${workflow.id}_${task.key}`,
295
- label: `[${workflow.name}] ${task.label}`,
296
- prompt: task.prompt,
297
- cwd: task.cwd,
298
- origin: workflow.origin,
299
- tags: [
300
- `workflow:${workflow.id}`,
301
- `task:${task.key}`,
302
- ...task.tags,
303
- ],
304
- config: {
305
- priority: task.priority,
306
- ...task.config,
307
- },
308
- };
309
- const instance = await this.pool.spawn(request);
310
- task.instanceId = instance.id;
311
- task.taskId = instance.taskId;
312
- // Wait for completion via bus events
313
- await this.waitForCompletion(instance.id, task);
314
- }
315
- catch (err) {
316
- task.status = "failed";
317
- task.error = err instanceof Error ? err.message : String(err);
318
- task.finishedAt = new Date().toISOString();
319
- log.warn("Task spawn failed", {
320
- workflowId: workflow.id,
321
- taskKey: task.key,
322
- error: task.error,
323
- });
324
- }
325
- }
326
- /** Wait for an agent instance to reach a terminal state. */
327
- waitForCompletion(instanceId, task) {
328
- return new Promise((resolve) => {
329
- let interval;
330
- let onLifecycle;
331
- const cleanup = () => {
332
- if (interval)
333
- clearInterval(interval);
334
- if (onLifecycle) {
335
- this.pool.runtimeManager.bus.off("agent:lifecycle", onLifecycle);
336
- }
337
- this.eventHandlers.delete(`${instanceId}:cleanup`);
338
- };
339
- const checkAndResolve = () => {
340
- const instance = this.pool.getInstance(instanceId);
341
- if (!instance) {
342
- task.status = "failed";
343
- task.error = "Instance disappeared";
344
- task.finishedAt = new Date().toISOString();
345
- cleanup();
346
- resolve();
347
- return true;
348
- }
349
- const terminalStates = new Set(["completed", "failed", "terminated"]);
350
- if (terminalStates.has(instance.state)) {
351
- task.status = instance.state === "completed" ? "completed" : "failed";
352
- task.finishedAt = new Date().toISOString();
353
- if (instance.error) {
354
- task.error = instance.error.message;
355
- }
356
- // Auto-record checkpoint data as output if available
357
- if (instance.checkpointData && !task.output) {
358
- task.output = instance.checkpointData;
359
- task.filesTouched = parseFilesFromOutput(instance.checkpointData);
360
- }
361
- cleanup();
362
- resolve();
363
- return true;
364
- }
365
- return false;
366
- };
367
- // Check immediately
368
- if (checkAndResolve())
369
- return;
370
- // Listen for lifecycle events
371
- onLifecycle = (event) => {
372
- if (event.agentId === instanceId) {
373
- checkAndResolve();
374
- }
375
- };
376
- this.pool.runtimeManager.bus.on("agent:lifecycle", onLifecycle);
377
- // Safety poll in case events are missed
378
- interval = setInterval(() => {
379
- checkAndResolve();
380
- }, 2000);
381
- // Store cleanup for cancellation
382
- this.eventHandlers.set(`${instanceId}:cleanup`, () => {
383
- cleanup();
384
- resolve();
385
- });
386
- });
387
- }
388
- // -------------------------------------------------------------------------
389
- // Conflict detection and resolution
390
- // -------------------------------------------------------------------------
391
- /** Detect file conflicts — multiple tasks claiming to touch the same files. */
392
- detectFileConflicts(workflow) {
393
- const fileMap = new Map();
394
- for (const [, task] of workflow.tasks) {
395
- if (task.status !== "completed" || !task.filesTouched)
396
- continue;
397
- for (const file of task.filesTouched) {
398
- if (!fileMap.has(file)) {
399
- fileMap.set(file, []);
400
- }
401
- fileMap.get(file).push(task.key);
402
- }
403
- }
404
- const conflicts = [];
405
- for (const [filePath, taskKeys] of fileMap) {
406
- if (taskKeys.length > 1) {
407
- conflicts.push({ filePath, taskKeys });
408
- }
409
- }
410
- return conflicts;
411
- }
412
- /** Apply conflict resolution strategy. */
413
- resolveConflicts(workflow, conflicts) {
414
- for (const conflict of conflicts) {
415
- switch (workflow.conflictStrategy) {
416
- case "last-write-wins":
417
- // Accept the last task's changes (by finish time)
418
- conflict.resolution = "accepted";
419
- conflict.resolvedBy = this.getLastFinished(workflow, conflict.taskKeys);
420
- break;
421
- case "first-write-wins":
422
- // Accept the first task's changes
423
- conflict.resolution = "accepted";
424
- conflict.resolvedBy = this.getFirstFinished(workflow, conflict.taskKeys);
425
- break;
426
- case "fail":
427
- // Mark as failed — requires human intervention
428
- conflict.resolution = "rejected";
429
- log.warn("Conflict resolution: fail strategy", {
430
- file: conflict.filePath,
431
- tasks: conflict.taskKeys,
432
- });
433
- break;
434
- case "manual":
435
- conflict.resolution = "deferred";
436
- break;
437
- }
438
- }
439
- }
440
- getLastFinished(workflow, taskKeys) {
441
- let latest = taskKeys[0];
442
- let latestTime = "";
443
- for (const key of taskKeys) {
444
- const task = workflow.tasks.get(key);
445
- if (task?.finishedAt && task.finishedAt > latestTime) {
446
- latestTime = task.finishedAt;
447
- latest = key;
448
- }
449
- }
450
- return latest;
451
- }
452
- getFirstFinished(workflow, taskKeys) {
453
- let earliest = taskKeys[0];
454
- let earliestTime = new Date().toISOString();
455
- for (const key of taskKeys) {
456
- const task = workflow.tasks.get(key);
457
- if (task?.finishedAt && task.finishedAt < earliestTime) {
458
- earliestTime = task.finishedAt;
459
- earliest = key;
460
- }
461
- }
462
- return earliest;
463
- }
464
- // -------------------------------------------------------------------------
465
- // Queue management
466
- // -------------------------------------------------------------------------
467
- /** Drain queued workflows when capacity frees up. */
468
- drainQueue() {
469
- while (this.workflowQueue.length > 0 &&
470
- this.activeWorkflowCount < this.maxActiveWorkflows) {
471
- const nextId = this.workflowQueue.shift();
472
- const resolve = this.queueResolvers.get(nextId);
473
- if (resolve) {
474
- this.queueResolvers.delete(nextId);
475
- resolve();
476
- }
477
- }
478
- }
479
- // -------------------------------------------------------------------------
480
- // Status tracking and reporting
481
- // -------------------------------------------------------------------------
482
- /** Get a single workflow by ID. */
483
- getWorkflow(workflowId) {
484
- return this.workflows.get(workflowId);
485
- }
486
- /** Get the aggregated result for a completed workflow. */
487
- getResult(workflowId) {
488
- const workflow = this.requireWorkflow(workflowId);
489
- return this.buildResult(workflow);
490
- }
491
- /** Get a monitoring report across all workflows. */
492
- getReport() {
493
- const workflows = [];
494
- let activeCount = 0;
495
- let pendingCount = 0;
496
- let completedCount = 0;
497
- for (const [, wf] of this.workflows) {
498
- const progress = this.getTaskProgress(wf);
499
- workflows.push({
500
- id: wf.id,
501
- name: wf.name,
502
- status: wf.status,
503
- progress,
504
- });
505
- if (wf.status === "running")
506
- activeCount++;
507
- else if (wf.status === "pending")
508
- pendingCount++;
509
- else
510
- completedCount++;
511
- }
512
- return {
513
- timestamp: new Date().toISOString(),
514
- activeWorkflows: activeCount,
515
- pendingWorkflows: pendingCount,
516
- completedWorkflows: completedCount,
517
- workflows,
518
- queueDepth: this.workflowQueue.length,
519
- };
520
- }
521
- /** Get file conflicts for a workflow. */
522
- getConflicts(workflowId) {
523
- return this.conflicts.get(workflowId) ?? [];
524
- }
525
- /** Record which files a task touched (called externally after parsing output). */
526
- recordFilesTouched(workflowId, taskKey, files) {
527
- const workflow = this.workflows.get(workflowId);
528
- if (!workflow)
529
- return;
530
- const task = workflow.tasks.get(taskKey);
531
- if (task) {
532
- task.filesTouched = files;
533
- }
534
- }
535
- /** Record output for a task. */
536
- recordTaskOutput(workflowId, taskKey, output) {
537
- const workflow = this.workflows.get(workflowId);
538
- if (!workflow)
539
- return;
540
- const task = workflow.tasks.get(taskKey);
541
- if (task) {
542
- task.output = output;
543
- }
544
- }
545
- /** Remove a completed workflow from tracking. */
546
- removeWorkflow(workflowId) {
547
- const workflow = this.workflows.get(workflowId);
548
- if (!workflow)
549
- return false;
550
- if (workflow.status === "running") {
551
- throw new Error("Cannot remove a running workflow — cancel it first");
552
- }
553
- this.workflows.delete(workflowId);
554
- this.conflicts.delete(workflowId);
555
- return true;
556
- }
557
- // -------------------------------------------------------------------------
558
- // Cleanup
559
- // -------------------------------------------------------------------------
560
- /** Cancel all workflows and clear state. */
561
- async shutdown() {
562
- // Cancel running workflows
563
- for (const [id, wf] of this.workflows) {
564
- if (wf.status === "running" || wf.status === "pending") {
565
- await this.cancel(id).catch(() => { });
566
- }
567
- }
568
- // Clean up event handlers
569
- for (const [, cleanup] of this.eventHandlers) {
570
- cleanup();
571
- }
572
- this.eventHandlers.clear();
573
- // Resolve any queued workflows so their execute() calls don't hang
574
- for (const [, resolve] of this.queueResolvers) {
575
- resolve();
576
- }
577
- this.queueResolvers.clear();
578
- this.workflowQueue.length = 0;
579
- this.activeWorkflowCount = 0;
580
- log.info("Orchestrator shut down");
581
- }
582
- // -------------------------------------------------------------------------
583
- // Private helpers
584
- // -------------------------------------------------------------------------
585
- requireWorkflow(id) {
586
- const workflow = this.workflows.get(id);
587
- if (!workflow) {
588
- throw new Error(`Workflow not found: ${id}`);
589
- }
590
- return workflow;
591
- }
592
- computeWorkflowStatus(workflow) {
593
- const tasks = [...workflow.tasks.values()];
594
- const allCompleted = tasks.every((t) => t.status === "completed");
595
- const allFailed = tasks.every((t) => t.status === "failed");
596
- const allCancelled = tasks.every((t) => t.status === "cancelled");
597
- const anyFailed = tasks.some((t) => t.status === "failed");
598
- const anyCompleted = tasks.some((t) => t.status === "completed");
599
- if (allCompleted)
600
- return "completed";
601
- if (allFailed)
602
- return "failed";
603
- if (allCancelled)
604
- return "cancelled";
605
- if (anyFailed && anyCompleted)
606
- return "partial";
607
- if (anyFailed)
608
- return "failed";
609
- return "completed";
610
- }
611
- getTaskProgress(workflow) {
612
- let completed = 0;
613
- let running = 0;
614
- let failed = 0;
615
- let total = 0;
616
- for (const [, task] of workflow.tasks) {
617
- total++;
618
- if (task.status === "completed")
619
- completed++;
620
- else if (task.status === "running")
621
- running++;
622
- else if (task.status === "failed")
623
- failed++;
624
- }
625
- return { completed, total, running, failed };
626
- }
627
- buildResult(workflow) {
628
- const tasks = [];
629
- for (const [, task] of workflow.tasks) {
630
- let durationMs;
631
- if (task.startedAt && task.finishedAt) {
632
- durationMs = new Date(task.finishedAt).getTime() - new Date(task.startedAt).getTime();
633
- }
634
- tasks.push({
635
- key: task.key,
636
- label: task.label,
637
- status: task.status,
638
- output: task.output,
639
- error: task.error,
640
- durationMs,
641
- });
642
- }
643
- const summary = {
644
- total: tasks.length,
645
- completed: tasks.filter((t) => t.status === "completed").length,
646
- failed: tasks.filter((t) => t.status === "failed").length,
647
- skipped: tasks.filter((t) => t.status === "skipped").length,
648
- cancelled: tasks.filter((t) => t.status === "cancelled").length,
649
- durationMs: workflow.startedAt && workflow.finishedAt
650
- ? new Date(workflow.finishedAt).getTime() - new Date(workflow.startedAt).getTime()
651
- : 0,
652
- };
653
- return {
654
- workflowId: workflow.id,
655
- workflowName: workflow.name,
656
- status: workflow.status,
657
- tasks,
658
- conflicts: this.conflicts.get(workflow.id) ?? [],
659
- summary,
660
- };
661
- }
662
- /** Detect cycles in dependency graph via DFS. */
663
- detectCycles(tasks) {
664
- const visited = new Set();
665
- const inStack = new Set();
666
- const visit = (key, path) => {
667
- if (inStack.has(key)) {
668
- const cycle = [...path.slice(path.indexOf(key)), key];
669
- throw new Error(`Dependency cycle detected: ${cycle.join(" → ")}`);
670
- }
671
- if (visited.has(key))
672
- return;
673
- visited.add(key);
674
- inStack.add(key);
675
- const task = tasks.get(key);
676
- if (task) {
677
- for (const dep of task.dependsOn) {
678
- visit(dep, [...path, key]);
679
- }
680
- }
681
- inStack.delete(key);
682
- };
683
- for (const key of tasks.keys()) {
684
- visit(key, []);
685
- }
686
- }
687
- }
688
- // ---------------------------------------------------------------------------
689
- // Helpers
690
- // ---------------------------------------------------------------------------
691
- function generateWorkflowId() {
692
- return `wf_${Date.now()}_${randomBytes(4).toString("hex")}`;
693
- }
694
- /**
695
- * Parse file paths from agent output.
696
- * Looks for common patterns: "Created X", "Modified X", "Wrote X", file paths in backticks.
697
- */
698
- export function parseFilesFromOutput(output) {
699
- const files = new Set();
700
- const patterns = [
701
- /(?:Created|Modified|Wrote|Updated|Edited|Deleted)\s+[`"]?([^\s`"]+\.\w+)[`"]?/gi,
702
- /^\s*[-+]\s+(\S+\.\w+)/gm,
703
- ];
704
- for (const pattern of patterns) {
705
- let match;
706
- while ((match = pattern.exec(output)) !== null) {
707
- const file = match[1].trim();
708
- if (file.length > 2 && file.length < 256) {
709
- files.add(file);
710
- }
711
- }
712
- }
713
- return [...files];
714
- }
715
- //# sourceMappingURL=orchestration.js.map