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,56 @@
1
+ # Agent Relay Python SDK
2
+
3
+ Python SDK for defining and running Agent Relay workflows.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install agent-relay
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Builder API
14
+
15
+ ```python
16
+ from agent_relay import workflow
17
+
18
+ result = (
19
+ workflow("my-migration")
20
+ .pattern("dag")
21
+ .agent("backend", cli="claude", role="Backend engineer")
22
+ .agent("tester", cli="claude", role="Test engineer")
23
+ .step("build", agent="backend", task="Build the API endpoints")
24
+ .step("test", agent="tester", task="Write tests", depends_on=["build"])
25
+ .run()
26
+ )
27
+ ```
28
+
29
+ ### Run from YAML
30
+
31
+ ```python
32
+ from agent_relay import run_yaml
33
+
34
+ result = run_yaml("workflows/daytona-migration.yaml")
35
+ ```
36
+
37
+ ### Export to YAML
38
+
39
+ ```python
40
+ config_yaml = (
41
+ workflow("my-workflow")
42
+ .pattern("fan-out")
43
+ .agent("worker", cli="claude")
44
+ .step("task1", agent="worker", task="Do something")
45
+ .to_yaml()
46
+ )
47
+ ```
48
+
49
+ ## Requirements
50
+
51
+ - Python 3.10+
52
+ - `agent-relay` CLI installed (`npm install -g agent-relay`)
53
+
54
+ ## License
55
+
56
+ Apache-2.0 — Copyright 2025 Agent Workforce Incorporated
@@ -0,0 +1,23 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "agent-relay"
7
+ version = "0.1.0"
8
+ description = "Python SDK for Agent Relay workflows"
9
+ readme = "README.md"
10
+ license = "Apache-2.0"
11
+ requires-python = ">=3.10"
12
+ dependencies = [
13
+ "pyyaml>=6.0",
14
+ ]
15
+
16
+ [project.optional-dependencies]
17
+ dev = [
18
+ "pytest>=8.0",
19
+ "pytest-asyncio>=0.23",
20
+ ]
21
+
22
+ [tool.hatch.build.targets.wheel]
23
+ packages = ["src/agent_relay"]
@@ -0,0 +1,27 @@
1
+ """Agent Relay Python SDK — workflow builder and runner."""
2
+
3
+ from .builder import workflow, WorkflowBuilder, run_yaml
4
+ from .types import (
5
+ AgentOptions,
6
+ StepOptions,
7
+ ErrorOptions,
8
+ RunOptions,
9
+ WorkflowResult,
10
+ SwarmPattern,
11
+ AgentCli,
12
+ VerificationCheck,
13
+ )
14
+
15
+ __all__ = [
16
+ "workflow",
17
+ "WorkflowBuilder",
18
+ "run_yaml",
19
+ "AgentOptions",
20
+ "StepOptions",
21
+ "ErrorOptions",
22
+ "RunOptions",
23
+ "WorkflowResult",
24
+ "SwarmPattern",
25
+ "AgentCli",
26
+ "VerificationCheck",
27
+ ]
@@ -0,0 +1,367 @@
1
+ """Fluent workflow builder for Agent Relay.
2
+
3
+ Example::
4
+
5
+ from agent_relay import workflow
6
+
7
+ result = (
8
+ workflow("my-migration")
9
+ .pattern("dag")
10
+ .agent("backend", cli="claude", role="Backend engineer")
11
+ .agent("tester", cli="claude", role="Test engineer")
12
+ .step("build", agent="backend", task="Build the API endpoints")
13
+ .step("test", agent="tester", task="Write integration tests", depends_on=["build"])
14
+ .run()
15
+ )
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import json
21
+ import shutil
22
+ import subprocess
23
+ import tempfile
24
+ from pathlib import Path
25
+ from typing import Any
26
+
27
+ import yaml
28
+
29
+ from .types import (
30
+ AgentCli,
31
+ AgentOptions,
32
+ ErrorOptions,
33
+ RunOptions,
34
+ StepOptions,
35
+ StepResult,
36
+ SwarmPattern,
37
+ VerificationCheck,
38
+ WorkflowResult,
39
+ )
40
+
41
+
42
+ class WorkflowBuilder:
43
+ """Fluent builder that constructs a RelayYamlConfig and runs it via agent-relay CLI."""
44
+
45
+ def __init__(self, name: str) -> None:
46
+ self._name = name
47
+ self._description: str | None = None
48
+ self._pattern: SwarmPattern = "dag"
49
+ self._max_concurrency: int | None = None
50
+ self._timeout_ms: int | None = None
51
+ self._channel: str | None = None
52
+ self._agents: list[dict[str, Any]] = []
53
+ self._steps: list[dict[str, Any]] = []
54
+ self._error_handling: dict[str, Any] | None = None
55
+
56
+ def description(self, desc: str) -> WorkflowBuilder:
57
+ """Set workflow description."""
58
+ self._description = desc
59
+ return self
60
+
61
+ def pattern(self, p: SwarmPattern) -> WorkflowBuilder:
62
+ """Set swarm pattern (default: "dag")."""
63
+ self._pattern = p
64
+ return self
65
+
66
+ def max_concurrency(self, n: int) -> WorkflowBuilder:
67
+ """Set maximum concurrent agents."""
68
+ self._max_concurrency = n
69
+ return self
70
+
71
+ def timeout(self, ms: int) -> WorkflowBuilder:
72
+ """Set global timeout in milliseconds."""
73
+ self._timeout_ms = ms
74
+ return self
75
+
76
+ def channel(self, ch: str) -> WorkflowBuilder:
77
+ """Set the relay channel for agent communication."""
78
+ self._channel = ch
79
+ return self
80
+
81
+ def agent(
82
+ self,
83
+ name: str,
84
+ *,
85
+ cli: AgentCli = "claude",
86
+ role: str | None = None,
87
+ task: str | None = None,
88
+ channels: list[str] | None = None,
89
+ model: str | None = None,
90
+ max_tokens: int | None = None,
91
+ timeout_ms: int | None = None,
92
+ retries: int | None = None,
93
+ ) -> WorkflowBuilder:
94
+ """Add an agent definition."""
95
+ opts = AgentOptions(
96
+ cli=cli,
97
+ role=role,
98
+ task=task,
99
+ channels=channels,
100
+ model=model,
101
+ max_tokens=max_tokens,
102
+ timeout_ms=timeout_ms,
103
+ retries=retries,
104
+ )
105
+ agent_def: dict[str, Any] = {"name": name, "cli": opts.cli}
106
+
107
+ if opts.role is not None:
108
+ agent_def["role"] = opts.role
109
+ if opts.task is not None:
110
+ agent_def["task"] = opts.task
111
+ if opts.channels is not None:
112
+ agent_def["channels"] = opts.channels
113
+
114
+ constraints: dict[str, Any] = {}
115
+ if opts.model is not None:
116
+ constraints["model"] = opts.model
117
+ if opts.max_tokens is not None:
118
+ constraints["maxTokens"] = opts.max_tokens
119
+ if opts.timeout_ms is not None:
120
+ constraints["timeoutMs"] = opts.timeout_ms
121
+ if opts.retries is not None:
122
+ constraints["retries"] = opts.retries
123
+ if constraints:
124
+ agent_def["constraints"] = constraints
125
+
126
+ self._agents.append(agent_def)
127
+ return self
128
+
129
+ def step(
130
+ self,
131
+ name: str,
132
+ *,
133
+ agent: str,
134
+ task: str,
135
+ depends_on: list[str] | None = None,
136
+ verification: VerificationCheck | None = None,
137
+ timeout_ms: int | None = None,
138
+ retries: int | None = None,
139
+ ) -> WorkflowBuilder:
140
+ """Add a workflow step."""
141
+ opts = StepOptions(
142
+ agent=agent,
143
+ task=task,
144
+ depends_on=depends_on,
145
+ verification=verification,
146
+ timeout_ms=timeout_ms,
147
+ retries=retries,
148
+ )
149
+ step_def: dict[str, Any] = {
150
+ "name": name,
151
+ "agent": opts.agent,
152
+ "task": opts.task,
153
+ }
154
+
155
+ if opts.depends_on is not None:
156
+ step_def["dependsOn"] = opts.depends_on
157
+ if opts.verification is not None:
158
+ step_def["verification"] = opts.verification.to_dict()
159
+ if opts.timeout_ms is not None:
160
+ step_def["timeoutMs"] = opts.timeout_ms
161
+ if opts.retries is not None:
162
+ step_def["retries"] = opts.retries
163
+
164
+ self._steps.append(step_def)
165
+ return self
166
+
167
+ def on_error(
168
+ self,
169
+ strategy: str,
170
+ *,
171
+ max_retries: int | None = None,
172
+ retry_delay_ms: int | None = None,
173
+ notify_channel: str | None = None,
174
+ ) -> WorkflowBuilder:
175
+ """Set error handling strategy."""
176
+ opts = ErrorOptions(
177
+ max_retries=max_retries,
178
+ retry_delay_ms=retry_delay_ms,
179
+ notify_channel=notify_channel,
180
+ )
181
+ self._error_handling = {"strategy": strategy}
182
+ if opts.max_retries is not None:
183
+ self._error_handling["maxRetries"] = opts.max_retries
184
+ if opts.retry_delay_ms is not None:
185
+ self._error_handling["retryDelayMs"] = opts.retry_delay_ms
186
+ if opts.notify_channel is not None:
187
+ self._error_handling["notifyChannel"] = opts.notify_channel
188
+ return self
189
+
190
+ def to_config(self) -> dict[str, Any]:
191
+ """Build and return the config as a dictionary (matches RelayYamlConfig shape)."""
192
+ if not self._agents:
193
+ raise ValueError("Workflow must have at least one agent")
194
+ if not self._steps:
195
+ raise ValueError("Workflow must have at least one step")
196
+
197
+ swarm: dict[str, Any] = {"pattern": self._pattern}
198
+ if self._max_concurrency is not None:
199
+ swarm["maxConcurrency"] = self._max_concurrency
200
+ if self._timeout_ms is not None:
201
+ swarm["timeoutMs"] = self._timeout_ms
202
+ if self._channel is not None:
203
+ swarm["channel"] = self._channel
204
+
205
+ config: dict[str, Any] = {
206
+ "version": "1.0",
207
+ "name": self._name,
208
+ "swarm": swarm,
209
+ "agents": list(self._agents),
210
+ "workflows": [
211
+ {
212
+ "name": f"{self._name}-workflow",
213
+ "steps": list(self._steps),
214
+ }
215
+ ],
216
+ }
217
+
218
+ if self._description is not None:
219
+ config["description"] = self._description
220
+ if self._error_handling is not None:
221
+ config["errorHandling"] = self._error_handling
222
+
223
+ return config
224
+
225
+ def to_yaml(self) -> str:
226
+ """Serialize the config to a YAML string."""
227
+ return yaml.dump(self.to_config(), default_flow_style=False, sort_keys=False)
228
+
229
+ def run(self, options: RunOptions | None = None) -> WorkflowResult:
230
+ """Build the config, write to a temp YAML file, and execute via agent-relay CLI.
231
+
232
+ Requires ``agent-relay`` to be installed and available on PATH.
233
+ """
234
+ opts = options or RunOptions()
235
+
236
+ cmd_prefix = _find_agent_relay()
237
+ if cmd_prefix is None:
238
+ raise RuntimeError(
239
+ "agent-relay CLI not found. Install it with: npm install -g agent-relay"
240
+ )
241
+
242
+ config_yaml = self.to_yaml()
243
+
244
+ with tempfile.NamedTemporaryFile(
245
+ mode="w", suffix=".yaml", prefix="relay-workflow-", delete=False
246
+ ) as f:
247
+ f.write(config_yaml)
248
+ yaml_path = f.name
249
+
250
+ try:
251
+ cmd = [*cmd_prefix, "run", yaml_path]
252
+ if opts.workflow:
253
+ cmd.extend(["--workflow", opts.workflow])
254
+
255
+ result = subprocess.run(
256
+ cmd,
257
+ capture_output=True,
258
+ text=True,
259
+ cwd=opts.cwd,
260
+ )
261
+
262
+ return _parse_cli_output(result)
263
+
264
+ finally:
265
+ Path(yaml_path).unlink(missing_ok=True)
266
+
267
+
268
+ def workflow(name: str) -> WorkflowBuilder:
269
+ """Create a new workflow builder.
270
+
271
+ Example::
272
+
273
+ from agent_relay import workflow
274
+
275
+ result = (
276
+ workflow("feature-build")
277
+ .pattern("dag")
278
+ .agent("dev", cli="claude", role="Developer")
279
+ .step("implement", agent="dev", task="Build the feature")
280
+ .run()
281
+ )
282
+ """
283
+ return WorkflowBuilder(name)
284
+
285
+
286
+ def run_yaml(yaml_path: str, options: RunOptions | None = None) -> WorkflowResult:
287
+ """Run an existing relay.yaml workflow file.
288
+
289
+ Example::
290
+
291
+ from agent_relay import run_yaml
292
+
293
+ result = run_yaml("workflows/daytona-migration.yaml")
294
+ """
295
+ opts = options or RunOptions()
296
+
297
+ cmd_prefix = _find_agent_relay()
298
+ if cmd_prefix is None:
299
+ raise RuntimeError(
300
+ "agent-relay CLI not found. Install it with: npm install -g agent-relay"
301
+ )
302
+
303
+ cmd = [*cmd_prefix, "run", yaml_path]
304
+ if opts.workflow:
305
+ cmd.extend(["--workflow", opts.workflow])
306
+
307
+ result = subprocess.run(
308
+ cmd,
309
+ capture_output=True,
310
+ text=True,
311
+ cwd=opts.cwd,
312
+ )
313
+
314
+ return _parse_cli_output(result)
315
+
316
+
317
+ # ── Internal helpers ─────────────────────────────────────────────────────────
318
+
319
+
320
+ def _find_agent_relay() -> list[str] | None:
321
+ """Find the agent-relay binary on PATH or via npx. Returns the command prefix."""
322
+ binary = shutil.which("agent-relay")
323
+ if binary:
324
+ return [binary]
325
+
326
+ # Check if npx is available as fallback
327
+ npx = shutil.which("npx")
328
+ if npx:
329
+ return [npx, "agent-relay"]
330
+
331
+ return None
332
+
333
+
334
+ def _parse_cli_output(result: subprocess.CompletedProcess[str]) -> WorkflowResult:
335
+ """Parse agent-relay CLI output into a WorkflowResult."""
336
+ output = result.stdout.strip()
337
+ lines = output.split("\n") if output else []
338
+
339
+ steps: list[StepResult] = []
340
+ for line in lines:
341
+ if line.startswith("[step]"):
342
+ parts = line.removeprefix("[step]").strip().split(" ", 1)
343
+ if len(parts) >= 2:
344
+ step_name = parts[0]
345
+ rest = parts[1]
346
+ if "completed" in rest:
347
+ steps.append(StepResult(name=step_name, status="completed"))
348
+ elif "failed" in rest:
349
+ error = rest.split(":", 1)[1].strip() if ":" in rest else None
350
+ steps.append(StepResult(name=step_name, status="failed", error=error))
351
+ elif "skipped" in rest:
352
+ steps.append(StepResult(name=step_name, status="skipped"))
353
+
354
+ if result.returncode == 0:
355
+ return WorkflowResult(
356
+ status="completed",
357
+ run_id="", # CLI doesn't expose run ID yet
358
+ steps=steps,
359
+ )
360
+
361
+ error = result.stderr.strip() or result.stdout.strip()
362
+ return WorkflowResult(
363
+ status="failed",
364
+ run_id="",
365
+ error=error,
366
+ steps=steps,
367
+ )
@@ -0,0 +1,92 @@
1
+ """Type definitions for Agent Relay workflows."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import Literal
7
+
8
+ SwarmPattern = Literal[
9
+ "fan-out",
10
+ "pipeline",
11
+ "hub-spoke",
12
+ "consensus",
13
+ "mesh",
14
+ "handoff",
15
+ "cascade",
16
+ "dag",
17
+ "debate",
18
+ "hierarchical",
19
+ ]
20
+
21
+ AgentCli = Literal["claude", "codex", "gemini", "aider", "goose"]
22
+
23
+
24
+ @dataclass
25
+ class VerificationCheck:
26
+ type: Literal["output_contains", "exit_code", "file_exists", "custom"]
27
+ value: str
28
+ description: str | None = None
29
+
30
+ def to_dict(self) -> dict:
31
+ d: dict = {"type": self.type, "value": self.value}
32
+ if self.description is not None:
33
+ d["description"] = self.description
34
+ return d
35
+
36
+
37
+ @dataclass
38
+ class AgentOptions:
39
+ cli: AgentCli
40
+ role: str | None = None
41
+ task: str | None = None
42
+ channels: list[str] | None = None
43
+ model: str | None = None
44
+ max_tokens: int | None = None
45
+ timeout_ms: int | None = None
46
+ retries: int | None = None
47
+
48
+
49
+ @dataclass
50
+ class StepOptions:
51
+ agent: str
52
+ task: str
53
+ depends_on: list[str] | None = None
54
+ verification: VerificationCheck | None = None
55
+ timeout_ms: int | None = None
56
+ retries: int | None = None
57
+
58
+
59
+ @dataclass
60
+ class ErrorOptions:
61
+ max_retries: int | None = None
62
+ retry_delay_ms: int | None = None
63
+ notify_channel: str | None = None
64
+
65
+
66
+ @dataclass
67
+ class RunOptions:
68
+ """Options for running a workflow."""
69
+
70
+ workflow: str | None = None
71
+ cwd: str | None = None
72
+ vars: dict[str, str] | None = None
73
+
74
+
75
+ @dataclass
76
+ class WorkflowResult:
77
+ """Result of a workflow execution."""
78
+
79
+ status: str
80
+ run_id: str
81
+ error: str | None = None
82
+ steps: list[StepResult] = field(default_factory=list)
83
+
84
+
85
+ @dataclass
86
+ class StepResult:
87
+ """Result of a single workflow step."""
88
+
89
+ name: str
90
+ status: str
91
+ output: str | None = None
92
+ error: str | None = None
File without changes
@@ -0,0 +1,101 @@
1
+ """Tests for the workflow builder."""
2
+
3
+ import yaml
4
+ import pytest
5
+ from agent_relay import workflow, VerificationCheck
6
+
7
+
8
+ def test_basic_builder():
9
+ config = (
10
+ workflow("test-workflow")
11
+ .pattern("dag")
12
+ .agent("worker", cli="claude", role="Test worker")
13
+ .step("do-work", agent="worker", task="Do the work")
14
+ .to_config()
15
+ )
16
+
17
+ assert config["version"] == "1.0"
18
+ assert config["name"] == "test-workflow"
19
+ assert config["swarm"]["pattern"] == "dag"
20
+ assert len(config["agents"]) == 1
21
+ assert config["agents"][0]["name"] == "worker"
22
+ assert config["agents"][0]["cli"] == "claude"
23
+ assert config["agents"][0]["role"] == "Test worker"
24
+ assert len(config["workflows"]) == 1
25
+ assert config["workflows"][0]["name"] == "test-workflow-workflow"
26
+ assert len(config["workflows"][0]["steps"]) == 1
27
+
28
+
29
+ def test_full_builder():
30
+ config = (
31
+ workflow("migration")
32
+ .description("Full migration workflow")
33
+ .pattern("dag")
34
+ .max_concurrency(3)
35
+ .timeout(5_400_000)
36
+ .channel("migration-channel")
37
+ .agent("backend", cli="claude", role="Backend engineer")
38
+ .agent("tester", cli="codex", role="Test engineer", model="gpt-4")
39
+ .step(
40
+ "build",
41
+ agent="backend",
42
+ task="Build the API",
43
+ verification=VerificationCheck(type="output_contains", value="BUILD_DONE"),
44
+ retries=2,
45
+ )
46
+ .step(
47
+ "test",
48
+ agent="tester",
49
+ task="Run tests",
50
+ depends_on=["build"],
51
+ timeout_ms=600_000,
52
+ )
53
+ .on_error("retry", max_retries=2, retry_delay_ms=5000)
54
+ .to_config()
55
+ )
56
+
57
+ assert config["description"] == "Full migration workflow"
58
+ assert config["swarm"]["maxConcurrency"] == 3
59
+ assert config["swarm"]["timeoutMs"] == 5_400_000
60
+ assert config["swarm"]["channel"] == "migration-channel"
61
+
62
+ assert len(config["agents"]) == 2
63
+ assert config["agents"][1]["cli"] == "codex"
64
+ assert config["agents"][1]["constraints"]["model"] == "gpt-4"
65
+
66
+ steps = config["workflows"][0]["steps"]
67
+ assert steps[0]["verification"] == {"type": "output_contains", "value": "BUILD_DONE"}
68
+ assert steps[0]["retries"] == 2
69
+ assert steps[1]["dependsOn"] == ["build"]
70
+ assert steps[1]["timeoutMs"] == 600_000
71
+
72
+ assert config["errorHandling"]["strategy"] == "retry"
73
+ assert config["errorHandling"]["maxRetries"] == 2
74
+
75
+
76
+ def test_to_yaml_roundtrip():
77
+ builder = (
78
+ workflow("roundtrip")
79
+ .pattern("fan-out")
80
+ .agent("a", cli="claude")
81
+ .step("s1", agent="a", task="Do something")
82
+ )
83
+
84
+ yaml_str = builder.to_yaml()
85
+ parsed = yaml.safe_load(yaml_str)
86
+
87
+ assert parsed["version"] == "1.0"
88
+ assert parsed["name"] == "roundtrip"
89
+ assert parsed["swarm"]["pattern"] == "fan-out"
90
+ assert parsed["agents"][0]["name"] == "a"
91
+ assert parsed["workflows"][0]["steps"][0]["task"] == "Do something"
92
+
93
+
94
+ def test_empty_agents_raises():
95
+ with pytest.raises(ValueError, match="at least one agent"):
96
+ workflow("empty").pattern("dag").step("s", agent="a", task="x").to_config()
97
+
98
+
99
+ def test_empty_steps_raises():
100
+ with pytest.raises(ValueError, match="at least one step"):
101
+ workflow("empty").pattern("dag").agent("a", cli="claude").to_config()
@@ -6,4 +6,5 @@ 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";
9
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC"}
@@ -6,4 +6,5 @@ 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";
9
10
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC"}