agent-relay 2.3.4 → 2.3.6

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 (297) hide show
  1. package/README.md +1 -1
  2. package/dist/src/cli/index.js +124 -7
  3. package/dist/src/cli/index.js.map +1 -1
  4. package/package.json +23 -26
  5. package/packages/acp-bridge/package.json +2 -2
  6. package/packages/bridge/package.json +7 -7
  7. package/packages/config/package.json +2 -2
  8. package/packages/continuity/package.json +2 -2
  9. package/packages/daemon/package.json +12 -12
  10. package/packages/hooks/package.json +4 -4
  11. package/packages/mcp/package.json +5 -5
  12. package/packages/memory/package.json +2 -2
  13. package/packages/policy/package.json +2 -2
  14. package/packages/protocol/package.json +1 -1
  15. package/packages/resiliency/package.json +1 -1
  16. package/packages/sdk/dist/index.d.ts +1 -29
  17. package/packages/sdk/dist/index.d.ts.map +1 -1
  18. package/packages/sdk/dist/index.js +1 -38
  19. package/packages/sdk/dist/index.js.map +1 -1
  20. package/packages/sdk/package.json +4 -25
  21. package/packages/sdk/src/index.ts +1 -69
  22. package/packages/sdk-py/README.md +56 -0
  23. package/packages/sdk-py/pyproject.toml +23 -0
  24. package/packages/sdk-py/src/agent_relay/__init__.py +27 -0
  25. package/packages/sdk-py/src/agent_relay/builder.py +367 -0
  26. package/packages/sdk-py/src/agent_relay/types.py +92 -0
  27. package/packages/sdk-py/tests/__init__.py +0 -0
  28. package/packages/sdk-py/tests/test_builder.py +101 -0
  29. package/packages/sdk-ts/dist/index.d.ts +1 -0
  30. package/packages/sdk-ts/dist/index.d.ts.map +1 -1
  31. package/packages/sdk-ts/dist/index.js +1 -0
  32. package/packages/sdk-ts/dist/index.js.map +1 -1
  33. package/packages/sdk-ts/dist/workflows/barrier.d.ts +72 -0
  34. package/packages/sdk-ts/dist/workflows/barrier.d.ts.map +1 -0
  35. package/packages/sdk-ts/dist/workflows/barrier.js +162 -0
  36. package/packages/sdk-ts/dist/workflows/barrier.js.map +1 -0
  37. package/packages/sdk-ts/dist/workflows/builder.d.ts +101 -0
  38. package/packages/sdk-ts/dist/workflows/builder.d.ts.map +1 -0
  39. package/packages/sdk-ts/dist/workflows/builder.js +179 -0
  40. package/packages/sdk-ts/dist/workflows/builder.js.map +1 -0
  41. package/packages/sdk-ts/dist/workflows/cli.d.ts +10 -0
  42. package/packages/sdk-ts/dist/workflows/cli.d.ts.map +1 -0
  43. package/packages/sdk-ts/dist/workflows/cli.js +82 -0
  44. package/packages/sdk-ts/dist/workflows/cli.js.map +1 -0
  45. package/packages/sdk-ts/dist/workflows/coordinator.d.ts +68 -0
  46. package/packages/sdk-ts/dist/workflows/coordinator.d.ts.map +1 -0
  47. package/packages/sdk-ts/dist/workflows/coordinator.js +353 -0
  48. package/packages/sdk-ts/dist/workflows/coordinator.js.map +1 -0
  49. package/packages/sdk-ts/dist/workflows/index.d.ts +10 -0
  50. package/packages/sdk-ts/dist/workflows/index.d.ts.map +1 -0
  51. package/packages/sdk-ts/dist/workflows/index.js +10 -0
  52. package/packages/sdk-ts/dist/workflows/index.js.map +1 -0
  53. package/packages/sdk-ts/dist/workflows/memory-db.d.ts +17 -0
  54. package/packages/sdk-ts/dist/workflows/memory-db.d.ts.map +1 -0
  55. package/packages/sdk-ts/dist/workflows/memory-db.js +33 -0
  56. package/packages/sdk-ts/dist/workflows/memory-db.js.map +1 -0
  57. package/packages/sdk-ts/dist/workflows/run.d.ts +31 -0
  58. package/packages/sdk-ts/dist/workflows/run.d.ts.map +1 -0
  59. package/packages/sdk-ts/dist/workflows/run.js +24 -0
  60. package/packages/sdk-ts/dist/workflows/run.js.map +1 -0
  61. package/packages/sdk-ts/dist/workflows/runner.d.ts +119 -0
  62. package/packages/sdk-ts/dist/workflows/runner.d.ts.map +1 -0
  63. package/packages/sdk-ts/dist/workflows/runner.js +650 -0
  64. package/packages/sdk-ts/dist/workflows/runner.js.map +1 -0
  65. package/packages/sdk-ts/dist/workflows/state.d.ts +77 -0
  66. package/packages/sdk-ts/dist/workflows/state.d.ts.map +1 -0
  67. package/packages/sdk-ts/dist/workflows/state.js +140 -0
  68. package/packages/sdk-ts/dist/workflows/state.js.map +1 -0
  69. package/packages/sdk-ts/dist/workflows/templates.d.ts +47 -0
  70. package/packages/sdk-ts/dist/workflows/templates.d.ts.map +1 -0
  71. package/packages/sdk-ts/dist/workflows/templates.js +395 -0
  72. package/packages/sdk-ts/dist/workflows/templates.js.map +1 -0
  73. package/packages/sdk-ts/dist/workflows/types.d.ts +126 -0
  74. package/packages/sdk-ts/dist/workflows/types.d.ts.map +1 -0
  75. package/packages/sdk-ts/dist/workflows/types.js +8 -0
  76. package/packages/sdk-ts/dist/workflows/types.js.map +1 -0
  77. package/packages/sdk-ts/package.json +8 -2
  78. package/packages/sdk-ts/src/__tests__/error-scenarios.test.ts +682 -0
  79. package/packages/sdk-ts/src/__tests__/swarm-coordinator.test.ts +416 -0
  80. package/packages/sdk-ts/src/__tests__/workflow-runner.test.ts +333 -0
  81. package/packages/sdk-ts/src/index.ts +1 -0
  82. package/packages/sdk-ts/src/workflows/README.md +450 -0
  83. package/packages/sdk-ts/src/workflows/barrier.ts +254 -0
  84. package/packages/sdk-ts/src/workflows/builder.ts +241 -0
  85. package/packages/sdk-ts/src/workflows/builtin-templates/bug-fix.yaml +75 -0
  86. package/packages/sdk-ts/src/workflows/builtin-templates/code-review.yaml +82 -0
  87. package/packages/sdk-ts/src/workflows/builtin-templates/documentation.yaml +70 -0
  88. package/packages/sdk-ts/src/workflows/builtin-templates/feature-dev.yaml +76 -0
  89. package/packages/sdk-ts/src/workflows/builtin-templates/refactor.yaml +82 -0
  90. package/packages/sdk-ts/src/workflows/builtin-templates/security-audit.yaml +84 -0
  91. package/packages/sdk-ts/src/workflows/cli.ts +93 -0
  92. package/packages/sdk-ts/src/workflows/coordinator.ts +520 -0
  93. package/packages/sdk-ts/src/workflows/index.ts +9 -0
  94. package/packages/sdk-ts/src/workflows/memory-db.ts +39 -0
  95. package/packages/sdk-ts/src/workflows/run.ts +47 -0
  96. package/packages/sdk-ts/src/workflows/runner.ts +873 -0
  97. package/packages/sdk-ts/src/workflows/schema.json +321 -0
  98. package/packages/sdk-ts/src/workflows/state.ts +279 -0
  99. package/packages/sdk-ts/src/workflows/templates.ts +544 -0
  100. package/packages/sdk-ts/src/workflows/types.ts +178 -0
  101. package/packages/sdk-ts/tsconfig.json +6 -1
  102. package/packages/spawner/package.json +1 -1
  103. package/packages/state/package.json +1 -1
  104. package/packages/storage/package.json +2 -2
  105. package/packages/telemetry/package.json +1 -1
  106. package/packages/trajectory/package.json +2 -2
  107. package/packages/user-directory/package.json +2 -2
  108. package/packages/utils/package.json +3 -3
  109. package/packages/wrapper/package.json +5 -6
  110. package/packages/api-types/.trajectories/active/traj_xbsvuzogscey.json +0 -15
  111. package/packages/api-types/.trajectories/index.json +0 -12
  112. package/packages/api-types/dist/index.d.ts +0 -21
  113. package/packages/api-types/dist/index.d.ts.map +0 -1
  114. package/packages/api-types/dist/index.js +0 -22
  115. package/packages/api-types/dist/index.js.map +0 -1
  116. package/packages/api-types/dist/schemas/agent.d.ts +0 -259
  117. package/packages/api-types/dist/schemas/agent.d.ts.map +0 -1
  118. package/packages/api-types/dist/schemas/agent.js +0 -102
  119. package/packages/api-types/dist/schemas/agent.js.map +0 -1
  120. package/packages/api-types/dist/schemas/api.d.ts +0 -290
  121. package/packages/api-types/dist/schemas/api.d.ts.map +0 -1
  122. package/packages/api-types/dist/schemas/api.js +0 -162
  123. package/packages/api-types/dist/schemas/api.js.map +0 -1
  124. package/packages/api-types/dist/schemas/decision.d.ts +0 -230
  125. package/packages/api-types/dist/schemas/decision.d.ts.map +0 -1
  126. package/packages/api-types/dist/schemas/decision.js +0 -104
  127. package/packages/api-types/dist/schemas/decision.js.map +0 -1
  128. package/packages/api-types/dist/schemas/fleet.d.ts +0 -615
  129. package/packages/api-types/dist/schemas/fleet.d.ts.map +0 -1
  130. package/packages/api-types/dist/schemas/fleet.js +0 -71
  131. package/packages/api-types/dist/schemas/fleet.js.map +0 -1
  132. package/packages/api-types/dist/schemas/history.d.ts +0 -180
  133. package/packages/api-types/dist/schemas/history.d.ts.map +0 -1
  134. package/packages/api-types/dist/schemas/history.js +0 -72
  135. package/packages/api-types/dist/schemas/history.js.map +0 -1
  136. package/packages/api-types/dist/schemas/index.d.ts +0 -14
  137. package/packages/api-types/dist/schemas/index.d.ts.map +0 -1
  138. package/packages/api-types/dist/schemas/index.js +0 -22
  139. package/packages/api-types/dist/schemas/index.js.map +0 -1
  140. package/packages/api-types/dist/schemas/message.d.ts +0 -456
  141. package/packages/api-types/dist/schemas/message.d.ts.map +0 -1
  142. package/packages/api-types/dist/schemas/message.js +0 -88
  143. package/packages/api-types/dist/schemas/message.js.map +0 -1
  144. package/packages/api-types/dist/schemas/session.d.ts +0 -60
  145. package/packages/api-types/dist/schemas/session.d.ts.map +0 -1
  146. package/packages/api-types/dist/schemas/session.js +0 -36
  147. package/packages/api-types/dist/schemas/session.js.map +0 -1
  148. package/packages/api-types/dist/schemas/task.d.ts +0 -111
  149. package/packages/api-types/dist/schemas/task.d.ts.map +0 -1
  150. package/packages/api-types/dist/schemas/task.js +0 -64
  151. package/packages/api-types/dist/schemas/task.js.map +0 -1
  152. package/packages/api-types/package.json +0 -61
  153. package/packages/api-types/scripts/generate-openapi.ts +0 -106
  154. package/packages/api-types/src/index.ts +0 -22
  155. package/packages/api-types/src/schemas/agent.test.ts +0 -164
  156. package/packages/api-types/src/schemas/agent.ts +0 -110
  157. package/packages/api-types/src/schemas/api.test.ts +0 -372
  158. package/packages/api-types/src/schemas/api.ts +0 -194
  159. package/packages/api-types/src/schemas/decision.test.ts +0 -324
  160. package/packages/api-types/src/schemas/decision.ts +0 -136
  161. package/packages/api-types/src/schemas/fleet.test.ts +0 -212
  162. package/packages/api-types/src/schemas/fleet.ts +0 -83
  163. package/packages/api-types/src/schemas/history.test.ts +0 -242
  164. package/packages/api-types/src/schemas/history.ts +0 -84
  165. package/packages/api-types/src/schemas/index.ts +0 -148
  166. package/packages/api-types/src/schemas/message.test.ts +0 -192
  167. package/packages/api-types/src/schemas/message.ts +0 -98
  168. package/packages/api-types/src/schemas/session.test.ts +0 -104
  169. package/packages/api-types/src/schemas/session.ts +0 -40
  170. package/packages/api-types/src/schemas/task.test.ts +0 -192
  171. package/packages/api-types/src/schemas/task.ts +0 -78
  172. package/packages/api-types/tsconfig.json +0 -19
  173. package/packages/api-types/vitest.config.ts +0 -9
  174. package/packages/benchmark/README.md +0 -200
  175. package/packages/benchmark/datasets/coding-tasks.yaml +0 -127
  176. package/packages/benchmark/datasets/coordination-tasks.yaml +0 -122
  177. package/packages/benchmark/datasets/quick-test.yaml +0 -20
  178. package/packages/benchmark/dist/benchmark.d.ts +0 -47
  179. package/packages/benchmark/dist/benchmark.d.ts.map +0 -1
  180. package/packages/benchmark/dist/benchmark.js +0 -224
  181. package/packages/benchmark/dist/benchmark.js.map +0 -1
  182. package/packages/benchmark/dist/cli.d.ts +0 -8
  183. package/packages/benchmark/dist/cli.d.ts.map +0 -1
  184. package/packages/benchmark/dist/cli.js +0 -185
  185. package/packages/benchmark/dist/cli.js.map +0 -1
  186. package/packages/benchmark/dist/harbor.d.ts +0 -53
  187. package/packages/benchmark/dist/harbor.d.ts.map +0 -1
  188. package/packages/benchmark/dist/harbor.js +0 -127
  189. package/packages/benchmark/dist/harbor.js.map +0 -1
  190. package/packages/benchmark/dist/index.d.ts +0 -48
  191. package/packages/benchmark/dist/index.d.ts.map +0 -1
  192. package/packages/benchmark/dist/index.js +0 -50
  193. package/packages/benchmark/dist/index.js.map +0 -1
  194. package/packages/benchmark/dist/runners/base.d.ts +0 -63
  195. package/packages/benchmark/dist/runners/base.d.ts.map +0 -1
  196. package/packages/benchmark/dist/runners/base.js +0 -156
  197. package/packages/benchmark/dist/runners/base.js.map +0 -1
  198. package/packages/benchmark/dist/runners/index.d.ts +0 -10
  199. package/packages/benchmark/dist/runners/index.d.ts.map +0 -1
  200. package/packages/benchmark/dist/runners/index.js +0 -10
  201. package/packages/benchmark/dist/runners/index.js.map +0 -1
  202. package/packages/benchmark/dist/runners/single.d.ts +0 -19
  203. package/packages/benchmark/dist/runners/single.d.ts.map +0 -1
  204. package/packages/benchmark/dist/runners/single.js +0 -111
  205. package/packages/benchmark/dist/runners/single.js.map +0 -1
  206. package/packages/benchmark/dist/runners/subagent.d.ts +0 -32
  207. package/packages/benchmark/dist/runners/subagent.d.ts.map +0 -1
  208. package/packages/benchmark/dist/runners/subagent.js +0 -212
  209. package/packages/benchmark/dist/runners/subagent.js.map +0 -1
  210. package/packages/benchmark/dist/runners/swarm.d.ts +0 -36
  211. package/packages/benchmark/dist/runners/swarm.d.ts.map +0 -1
  212. package/packages/benchmark/dist/runners/swarm.js +0 -273
  213. package/packages/benchmark/dist/runners/swarm.js.map +0 -1
  214. package/packages/benchmark/dist/types.d.ts +0 -178
  215. package/packages/benchmark/dist/types.d.ts.map +0 -1
  216. package/packages/benchmark/dist/types.js +0 -16
  217. package/packages/benchmark/dist/types.js.map +0 -1
  218. package/packages/benchmark/package.json +0 -80
  219. package/packages/benchmark/src/benchmark.ts +0 -298
  220. package/packages/benchmark/src/cli.ts +0 -240
  221. package/packages/benchmark/src/harbor.ts +0 -170
  222. package/packages/benchmark/src/index.ts +0 -73
  223. package/packages/benchmark/src/runners/base.ts +0 -205
  224. package/packages/benchmark/src/runners/index.ts +0 -10
  225. package/packages/benchmark/src/runners/single.ts +0 -121
  226. package/packages/benchmark/src/runners/subagent.ts +0 -240
  227. package/packages/benchmark/src/runners/swarm.ts +0 -326
  228. package/packages/benchmark/src/types.ts +0 -205
  229. package/packages/benchmark/tsconfig.json +0 -20
  230. package/packages/cli-tester/README.md +0 -277
  231. package/packages/cli-tester/dist/index.d.ts +0 -21
  232. package/packages/cli-tester/dist/index.d.ts.map +0 -1
  233. package/packages/cli-tester/dist/index.js +0 -21
  234. package/packages/cli-tester/dist/index.js.map +0 -1
  235. package/packages/cli-tester/dist/utils/credential-check.d.ts +0 -56
  236. package/packages/cli-tester/dist/utils/credential-check.d.ts.map +0 -1
  237. package/packages/cli-tester/dist/utils/credential-check.js +0 -230
  238. package/packages/cli-tester/dist/utils/credential-check.js.map +0 -1
  239. package/packages/cli-tester/dist/utils/socket-client.d.ts +0 -76
  240. package/packages/cli-tester/dist/utils/socket-client.d.ts.map +0 -1
  241. package/packages/cli-tester/dist/utils/socket-client.js +0 -153
  242. package/packages/cli-tester/dist/utils/socket-client.js.map +0 -1
  243. package/packages/cli-tester/docker/Dockerfile +0 -61
  244. package/packages/cli-tester/docker/docker-compose.yml +0 -71
  245. package/packages/cli-tester/docker/entrypoint.sh +0 -58
  246. package/packages/cli-tester/package.json +0 -32
  247. package/packages/cli-tester/scripts/clear-auth.sh +0 -101
  248. package/packages/cli-tester/scripts/inject-message.sh +0 -42
  249. package/packages/cli-tester/scripts/start.sh +0 -71
  250. package/packages/cli-tester/scripts/test-cli.sh +0 -56
  251. package/packages/cli-tester/scripts/test-full-spawn.sh +0 -238
  252. package/packages/cli-tester/scripts/test-registration.sh +0 -182
  253. package/packages/cli-tester/scripts/test-setup-flow.sh +0 -202
  254. package/packages/cli-tester/scripts/test-spawn.sh +0 -140
  255. package/packages/cli-tester/scripts/test-with-daemon.sh +0 -247
  256. package/packages/cli-tester/scripts/verify-auth.sh +0 -112
  257. package/packages/cli-tester/src/index.ts +0 -40
  258. package/packages/cli-tester/src/utils/credential-check.ts +0 -284
  259. package/packages/cli-tester/src/utils/socket-client.ts +0 -211
  260. package/packages/cli-tester/tests/credential-check.test.ts +0 -56
  261. package/packages/cli-tester/tsconfig.json +0 -11
  262. package/packages/sdk/dist/browser-client.d.ts +0 -212
  263. package/packages/sdk/dist/browser-client.d.ts.map +0 -1
  264. package/packages/sdk/dist/browser-client.js +0 -750
  265. package/packages/sdk/dist/browser-client.js.map +0 -1
  266. package/packages/sdk/dist/browser-framing.d.ts +0 -46
  267. package/packages/sdk/dist/browser-framing.d.ts.map +0 -1
  268. package/packages/sdk/dist/browser-framing.js +0 -122
  269. package/packages/sdk/dist/browser-framing.js.map +0 -1
  270. package/packages/sdk/dist/standalone.d.ts +0 -89
  271. package/packages/sdk/dist/standalone.d.ts.map +0 -1
  272. package/packages/sdk/dist/standalone.js +0 -131
  273. package/packages/sdk/dist/standalone.js.map +0 -1
  274. package/packages/sdk/dist/transports/index.d.ts +0 -92
  275. package/packages/sdk/dist/transports/index.d.ts.map +0 -1
  276. package/packages/sdk/dist/transports/index.js +0 -129
  277. package/packages/sdk/dist/transports/index.js.map +0 -1
  278. package/packages/sdk/dist/transports/socket-transport.d.ts +0 -30
  279. package/packages/sdk/dist/transports/socket-transport.d.ts.map +0 -1
  280. package/packages/sdk/dist/transports/socket-transport.js +0 -94
  281. package/packages/sdk/dist/transports/socket-transport.js.map +0 -1
  282. package/packages/sdk/dist/transports/types.d.ts +0 -69
  283. package/packages/sdk/dist/transports/types.d.ts.map +0 -1
  284. package/packages/sdk/dist/transports/types.js +0 -10
  285. package/packages/sdk/dist/transports/types.js.map +0 -1
  286. package/packages/sdk/dist/transports/websocket-transport.d.ts +0 -55
  287. package/packages/sdk/dist/transports/websocket-transport.d.ts.map +0 -1
  288. package/packages/sdk/dist/transports/websocket-transport.js +0 -180
  289. package/packages/sdk/dist/transports/websocket-transport.js.map +0 -1
  290. package/packages/sdk/src/browser-client.ts +0 -985
  291. package/packages/sdk/src/browser-framing.test.ts +0 -115
  292. package/packages/sdk/src/browser-framing.ts +0 -150
  293. package/packages/sdk/src/standalone.ts +0 -183
  294. package/packages/sdk/src/transports/index.ts +0 -197
  295. package/packages/sdk/src/transports/socket-transport.ts +0 -115
  296. package/packages/sdk/src/transports/types.ts +0 -77
  297. package/packages/sdk/src/transports/websocket-transport.ts +0 -245
