agent-relay 3.2.16 → 3.2.18
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/bin/agent-relay-broker-darwin-arm64 +0 -0
- package/bin/agent-relay-broker-darwin-x64 +0 -0
- package/bin/agent-relay-broker-linux-arm64 +0 -0
- package/bin/agent-relay-broker-linux-x64 +0 -0
- package/dist/index.cjs +12 -16
- package/dist/src/cli/commands/setup.d.ts.map +1 -1
- package/dist/src/cli/commands/setup.js +2 -0
- package/dist/src/cli/commands/setup.js.map +1 -1
- package/package.json +8 -8
- package/packages/acp-bridge/package.json +2 -2
- package/packages/config/package.json +1 -1
- package/packages/hooks/package.json +4 -4
- package/packages/memory/package.json +2 -2
- package/packages/openclaw/package.json +2 -2
- package/packages/policy/package.json +2 -2
- package/packages/sdk/dist/workflows/runner.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/runner.js +15 -18
- package/packages/sdk/dist/workflows/runner.js.map +1 -1
- package/packages/sdk/package.json +2 -2
- package/packages/sdk/src/workflows/runner.ts +16 -19
- package/packages/sdk-py/pyproject.toml +1 -1
- package/packages/sdk-py/src/agent_relay/builder.py +64 -7
- package/packages/sdk-py/src/agent_relay/types.py +1 -0
- package/packages/sdk-py/tests/test_builder.py +58 -0
- package/packages/sdk-py/tests/test_dry_run.py +215 -0
- 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 +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/sdk",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.18",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"typescript": "^5.7.3"
|
|
113
113
|
},
|
|
114
114
|
"dependencies": {
|
|
115
|
-
"@agent-relay/config": "3.2.
|
|
115
|
+
"@agent-relay/config": "3.2.18",
|
|
116
116
|
"@relaycast/sdk": "^1.1.0",
|
|
117
117
|
"@sinclair/typebox": "^0.34.48",
|
|
118
118
|
"chalk": "^4.1.2",
|
|
@@ -6223,26 +6223,23 @@ export class WorkflowRunner {
|
|
|
6223
6223
|
);
|
|
6224
6224
|
console.log(chalk.dim('━'.repeat(70)));
|
|
6225
6225
|
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
for (const outcome of outcomes) {
|
|
6230
|
-
const icon =
|
|
6231
|
-
outcome.status === 'completed' ? chalk.green('✓') : outcome.status === 'failed' ? chalk.red('✗') : chalk.dim('⊘');
|
|
6232
|
-
const retryNote = outcome.attempts > 1 ? ` (${outcome.attempts} attempts)` : '';
|
|
6233
|
-
console.log(` ${icon} ${outcome.name} [${outcome.agent}]${retryNote}`);
|
|
6234
|
-
|
|
6235
|
-
if (outcome.error) {
|
|
6236
|
-
console.log(` Error: ${outcome.error}`);
|
|
6237
|
-
}
|
|
6226
|
+
// Always show the summary table — with agent reports when available,
|
|
6227
|
+
// with just step/status/duration when not (non-interactive agents).
|
|
6228
|
+
console.log(formatRunSummaryTable(outcomes, this.agentReports));
|
|
6238
6229
|
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6230
|
+
// Show errors and output excerpts for failed steps below the table
|
|
6231
|
+
for (const outcome of outcomes) {
|
|
6232
|
+
if (outcome.status !== 'failed') continue;
|
|
6233
|
+
|
|
6234
|
+
if (outcome.error) {
|
|
6235
|
+
console.log(chalk.red(` ${outcome.name}: ${outcome.error}`));
|
|
6236
|
+
}
|
|
6237
|
+
|
|
6238
|
+
if (outcome.output) {
|
|
6239
|
+
const excerpt = this.extractOutputExcerpt(outcome.output);
|
|
6240
|
+
if (excerpt) {
|
|
6241
|
+
for (const line of excerpt.split('\n')) {
|
|
6242
|
+
console.log(` ${line}`);
|
|
6246
6243
|
}
|
|
6247
6244
|
}
|
|
6248
6245
|
}
|
|
@@ -18,9 +18,11 @@ Example::
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
20
|
import copy
|
|
21
|
+
import os
|
|
21
22
|
import re
|
|
22
23
|
import shutil
|
|
23
24
|
import subprocess
|
|
25
|
+
import sys
|
|
24
26
|
import tempfile
|
|
25
27
|
from pathlib import Path
|
|
26
28
|
from typing import Any
|
|
@@ -388,9 +390,36 @@ class WorkflowBuilder:
|
|
|
388
390
|
"""Serialize the config to a YAML string."""
|
|
389
391
|
return yaml.dump(self.to_config(), default_flow_style=False, sort_keys=False)
|
|
390
392
|
|
|
393
|
+
def dry_run(self, options: RunOptions | None = None) -> WorkflowResult:
|
|
394
|
+
"""Validate the workflow and show execution plan without running."""
|
|
395
|
+
opts = RunOptions(
|
|
396
|
+
workflow=(options.workflow if options else None),
|
|
397
|
+
cwd=(options.cwd if options else None),
|
|
398
|
+
vars=(options.vars if options else None),
|
|
399
|
+
trajectories=(options.trajectories if options else None),
|
|
400
|
+
on_event=(options.on_event if options else None),
|
|
401
|
+
dry_run=True,
|
|
402
|
+
)
|
|
403
|
+
return self.run(opts)
|
|
404
|
+
|
|
391
405
|
def run(self, options: RunOptions | None = None) -> WorkflowResult:
|
|
392
|
-
"""Build the config and execute it via ``agent-relay run <tempfile>``.
|
|
393
|
-
|
|
406
|
+
"""Build the config and execute it via ``agent-relay run <tempfile>``.
|
|
407
|
+
|
|
408
|
+
Dry-run is enabled when:
|
|
409
|
+
- ``options.dry_run`` is ``True``, or
|
|
410
|
+
- the ``DRY_RUN`` environment variable is set to ``"true"``
|
|
411
|
+
(set automatically by ``agent-relay run script.py --dry-run``).
|
|
412
|
+
"""
|
|
413
|
+
opts = RunOptions(
|
|
414
|
+
workflow=(options.workflow if options else None),
|
|
415
|
+
cwd=(options.cwd if options else None),
|
|
416
|
+
vars=(options.vars if options else None),
|
|
417
|
+
trajectories=(options.trajectories if options else None),
|
|
418
|
+
on_event=(options.on_event if options else None),
|
|
419
|
+
dry_run=(options.dry_run if options else None),
|
|
420
|
+
)
|
|
421
|
+
if opts.dry_run is None and os.environ.get("DRY_RUN") == "true":
|
|
422
|
+
opts.dry_run = True
|
|
394
423
|
config = _apply_runtime_overrides(self.to_config(), opts)
|
|
395
424
|
return _run_config(config, opts)
|
|
396
425
|
|
|
@@ -402,7 +431,16 @@ def workflow(name: str) -> WorkflowBuilder:
|
|
|
402
431
|
|
|
403
432
|
def run_yaml(yaml_path: str, options: RunOptions | None = None) -> WorkflowResult:
|
|
404
433
|
"""Run an existing relay YAML workflow file."""
|
|
405
|
-
opts =
|
|
434
|
+
opts = RunOptions(
|
|
435
|
+
workflow=(options.workflow if options else None),
|
|
436
|
+
cwd=(options.cwd if options else None),
|
|
437
|
+
vars=(options.vars if options else None),
|
|
438
|
+
trajectories=(options.trajectories if options else None),
|
|
439
|
+
on_event=(options.on_event if options else None),
|
|
440
|
+
dry_run=(options.dry_run if options else None),
|
|
441
|
+
)
|
|
442
|
+
if opts.dry_run is None and os.environ.get("DRY_RUN") == "true":
|
|
443
|
+
opts.dry_run = True
|
|
406
444
|
|
|
407
445
|
if opts.trajectories is None and not opts.vars:
|
|
408
446
|
return _run_yaml_path(yaml_path, opts)
|
|
@@ -436,6 +474,8 @@ def _run_yaml_path(yaml_path: str, options: RunOptions) -> WorkflowResult:
|
|
|
436
474
|
)
|
|
437
475
|
|
|
438
476
|
cmd = [*cmd_prefix, "run", yaml_path]
|
|
477
|
+
if options.dry_run:
|
|
478
|
+
cmd.append("--dry-run")
|
|
439
479
|
if options.workflow:
|
|
440
480
|
cmd.extend(["--workflow", options.workflow])
|
|
441
481
|
|
|
@@ -462,7 +502,26 @@ def _execute_cli(
|
|
|
462
502
|
cwd: str | None,
|
|
463
503
|
on_event: WorkflowEventCallback | None,
|
|
464
504
|
) -> WorkflowResult:
|
|
465
|
-
"""Execute CLI command and parse emitted workflow events.
|
|
505
|
+
"""Execute CLI command and parse emitted workflow events.
|
|
506
|
+
|
|
507
|
+
When no event callback is registered, stdio is passed straight through so
|
|
508
|
+
the TypeScript runner's listr progress and summary table render directly
|
|
509
|
+
to the terminal — identical output to YAML and TypeScript workflows.
|
|
510
|
+
"""
|
|
511
|
+
# Passthrough mode: no callback → let the TS runner render directly
|
|
512
|
+
if on_event is None:
|
|
513
|
+
process = subprocess.Popen(cmd, cwd=cwd)
|
|
514
|
+
process.wait()
|
|
515
|
+
|
|
516
|
+
return WorkflowResult(
|
|
517
|
+
status="completed" if process.returncode == 0 else "failed",
|
|
518
|
+
run_id="",
|
|
519
|
+
error=None if process.returncode == 0 else "Workflow failed",
|
|
520
|
+
steps=[],
|
|
521
|
+
events=[],
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
# Capture mode: callback registered → parse events line by line
|
|
466
525
|
process = subprocess.Popen(
|
|
467
526
|
cmd,
|
|
468
527
|
cwd=cwd,
|
|
@@ -487,9 +546,7 @@ def _execute_cli(
|
|
|
487
546
|
|
|
488
547
|
events.append(event)
|
|
489
548
|
_sync_step_result(steps, event)
|
|
490
|
-
|
|
491
|
-
if on_event is not None:
|
|
492
|
-
on_event(event)
|
|
549
|
+
on_event(event)
|
|
493
550
|
|
|
494
551
|
return_code = process.wait()
|
|
495
552
|
output = "\n".join(lines).strip()
|
|
@@ -562,6 +562,7 @@ class RunOptions:
|
|
|
562
562
|
vars: dict[str, str | int | bool] | None = None
|
|
563
563
|
trajectories: TrajectoryConfig | Literal[False] | dict[str, Any] | bool | None = None
|
|
564
564
|
on_event: WorkflowEventCallback | None = None
|
|
565
|
+
dry_run: bool | None = None
|
|
565
566
|
|
|
566
567
|
|
|
567
568
|
@dataclass
|
|
@@ -213,3 +213,61 @@ def test_dag_empty_agents_raises():
|
|
|
213
213
|
def test_dag_empty_steps_raises():
|
|
214
214
|
with pytest.raises(ValueError, match="at least one step"):
|
|
215
215
|
dag("empty", agents=[TemplateAgent(name="a")], steps=[])
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def test_run_options_dry_run_flag():
|
|
219
|
+
"""dry_run option should be passed through to CLI as --dry-run."""
|
|
220
|
+
from agent_relay.types import RunOptions
|
|
221
|
+
|
|
222
|
+
opts = RunOptions(dry_run=True)
|
|
223
|
+
assert opts.dry_run is True
|
|
224
|
+
|
|
225
|
+
opts_default = RunOptions()
|
|
226
|
+
assert opts_default.dry_run is None
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def test_dry_run_env_var(monkeypatch):
|
|
230
|
+
"""DRY_RUN=true env var should enable dry_run on .run()."""
|
|
231
|
+
monkeypatch.setenv("DRY_RUN", "true")
|
|
232
|
+
|
|
233
|
+
builder = (
|
|
234
|
+
workflow("test-dry")
|
|
235
|
+
.pattern("dag")
|
|
236
|
+
.agent("worker", cli="claude")
|
|
237
|
+
.step("s1", agent="worker", task="Do something")
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
# Calling .run() should resolve dry_run from env — we test via RunOptions
|
|
241
|
+
from agent_relay.types import RunOptions
|
|
242
|
+
import os
|
|
243
|
+
|
|
244
|
+
opts = RunOptions()
|
|
245
|
+
if opts.dry_run is None and os.environ.get("DRY_RUN") == "true":
|
|
246
|
+
opts.dry_run = True
|
|
247
|
+
assert opts.dry_run is True
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def test_dry_run_env_var_not_set():
|
|
251
|
+
"""Without DRY_RUN env var, dry_run should remain None."""
|
|
252
|
+
from agent_relay.types import RunOptions
|
|
253
|
+
import os
|
|
254
|
+
|
|
255
|
+
# Ensure env var is not set
|
|
256
|
+
os.environ.pop("DRY_RUN", None)
|
|
257
|
+
opts = RunOptions()
|
|
258
|
+
assert opts.dry_run is None
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def test_dry_run_method():
|
|
262
|
+
"""WorkflowBuilder.dry_run() should set dry_run=True."""
|
|
263
|
+
builder = (
|
|
264
|
+
workflow("test-dry-method")
|
|
265
|
+
.pattern("dag")
|
|
266
|
+
.agent("worker", cli="claude")
|
|
267
|
+
.step("s1", agent="worker", task="Do something")
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
# We can't actually call dry_run() without the CLI, but we can verify
|
|
271
|
+
# the method exists and the config is still valid
|
|
272
|
+
config = builder.to_config()
|
|
273
|
+
assert config["name"] == "test-dry-method"
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""Tests for dry-run support in the Python workflow builder."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
from unittest.mock import MagicMock, patch
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from agent_relay import workflow, fan_out, pipeline, PipelineStage, run_yaml
|
|
10
|
+
from agent_relay.types import RunOptions
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestDryRunOption:
|
|
14
|
+
"""RunOptions.dry_run field."""
|
|
15
|
+
|
|
16
|
+
def test_default_is_none(self):
|
|
17
|
+
opts = RunOptions()
|
|
18
|
+
assert opts.dry_run is None
|
|
19
|
+
|
|
20
|
+
def test_explicit_true(self):
|
|
21
|
+
opts = RunOptions(dry_run=True)
|
|
22
|
+
assert opts.dry_run is True
|
|
23
|
+
|
|
24
|
+
def test_explicit_false(self):
|
|
25
|
+
opts = RunOptions(dry_run=False)
|
|
26
|
+
assert opts.dry_run is False
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class TestDryRunEnvVar:
|
|
30
|
+
"""DRY_RUN environment variable auto-detection."""
|
|
31
|
+
|
|
32
|
+
def test_env_var_enables_dry_run(self, monkeypatch):
|
|
33
|
+
monkeypatch.setenv("DRY_RUN", "true")
|
|
34
|
+
builder = (
|
|
35
|
+
workflow("test")
|
|
36
|
+
.agent("w", cli="claude")
|
|
37
|
+
.step("s", agent="w", task="t")
|
|
38
|
+
)
|
|
39
|
+
with patch("agent_relay.builder._run_config") as mock_run:
|
|
40
|
+
mock_run.return_value = MagicMock(status="completed")
|
|
41
|
+
builder.run()
|
|
42
|
+
# The opts passed to _run_config should have dry_run=True
|
|
43
|
+
call_opts = mock_run.call_args[0][1]
|
|
44
|
+
assert call_opts.dry_run is True
|
|
45
|
+
|
|
46
|
+
def test_env_var_not_set_leaves_none(self, monkeypatch):
|
|
47
|
+
monkeypatch.delenv("DRY_RUN", raising=False)
|
|
48
|
+
builder = (
|
|
49
|
+
workflow("test")
|
|
50
|
+
.agent("w", cli="claude")
|
|
51
|
+
.step("s", agent="w", task="t")
|
|
52
|
+
)
|
|
53
|
+
with patch("agent_relay.builder._run_config") as mock_run:
|
|
54
|
+
mock_run.return_value = MagicMock(status="completed")
|
|
55
|
+
builder.run()
|
|
56
|
+
call_opts = mock_run.call_args[0][1]
|
|
57
|
+
assert call_opts.dry_run is None
|
|
58
|
+
|
|
59
|
+
def test_explicit_false_overrides_env(self, monkeypatch):
|
|
60
|
+
monkeypatch.setenv("DRY_RUN", "true")
|
|
61
|
+
builder = (
|
|
62
|
+
workflow("test")
|
|
63
|
+
.agent("w", cli="claude")
|
|
64
|
+
.step("s", agent="w", task="t")
|
|
65
|
+
)
|
|
66
|
+
with patch("agent_relay.builder._run_config") as mock_run:
|
|
67
|
+
mock_run.return_value = MagicMock(status="completed")
|
|
68
|
+
builder.run(RunOptions(dry_run=False))
|
|
69
|
+
call_opts = mock_run.call_args[0][1]
|
|
70
|
+
assert call_opts.dry_run is False
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class TestDryRunCLIFlag:
|
|
74
|
+
"""--dry-run flag is passed to the agent-relay CLI."""
|
|
75
|
+
|
|
76
|
+
def test_dry_run_adds_flag(self):
|
|
77
|
+
"""When dry_run=True, the CLI command should include --dry-run."""
|
|
78
|
+
from agent_relay.builder import _find_agent_relay
|
|
79
|
+
|
|
80
|
+
cmd_prefix = _find_agent_relay()
|
|
81
|
+
if cmd_prefix is None:
|
|
82
|
+
pytest.skip("agent-relay CLI not installed")
|
|
83
|
+
|
|
84
|
+
builder = (
|
|
85
|
+
workflow("test-flag")
|
|
86
|
+
.agent("w", cli="claude")
|
|
87
|
+
.step("s", agent="w", task="t")
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
with patch("agent_relay.builder._execute_cli") as mock_exec:
|
|
91
|
+
mock_run_result = MagicMock(status="completed")
|
|
92
|
+
mock_exec.return_value = mock_run_result
|
|
93
|
+
|
|
94
|
+
builder.run(RunOptions(dry_run=True))
|
|
95
|
+
|
|
96
|
+
cmd = mock_exec.call_args[0][0]
|
|
97
|
+
assert "--dry-run" in cmd
|
|
98
|
+
|
|
99
|
+
def test_no_dry_run_omits_flag(self):
|
|
100
|
+
"""When dry_run is not set, --dry-run should not be in the command."""
|
|
101
|
+
from agent_relay.builder import _find_agent_relay
|
|
102
|
+
|
|
103
|
+
cmd_prefix = _find_agent_relay()
|
|
104
|
+
if cmd_prefix is None:
|
|
105
|
+
pytest.skip("agent-relay CLI not installed")
|
|
106
|
+
|
|
107
|
+
builder = (
|
|
108
|
+
workflow("test-no-flag")
|
|
109
|
+
.agent("w", cli="claude")
|
|
110
|
+
.step("s", agent="w", task="t")
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
with patch("agent_relay.builder._execute_cli") as mock_exec:
|
|
114
|
+
mock_run_result = MagicMock(status="completed")
|
|
115
|
+
mock_exec.return_value = mock_run_result
|
|
116
|
+
|
|
117
|
+
builder.run()
|
|
118
|
+
|
|
119
|
+
cmd = mock_exec.call_args[0][0]
|
|
120
|
+
assert "--dry-run" not in cmd
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class TestDryRunMethod:
|
|
124
|
+
""".dry_run() convenience method."""
|
|
125
|
+
|
|
126
|
+
def test_dry_run_method_sets_flag(self):
|
|
127
|
+
builder = (
|
|
128
|
+
workflow("test-method")
|
|
129
|
+
.agent("w", cli="claude")
|
|
130
|
+
.step("s", agent="w", task="t")
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
with patch("agent_relay.builder._run_config") as mock_run:
|
|
134
|
+
mock_run.return_value = MagicMock(status="completed")
|
|
135
|
+
builder.dry_run()
|
|
136
|
+
call_opts = mock_run.call_args[0][1]
|
|
137
|
+
assert call_opts.dry_run is True
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class TestDryRunE2E:
|
|
141
|
+
"""End-to-end dry-run through agent-relay CLI (requires CLI installed)."""
|
|
142
|
+
|
|
143
|
+
def test_builder_dry_run_e2e(self):
|
|
144
|
+
from agent_relay.builder import _find_agent_relay
|
|
145
|
+
|
|
146
|
+
if _find_agent_relay() is None:
|
|
147
|
+
pytest.skip("agent-relay CLI not installed")
|
|
148
|
+
|
|
149
|
+
result = (
|
|
150
|
+
workflow("e2e-dry")
|
|
151
|
+
.agent("w", cli="claude")
|
|
152
|
+
.step("s", agent="w", task="Do something")
|
|
153
|
+
.dry_run()
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
assert result.status == "completed"
|
|
157
|
+
|
|
158
|
+
def test_fan_out_dry_run_e2e(self):
|
|
159
|
+
from agent_relay.builder import _find_agent_relay
|
|
160
|
+
|
|
161
|
+
if _find_agent_relay() is None:
|
|
162
|
+
pytest.skip("agent-relay CLI not installed")
|
|
163
|
+
|
|
164
|
+
result = (
|
|
165
|
+
fan_out("e2e-fan", tasks=["task A", "task B"], worker_cli="claude")
|
|
166
|
+
.dry_run()
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
assert result.status == "completed"
|
|
170
|
+
|
|
171
|
+
def test_pipeline_dry_run_e2e(self):
|
|
172
|
+
from agent_relay.builder import _find_agent_relay
|
|
173
|
+
|
|
174
|
+
if _find_agent_relay() is None:
|
|
175
|
+
pytest.skip("agent-relay CLI not installed")
|
|
176
|
+
|
|
177
|
+
result = pipeline(
|
|
178
|
+
"e2e-pipe",
|
|
179
|
+
stages=[
|
|
180
|
+
PipelineStage(name="s1", task="First"),
|
|
181
|
+
PipelineStage(name="s2", task="Second"),
|
|
182
|
+
],
|
|
183
|
+
).dry_run()
|
|
184
|
+
|
|
185
|
+
assert result.status == "completed"
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class TestRunYamlDryRun:
|
|
189
|
+
"""run_yaml() respects dry_run and DRY_RUN env var."""
|
|
190
|
+
|
|
191
|
+
def test_run_yaml_env_var(self, monkeypatch, tmp_path):
|
|
192
|
+
monkeypatch.setenv("DRY_RUN", "true")
|
|
193
|
+
|
|
194
|
+
yaml_file = tmp_path / "test.yaml"
|
|
195
|
+
yaml_file.write_text("""
|
|
196
|
+
version: "1.0"
|
|
197
|
+
name: yaml-dry-test
|
|
198
|
+
swarm:
|
|
199
|
+
pattern: dag
|
|
200
|
+
agents:
|
|
201
|
+
- name: w
|
|
202
|
+
cli: claude
|
|
203
|
+
workflows:
|
|
204
|
+
- name: wf
|
|
205
|
+
steps:
|
|
206
|
+
- name: s
|
|
207
|
+
agent: w
|
|
208
|
+
task: do something
|
|
209
|
+
""")
|
|
210
|
+
|
|
211
|
+
with patch("agent_relay.builder._run_yaml_path") as mock_run:
|
|
212
|
+
mock_run.return_value = MagicMock(status="completed")
|
|
213
|
+
run_yaml(str(yaml_file))
|
|
214
|
+
call_opts = mock_run.call_args[0][1]
|
|
215
|
+
assert call_opts.dry_run is True
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/trajectory",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.18",
|
|
4
4
|
"description": "Trajectory integration utilities (trail/PDERO) for Relay",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"test:watch": "vitest"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@agent-relay/config": "3.2.
|
|
25
|
+
"@agent-relay/config": "3.2.18"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^22.19.3",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/user-directory",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.18",
|
|
4
4
|
"description": "User directory service for agent-relay (per-user credential storage)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"test:watch": "vitest"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@agent-relay/utils": "3.2.
|
|
25
|
+
"@agent-relay/utils": "3.2.18"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^22.19.3",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/utils",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.18",
|
|
4
4
|
"description": "Shared utilities for agent-relay: logging, name generation, command resolution, update checking",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cjs/index.js",
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"vitest": "^3.2.4"
|
|
113
113
|
},
|
|
114
114
|
"dependencies": {
|
|
115
|
-
"@agent-relay/config": "3.2.
|
|
115
|
+
"@agent-relay/config": "3.2.18",
|
|
116
116
|
"compare-versions": "^6.1.1"
|
|
117
117
|
},
|
|
118
118
|
"publishConfig": {
|