agent-relay 2.3.2 → 2.3.5
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/index.cjs +1 -1
- package/dist/src/cli/index.js +124 -7
- package/dist/src/cli/index.js.map +1 -1
- package/package.json +20 -26
- package/packages/acp-bridge/package.json +2 -2
- package/packages/bridge/package.json +7 -7
- package/packages/config/dist/cloud-config.d.ts +1 -1
- package/packages/config/dist/cloud-config.d.ts.map +1 -1
- package/packages/config/dist/cloud-config.js.map +1 -1
- package/packages/config/dist/schemas.d.ts +5 -5
- package/packages/config/dist/schemas.js +1 -1
- package/packages/config/dist/schemas.js.map +1 -1
- package/packages/config/package.json +2 -2
- package/packages/config/src/cloud-config.ts +2 -2
- package/packages/config/src/schemas.test.ts +48 -0
- package/packages/config/src/schemas.ts +1 -1
- 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/__tests__/facade.test.d.ts +2 -0
- package/packages/sdk-ts/dist/__tests__/facade.test.d.ts.map +1 -0
- package/packages/sdk-ts/dist/__tests__/facade.test.js +257 -0
- package/packages/sdk-ts/dist/__tests__/facade.test.js.map +1 -0
- package/packages/sdk-ts/dist/__tests__/unit.test.d.ts +2 -0
- package/packages/sdk-ts/dist/__tests__/unit.test.d.ts.map +1 -0
- package/packages/sdk-ts/dist/__tests__/unit.test.js +124 -0
- package/packages/sdk-ts/dist/__tests__/unit.test.js.map +1 -0
- package/packages/sdk-ts/dist/client.d.ts +2 -0
- package/packages/sdk-ts/dist/client.d.ts.map +1 -1
- package/packages/sdk-ts/dist/client.js +2 -0
- package/packages/sdk-ts/dist/client.js.map +1 -1
- 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/protocol.d.ts +1 -0
- package/packages/sdk-ts/dist/protocol.d.ts.map +1 -1
- package/packages/sdk-ts/dist/relay.d.ts +44 -0
- package/packages/sdk-ts/dist/relay.d.ts.map +1 -1
- package/packages/sdk-ts/dist/relay.js +89 -11
- package/packages/sdk-ts/dist/relay.js.map +1 -1
- package/packages/sdk-ts/dist/relaycast.js +2 -2
- package/packages/sdk-ts/dist/relaycast.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 +9 -3
- package/packages/sdk-ts/src/__tests__/error-scenarios.test.ts +682 -0
- package/packages/sdk-ts/src/__tests__/facade.test.ts +296 -0
- package/packages/sdk-ts/src/__tests__/swarm-coordinator.test.ts +416 -0
- package/packages/sdk-ts/src/__tests__/unit.test.ts +152 -0
- package/packages/sdk-ts/src/__tests__/workflow-runner.test.ts +333 -0
- package/packages/sdk-ts/src/client.ts +4 -0
- package/packages/sdk-ts/src/index.ts +1 -0
- package/packages/sdk-ts/src/protocol.ts +1 -1
- package/packages/sdk-ts/src/relay.ts +112 -11
- package/packages/sdk-ts/src/relaycast.ts +2 -2
- 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/scripts/postinstall.js +106 -2
- 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 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/workflows/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkflowRunner — parses relay.yaml, validates config, resolves templates,
|
|
3
|
+
* executes steps (sequential/parallel/DAG), runs verification checks,
|
|
4
|
+
* persists state to DB, and supports pause/resume/abort with retries.
|
|
5
|
+
*/
|
|
6
|
+
import { randomBytes } from 'node:crypto';
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
8
|
+
import { readFile } from 'node:fs/promises';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { parse as parseYaml } from 'yaml';
|
|
11
|
+
import { InMemoryWorkflowDb } from './memory-db.js';
|
|
12
|
+
// ── AgentRelay SDK imports ──────────────────────────────────────────────────
|
|
13
|
+
// Import from sub-paths to avoid pulling in the full @relaycast/sdk dependency.
|
|
14
|
+
import { AgentRelay } from '../relay.js';
|
|
15
|
+
// ── WorkflowRunner ──────────────────────────────────────────────────────────
|
|
16
|
+
export class WorkflowRunner {
|
|
17
|
+
db;
|
|
18
|
+
workspaceId;
|
|
19
|
+
relayOptions;
|
|
20
|
+
cwd;
|
|
21
|
+
summaryDir;
|
|
22
|
+
relay;
|
|
23
|
+
abortController;
|
|
24
|
+
paused = false;
|
|
25
|
+
pauseResolver;
|
|
26
|
+
listeners = [];
|
|
27
|
+
constructor(options = {}) {
|
|
28
|
+
this.db = options.db ?? new InMemoryWorkflowDb();
|
|
29
|
+
this.workspaceId = options.workspaceId ?? 'local';
|
|
30
|
+
this.relayOptions = options.relay ?? {};
|
|
31
|
+
this.cwd = options.cwd ?? process.cwd();
|
|
32
|
+
this.summaryDir = options.summaryDir ?? path.join(this.cwd, '.relay', 'summaries');
|
|
33
|
+
}
|
|
34
|
+
// ── Event subscription ──────────────────────────────────────────────────
|
|
35
|
+
on(listener) {
|
|
36
|
+
this.listeners.push(listener);
|
|
37
|
+
return () => {
|
|
38
|
+
this.listeners = this.listeners.filter((l) => l !== listener);
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
emit(event) {
|
|
42
|
+
for (const listener of this.listeners) {
|
|
43
|
+
listener(event);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// ── Parsing & validation ────────────────────────────────────────────────
|
|
47
|
+
/** Parse a relay.yaml file from disk. */
|
|
48
|
+
async parseYamlFile(filePath) {
|
|
49
|
+
const absPath = path.resolve(this.cwd, filePath);
|
|
50
|
+
const raw = await readFile(absPath, 'utf-8');
|
|
51
|
+
return this.parseYamlString(raw, absPath);
|
|
52
|
+
}
|
|
53
|
+
/** Parse a relay.yaml string. */
|
|
54
|
+
parseYamlString(raw, source = '<string>') {
|
|
55
|
+
const parsed = parseYaml(raw);
|
|
56
|
+
this.validateConfig(parsed, source);
|
|
57
|
+
return parsed;
|
|
58
|
+
}
|
|
59
|
+
/** Validate a config object against the RelayYamlConfig shape. */
|
|
60
|
+
validateConfig(config, source = '<config>') {
|
|
61
|
+
if (typeof config !== 'object' || config === null) {
|
|
62
|
+
throw new Error(`${source}: config must be a non-null object`);
|
|
63
|
+
}
|
|
64
|
+
const c = config;
|
|
65
|
+
if (typeof c.version !== 'string') {
|
|
66
|
+
throw new Error(`${source}: missing required field "version"`);
|
|
67
|
+
}
|
|
68
|
+
if (typeof c.name !== 'string') {
|
|
69
|
+
throw new Error(`${source}: missing required field "name"`);
|
|
70
|
+
}
|
|
71
|
+
if (typeof c.swarm !== 'object' || c.swarm === null) {
|
|
72
|
+
throw new Error(`${source}: missing required field "swarm"`);
|
|
73
|
+
}
|
|
74
|
+
const swarm = c.swarm;
|
|
75
|
+
if (typeof swarm.pattern !== 'string') {
|
|
76
|
+
throw new Error(`${source}: missing required field "swarm.pattern"`);
|
|
77
|
+
}
|
|
78
|
+
if (!Array.isArray(c.agents) || c.agents.length === 0) {
|
|
79
|
+
throw new Error(`${source}: "agents" must be a non-empty array`);
|
|
80
|
+
}
|
|
81
|
+
for (const agent of c.agents) {
|
|
82
|
+
if (typeof agent !== 'object' || agent === null) {
|
|
83
|
+
throw new Error(`${source}: each agent must be an object`);
|
|
84
|
+
}
|
|
85
|
+
const a = agent;
|
|
86
|
+
if (typeof a.name !== 'string') {
|
|
87
|
+
throw new Error(`${source}: each agent must have a string "name"`);
|
|
88
|
+
}
|
|
89
|
+
if (typeof a.cli !== 'string') {
|
|
90
|
+
throw new Error(`${source}: each agent must have a string "cli"`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (c.workflows !== undefined) {
|
|
94
|
+
if (!Array.isArray(c.workflows)) {
|
|
95
|
+
throw new Error(`${source}: "workflows" must be an array`);
|
|
96
|
+
}
|
|
97
|
+
for (const wf of c.workflows) {
|
|
98
|
+
this.validateWorkflow(wf, source);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
validateWorkflow(wf, source) {
|
|
103
|
+
if (typeof wf !== 'object' || wf === null) {
|
|
104
|
+
throw new Error(`${source}: each workflow must be an object`);
|
|
105
|
+
}
|
|
106
|
+
const w = wf;
|
|
107
|
+
if (typeof w.name !== 'string') {
|
|
108
|
+
throw new Error(`${source}: each workflow must have a string "name"`);
|
|
109
|
+
}
|
|
110
|
+
if (!Array.isArray(w.steps) || w.steps.length === 0) {
|
|
111
|
+
throw new Error(`${source}: workflow "${w.name}" must have a non-empty "steps" array`);
|
|
112
|
+
}
|
|
113
|
+
for (const step of w.steps) {
|
|
114
|
+
if (typeof step !== 'object' || step === null) {
|
|
115
|
+
throw new Error(`${source}: each step must be an object`);
|
|
116
|
+
}
|
|
117
|
+
const s = step;
|
|
118
|
+
if (typeof s.name !== 'string' || typeof s.agent !== 'string' || typeof s.task !== 'string') {
|
|
119
|
+
throw new Error(`${source}: each step must have "name", "agent", and "task" string fields`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Validate DAG: check for unknown dependencies and cycles
|
|
123
|
+
const stepNames = new Set(w.steps.map((s) => s.name));
|
|
124
|
+
for (const step of w.steps) {
|
|
125
|
+
if (step.dependsOn) {
|
|
126
|
+
for (const dep of step.dependsOn) {
|
|
127
|
+
if (!stepNames.has(dep)) {
|
|
128
|
+
throw new Error(`${source}: step "${step.name}" depends on unknown step "${dep}"`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
this.detectCycles(w.steps, source, w.name);
|
|
134
|
+
}
|
|
135
|
+
detectCycles(steps, source, workflowName) {
|
|
136
|
+
const adj = new Map();
|
|
137
|
+
for (const step of steps) {
|
|
138
|
+
adj.set(step.name, step.dependsOn ?? []);
|
|
139
|
+
}
|
|
140
|
+
const visited = new Set();
|
|
141
|
+
const inStack = new Set();
|
|
142
|
+
const dfs = (node) => {
|
|
143
|
+
if (inStack.has(node)) {
|
|
144
|
+
throw new Error(`${source}: workflow "${workflowName}" contains a dependency cycle involving "${node}"`);
|
|
145
|
+
}
|
|
146
|
+
if (visited.has(node))
|
|
147
|
+
return;
|
|
148
|
+
inStack.add(node);
|
|
149
|
+
for (const dep of adj.get(node) ?? []) {
|
|
150
|
+
dfs(dep);
|
|
151
|
+
}
|
|
152
|
+
inStack.delete(node);
|
|
153
|
+
visited.add(node);
|
|
154
|
+
};
|
|
155
|
+
for (const step of steps) {
|
|
156
|
+
dfs(step.name);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// ── Template variable resolution ────────────────────────────────────────
|
|
160
|
+
/** Resolve {{variable}} placeholders in all task strings. */
|
|
161
|
+
resolveVariables(config, vars) {
|
|
162
|
+
const resolved = structuredClone(config);
|
|
163
|
+
for (const agent of resolved.agents) {
|
|
164
|
+
if (agent.task) {
|
|
165
|
+
agent.task = this.interpolate(agent.task, vars);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (resolved.workflows) {
|
|
169
|
+
for (const wf of resolved.workflows) {
|
|
170
|
+
for (const step of wf.steps) {
|
|
171
|
+
step.task = this.interpolate(step.task, vars);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return resolved;
|
|
176
|
+
}
|
|
177
|
+
interpolate(template, vars) {
|
|
178
|
+
return template.replace(/\{\{([\w][\w.\-]*)\}\}/g, (_match, key) => {
|
|
179
|
+
// Skip step-output placeholders — they are resolved at execution time by interpolateStepTask()
|
|
180
|
+
if (key.startsWith('steps.')) {
|
|
181
|
+
return _match;
|
|
182
|
+
}
|
|
183
|
+
// Resolve dot-path variables like steps.plan.output
|
|
184
|
+
const value = this.resolveDotPath(key, vars);
|
|
185
|
+
if (value === undefined) {
|
|
186
|
+
throw new Error(`Unresolved variable: {{${key}}}`);
|
|
187
|
+
}
|
|
188
|
+
return String(value);
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
resolveDotPath(key, vars) {
|
|
192
|
+
// Simple key — direct lookup
|
|
193
|
+
if (!key.includes('.')) {
|
|
194
|
+
return vars[key];
|
|
195
|
+
}
|
|
196
|
+
// Dot-path — walk into nested context
|
|
197
|
+
const parts = key.split('.');
|
|
198
|
+
let current = vars;
|
|
199
|
+
for (const part of parts) {
|
|
200
|
+
if (current === null || current === undefined || typeof current !== 'object') {
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
current = current[part];
|
|
204
|
+
}
|
|
205
|
+
if (current === undefined || current === null) {
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
if (typeof current === 'string' || typeof current === 'number' || typeof current === 'boolean') {
|
|
209
|
+
return current;
|
|
210
|
+
}
|
|
211
|
+
return String(current);
|
|
212
|
+
}
|
|
213
|
+
/** Build a nested context from completed step outputs for {{steps.X.output}} resolution. */
|
|
214
|
+
buildStepOutputContext(stepStates) {
|
|
215
|
+
const steps = {};
|
|
216
|
+
for (const [name, state] of stepStates) {
|
|
217
|
+
if (state.row.status === 'completed' && state.row.output !== undefined) {
|
|
218
|
+
steps[name] = { output: state.row.output };
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return { steps };
|
|
222
|
+
}
|
|
223
|
+
/** Interpolate step-output variables, silently skipping unresolved ones (they may be user vars). */
|
|
224
|
+
interpolateStepTask(template, context) {
|
|
225
|
+
return template.replace(/\{\{(steps\.[\w\-]+\.output)\}\}/g, (_match, key) => {
|
|
226
|
+
const value = this.resolveDotPath(key, context);
|
|
227
|
+
if (value === undefined) {
|
|
228
|
+
// Leave unresolved — may not be an error if the template doesn't depend on prior steps
|
|
229
|
+
return _match;
|
|
230
|
+
}
|
|
231
|
+
return String(value);
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
// ── Execution ───────────────────────────────────────────────────────────
|
|
235
|
+
/** Execute a named workflow from a validated config. */
|
|
236
|
+
async execute(config, workflowName, vars) {
|
|
237
|
+
const resolved = vars ? this.resolveVariables(config, vars) : config;
|
|
238
|
+
const workflows = resolved.workflows ?? [];
|
|
239
|
+
const workflow = workflowName
|
|
240
|
+
? workflows.find((w) => w.name === workflowName)
|
|
241
|
+
: workflows[0];
|
|
242
|
+
if (!workflow) {
|
|
243
|
+
throw new Error(workflowName
|
|
244
|
+
? `Workflow "${workflowName}" not found in config`
|
|
245
|
+
: 'No workflows defined in config');
|
|
246
|
+
}
|
|
247
|
+
const runId = this.generateId();
|
|
248
|
+
const now = new Date().toISOString();
|
|
249
|
+
const run = {
|
|
250
|
+
id: runId,
|
|
251
|
+
workspaceId: this.workspaceId,
|
|
252
|
+
workflowName: workflow.name,
|
|
253
|
+
pattern: resolved.swarm.pattern,
|
|
254
|
+
status: 'pending',
|
|
255
|
+
config: resolved,
|
|
256
|
+
startedAt: now,
|
|
257
|
+
createdAt: now,
|
|
258
|
+
updatedAt: now,
|
|
259
|
+
};
|
|
260
|
+
await this.db.insertRun(run);
|
|
261
|
+
// Build step rows
|
|
262
|
+
const stepStates = new Map();
|
|
263
|
+
for (const step of workflow.steps) {
|
|
264
|
+
const stepRow = {
|
|
265
|
+
id: this.generateId(),
|
|
266
|
+
runId,
|
|
267
|
+
stepName: step.name,
|
|
268
|
+
agentName: step.agent,
|
|
269
|
+
status: 'pending',
|
|
270
|
+
task: step.task,
|
|
271
|
+
dependsOn: step.dependsOn ?? [],
|
|
272
|
+
retryCount: 0,
|
|
273
|
+
createdAt: now,
|
|
274
|
+
updatedAt: now,
|
|
275
|
+
};
|
|
276
|
+
await this.db.insertStep(stepRow);
|
|
277
|
+
stepStates.set(step.name, { row: stepRow });
|
|
278
|
+
}
|
|
279
|
+
// Start execution
|
|
280
|
+
this.abortController = new AbortController();
|
|
281
|
+
this.paused = false;
|
|
282
|
+
try {
|
|
283
|
+
await this.updateRunStatus(runId, 'running');
|
|
284
|
+
this.emit({ type: 'run:started', runId });
|
|
285
|
+
this.relay = new AgentRelay({
|
|
286
|
+
...this.relayOptions,
|
|
287
|
+
channels: [resolved.swarm.channel ?? 'general'],
|
|
288
|
+
});
|
|
289
|
+
const agentMap = new Map();
|
|
290
|
+
for (const agent of resolved.agents) {
|
|
291
|
+
agentMap.set(agent.name, agent);
|
|
292
|
+
}
|
|
293
|
+
await this.executeSteps(workflow, stepStates, agentMap, resolved.errorHandling, runId);
|
|
294
|
+
// Check if all steps completed
|
|
295
|
+
const allCompleted = [...stepStates.values()].every((s) => s.row.status === 'completed' || s.row.status === 'skipped');
|
|
296
|
+
if (allCompleted) {
|
|
297
|
+
await this.updateRunStatus(runId, 'completed');
|
|
298
|
+
this.emit({ type: 'run:completed', runId });
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
const failedStep = [...stepStates.values()].find((s) => s.row.status === 'failed');
|
|
302
|
+
const errorMsg = failedStep?.row.error ?? 'One or more steps failed';
|
|
303
|
+
await this.updateRunStatus(runId, 'failed', errorMsg);
|
|
304
|
+
this.emit({ type: 'run:failed', runId, error: errorMsg });
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
catch (err) {
|
|
308
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
309
|
+
const status = this.abortController?.signal.aborted ? 'cancelled' : 'failed';
|
|
310
|
+
await this.updateRunStatus(runId, status, errorMsg);
|
|
311
|
+
if (status === 'cancelled') {
|
|
312
|
+
this.emit({ type: 'run:cancelled', runId });
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
this.emit({ type: 'run:failed', runId, error: errorMsg });
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
finally {
|
|
319
|
+
await this.relay?.shutdown();
|
|
320
|
+
this.relay = undefined;
|
|
321
|
+
this.abortController = undefined;
|
|
322
|
+
}
|
|
323
|
+
const finalRun = await this.db.getRun(runId);
|
|
324
|
+
return finalRun ?? run;
|
|
325
|
+
}
|
|
326
|
+
/** Resume a previously paused or partially completed run. */
|
|
327
|
+
async resume(runId, vars) {
|
|
328
|
+
const run = await this.db.getRun(runId);
|
|
329
|
+
if (!run) {
|
|
330
|
+
throw new Error(`Run "${runId}" not found`);
|
|
331
|
+
}
|
|
332
|
+
if (run.status !== 'running' && run.status !== 'failed') {
|
|
333
|
+
throw new Error(`Run "${runId}" is in status "${run.status}" and cannot be resumed`);
|
|
334
|
+
}
|
|
335
|
+
const config = vars ? this.resolveVariables(run.config, vars) : run.config;
|
|
336
|
+
const workflows = config.workflows ?? [];
|
|
337
|
+
const workflow = workflows.find((w) => w.name === run.workflowName);
|
|
338
|
+
if (!workflow) {
|
|
339
|
+
throw new Error(`Workflow "${run.workflowName}" not found in stored config`);
|
|
340
|
+
}
|
|
341
|
+
const existingSteps = await this.db.getStepsByRunId(runId);
|
|
342
|
+
const stepStates = new Map();
|
|
343
|
+
for (const stepRow of existingSteps) {
|
|
344
|
+
stepStates.set(stepRow.stepName, { row: stepRow });
|
|
345
|
+
}
|
|
346
|
+
// Reset failed steps to pending for retry
|
|
347
|
+
for (const [, state] of stepStates) {
|
|
348
|
+
if (state.row.status === 'failed') {
|
|
349
|
+
state.row.status = 'pending';
|
|
350
|
+
state.row.error = undefined;
|
|
351
|
+
await this.db.updateStep(state.row.id, {
|
|
352
|
+
status: 'pending',
|
|
353
|
+
error: undefined,
|
|
354
|
+
updatedAt: new Date().toISOString(),
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
this.abortController = new AbortController();
|
|
359
|
+
this.paused = false;
|
|
360
|
+
try {
|
|
361
|
+
await this.updateRunStatus(runId, 'running');
|
|
362
|
+
this.relay = new AgentRelay({
|
|
363
|
+
...this.relayOptions,
|
|
364
|
+
channels: [config.swarm.channel ?? 'general'],
|
|
365
|
+
});
|
|
366
|
+
const agentMap = new Map();
|
|
367
|
+
for (const agent of config.agents) {
|
|
368
|
+
agentMap.set(agent.name, agent);
|
|
369
|
+
}
|
|
370
|
+
await this.executeSteps(workflow, stepStates, agentMap, config.errorHandling, runId);
|
|
371
|
+
const allCompleted = [...stepStates.values()].every((s) => s.row.status === 'completed' || s.row.status === 'skipped');
|
|
372
|
+
if (allCompleted) {
|
|
373
|
+
await this.updateRunStatus(runId, 'completed');
|
|
374
|
+
this.emit({ type: 'run:completed', runId });
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
const failedStep = [...stepStates.values()].find((s) => s.row.status === 'failed');
|
|
378
|
+
const errorMsg = failedStep?.row.error ?? 'One or more steps failed';
|
|
379
|
+
await this.updateRunStatus(runId, 'failed', errorMsg);
|
|
380
|
+
this.emit({ type: 'run:failed', runId, error: errorMsg });
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
catch (err) {
|
|
384
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
385
|
+
await this.updateRunStatus(runId, 'failed', errorMsg);
|
|
386
|
+
this.emit({ type: 'run:failed', runId, error: errorMsg });
|
|
387
|
+
}
|
|
388
|
+
finally {
|
|
389
|
+
await this.relay?.shutdown();
|
|
390
|
+
this.relay = undefined;
|
|
391
|
+
this.abortController = undefined;
|
|
392
|
+
}
|
|
393
|
+
const finalRun = await this.db.getRun(runId);
|
|
394
|
+
return finalRun ?? run;
|
|
395
|
+
}
|
|
396
|
+
/** Pause execution. Currently-running steps will finish but no new steps start. */
|
|
397
|
+
pause() {
|
|
398
|
+
this.paused = true;
|
|
399
|
+
}
|
|
400
|
+
/** Resume after a pause(). */
|
|
401
|
+
unpause() {
|
|
402
|
+
this.paused = false;
|
|
403
|
+
this.pauseResolver?.();
|
|
404
|
+
this.pauseResolver = undefined;
|
|
405
|
+
}
|
|
406
|
+
/** Abort the current run. Running agents are released. */
|
|
407
|
+
abort() {
|
|
408
|
+
// Unblock waitIfPaused() so the run loop can exit
|
|
409
|
+
this.pauseResolver?.();
|
|
410
|
+
this.pauseResolver = undefined;
|
|
411
|
+
this.abortController?.abort();
|
|
412
|
+
}
|
|
413
|
+
// ── Step execution engine ─────────────────────────────────────────────
|
|
414
|
+
async executeSteps(workflow, stepStates, agentMap, errorHandling, runId) {
|
|
415
|
+
const rawStrategy = errorHandling?.strategy ?? workflow.onError ?? 'fail-fast';
|
|
416
|
+
// Map shorthand onError values to canonical strategy names.
|
|
417
|
+
// 'retry' maps to 'fail-fast' so downstream steps are properly skipped after retries exhaust.
|
|
418
|
+
const strategy = rawStrategy === 'fail' ? 'fail-fast'
|
|
419
|
+
: rawStrategy === 'skip' ? 'continue'
|
|
420
|
+
: rawStrategy === 'retry' ? 'fail-fast'
|
|
421
|
+
: rawStrategy;
|
|
422
|
+
// DAG-based execution: repeatedly find ready steps and run them in parallel
|
|
423
|
+
while (true) {
|
|
424
|
+
this.checkAborted();
|
|
425
|
+
await this.waitIfPaused();
|
|
426
|
+
const readySteps = this.findReadySteps(workflow.steps, stepStates);
|
|
427
|
+
if (readySteps.length === 0) {
|
|
428
|
+
// No steps ready — either all done or blocked
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
const results = await Promise.allSettled(readySteps.map((step) => this.executeStep(step, stepStates, agentMap, errorHandling, runId)));
|
|
432
|
+
for (let i = 0; i < results.length; i++) {
|
|
433
|
+
const result = results[i];
|
|
434
|
+
const step = readySteps[i];
|
|
435
|
+
if (result.status === 'rejected') {
|
|
436
|
+
const error = result.reason instanceof Error ? result.reason.message : String(result.reason);
|
|
437
|
+
const state = stepStates.get(step.name);
|
|
438
|
+
if (state && state.row.status !== 'failed') {
|
|
439
|
+
await this.markStepFailed(state, error, runId);
|
|
440
|
+
}
|
|
441
|
+
if (strategy === 'fail-fast') {
|
|
442
|
+
// Mark all pending downstream steps as skipped
|
|
443
|
+
await this.markDownstreamSkipped(step.name, workflow.steps, stepStates, runId);
|
|
444
|
+
throw new Error(`Step "${step.name}" failed: ${error}`);
|
|
445
|
+
}
|
|
446
|
+
if (strategy === 'continue') {
|
|
447
|
+
await this.markDownstreamSkipped(step.name, workflow.steps, stepStates, runId);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
findReadySteps(steps, stepStates) {
|
|
454
|
+
return steps.filter((step) => {
|
|
455
|
+
const state = stepStates.get(step.name);
|
|
456
|
+
if (!state || state.row.status !== 'pending')
|
|
457
|
+
return false;
|
|
458
|
+
const deps = step.dependsOn ?? [];
|
|
459
|
+
return deps.every((dep) => {
|
|
460
|
+
const depState = stepStates.get(dep);
|
|
461
|
+
return depState && (depState.row.status === 'completed' || depState.row.status === 'skipped');
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
async executeStep(step, stepStates, agentMap, errorHandling, runId) {
|
|
466
|
+
const state = stepStates.get(step.name);
|
|
467
|
+
if (!state)
|
|
468
|
+
throw new Error(`Step state not found: ${step.name}`);
|
|
469
|
+
const agentDef = agentMap.get(step.agent);
|
|
470
|
+
if (!agentDef) {
|
|
471
|
+
throw new Error(`Agent "${step.agent}" not found in config`);
|
|
472
|
+
}
|
|
473
|
+
const maxRetries = step.retries ?? agentDef.constraints?.retries ?? errorHandling?.maxRetries ?? 0;
|
|
474
|
+
const retryDelay = errorHandling?.retryDelayMs ?? 1000;
|
|
475
|
+
const timeoutMs = step.timeoutMs ?? agentDef.constraints?.timeoutMs;
|
|
476
|
+
let lastError;
|
|
477
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
478
|
+
this.checkAborted();
|
|
479
|
+
if (attempt > 0) {
|
|
480
|
+
this.emit({ type: 'step:retrying', runId, stepName: step.name, attempt });
|
|
481
|
+
state.row.retryCount = attempt;
|
|
482
|
+
await this.db.updateStep(state.row.id, {
|
|
483
|
+
retryCount: attempt,
|
|
484
|
+
updatedAt: new Date().toISOString(),
|
|
485
|
+
});
|
|
486
|
+
await this.delay(retryDelay);
|
|
487
|
+
}
|
|
488
|
+
try {
|
|
489
|
+
// Mark step as running
|
|
490
|
+
state.row.status = 'running';
|
|
491
|
+
state.row.startedAt = new Date().toISOString();
|
|
492
|
+
await this.db.updateStep(state.row.id, {
|
|
493
|
+
status: 'running',
|
|
494
|
+
startedAt: state.row.startedAt,
|
|
495
|
+
updatedAt: new Date().toISOString(),
|
|
496
|
+
});
|
|
497
|
+
this.emit({ type: 'step:started', runId, stepName: step.name });
|
|
498
|
+
// Resolve step-output variables (e.g. {{steps.plan.output}}) at execution time
|
|
499
|
+
const stepOutputContext = this.buildStepOutputContext(stepStates);
|
|
500
|
+
const resolvedTask = this.interpolateStepTask(step.task, stepOutputContext);
|
|
501
|
+
// Spawn agent via AgentRelay
|
|
502
|
+
const resolvedStep = { ...step, task: resolvedTask };
|
|
503
|
+
const output = await this.spawnAndWait(agentDef, resolvedStep, timeoutMs);
|
|
504
|
+
// Run verification if configured
|
|
505
|
+
if (step.verification) {
|
|
506
|
+
this.runVerification(step.verification, output, step.name);
|
|
507
|
+
}
|
|
508
|
+
// Mark completed
|
|
509
|
+
state.row.status = 'completed';
|
|
510
|
+
state.row.output = output;
|
|
511
|
+
state.row.completedAt = new Date().toISOString();
|
|
512
|
+
await this.db.updateStep(state.row.id, {
|
|
513
|
+
status: 'completed',
|
|
514
|
+
output,
|
|
515
|
+
completedAt: state.row.completedAt,
|
|
516
|
+
updatedAt: new Date().toISOString(),
|
|
517
|
+
});
|
|
518
|
+
this.emit({ type: 'step:completed', runId, stepName: step.name, output });
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
catch (err) {
|
|
522
|
+
lastError = err instanceof Error ? err.message : String(err);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
// All retries exhausted — mark failed and throw so callers can apply error strategy
|
|
526
|
+
await this.markStepFailed(state, lastError ?? 'Unknown error', runId);
|
|
527
|
+
throw new Error(`Step "${step.name}" failed after ${maxRetries} retries: ${lastError ?? 'Unknown error'}`);
|
|
528
|
+
}
|
|
529
|
+
async spawnAndWait(agentDef, step, timeoutMs) {
|
|
530
|
+
if (!this.relay) {
|
|
531
|
+
throw new Error('AgentRelay not initialized');
|
|
532
|
+
}
|
|
533
|
+
const agent = await this.relay.spawnPty({
|
|
534
|
+
name: `${step.name}-${this.generateShortId()}`,
|
|
535
|
+
cli: agentDef.cli,
|
|
536
|
+
args: agentDef.constraints?.model ? ['--model', agentDef.constraints.model] : [],
|
|
537
|
+
channels: agentDef.channels,
|
|
538
|
+
});
|
|
539
|
+
// Send the task as a message to the agent
|
|
540
|
+
const system = this.relay.human({ name: 'WorkflowRunner' });
|
|
541
|
+
await system.sendMessage({ to: agent.name, text: step.task });
|
|
542
|
+
// Wait for agent to exit
|
|
543
|
+
const exitResult = await agent.waitForExit(timeoutMs);
|
|
544
|
+
if (exitResult === 'timeout') {
|
|
545
|
+
await agent.release();
|
|
546
|
+
throw new Error(`Step "${step.name}" timed out after ${timeoutMs}ms`);
|
|
547
|
+
}
|
|
548
|
+
// Read output from summary file if it exists
|
|
549
|
+
const summaryPath = path.join(this.summaryDir, `${step.name}.md`);
|
|
550
|
+
if (existsSync(summaryPath)) {
|
|
551
|
+
return await readFile(summaryPath, 'utf-8');
|
|
552
|
+
}
|
|
553
|
+
return `Agent exited (${exitResult})`;
|
|
554
|
+
}
|
|
555
|
+
// ── Verification ────────────────────────────────────────────────────────
|
|
556
|
+
runVerification(check, output, stepName) {
|
|
557
|
+
switch (check.type) {
|
|
558
|
+
case 'output_contains':
|
|
559
|
+
if (!output.includes(check.value)) {
|
|
560
|
+
throw new Error(`Verification failed for "${stepName}": output does not contain "${check.value}"`);
|
|
561
|
+
}
|
|
562
|
+
break;
|
|
563
|
+
case 'exit_code':
|
|
564
|
+
// exit_code verification is implicitly satisfied if the agent exited successfully
|
|
565
|
+
break;
|
|
566
|
+
case 'file_exists':
|
|
567
|
+
if (!existsSync(path.resolve(this.cwd, check.value))) {
|
|
568
|
+
throw new Error(`Verification failed for "${stepName}": file "${check.value}" does not exist`);
|
|
569
|
+
}
|
|
570
|
+
break;
|
|
571
|
+
case 'custom':
|
|
572
|
+
// Custom verifications are evaluated by callers; no-op here
|
|
573
|
+
break;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
// ── State helpers ─────────────────────────────────────────────────────
|
|
577
|
+
async updateRunStatus(runId, status, error) {
|
|
578
|
+
const patch = {
|
|
579
|
+
status,
|
|
580
|
+
updatedAt: new Date().toISOString(),
|
|
581
|
+
};
|
|
582
|
+
if (status === 'completed' || status === 'failed' || status === 'cancelled') {
|
|
583
|
+
patch.completedAt = new Date().toISOString();
|
|
584
|
+
}
|
|
585
|
+
if (error) {
|
|
586
|
+
patch.error = error;
|
|
587
|
+
}
|
|
588
|
+
await this.db.updateRun(runId, patch);
|
|
589
|
+
}
|
|
590
|
+
async markStepFailed(state, error, runId) {
|
|
591
|
+
state.row.status = 'failed';
|
|
592
|
+
state.row.error = error;
|
|
593
|
+
state.row.completedAt = new Date().toISOString();
|
|
594
|
+
await this.db.updateStep(state.row.id, {
|
|
595
|
+
status: 'failed',
|
|
596
|
+
error,
|
|
597
|
+
completedAt: state.row.completedAt,
|
|
598
|
+
updatedAt: new Date().toISOString(),
|
|
599
|
+
});
|
|
600
|
+
this.emit({ type: 'step:failed', runId, stepName: state.row.stepName, error });
|
|
601
|
+
}
|
|
602
|
+
async markDownstreamSkipped(failedStepName, allSteps, stepStates, runId) {
|
|
603
|
+
const queue = [failedStepName];
|
|
604
|
+
const visited = new Set();
|
|
605
|
+
while (queue.length > 0) {
|
|
606
|
+
const current = queue.shift();
|
|
607
|
+
if (visited.has(current))
|
|
608
|
+
continue;
|
|
609
|
+
visited.add(current);
|
|
610
|
+
for (const step of allSteps) {
|
|
611
|
+
if (step.dependsOn?.includes(current)) {
|
|
612
|
+
const state = stepStates.get(step.name);
|
|
613
|
+
if (state && state.row.status === 'pending') {
|
|
614
|
+
state.row.status = 'skipped';
|
|
615
|
+
await this.db.updateStep(state.row.id, {
|
|
616
|
+
status: 'skipped',
|
|
617
|
+
updatedAt: new Date().toISOString(),
|
|
618
|
+
});
|
|
619
|
+
this.emit({ type: 'step:skipped', runId, stepName: step.name });
|
|
620
|
+
queue.push(step.name);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
// ── Control flow helpers ──────────────────────────────────────────────
|
|
627
|
+
checkAborted() {
|
|
628
|
+
if (this.abortController?.signal.aborted) {
|
|
629
|
+
throw new Error('Workflow aborted');
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
async waitIfPaused() {
|
|
633
|
+
if (!this.paused)
|
|
634
|
+
return;
|
|
635
|
+
await new Promise((resolve) => {
|
|
636
|
+
this.pauseResolver = resolve;
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
delay(ms) {
|
|
640
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
641
|
+
}
|
|
642
|
+
// ── ID generation ─────────────────────────────────────────────────────
|
|
643
|
+
generateId() {
|
|
644
|
+
return randomBytes(12).toString('hex');
|
|
645
|
+
}
|
|
646
|
+
generateShortId() {
|
|
647
|
+
return randomBytes(4).toString('hex');
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
//# sourceMappingURL=runner.js.map
|