agent-relay 3.2.2 → 3.2.4
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 +1358 -941
- package/dist/src/cli/commands/agent-management.d.ts +2 -2
- package/dist/src/cli/commands/agent-management.d.ts.map +1 -1
- package/dist/src/cli/commands/agent-management.js +41 -240
- package/dist/src/cli/commands/agent-management.js.map +1 -1
- package/dist/src/cli/commands/messaging.d.ts +1 -1
- package/dist/src/cli/commands/messaging.d.ts.map +1 -1
- package/dist/src/cli/commands/messaging.js +14 -5
- package/dist/src/cli/commands/messaging.js.map +1 -1
- package/dist/src/cli/lib/agent-management-listing.d.ts +4 -1
- package/dist/src/cli/lib/agent-management-listing.d.ts.map +1 -1
- package/dist/src/cli/lib/agent-management-listing.js +27 -2
- package/dist/src/cli/lib/agent-management-listing.js.map +1 -1
- package/package.json +11 -10
- 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/ADAPTER_REVIEW.md +109 -0
- package/packages/sdk/dist/client.d.ts +66 -0
- package/packages/sdk/dist/client.d.ts.map +1 -1
- package/packages/sdk/dist/client.js +230 -0
- package/packages/sdk/dist/client.js.map +1 -1
- package/packages/sdk/dist/communicate/a2a-bridge.d.ts +25 -0
- package/packages/sdk/dist/communicate/a2a-bridge.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/a2a-bridge.js +89 -0
- package/packages/sdk/dist/communicate/a2a-bridge.js.map +1 -0
- package/packages/sdk/dist/communicate/a2a-server.d.ts +31 -0
- package/packages/sdk/dist/communicate/a2a-server.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/a2a-server.js +220 -0
- package/packages/sdk/dist/communicate/a2a-server.js.map +1 -0
- package/packages/sdk/dist/communicate/a2a-transport.d.ts +48 -0
- package/packages/sdk/dist/communicate/a2a-transport.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/a2a-transport.js +302 -0
- package/packages/sdk/dist/communicate/a2a-transport.js.map +1 -0
- package/packages/sdk/dist/communicate/a2a-types.d.ts +107 -0
- package/packages/sdk/dist/communicate/a2a-types.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/a2a-types.js +209 -0
- package/packages/sdk/dist/communicate/a2a-types.js.map +1 -0
- package/packages/sdk/dist/communicate/adapters/claude-sdk.d.ts +28 -0
- package/packages/sdk/dist/communicate/adapters/claude-sdk.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/adapters/claude-sdk.js +47 -0
- package/packages/sdk/dist/communicate/adapters/claude-sdk.js.map +1 -0
- package/packages/sdk/dist/communicate/adapters/crewai.d.ts +42 -0
- package/packages/sdk/dist/communicate/adapters/crewai.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/adapters/crewai.js +95 -0
- package/packages/sdk/dist/communicate/adapters/crewai.js.map +1 -0
- package/packages/sdk/dist/communicate/adapters/google-adk.d.ts +53 -0
- package/packages/sdk/dist/communicate/adapters/google-adk.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/adapters/google-adk.js +77 -0
- package/packages/sdk/dist/communicate/adapters/google-adk.js.map +1 -0
- package/packages/sdk/dist/communicate/adapters/index.d.ts +7 -0
- package/packages/sdk/dist/communicate/adapters/index.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/adapters/index.js +7 -0
- package/packages/sdk/dist/communicate/adapters/index.js.map +1 -0
- package/packages/sdk/dist/communicate/adapters/langgraph.d.ts +40 -0
- package/packages/sdk/dist/communicate/adapters/langgraph.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/adapters/langgraph.js +77 -0
- package/packages/sdk/dist/communicate/adapters/langgraph.js.map +1 -0
- package/packages/sdk/dist/communicate/adapters/openai-agents.d.ts +25 -0
- package/packages/sdk/dist/communicate/adapters/openai-agents.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/adapters/openai-agents.js +70 -0
- package/packages/sdk/dist/communicate/adapters/openai-agents.js.map +1 -0
- package/packages/sdk/dist/communicate/adapters/pi.d.ts +45 -0
- package/packages/sdk/dist/communicate/adapters/pi.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/adapters/pi.js +59 -0
- package/packages/sdk/dist/communicate/adapters/pi.js.map +1 -0
- package/packages/sdk/dist/communicate/core.d.ts +58 -0
- package/packages/sdk/dist/communicate/core.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/core.js +128 -0
- package/packages/sdk/dist/communicate/core.js.map +1 -0
- package/packages/sdk/dist/communicate/index.d.ts +4 -0
- package/packages/sdk/dist/communicate/index.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/index.js +4 -0
- package/packages/sdk/dist/communicate/index.js.map +1 -0
- package/packages/sdk/dist/communicate/transport.d.ts +36 -0
- package/packages/sdk/dist/communicate/transport.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/transport.js +371 -0
- package/packages/sdk/dist/communicate/transport.js.map +1 -0
- package/packages/sdk/dist/communicate/types.d.ts +58 -0
- package/packages/sdk/dist/communicate/types.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/types.js +66 -0
- package/packages/sdk/dist/communicate/types.js.map +1 -0
- package/packages/sdk/dist/workflows/builder.d.ts +35 -5
- package/packages/sdk/dist/workflows/builder.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/builder.js +81 -7
- package/packages/sdk/dist/workflows/builder.js.map +1 -1
- package/packages/sdk/dist/workflows/cli.js +14 -1
- package/packages/sdk/dist/workflows/cli.js.map +1 -1
- package/packages/sdk/dist/workflows/runner.d.ts +10 -2
- package/packages/sdk/dist/workflows/runner.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/runner.js +95 -1
- package/packages/sdk/dist/workflows/runner.js.map +1 -1
- package/packages/sdk/dist/workflows/types.d.ts +11 -0
- package/packages/sdk/dist/workflows/types.d.ts.map +1 -1
- package/packages/sdk/examples/communicate/claude_sdk_example.ts +5 -0
- package/packages/sdk/examples/communicate/pi_example.ts +8 -0
- package/packages/sdk/package.json +48 -2
- package/packages/sdk/src/__tests__/builder-deterministic.test.ts +132 -0
- package/packages/sdk/src/__tests__/communicate/a2a-bridge.test.ts +211 -0
- package/packages/sdk/src/__tests__/communicate/a2a-server.test.ts +359 -0
- package/packages/sdk/src/__tests__/communicate/a2a-transport.test.ts +537 -0
- package/packages/sdk/src/__tests__/communicate/a2a-types.test.ts +297 -0
- package/packages/sdk/src/__tests__/communicate/adapters/claude-sdk.test.ts +163 -0
- package/packages/sdk/src/__tests__/communicate/adapters/crewai.test.ts +219 -0
- package/packages/sdk/src/__tests__/communicate/adapters/e2e-crewai.test.ts +101 -0
- package/packages/sdk/src/__tests__/communicate/adapters/e2e-google-adk.test.ts +166 -0
- package/packages/sdk/src/__tests__/communicate/adapters/e2e-langgraph.test.ts +181 -0
- package/packages/sdk/src/__tests__/communicate/adapters/e2e-openai-agents.test.ts +137 -0
- package/packages/sdk/src/__tests__/communicate/adapters/e2e-pi.test.ts +140 -0
- package/packages/sdk/src/__tests__/communicate/adapters/google-adk.test.ts +200 -0
- package/packages/sdk/src/__tests__/communicate/adapters/langgraph.test.ts +162 -0
- package/packages/sdk/src/__tests__/communicate/adapters/openai-agents.test.ts +166 -0
- package/packages/sdk/src/__tests__/communicate/adapters/pi.test.ts +140 -0
- package/packages/sdk/src/__tests__/communicate/core.test.ts +574 -0
- package/packages/sdk/src/__tests__/communicate/integration/cross-framework.test.ts +353 -0
- package/packages/sdk/src/__tests__/communicate/transport.test.ts +613 -0
- package/packages/sdk/src/__tests__/start-from.test.ts +346 -0
- package/packages/sdk/src/client.ts +301 -0
- package/packages/sdk/src/communicate/a2a-bridge.ts +111 -0
- package/packages/sdk/src/communicate/a2a-server.ts +277 -0
- package/packages/sdk/src/communicate/a2a-transport.ts +395 -0
- package/packages/sdk/src/communicate/a2a-types.ts +338 -0
- package/packages/sdk/src/communicate/adapters/claude-sdk.ts +85 -0
- package/packages/sdk/src/communicate/adapters/crewai.ts +141 -0
- package/packages/sdk/src/communicate/adapters/google-adk.ts +139 -0
- package/packages/sdk/src/communicate/adapters/index.ts +6 -0
- package/packages/sdk/src/communicate/adapters/langgraph.ts +112 -0
- package/packages/sdk/src/communicate/adapters/openai-agents.ts +113 -0
- package/packages/sdk/src/communicate/adapters/pi.ts +105 -0
- package/packages/sdk/src/communicate/core.ts +157 -0
- package/packages/sdk/src/communicate/index.ts +3 -0
- package/packages/sdk/src/communicate/transport.ts +489 -0
- package/packages/sdk/src/communicate/types.ts +106 -0
- package/packages/sdk/src/examples/workflows/fix-dashboard-user-registration.yaml +182 -0
- package/packages/sdk/src/workflows/builder.ts +97 -9
- package/packages/sdk/src/workflows/cli.ts +16 -1
- package/packages/sdk/src/workflows/runner.ts +110 -1
- package/packages/sdk/src/workflows/types.ts +14 -0
- package/packages/sdk/tsconfig.build.json +1 -7
- package/packages/sdk/tsconfig.json +1 -7
- package/packages/sdk-py/README.md +67 -25
- package/packages/sdk-py/examples/communicate/agno_example.py +8 -0
- package/packages/sdk-py/examples/communicate/claude_sdk_example.py +6 -0
- package/packages/sdk-py/examples/communicate/crewai_example.py +7 -0
- package/packages/sdk-py/examples/communicate/google_adk_example.py +7 -0
- package/packages/sdk-py/examples/communicate/openai_agents_example.py +8 -0
- package/packages/sdk-py/examples/communicate/swarms_example.py +7 -0
- package/packages/sdk-py/pyproject.toml +12 -1
- package/packages/sdk-py/src/agent_relay/__init__.py +8 -0
- package/packages/sdk-py/src/agent_relay/builder.py +65 -26
- package/packages/sdk-py/src/agent_relay/communicate/__init__.py +6 -0
- package/packages/sdk-py/src/agent_relay/communicate/a2a_bridge.py +138 -0
- package/packages/sdk-py/src/agent_relay/communicate/a2a_server.py +242 -0
- package/packages/sdk-py/src/agent_relay/communicate/a2a_transport.py +366 -0
- package/packages/sdk-py/src/agent_relay/communicate/a2a_types.py +294 -0
- package/packages/sdk-py/src/agent_relay/communicate/adapters/__init__.py +10 -0
- package/packages/sdk-py/src/agent_relay/communicate/adapters/agno.py +74 -0
- package/packages/sdk-py/src/agent_relay/communicate/adapters/claude_sdk.py +78 -0
- package/packages/sdk-py/src/agent_relay/communicate/adapters/crewai.py +143 -0
- package/packages/sdk-py/src/agent_relay/communicate/adapters/google_adk.py +69 -0
- package/packages/sdk-py/src/agent_relay/communicate/adapters/openai_agents.py +86 -0
- package/packages/sdk-py/src/agent_relay/communicate/adapters/pi.py +175 -0
- package/packages/sdk-py/src/agent_relay/communicate/adapters/swarms.py +44 -0
- package/packages/sdk-py/src/agent_relay/communicate/core.py +293 -0
- package/packages/sdk-py/src/agent_relay/communicate/transport.py +502 -0
- package/packages/sdk-py/src/agent_relay/communicate/types.py +89 -0
- package/packages/sdk-py/src/agent_relay/types.py +2 -1
- package/packages/sdk-py/tests/communicate/__init__.py +0 -0
- package/packages/sdk-py/tests/communicate/adapters/__init__.py +0 -0
- package/packages/sdk-py/tests/communicate/adapters/e2e_test_agno.py +154 -0
- package/packages/sdk-py/tests/communicate/adapters/e2e_test_claude_sdk.py +428 -0
- package/packages/sdk-py/tests/communicate/adapters/e2e_test_crewai.py +234 -0
- package/packages/sdk-py/tests/communicate/adapters/e2e_test_google_adk.py +182 -0
- package/packages/sdk-py/tests/communicate/adapters/e2e_test_langgraph.py +262 -0
- package/packages/sdk-py/tests/communicate/adapters/e2e_test_openai_agents.py +88 -0
- package/packages/sdk-py/tests/communicate/adapters/e2e_test_pi.py +156 -0
- package/packages/sdk-py/tests/communicate/adapters/e2e_test_swarms.py +239 -0
- package/packages/sdk-py/tests/communicate/adapters/test_agno.py +140 -0
- package/packages/sdk-py/tests/communicate/adapters/test_claude_sdk.py +147 -0
- package/packages/sdk-py/tests/communicate/adapters/test_crewai.py +136 -0
- package/packages/sdk-py/tests/communicate/adapters/test_google_adk.py +125 -0
- package/packages/sdk-py/tests/communicate/adapters/test_openai_agents.py +99 -0
- package/packages/sdk-py/tests/communicate/adapters/test_pi.py +270 -0
- package/packages/sdk-py/tests/communicate/adapters/test_swarms.py +113 -0
- package/packages/sdk-py/tests/communicate/conftest.py +555 -0
- package/packages/sdk-py/tests/communicate/integration/__init__.py +1 -0
- package/packages/sdk-py/tests/communicate/integration/test_cross_framework.py +331 -0
- package/packages/sdk-py/tests/communicate/integration/test_end_to_end.py +151 -0
- package/packages/sdk-py/tests/communicate/test_a2a_bridge.py +363 -0
- package/packages/sdk-py/tests/communicate/test_a2a_server.py +346 -0
- package/packages/sdk-py/tests/communicate/test_a2a_transport.py +561 -0
- package/packages/sdk-py/tests/communicate/test_a2a_types.py +342 -0
- package/packages/sdk-py/tests/communicate/test_auto_detect.py +67 -0
- package/packages/sdk-py/tests/communicate/test_core.py +331 -0
- package/packages/sdk-py/tests/communicate/test_transport.py +373 -0
- package/packages/sdk-py/tests/communicate/test_types.py +285 -0
- package/packages/sdk-py/tests/test_builder_deterministic.py +118 -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
- package/packages/sdk/dist/__tests__/completion-pipeline.test.d.ts +0 -14
- package/packages/sdk/dist/__tests__/completion-pipeline.test.d.ts.map +0 -1
- package/packages/sdk/dist/__tests__/completion-pipeline.test.js +0 -1476
- package/packages/sdk/dist/__tests__/completion-pipeline.test.js.map +0 -1
- package/packages/sdk/dist/__tests__/contract-fixtures.test.d.ts +0 -2
- package/packages/sdk/dist/__tests__/contract-fixtures.test.d.ts.map +0 -1
- package/packages/sdk/dist/__tests__/contract-fixtures.test.js +0 -152
- package/packages/sdk/dist/__tests__/contract-fixtures.test.js.map +0 -1
- package/packages/sdk/dist/__tests__/e2e-owner-review.test.d.ts +0 -16
- package/packages/sdk/dist/__tests__/e2e-owner-review.test.d.ts.map +0 -1
- package/packages/sdk/dist/__tests__/e2e-owner-review.test.js +0 -640
- package/packages/sdk/dist/__tests__/e2e-owner-review.test.js.map +0 -1
- package/packages/sdk/dist/__tests__/facade.test.d.ts +0 -2
- package/packages/sdk/dist/__tests__/facade.test.d.ts.map +0 -1
- package/packages/sdk/dist/__tests__/facade.test.js +0 -305
- package/packages/sdk/dist/__tests__/facade.test.js.map +0 -1
- package/packages/sdk/dist/__tests__/integration.test.d.ts +0 -2
- package/packages/sdk/dist/__tests__/integration.test.d.ts.map +0 -1
- package/packages/sdk/dist/__tests__/integration.test.js +0 -205
- package/packages/sdk/dist/__tests__/integration.test.js.map +0 -1
- package/packages/sdk/dist/__tests__/pty.test.d.ts +0 -2
- package/packages/sdk/dist/__tests__/pty.test.d.ts.map +0 -1
- package/packages/sdk/dist/__tests__/pty.test.js +0 -20
- package/packages/sdk/dist/__tests__/pty.test.js.map +0 -1
- package/packages/sdk/dist/__tests__/quickstart.test.d.ts +0 -2
- package/packages/sdk/dist/__tests__/quickstart.test.d.ts.map +0 -1
- package/packages/sdk/dist/__tests__/quickstart.test.js +0 -176
- package/packages/sdk/dist/__tests__/quickstart.test.js.map +0 -1
- package/packages/sdk/dist/__tests__/spawn-from-env.test.d.ts +0 -2
- package/packages/sdk/dist/__tests__/spawn-from-env.test.d.ts.map +0 -1
- package/packages/sdk/dist/__tests__/spawn-from-env.test.js +0 -222
- package/packages/sdk/dist/__tests__/spawn-from-env.test.js.map +0 -1
- package/packages/sdk/dist/__tests__/unit.test.d.ts +0 -2
- package/packages/sdk/dist/__tests__/unit.test.d.ts.map +0 -1
- package/packages/sdk/dist/__tests__/unit.test.js +0 -357
- package/packages/sdk/dist/__tests__/unit.test.js.map +0 -1
- package/packages/sdk-py/agent_relay/__init__.py +0 -21
- package/packages/sdk-py/agent_relay/models.py +0 -398
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"""Tests for communicate mode types (TDD - red phase).
|
|
2
|
+
|
|
3
|
+
Tests cover:
|
|
4
|
+
- Message dataclass (creation, immutability, defaults)
|
|
5
|
+
- RelayConfig dataclass (defaults, env var resolution)
|
|
6
|
+
- Exception types (RelayConnectionError, RelayConfigError, RelayAuthError)
|
|
7
|
+
- MessageCallback type alias compatibility
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
import dataclasses
|
|
12
|
+
|
|
13
|
+
import pytest
|
|
14
|
+
|
|
15
|
+
from agent_relay.communicate.types import (
|
|
16
|
+
Message,
|
|
17
|
+
MessageCallback,
|
|
18
|
+
RelayAuthError,
|
|
19
|
+
RelayConfig,
|
|
20
|
+
RelayConfigError,
|
|
21
|
+
RelayConnectionError,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
# Message dataclass
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TestMessage:
|
|
31
|
+
"""Tests for the Message frozen dataclass."""
|
|
32
|
+
|
|
33
|
+
def test_create_with_required_fields_only(self):
|
|
34
|
+
msg = Message(sender="Alice", text="Hello")
|
|
35
|
+
assert msg.sender == "Alice"
|
|
36
|
+
assert msg.text == "Hello"
|
|
37
|
+
|
|
38
|
+
def test_optional_fields_default_to_none(self):
|
|
39
|
+
msg = Message(sender="Alice", text="Hello")
|
|
40
|
+
assert msg.channel is None
|
|
41
|
+
assert msg.thread_id is None
|
|
42
|
+
assert msg.timestamp is None
|
|
43
|
+
assert msg.message_id is None
|
|
44
|
+
|
|
45
|
+
def test_create_with_all_fields(self):
|
|
46
|
+
msg = Message(
|
|
47
|
+
sender="Bob",
|
|
48
|
+
text="Hi there",
|
|
49
|
+
channel="general",
|
|
50
|
+
thread_id="thread-123",
|
|
51
|
+
timestamp=1710300000.0,
|
|
52
|
+
message_id="msg-456",
|
|
53
|
+
)
|
|
54
|
+
assert msg.sender == "Bob"
|
|
55
|
+
assert msg.text == "Hi there"
|
|
56
|
+
assert msg.channel == "general"
|
|
57
|
+
assert msg.thread_id == "thread-123"
|
|
58
|
+
assert msg.timestamp == 1710300000.0
|
|
59
|
+
assert msg.message_id == "msg-456"
|
|
60
|
+
|
|
61
|
+
def test_is_frozen_cannot_set_sender(self):
|
|
62
|
+
msg = Message(sender="Alice", text="Hello")
|
|
63
|
+
with pytest.raises(dataclasses.FrozenInstanceError):
|
|
64
|
+
msg.sender = "Eve" # type: ignore[misc]
|
|
65
|
+
|
|
66
|
+
def test_is_frozen_cannot_set_text(self):
|
|
67
|
+
msg = Message(sender="Alice", text="Hello")
|
|
68
|
+
with pytest.raises(dataclasses.FrozenInstanceError):
|
|
69
|
+
msg.text = "Tampered" # type: ignore[misc]
|
|
70
|
+
|
|
71
|
+
def test_is_frozen_cannot_set_optional_field(self):
|
|
72
|
+
msg = Message(sender="Alice", text="Hello")
|
|
73
|
+
with pytest.raises(dataclasses.FrozenInstanceError):
|
|
74
|
+
msg.channel = "secret" # type: ignore[misc]
|
|
75
|
+
|
|
76
|
+
def test_is_dataclass(self):
|
|
77
|
+
assert dataclasses.is_dataclass(Message)
|
|
78
|
+
|
|
79
|
+
def test_equality_same_values(self):
|
|
80
|
+
a = Message(sender="Alice", text="Hi")
|
|
81
|
+
b = Message(sender="Alice", text="Hi")
|
|
82
|
+
assert a == b
|
|
83
|
+
|
|
84
|
+
def test_equality_different_values(self):
|
|
85
|
+
a = Message(sender="Alice", text="Hi")
|
|
86
|
+
b = Message(sender="Bob", text="Hi")
|
|
87
|
+
assert a != b
|
|
88
|
+
|
|
89
|
+
def test_channel_none_means_dm(self):
|
|
90
|
+
"""When channel is None, the message is a direct message."""
|
|
91
|
+
msg = Message(sender="Alice", text="DM content")
|
|
92
|
+
assert msg.channel is None
|
|
93
|
+
|
|
94
|
+
def test_channel_set_means_channel_message(self):
|
|
95
|
+
msg = Message(sender="Alice", text="Channel content", channel="general")
|
|
96
|
+
assert msg.channel == "general"
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# ---------------------------------------------------------------------------
|
|
100
|
+
# RelayConfig dataclass
|
|
101
|
+
# ---------------------------------------------------------------------------
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class TestRelayConfig:
|
|
105
|
+
"""Tests for the RelayConfig dataclass with env var defaults."""
|
|
106
|
+
|
|
107
|
+
def test_all_defaults(self, monkeypatch):
|
|
108
|
+
monkeypatch.delenv("RELAY_WORKSPACE", raising=False)
|
|
109
|
+
monkeypatch.delenv("RELAY_API_KEY", raising=False)
|
|
110
|
+
monkeypatch.delenv("RELAY_BASE_URL", raising=False)
|
|
111
|
+
|
|
112
|
+
config = RelayConfig()
|
|
113
|
+
assert config.workspace is None
|
|
114
|
+
assert config.api_key is None
|
|
115
|
+
assert config.base_url is None
|
|
116
|
+
assert config.channels == ["general"]
|
|
117
|
+
assert config.poll_interval_ms == 1000
|
|
118
|
+
assert config.auto_cleanup is True
|
|
119
|
+
|
|
120
|
+
def test_explicit_values_override_defaults(self):
|
|
121
|
+
config = RelayConfig(
|
|
122
|
+
workspace="my-ws",
|
|
123
|
+
api_key="key-123",
|
|
124
|
+
base_url="https://custom.api.dev",
|
|
125
|
+
channels=["dev", "ops"],
|
|
126
|
+
poll_interval_ms=500,
|
|
127
|
+
auto_cleanup=False,
|
|
128
|
+
)
|
|
129
|
+
assert config.workspace == "my-ws"
|
|
130
|
+
assert config.api_key == "key-123"
|
|
131
|
+
assert config.base_url == "https://custom.api.dev"
|
|
132
|
+
assert config.channels == ["dev", "ops"]
|
|
133
|
+
assert config.poll_interval_ms == 500
|
|
134
|
+
assert config.auto_cleanup is False
|
|
135
|
+
|
|
136
|
+
def test_channels_default_is_independent_per_instance(self):
|
|
137
|
+
"""Each RelayConfig gets its own channels list (no shared mutable default)."""
|
|
138
|
+
a = RelayConfig()
|
|
139
|
+
b = RelayConfig()
|
|
140
|
+
a.channels.append("extra")
|
|
141
|
+
assert "extra" not in b.channels
|
|
142
|
+
|
|
143
|
+
def test_env_var_workspace(self, monkeypatch):
|
|
144
|
+
monkeypatch.setenv("RELAY_WORKSPACE", "env-workspace")
|
|
145
|
+
config = RelayConfig()
|
|
146
|
+
assert config.workspace == "env-workspace"
|
|
147
|
+
|
|
148
|
+
def test_env_var_api_key(self, monkeypatch):
|
|
149
|
+
monkeypatch.setenv("RELAY_API_KEY", "env-key-abc")
|
|
150
|
+
config = RelayConfig()
|
|
151
|
+
assert config.api_key == "env-key-abc"
|
|
152
|
+
|
|
153
|
+
def test_env_var_base_url(self, monkeypatch):
|
|
154
|
+
monkeypatch.setenv("RELAY_BASE_URL", "https://env.api.dev")
|
|
155
|
+
config = RelayConfig()
|
|
156
|
+
assert config.base_url == "https://env.api.dev"
|
|
157
|
+
|
|
158
|
+
def test_base_url_default_when_no_env(self, monkeypatch):
|
|
159
|
+
"""When RELAY_BASE_URL is not set, base_url defaults to the Relaycast cloud URL."""
|
|
160
|
+
monkeypatch.delenv("RELAY_BASE_URL", raising=False)
|
|
161
|
+
config = RelayConfig()
|
|
162
|
+
# base_url should resolve to the default cloud URL when accessed
|
|
163
|
+
# The exact resolution may happen at init or lazily; either None or the default is acceptable
|
|
164
|
+
# but the resolved value should be "https://api.relaycast.dev"
|
|
165
|
+
assert config.base_url is None or config.base_url == "https://api.relaycast.dev"
|
|
166
|
+
|
|
167
|
+
def test_explicit_value_overrides_env_var(self, monkeypatch):
|
|
168
|
+
monkeypatch.setenv("RELAY_WORKSPACE", "env-workspace")
|
|
169
|
+
config = RelayConfig(workspace="explicit-workspace")
|
|
170
|
+
assert config.workspace == "explicit-workspace"
|
|
171
|
+
|
|
172
|
+
def test_is_dataclass(self):
|
|
173
|
+
assert dataclasses.is_dataclass(RelayConfig)
|
|
174
|
+
|
|
175
|
+
def test_is_not_frozen(self):
|
|
176
|
+
"""RelayConfig should be mutable (not frozen)."""
|
|
177
|
+
config = RelayConfig()
|
|
178
|
+
config.workspace = "updated"
|
|
179
|
+
assert config.workspace == "updated"
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
# ---------------------------------------------------------------------------
|
|
183
|
+
# Exception types
|
|
184
|
+
# ---------------------------------------------------------------------------
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class TestRelayConnectionError:
|
|
188
|
+
"""Tests for RelayConnectionError which stores status_code and message."""
|
|
189
|
+
|
|
190
|
+
def test_inherits_from_exception(self):
|
|
191
|
+
assert issubclass(RelayConnectionError, Exception)
|
|
192
|
+
|
|
193
|
+
def test_stores_status_code_and_message(self):
|
|
194
|
+
err = RelayConnectionError(status_code=500, message="Internal Server Error")
|
|
195
|
+
assert err.status_code == 500
|
|
196
|
+
assert err.message == "Internal Server Error"
|
|
197
|
+
|
|
198
|
+
def test_str_contains_status_and_message(self):
|
|
199
|
+
err = RelayConnectionError(status_code=503, message="Service Unavailable")
|
|
200
|
+
text = str(err)
|
|
201
|
+
assert "503" in text
|
|
202
|
+
assert "Service Unavailable" in text
|
|
203
|
+
|
|
204
|
+
def test_different_status_codes(self):
|
|
205
|
+
for code in [400, 404, 429, 500, 502, 503]:
|
|
206
|
+
err = RelayConnectionError(status_code=code, message=f"Error {code}")
|
|
207
|
+
assert err.status_code == code
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class TestRelayConfigError:
|
|
211
|
+
"""Tests for RelayConfigError which signals missing configuration."""
|
|
212
|
+
|
|
213
|
+
def test_inherits_from_exception(self):
|
|
214
|
+
assert issubclass(RelayConfigError, Exception)
|
|
215
|
+
|
|
216
|
+
def test_has_descriptive_message(self):
|
|
217
|
+
err = RelayConfigError("RELAY_API_KEY environment variable is required")
|
|
218
|
+
assert "RELAY_API_KEY" in str(err)
|
|
219
|
+
|
|
220
|
+
def test_message_accessible(self):
|
|
221
|
+
msg = "Missing RELAY_WORKSPACE. Set the environment variable or pass workspace= to RelayConfig."
|
|
222
|
+
err = RelayConfigError(msg)
|
|
223
|
+
assert str(err) == msg
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class TestRelayAuthError:
|
|
227
|
+
"""Tests for RelayAuthError which signals HTTP 401 responses."""
|
|
228
|
+
|
|
229
|
+
def test_inherits_from_exception(self):
|
|
230
|
+
assert issubclass(RelayAuthError, Exception)
|
|
231
|
+
|
|
232
|
+
def test_message(self):
|
|
233
|
+
err = RelayAuthError("Invalid API key")
|
|
234
|
+
assert "Invalid API key" in str(err)
|
|
235
|
+
|
|
236
|
+
def test_inherits_from_relay_connection_error_or_exception(self):
|
|
237
|
+
"""RelayAuthError should be catchable as a general exception."""
|
|
238
|
+
err = RelayAuthError("Unauthorized")
|
|
239
|
+
assert isinstance(err, Exception)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
# ---------------------------------------------------------------------------
|
|
243
|
+
# MessageCallback type alias
|
|
244
|
+
# ---------------------------------------------------------------------------
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
class TestMessageCallback:
|
|
248
|
+
"""Tests that both sync and async callables satisfy MessageCallback."""
|
|
249
|
+
|
|
250
|
+
def test_sync_callable_is_valid(self):
|
|
251
|
+
"""A synchronous function that takes a Message and returns None should be valid."""
|
|
252
|
+
received: list[Message] = []
|
|
253
|
+
|
|
254
|
+
def handler(msg: Message) -> None:
|
|
255
|
+
received.append(msg)
|
|
256
|
+
|
|
257
|
+
# Verify the callable works as expected
|
|
258
|
+
callback: MessageCallback = handler
|
|
259
|
+
msg = Message(sender="Alice", text="Test")
|
|
260
|
+
callback(msg)
|
|
261
|
+
assert len(received) == 1
|
|
262
|
+
assert received[0].sender == "Alice"
|
|
263
|
+
|
|
264
|
+
async def test_async_callable_is_valid(self):
|
|
265
|
+
"""An async function that takes a Message and returns None should be valid."""
|
|
266
|
+
received: list[Message] = []
|
|
267
|
+
|
|
268
|
+
async def handler(msg: Message) -> None:
|
|
269
|
+
received.append(msg)
|
|
270
|
+
|
|
271
|
+
callback: MessageCallback = handler
|
|
272
|
+
msg = Message(sender="Bob", text="Async test")
|
|
273
|
+
result = callback(msg)
|
|
274
|
+
# If it returns a coroutine, await it
|
|
275
|
+
if asyncio.iscoroutine(result):
|
|
276
|
+
await result
|
|
277
|
+
assert len(received) == 1
|
|
278
|
+
assert received[0].sender == "Bob"
|
|
279
|
+
|
|
280
|
+
def test_lambda_callable_is_valid(self):
|
|
281
|
+
"""A lambda that takes a Message should work as a callback."""
|
|
282
|
+
results: list[str] = []
|
|
283
|
+
callback: MessageCallback = lambda msg: results.append(msg.text)
|
|
284
|
+
callback(Message(sender="X", text="lambda-test"))
|
|
285
|
+
assert results == ["lambda-test"]
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""Tests for deterministic and worktree step support in the Python workflow builder."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from agent_relay import workflow, VerificationCheck
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_deterministic_step_emits_correct_config():
|
|
8
|
+
config = (
|
|
9
|
+
workflow("test")
|
|
10
|
+
.agent("worker", cli="claude")
|
|
11
|
+
.step("read-files", type="deterministic", command="cat src/index.ts",
|
|
12
|
+
verification=VerificationCheck(type="exit_code", value="0"))
|
|
13
|
+
.step("build", agent="worker", task="Build the project")
|
|
14
|
+
.to_config()
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
steps = config["workflows"][0]["steps"]
|
|
18
|
+
assert len(steps) == 2
|
|
19
|
+
|
|
20
|
+
# Deterministic step
|
|
21
|
+
assert steps[0]["name"] == "read-files"
|
|
22
|
+
assert steps[0]["type"] == "deterministic"
|
|
23
|
+
assert steps[0]["command"] == "cat src/index.ts"
|
|
24
|
+
assert "agent" not in steps[0]
|
|
25
|
+
assert "task" not in steps[0]
|
|
26
|
+
assert steps[0]["verification"] == {"type": "exit_code", "value": "0"}
|
|
27
|
+
|
|
28
|
+
# Agent step
|
|
29
|
+
assert steps[1]["name"] == "build"
|
|
30
|
+
assert steps[1]["agent"] == "worker"
|
|
31
|
+
assert steps[1]["task"] == "Build the project"
|
|
32
|
+
assert "type" not in steps[1]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_deterministic_step_with_all_options():
|
|
36
|
+
config = (
|
|
37
|
+
workflow("test")
|
|
38
|
+
.agent("worker", cli="claude")
|
|
39
|
+
.step("run-cmd", type="deterministic", command="npm test",
|
|
40
|
+
capture_output=True, fail_on_error=False,
|
|
41
|
+
depends_on=["build"], timeout_ms=30000)
|
|
42
|
+
.step("final", agent="worker", task="Finalize")
|
|
43
|
+
.to_config()
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
step = config["workflows"][0]["steps"][0]
|
|
47
|
+
assert step["captureOutput"] is True
|
|
48
|
+
assert step["failOnError"] is False
|
|
49
|
+
assert step["dependsOn"] == ["build"]
|
|
50
|
+
assert step["timeoutMs"] == 30000
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_worktree_step_emits_correct_config():
|
|
54
|
+
config = (
|
|
55
|
+
workflow("test")
|
|
56
|
+
.agent("worker", cli="claude")
|
|
57
|
+
.step("setup-worktree", type="worktree", branch="feature/new",
|
|
58
|
+
base_branch="main", path=".worktrees/feature-new", create_branch=True)
|
|
59
|
+
.step("work", agent="worker", task="Do work", depends_on=["setup-worktree"])
|
|
60
|
+
.to_config()
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
step = config["workflows"][0]["steps"][0]
|
|
64
|
+
assert step["type"] == "worktree"
|
|
65
|
+
assert step["branch"] == "feature/new"
|
|
66
|
+
assert step["baseBranch"] == "main"
|
|
67
|
+
assert step["path"] == ".worktrees/feature-new"
|
|
68
|
+
assert step["createBranch"] is True
|
|
69
|
+
assert "agent" not in step
|
|
70
|
+
assert "command" not in step
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_deterministic_only_workflow_no_agents_required():
|
|
74
|
+
config = (
|
|
75
|
+
workflow("infra")
|
|
76
|
+
.step("lint", type="deterministic", command="npm run lint")
|
|
77
|
+
.step("test", type="deterministic", command="npm test", depends_on=["lint"])
|
|
78
|
+
.to_config()
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
assert config["agents"] == []
|
|
82
|
+
assert len(config["workflows"][0]["steps"]) == 2
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_deterministic_step_without_command_raises():
|
|
86
|
+
with pytest.raises(ValueError, match="deterministic steps must have a command"):
|
|
87
|
+
workflow("test").step("bad", type="deterministic")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_deterministic_step_with_agent_raises():
|
|
91
|
+
with pytest.raises(ValueError, match="deterministic steps must not have agent or task"):
|
|
92
|
+
workflow("test").step("bad", type="deterministic", command="ls", agent="x", task="y")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_agent_step_without_agent_task_raises():
|
|
96
|
+
with pytest.raises(ValueError, match="Agent steps must have both agent and task"):
|
|
97
|
+
workflow("test").step("bad")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_agent_steps_without_agent_definition_raises():
|
|
101
|
+
with pytest.raises(ValueError, match="Workflow must have at least one agent when using agent steps"):
|
|
102
|
+
workflow("test").step("work", agent="worker", task="Do work").to_config()
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_worktree_step_without_branch_raises():
|
|
106
|
+
with pytest.raises(ValueError, match="worktree steps must have a branch"):
|
|
107
|
+
workflow("test").step("bad", type="worktree")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_to_yaml_includes_deterministic_steps():
|
|
111
|
+
yaml_str = (
|
|
112
|
+
workflow("test")
|
|
113
|
+
.step("check", type="deterministic", command="echo hello")
|
|
114
|
+
.to_yaml()
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
assert "type: deterministic" in yaml_str
|
|
118
|
+
assert "command: echo hello" in yaml_str
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/trajectory",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.4",
|
|
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.4"
|
|
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.4",
|
|
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.4"
|
|
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.4",
|
|
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.4",
|
|
116
116
|
"compare-versions": "^6.1.1"
|
|
117
117
|
},
|
|
118
118
|
"publishConfig": {
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Completion Pipeline tests for Point-Person-Led Completion spec.
|
|
3
|
-
*
|
|
4
|
-
* Validates:
|
|
5
|
-
* 1. Evidence-based completion (verification passes without marker)
|
|
6
|
-
* 2. Owner decision parsing (OWNER_DECISION: COMPLETE/INCOMPLETE_RETRY/INCOMPLETE_FAIL)
|
|
7
|
-
* 3. Tolerant review parsing (accepts semantic equivalents)
|
|
8
|
-
* 4. Channel evidence contributions (WORKER_DONE signals)
|
|
9
|
-
* 5. Backward compatibility with marker-based workflows
|
|
10
|
-
* 6. Codex/Gemini/Supervisor pattern compatibility
|
|
11
|
-
* 7. Map-reduce workflows remain unaffected
|
|
12
|
-
*/
|
|
13
|
-
export {};
|
|
14
|
-
//# sourceMappingURL=completion-pipeline.test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"completion-pipeline.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/completion-pipeline.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
|