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.
- package/README.md +1 -1
- package/dist/src/cli/index.js +124 -7
- package/dist/src/cli/index.js.map +1 -1
- package/package.json +23 -26
- package/packages/acp-bridge/package.json +2 -2
- package/packages/bridge/package.json +7 -7
- package/packages/config/package.json +2 -2
- package/packages/continuity/package.json +2 -2
- package/packages/daemon/package.json +12 -12
- package/packages/hooks/package.json +4 -4
- package/packages/mcp/package.json +5 -5
- package/packages/memory/package.json +2 -2
- package/packages/policy/package.json +2 -2
- package/packages/protocol/package.json +1 -1
- package/packages/resiliency/package.json +1 -1
- package/packages/sdk/dist/index.d.ts +1 -29
- package/packages/sdk/dist/index.d.ts.map +1 -1
- package/packages/sdk/dist/index.js +1 -38
- package/packages/sdk/dist/index.js.map +1 -1
- package/packages/sdk/package.json +4 -25
- package/packages/sdk/src/index.ts +1 -69
- package/packages/sdk-py/README.md +56 -0
- package/packages/sdk-py/pyproject.toml +23 -0
- package/packages/sdk-py/src/agent_relay/__init__.py +27 -0
- package/packages/sdk-py/src/agent_relay/builder.py +367 -0
- package/packages/sdk-py/src/agent_relay/types.py +92 -0
- package/packages/sdk-py/tests/__init__.py +0 -0
- package/packages/sdk-py/tests/test_builder.py +101 -0
- package/packages/sdk-ts/dist/index.d.ts +1 -0
- package/packages/sdk-ts/dist/index.d.ts.map +1 -1
- package/packages/sdk-ts/dist/index.js +1 -0
- package/packages/sdk-ts/dist/index.js.map +1 -1
- package/packages/sdk-ts/dist/workflows/barrier.d.ts +72 -0
- package/packages/sdk-ts/dist/workflows/barrier.d.ts.map +1 -0
- package/packages/sdk-ts/dist/workflows/barrier.js +162 -0
- package/packages/sdk-ts/dist/workflows/barrier.js.map +1 -0
- package/packages/sdk-ts/dist/workflows/builder.d.ts +101 -0
- package/packages/sdk-ts/dist/workflows/builder.d.ts.map +1 -0
- package/packages/sdk-ts/dist/workflows/builder.js +179 -0
- package/packages/sdk-ts/dist/workflows/builder.js.map +1 -0
- package/packages/sdk-ts/dist/workflows/cli.d.ts +10 -0
- package/packages/sdk-ts/dist/workflows/cli.d.ts.map +1 -0
- package/packages/sdk-ts/dist/workflows/cli.js +82 -0
- package/packages/sdk-ts/dist/workflows/cli.js.map +1 -0
- package/packages/sdk-ts/dist/workflows/coordinator.d.ts +68 -0
- package/packages/sdk-ts/dist/workflows/coordinator.d.ts.map +1 -0
- package/packages/sdk-ts/dist/workflows/coordinator.js +353 -0
- package/packages/sdk-ts/dist/workflows/coordinator.js.map +1 -0
- package/packages/sdk-ts/dist/workflows/index.d.ts +10 -0
- package/packages/sdk-ts/dist/workflows/index.d.ts.map +1 -0
- package/packages/sdk-ts/dist/workflows/index.js +10 -0
- package/packages/sdk-ts/dist/workflows/index.js.map +1 -0
- package/packages/sdk-ts/dist/workflows/memory-db.d.ts +17 -0
- package/packages/sdk-ts/dist/workflows/memory-db.d.ts.map +1 -0
- package/packages/sdk-ts/dist/workflows/memory-db.js +33 -0
- package/packages/sdk-ts/dist/workflows/memory-db.js.map +1 -0
- package/packages/sdk-ts/dist/workflows/run.d.ts +31 -0
- package/packages/sdk-ts/dist/workflows/run.d.ts.map +1 -0
- package/packages/sdk-ts/dist/workflows/run.js +24 -0
- package/packages/sdk-ts/dist/workflows/run.js.map +1 -0
- package/packages/sdk-ts/dist/workflows/runner.d.ts +119 -0
- package/packages/sdk-ts/dist/workflows/runner.d.ts.map +1 -0
- package/packages/sdk-ts/dist/workflows/runner.js +650 -0
- package/packages/sdk-ts/dist/workflows/runner.js.map +1 -0
- package/packages/sdk-ts/dist/workflows/state.d.ts +77 -0
- package/packages/sdk-ts/dist/workflows/state.d.ts.map +1 -0
- package/packages/sdk-ts/dist/workflows/state.js +140 -0
- package/packages/sdk-ts/dist/workflows/state.js.map +1 -0
- package/packages/sdk-ts/dist/workflows/templates.d.ts +47 -0
- package/packages/sdk-ts/dist/workflows/templates.d.ts.map +1 -0
- package/packages/sdk-ts/dist/workflows/templates.js +395 -0
- package/packages/sdk-ts/dist/workflows/templates.js.map +1 -0
- package/packages/sdk-ts/dist/workflows/types.d.ts +126 -0
- package/packages/sdk-ts/dist/workflows/types.d.ts.map +1 -0
- package/packages/sdk-ts/dist/workflows/types.js +8 -0
- package/packages/sdk-ts/dist/workflows/types.js.map +1 -0
- package/packages/sdk-ts/package.json +8 -2
- package/packages/sdk-ts/src/__tests__/error-scenarios.test.ts +682 -0
- package/packages/sdk-ts/src/__tests__/swarm-coordinator.test.ts +416 -0
- package/packages/sdk-ts/src/__tests__/workflow-runner.test.ts +333 -0
- package/packages/sdk-ts/src/index.ts +1 -0
- package/packages/sdk-ts/src/workflows/README.md +450 -0
- package/packages/sdk-ts/src/workflows/barrier.ts +254 -0
- package/packages/sdk-ts/src/workflows/builder.ts +241 -0
- package/packages/sdk-ts/src/workflows/builtin-templates/bug-fix.yaml +75 -0
- package/packages/sdk-ts/src/workflows/builtin-templates/code-review.yaml +82 -0
- package/packages/sdk-ts/src/workflows/builtin-templates/documentation.yaml +70 -0
- package/packages/sdk-ts/src/workflows/builtin-templates/feature-dev.yaml +76 -0
- package/packages/sdk-ts/src/workflows/builtin-templates/refactor.yaml +82 -0
- package/packages/sdk-ts/src/workflows/builtin-templates/security-audit.yaml +84 -0
- package/packages/sdk-ts/src/workflows/cli.ts +93 -0
- package/packages/sdk-ts/src/workflows/coordinator.ts +520 -0
- package/packages/sdk-ts/src/workflows/index.ts +9 -0
- package/packages/sdk-ts/src/workflows/memory-db.ts +39 -0
- package/packages/sdk-ts/src/workflows/run.ts +47 -0
- package/packages/sdk-ts/src/workflows/runner.ts +873 -0
- package/packages/sdk-ts/src/workflows/schema.json +321 -0
- package/packages/sdk-ts/src/workflows/state.ts +279 -0
- package/packages/sdk-ts/src/workflows/templates.ts +544 -0
- package/packages/sdk-ts/src/workflows/types.ts +178 -0
- package/packages/sdk-ts/tsconfig.json +6 -1
- package/packages/spawner/package.json +1 -1
- package/packages/state/package.json +1 -1
- package/packages/storage/package.json +2 -2
- package/packages/telemetry/package.json +1 -1
- package/packages/trajectory/package.json +2 -2
- package/packages/user-directory/package.json +2 -2
- package/packages/utils/package.json +3 -3
- package/packages/wrapper/package.json +5 -6
- package/packages/api-types/.trajectories/active/traj_xbsvuzogscey.json +0 -15
- package/packages/api-types/.trajectories/index.json +0 -12
- package/packages/api-types/dist/index.d.ts +0 -21
- package/packages/api-types/dist/index.d.ts.map +0 -1
- package/packages/api-types/dist/index.js +0 -22
- package/packages/api-types/dist/index.js.map +0 -1
- package/packages/api-types/dist/schemas/agent.d.ts +0 -259
- package/packages/api-types/dist/schemas/agent.d.ts.map +0 -1
- package/packages/api-types/dist/schemas/agent.js +0 -102
- package/packages/api-types/dist/schemas/agent.js.map +0 -1
- package/packages/api-types/dist/schemas/api.d.ts +0 -290
- package/packages/api-types/dist/schemas/api.d.ts.map +0 -1
- package/packages/api-types/dist/schemas/api.js +0 -162
- package/packages/api-types/dist/schemas/api.js.map +0 -1
- package/packages/api-types/dist/schemas/decision.d.ts +0 -230
- package/packages/api-types/dist/schemas/decision.d.ts.map +0 -1
- package/packages/api-types/dist/schemas/decision.js +0 -104
- package/packages/api-types/dist/schemas/decision.js.map +0 -1
- package/packages/api-types/dist/schemas/fleet.d.ts +0 -615
- package/packages/api-types/dist/schemas/fleet.d.ts.map +0 -1
- package/packages/api-types/dist/schemas/fleet.js +0 -71
- package/packages/api-types/dist/schemas/fleet.js.map +0 -1
- package/packages/api-types/dist/schemas/history.d.ts +0 -180
- package/packages/api-types/dist/schemas/history.d.ts.map +0 -1
- package/packages/api-types/dist/schemas/history.js +0 -72
- package/packages/api-types/dist/schemas/history.js.map +0 -1
- package/packages/api-types/dist/schemas/index.d.ts +0 -14
- package/packages/api-types/dist/schemas/index.d.ts.map +0 -1
- package/packages/api-types/dist/schemas/index.js +0 -22
- package/packages/api-types/dist/schemas/index.js.map +0 -1
- package/packages/api-types/dist/schemas/message.d.ts +0 -456
- package/packages/api-types/dist/schemas/message.d.ts.map +0 -1
- package/packages/api-types/dist/schemas/message.js +0 -88
- package/packages/api-types/dist/schemas/message.js.map +0 -1
- package/packages/api-types/dist/schemas/session.d.ts +0 -60
- package/packages/api-types/dist/schemas/session.d.ts.map +0 -1
- package/packages/api-types/dist/schemas/session.js +0 -36
- package/packages/api-types/dist/schemas/session.js.map +0 -1
- package/packages/api-types/dist/schemas/task.d.ts +0 -111
- package/packages/api-types/dist/schemas/task.d.ts.map +0 -1
- package/packages/api-types/dist/schemas/task.js +0 -64
- package/packages/api-types/dist/schemas/task.js.map +0 -1
- package/packages/api-types/package.json +0 -61
- package/packages/api-types/scripts/generate-openapi.ts +0 -106
- package/packages/api-types/src/index.ts +0 -22
- package/packages/api-types/src/schemas/agent.test.ts +0 -164
- package/packages/api-types/src/schemas/agent.ts +0 -110
- package/packages/api-types/src/schemas/api.test.ts +0 -372
- package/packages/api-types/src/schemas/api.ts +0 -194
- package/packages/api-types/src/schemas/decision.test.ts +0 -324
- package/packages/api-types/src/schemas/decision.ts +0 -136
- package/packages/api-types/src/schemas/fleet.test.ts +0 -212
- package/packages/api-types/src/schemas/fleet.ts +0 -83
- package/packages/api-types/src/schemas/history.test.ts +0 -242
- package/packages/api-types/src/schemas/history.ts +0 -84
- package/packages/api-types/src/schemas/index.ts +0 -148
- package/packages/api-types/src/schemas/message.test.ts +0 -192
- package/packages/api-types/src/schemas/message.ts +0 -98
- package/packages/api-types/src/schemas/session.test.ts +0 -104
- package/packages/api-types/src/schemas/session.ts +0 -40
- package/packages/api-types/src/schemas/task.test.ts +0 -192
- package/packages/api-types/src/schemas/task.ts +0 -78
- package/packages/api-types/tsconfig.json +0 -19
- package/packages/api-types/vitest.config.ts +0 -9
- package/packages/benchmark/README.md +0 -200
- package/packages/benchmark/datasets/coding-tasks.yaml +0 -127
- package/packages/benchmark/datasets/coordination-tasks.yaml +0 -122
- package/packages/benchmark/datasets/quick-test.yaml +0 -20
- package/packages/benchmark/dist/benchmark.d.ts +0 -47
- package/packages/benchmark/dist/benchmark.d.ts.map +0 -1
- package/packages/benchmark/dist/benchmark.js +0 -224
- package/packages/benchmark/dist/benchmark.js.map +0 -1
- package/packages/benchmark/dist/cli.d.ts +0 -8
- package/packages/benchmark/dist/cli.d.ts.map +0 -1
- package/packages/benchmark/dist/cli.js +0 -185
- package/packages/benchmark/dist/cli.js.map +0 -1
- package/packages/benchmark/dist/harbor.d.ts +0 -53
- package/packages/benchmark/dist/harbor.d.ts.map +0 -1
- package/packages/benchmark/dist/harbor.js +0 -127
- package/packages/benchmark/dist/harbor.js.map +0 -1
- package/packages/benchmark/dist/index.d.ts +0 -48
- package/packages/benchmark/dist/index.d.ts.map +0 -1
- package/packages/benchmark/dist/index.js +0 -50
- package/packages/benchmark/dist/index.js.map +0 -1
- package/packages/benchmark/dist/runners/base.d.ts +0 -63
- package/packages/benchmark/dist/runners/base.d.ts.map +0 -1
- package/packages/benchmark/dist/runners/base.js +0 -156
- package/packages/benchmark/dist/runners/base.js.map +0 -1
- package/packages/benchmark/dist/runners/index.d.ts +0 -10
- package/packages/benchmark/dist/runners/index.d.ts.map +0 -1
- package/packages/benchmark/dist/runners/index.js +0 -10
- package/packages/benchmark/dist/runners/index.js.map +0 -1
- package/packages/benchmark/dist/runners/single.d.ts +0 -19
- package/packages/benchmark/dist/runners/single.d.ts.map +0 -1
- package/packages/benchmark/dist/runners/single.js +0 -111
- package/packages/benchmark/dist/runners/single.js.map +0 -1
- package/packages/benchmark/dist/runners/subagent.d.ts +0 -32
- package/packages/benchmark/dist/runners/subagent.d.ts.map +0 -1
- package/packages/benchmark/dist/runners/subagent.js +0 -212
- package/packages/benchmark/dist/runners/subagent.js.map +0 -1
- package/packages/benchmark/dist/runners/swarm.d.ts +0 -36
- package/packages/benchmark/dist/runners/swarm.d.ts.map +0 -1
- package/packages/benchmark/dist/runners/swarm.js +0 -273
- package/packages/benchmark/dist/runners/swarm.js.map +0 -1
- package/packages/benchmark/dist/types.d.ts +0 -178
- package/packages/benchmark/dist/types.d.ts.map +0 -1
- package/packages/benchmark/dist/types.js +0 -16
- package/packages/benchmark/dist/types.js.map +0 -1
- package/packages/benchmark/package.json +0 -80
- package/packages/benchmark/src/benchmark.ts +0 -298
- package/packages/benchmark/src/cli.ts +0 -240
- package/packages/benchmark/src/harbor.ts +0 -170
- package/packages/benchmark/src/index.ts +0 -73
- package/packages/benchmark/src/runners/base.ts +0 -205
- package/packages/benchmark/src/runners/index.ts +0 -10
- package/packages/benchmark/src/runners/single.ts +0 -121
- package/packages/benchmark/src/runners/subagent.ts +0 -240
- package/packages/benchmark/src/runners/swarm.ts +0 -326
- package/packages/benchmark/src/types.ts +0 -205
- package/packages/benchmark/tsconfig.json +0 -20
- package/packages/cli-tester/README.md +0 -277
- package/packages/cli-tester/dist/index.d.ts +0 -21
- package/packages/cli-tester/dist/index.d.ts.map +0 -1
- package/packages/cli-tester/dist/index.js +0 -21
- package/packages/cli-tester/dist/index.js.map +0 -1
- package/packages/cli-tester/dist/utils/credential-check.d.ts +0 -56
- package/packages/cli-tester/dist/utils/credential-check.d.ts.map +0 -1
- package/packages/cli-tester/dist/utils/credential-check.js +0 -230
- package/packages/cli-tester/dist/utils/credential-check.js.map +0 -1
- package/packages/cli-tester/dist/utils/socket-client.d.ts +0 -76
- package/packages/cli-tester/dist/utils/socket-client.d.ts.map +0 -1
- package/packages/cli-tester/dist/utils/socket-client.js +0 -153
- package/packages/cli-tester/dist/utils/socket-client.js.map +0 -1
- package/packages/cli-tester/docker/Dockerfile +0 -61
- package/packages/cli-tester/docker/docker-compose.yml +0 -71
- package/packages/cli-tester/docker/entrypoint.sh +0 -58
- package/packages/cli-tester/package.json +0 -32
- package/packages/cli-tester/scripts/clear-auth.sh +0 -101
- package/packages/cli-tester/scripts/inject-message.sh +0 -42
- package/packages/cli-tester/scripts/start.sh +0 -71
- package/packages/cli-tester/scripts/test-cli.sh +0 -56
- package/packages/cli-tester/scripts/test-full-spawn.sh +0 -238
- package/packages/cli-tester/scripts/test-registration.sh +0 -182
- package/packages/cli-tester/scripts/test-setup-flow.sh +0 -202
- package/packages/cli-tester/scripts/test-spawn.sh +0 -140
- package/packages/cli-tester/scripts/test-with-daemon.sh +0 -247
- package/packages/cli-tester/scripts/verify-auth.sh +0 -112
- package/packages/cli-tester/src/index.ts +0 -40
- package/packages/cli-tester/src/utils/credential-check.ts +0 -284
- package/packages/cli-tester/src/utils/socket-client.ts +0 -211
- package/packages/cli-tester/tests/credential-check.test.ts +0 -56
- package/packages/cli-tester/tsconfig.json +0 -11
- package/packages/sdk/dist/browser-client.d.ts +0 -212
- package/packages/sdk/dist/browser-client.d.ts.map +0 -1
- package/packages/sdk/dist/browser-client.js +0 -750
- package/packages/sdk/dist/browser-client.js.map +0 -1
- package/packages/sdk/dist/browser-framing.d.ts +0 -46
- package/packages/sdk/dist/browser-framing.d.ts.map +0 -1
- package/packages/sdk/dist/browser-framing.js +0 -122
- package/packages/sdk/dist/browser-framing.js.map +0 -1
- package/packages/sdk/dist/standalone.d.ts +0 -89
- package/packages/sdk/dist/standalone.d.ts.map +0 -1
- package/packages/sdk/dist/standalone.js +0 -131
- package/packages/sdk/dist/standalone.js.map +0 -1
- package/packages/sdk/dist/transports/index.d.ts +0 -92
- package/packages/sdk/dist/transports/index.d.ts.map +0 -1
- package/packages/sdk/dist/transports/index.js +0 -129
- package/packages/sdk/dist/transports/index.js.map +0 -1
- package/packages/sdk/dist/transports/socket-transport.d.ts +0 -30
- package/packages/sdk/dist/transports/socket-transport.d.ts.map +0 -1
- package/packages/sdk/dist/transports/socket-transport.js +0 -94
- package/packages/sdk/dist/transports/socket-transport.js.map +0 -1
- package/packages/sdk/dist/transports/types.d.ts +0 -69
- package/packages/sdk/dist/transports/types.d.ts.map +0 -1
- package/packages/sdk/dist/transports/types.js +0 -10
- package/packages/sdk/dist/transports/types.js.map +0 -1
- package/packages/sdk/dist/transports/websocket-transport.d.ts +0 -55
- package/packages/sdk/dist/transports/websocket-transport.d.ts.map +0 -1
- package/packages/sdk/dist/transports/websocket-transport.js +0 -180
- package/packages/sdk/dist/transports/websocket-transport.js.map +0 -1
- package/packages/sdk/src/browser-client.ts +0 -985
- package/packages/sdk/src/browser-framing.test.ts +0 -115
- package/packages/sdk/src/browser-framing.ts +0 -150
- package/packages/sdk/src/standalone.ts +0 -183
- package/packages/sdk/src/transports/index.ts +0 -197
- package/packages/sdk/src/transports/socket-transport.ts +0 -115
- package/packages/sdk/src/transports/types.ts +0 -77
- 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()
|
|
@@ -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"}
|
|
@@ -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"}
|