@@ -0,0 +1,333 @@
1
+ /**
2
+ * WorkflowRunner integration tests.
3
+ *
4
+ * Tests parsing, validation, variable resolution, and DAG execution
5
+ * with a mocked DB adapter and mocked AgentRelay.
6
+ */
7
+
8
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
9
+ import type { WorkflowDb } from '../workflows/runner.js';
10
+ import type { RelayYamlConfig, WorkflowRunRow, WorkflowStepRow } from '../workflows/types.js';
11
+
12
+ // ── Mock AgentRelay ──────────────────────────────────────────────────────────
13
+
14
+ const mockAgent = {
15
+ name: 'test-agent-abc',
16
+ waitForExit: vi.fn().mockResolvedValue(0),
17
+ release: vi.fn().mockResolvedValue(undefined),
18
+ };
19
+
20
+ const mockHuman = {
21
+ name: 'WorkflowRunner',
22
+ sendMessage: vi.fn().mockResolvedValue(undefined),
23
+ };
24
+
25
+ vi.mock('@agent-relay/sdk-ts/relay', () => ({
26
+ AgentRelay: vi.fn().mockImplementation(() => ({
27
+ spawnPty: vi.fn().mockResolvedValue(mockAgent),
28
+ human: vi.fn().mockReturnValue(mockHuman),
29
+ shutdown: vi.fn().mockResolvedValue(undefined),
30
+ })),
31
+ }));
32
+
33
+ // Import after mocking
34
+ const { WorkflowRunner } = await import('../workflows/runner.js');
35
+
36
+ // ── Test fixtures ────────────────────────────────────────────────────────────
37
+
38
+ function makeDb(): WorkflowDb {
39
+ const runs = new Map<string, WorkflowRunRow>();
40
+ const steps = new Map<string, WorkflowStepRow>();
41
+
42
+ return {
43
+ insertRun: vi.fn(async (run: WorkflowRunRow) => {
44
+ runs.set(run.id, { ...run });
45
+ }),
46
+ updateRun: vi.fn(async (id: string, patch: Partial<WorkflowRunRow>) => {
47
+ const existing = runs.get(id);
48
+ if (existing) runs.set(id, { ...existing, ...patch });
49
+ }),
50
+ getRun: vi.fn(async (id: string) => {
51
+ const run = runs.get(id);
52
+ return run ? { ...run } : null;
53
+ }),
54
+ insertStep: vi.fn(async (step: WorkflowStepRow) => {
55
+ steps.set(step.id, { ...step });
56
+ }),
57
+ updateStep: vi.fn(async (id: string, patch: Partial<WorkflowStepRow>) => {
58
+ const existing = steps.get(id);
59
+ if (existing) steps.set(id, { ...existing, ...patch });
60
+ }),
61
+ getStepsByRunId: vi.fn(async (runId: string) => {
62
+ return [...steps.values()].filter((s) => s.runId === runId);
63
+ }),
64
+ };
65
+ }
66
+
67
+ function makeConfig(overrides: Partial<RelayYamlConfig> = {}): RelayYamlConfig {
68
+ return {
69
+ version: '1',
70
+ name: 'test-workflow',
71
+ swarm: { pattern: 'dag' },
72
+ agents: [
73
+ { name: 'agent-a', cli: 'claude' },
74
+ { name: 'agent-b', cli: 'claude' },
75
+ ],
76
+ workflows: [
77
+ {
78
+ name: 'default',
79
+ steps: [
80
+ { name: 'step-1', agent: 'agent-a', task: 'Do step 1' },
81
+ { name: 'step-2', agent: 'agent-b', task: 'Do step 2', dependsOn: ['step-1'] },
82
+ ],
83
+ },
84
+ ],
85
+ ...overrides,
86
+ };
87
+ }
88
+
89
+ // ── Tests ────────────────────────────────────────────────────────────────────
90
+
91
+ describe('WorkflowRunner', () => {
92
+ let db: WorkflowDb;
93
+ let runner: InstanceType<typeof WorkflowRunner>;
94
+
95
+ beforeEach(() => {
96
+ vi.clearAllMocks();
97
+ db = makeDb();
98
+ runner = new WorkflowRunner({ db, workspaceId: 'ws-test' });
99
+ });
100
+
101
+ // ── Parsing & validation ───────────────────────────────────────────────
102
+
103
+ describe('parseYamlString', () => {
104
+ it('should parse valid YAML config', () => {
105
+ const yaml = `
106
+ version: "1"
107
+ name: test
108
+ swarm:
109
+ pattern: fan-out
110
+ agents:
111
+ - name: a1
112
+ cli: claude
113
+ `;
114
+ const config = runner.parseYamlString(yaml);
115
+ expect(config.name).toBe('test');
116
+ expect(config.swarm.pattern).toBe('fan-out');
117
+ expect(config.agents).toHaveLength(1);
118
+ });
119
+
120
+ it('should throw on null YAML', () => {
121
+ expect(() => runner.parseYamlString('null')).toThrow('config must be a non-null object');
122
+ });
123
+
124
+ it('should throw on invalid YAML syntax', () => {
125
+ expect(() => runner.parseYamlString('not: valid: yaml: []')).toThrow();
126
+ });
127
+ });
128
+
129
+ describe('validateConfig', () => {
130
+ it('should accept valid config', () => {
131
+ expect(() => runner.validateConfig(makeConfig())).not.toThrow();
132
+ });
133
+
134
+ it('should reject null config', () => {
135
+ expect(() => runner.validateConfig(null)).toThrow('non-null object');
136
+ });
137
+
138
+ it('should reject missing version', () => {
139
+ expect(() =>
140
+ runner.validateConfig({ name: 'x', swarm: { pattern: 'dag' }, agents: [{ name: 'a', cli: 'claude' }] }),
141
+ ).toThrow('missing required field "version"');
142
+ });
143
+
144
+ it('should reject missing name', () => {
145
+ expect(() =>
146
+ runner.validateConfig({ version: '1', swarm: { pattern: 'dag' }, agents: [{ name: 'a', cli: 'claude' }] }),
147
+ ).toThrow('missing required field "name"');
148
+ });
149
+
150
+ it('should reject empty agents array', () => {
151
+ expect(() =>
152
+ runner.validateConfig({ version: '1', name: 'x', swarm: { pattern: 'dag' }, agents: [] }),
153
+ ).toThrow('non-empty array');
154
+ });
155
+
156
+ it('should reject agent without cli', () => {
157
+ expect(() =>
158
+ runner.validateConfig({
159
+ version: '1',
160
+ name: 'x',
161
+ swarm: { pattern: 'dag' },
162
+ agents: [{ name: 'a' }],
163
+ }),
164
+ ).toThrow('each agent must have a string "cli"');
165
+ });
166
+
167
+ it('should detect unknown dependencies in workflows', () => {
168
+ const config = makeConfig({
169
+ workflows: [
170
+ {
171
+ name: 'wf',
172
+ steps: [
173
+ { name: 's1', agent: 'agent-a', task: 'do', dependsOn: ['nonexistent'] },
174
+ ],
175
+ },
176
+ ],
177
+ });
178
+ expect(() => runner.validateConfig(config)).toThrow('depends on unknown step "nonexistent"');
179
+ });
180
+
181
+ it('should detect dependency cycles', () => {
182
+ const config = makeConfig({
183
+ workflows: [
184
+ {
185
+ name: 'wf',
186
+ steps: [
187
+ { name: 's1', agent: 'agent-a', task: 'do', dependsOn: ['s2'] },
188
+ { name: 's2', agent: 'agent-b', task: 'do', dependsOn: ['s1'] },
189
+ ],
190
+ },
191
+ ],
192
+ });
193
+ expect(() => runner.validateConfig(config)).toThrow('dependency cycle');
194
+ });
195
+ });
196
+
197
+ // ── Variable resolution ────────────────────────────────────────────────
198
+
199
+ describe('resolveVariables', () => {
200
+ it('should replace {{var}} in agent tasks', () => {
201
+ const config = makeConfig({
202
+ agents: [
203
+ { name: 'a', cli: 'claude', task: 'Fix bug {{bugId}}' },
204
+ ],
205
+ });
206
+ const resolved = runner.resolveVariables(config, { bugId: '42' });
207
+ expect(resolved.agents[0].task).toBe('Fix bug 42');
208
+ });
209
+
210
+ it('should replace {{var}} in workflow step tasks', () => {
211
+ const config = makeConfig();
212
+ config.workflows![0].steps[0].task = 'Process {{item}}';
213
+ const resolved = runner.resolveVariables(config, { item: 'test-item' });
214
+ expect(resolved.workflows![0].steps[0].task).toBe('Process test-item');
215
+ });
216
+
217
+ it('should throw on unresolved variables', () => {
218
+ const config = makeConfig({
219
+ agents: [
220
+ { name: 'a', cli: 'claude', task: 'Fix {{unknown}}' },
221
+ ],
222
+ });
223
+ expect(() => runner.resolveVariables(config, {})).toThrow('Unresolved variable: {{unknown}}');
224
+ });
225
+
226
+ it('should not mutate original config', () => {
227
+ const config = makeConfig({
228
+ agents: [
229
+ { name: 'a', cli: 'claude', task: 'Fix {{id}}' },
230
+ ],
231
+ });
232
+ runner.resolveVariables(config, { id: '1' });
233
+ expect(config.agents[0].task).toBe('Fix {{id}}');
234
+ });
235
+ });
236
+
237
+ // ── Execution ──────────────────────────────────────────────────────────
238
+
239
+ describe('execute', () => {
240
+ it('should create run and steps in DB', async () => {
241
+ const config = makeConfig();
242
+ const run = await runner.execute(config, 'default');
243
+
244
+ expect(db.insertRun).toHaveBeenCalledTimes(1);
245
+ expect(db.insertStep).toHaveBeenCalledTimes(2);
246
+ expect(run.status).toBe('completed');
247
+ });
248
+
249
+ it('should throw when workflow not found', async () => {
250
+ const config = makeConfig();
251
+ await expect(runner.execute(config, 'nonexistent')).rejects.toThrow(
252
+ 'Workflow "nonexistent" not found',
253
+ );
254
+ });
255
+
256
+ it('should throw when no workflows defined', async () => {
257
+ const config = makeConfig({ workflows: undefined });
258
+ await expect(runner.execute(config)).rejects.toThrow('No workflows defined');
259
+ });
260
+
261
+ it('should emit run:started and run:completed events', async () => {
262
+ const events: string[] = [];
263
+ runner.on((event) => events.push(event.type));
264
+
265
+ await runner.execute(makeConfig(), 'default');
266
+
267
+ expect(events).toContain('run:started');
268
+ expect(events).toContain('run:completed');
269
+ });
270
+
271
+ it('should emit step events in order', async () => {
272
+ const stepEvents: Array<{ type: string; stepName?: string }> = [];
273
+ runner.on((event) => {
274
+ if (event.type.startsWith('step:')) {
275
+ stepEvents.push({
276
+ type: event.type,
277
+ stepName: 'stepName' in event ? event.stepName : undefined,
278
+ });
279
+ }
280
+ });
281
+
282
+ await runner.execute(makeConfig(), 'default');
283
+
284
+ const startedSteps = stepEvents.filter((e) => e.type === 'step:started');
285
+ expect(startedSteps).toHaveLength(2);
286
+ });
287
+
288
+ it('should resolve variables during execution', async () => {
289
+ const config = makeConfig();
290
+ config.workflows![0].steps[0].task = 'Build {{feature}}';
291
+ const run = await runner.execute(config, 'default', { feature: 'auth' });
292
+ expect(run.status).toBe('completed');
293
+ });
294
+ });
295
+
296
+ // ── Event subscription ─────────────────────────────────────────────────
297
+
298
+ describe('on / event subscription', () => {
299
+ it('should return unsubscribe function', async () => {
300
+ const events: string[] = [];
301
+ const unsub = runner.on((event) => events.push(event.type));
302
+
303
+ await runner.execute(makeConfig(), 'default');
304
+ const count = events.length;
305
+
306
+ unsub();
307
+ // Events after unsubscribe are not captured (no second execute needed to prove this,
308
+ // just verify the unsub function works without error)
309
+ expect(count).toBeGreaterThan(0);
310
+ });
311
+ });
312
+
313
+ // ── Pause / abort ──────────────────────────────────────────────────────
314
+
315
+ describe('pause and abort', () => {
316
+ it('should support pause/unpause without error', () => {
317
+ expect(() => runner.pause()).not.toThrow();
318
+ expect(() => runner.unpause()).not.toThrow();
319
+ });
320
+
321
+ it('should support abort without error', () => {
322
+ expect(() => runner.abort()).not.toThrow();
323
+ });
324
+ });
325
+
326
+ // ── Resume ─────────────────────────────────────────────────────────────
327
+
328
+ describe('resume', () => {
329
+ it('should throw when run not found', async () => {
330
+ await expect(runner.resume('nonexistent')).rejects.toThrow('Run "nonexistent" not found');
331
+ });
332
+ });
333
+ });
@@ -6,3 +6,4 @@ export * from "./relay.js";
6
6
  export * from "./logs.js";
7
7
  export * from "./consensus.js";
8
8
  export * from "./shadow.js";
9
+ export * from "./workflows/index.js";