agent-relay 3.0.1 → 3.1.0
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 +37 -244
- 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 +342 -60
- package/dist/src/cli/commands/core.d.ts +2 -0
- package/dist/src/cli/commands/core.d.ts.map +1 -1
- package/dist/src/cli/commands/core.js +9 -2
- package/dist/src/cli/commands/core.js.map +1 -1
- package/dist/src/cli/lib/broker-lifecycle.d.ts.map +1 -1
- package/dist/src/cli/lib/broker-lifecycle.js +87 -28
- package/dist/src/cli/lib/broker-lifecycle.js.map +1 -1
- package/package.json +9 -8
- package/packages/acp-bridge/README.md +50 -67
- 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/policy/package.json +2 -2
- package/packages/sdk/README.md +169 -64
- package/packages/sdk/dist/__tests__/contract-fixtures.test.js +76 -9
- package/packages/sdk/dist/__tests__/contract-fixtures.test.js.map +1 -1
- package/packages/sdk/dist/__tests__/facade.test.js +48 -0
- package/packages/sdk/dist/__tests__/facade.test.js.map +1 -1
- package/packages/sdk/dist/__tests__/integration.test.js +11 -5
- package/packages/sdk/dist/__tests__/integration.test.js.map +1 -1
- package/packages/sdk/dist/__tests__/unit.test.js +36 -0
- package/packages/sdk/dist/__tests__/unit.test.js.map +1 -1
- package/packages/sdk/dist/client.d.ts +36 -3
- package/packages/sdk/dist/client.d.ts.map +1 -1
- package/packages/sdk/dist/client.js +142 -9
- package/packages/sdk/dist/client.js.map +1 -1
- package/packages/sdk/dist/protocol.d.ts +7 -1
- package/packages/sdk/dist/protocol.d.ts.map +1 -1
- package/packages/sdk/dist/relay.d.ts +74 -11
- package/packages/sdk/dist/relay.d.ts.map +1 -1
- package/packages/sdk/dist/relay.js +175 -27
- package/packages/sdk/dist/relay.js.map +1 -1
- package/packages/sdk/dist/workflows/runner.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/runner.js +71 -36
- package/packages/sdk/dist/workflows/runner.js.map +1 -1
- package/packages/sdk/dist/workflows/types.d.ts +1 -1
- package/packages/sdk/dist/workflows/types.d.ts.map +1 -1
- package/packages/sdk/package.json +2 -2
- package/packages/sdk/src/__tests__/contract-fixtures.test.ts +88 -9
- package/packages/sdk/src/__tests__/error-scenarios.test.ts +1 -1
- package/packages/sdk/src/__tests__/facade.test.ts +68 -0
- package/packages/sdk/src/__tests__/idle-nudge.test.ts +205 -257
- package/packages/sdk/src/__tests__/integration.test.ts +11 -5
- package/packages/sdk/src/__tests__/orchestration-upgrades.test.ts +277 -13
- package/packages/sdk/src/__tests__/swarm-coordinator.test.ts +1 -0
- package/packages/sdk/src/__tests__/unit.test.ts +44 -0
- package/packages/sdk/src/__tests__/workflow-runner.test.ts +67 -7
- package/packages/sdk/src/__tests__/workflow-trajectory.test.ts +4 -5
- package/packages/sdk/src/client.ts +195 -14
- package/packages/sdk/src/examples/workflows/runner-idle-refactor.yaml +306 -0
- package/packages/sdk/src/protocol.ts +7 -2
- package/packages/sdk/src/relay.ts +271 -38
- package/packages/sdk/src/workflows/runner.ts +73 -42
- package/packages/sdk/src/workflows/schema.json +1 -1
- package/packages/sdk/src/workflows/types.ts +1 -1
- package/packages/sdk/vitest.config.ts +1 -0
- package/packages/sdk-py/README.md +89 -102
- package/packages/sdk-py/agent_relay/__init__.py +16 -19
- package/packages/sdk-py/pyproject.toml +6 -2
- package/packages/sdk-py/src/agent_relay/__init__.py +35 -1
- package/packages/sdk-py/src/agent_relay/client.py +776 -0
- package/packages/sdk-py/src/agent_relay/models.py +27 -0
- package/packages/sdk-py/src/agent_relay/protocol.py +114 -0
- package/packages/sdk-py/src/agent_relay/relay.py +860 -0
- package/packages/sdk-py/tests/test_relay_lifecycle_hooks.py +250 -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/scripts/postinstall.js +35 -162
- package/packages/sdk/.trajectories/active/traj_1771875803391_84ca57b2.json +0 -50
- package/packages/sdk/.trajectories/active/traj_1771891934534_06504121.json +0 -50
- package/packages/sdk/.trajectories/active/traj_1771891957929_211afc4e.json +0 -50
- package/packages/sdk/.trajectories/active/traj_1771891982509_38c84638.json +0 -50
- package/packages/sdk/.trajectories/completed/traj_1771875803188_cd6d181c.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771875803204_f2aeb8c8.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771875803210_d65f3f1a.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771875803218_e454a25d.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771875803223_d7a64815.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771875803227_7e56da5b.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771875803235_4fbf93b4.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771875803243_47931c71.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771875803258_3816f3fe.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771875803268_8061140e.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771875803326_ae6f9c78.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771875808396_cbde0a6c.json +0 -91
- package/packages/sdk/.trajectories/completed/traj_1771875812026_aa2442bb.json +0 -91
- package/packages/sdk/.trajectories/completed/traj_1771875815431_c2c656c5.json +0 -91
- package/packages/sdk/.trajectories/completed/traj_1771875818645_3a4dbf02.json +0 -91
- package/packages/sdk/.trajectories/completed/traj_1771891934403_24923c03.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891934421_dca16e24.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891934430_057706f7.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891934442_faf97382.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891934454_5542ecd5.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891934464_12202a08.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891934487_94378275.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891934503_ca728c13.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891934519_100af69a.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891934536_62ad39d9.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891934553_d6798a52.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891939537_541c8096.json +0 -91
- package/packages/sdk/.trajectories/completed/traj_1771891942985_36ab9a4d.json +0 -91
- package/packages/sdk/.trajectories/completed/traj_1771891946453_e8a6e05f.json +0 -91
- package/packages/sdk/.trajectories/completed/traj_1771891949838_5de0de84.json +0 -91
- package/packages/sdk/.trajectories/completed/traj_1771891957807_0ecfb4f4.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891957827_c4539239.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891957836_91168b48.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891957848_8c5cad0b.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891957857_0986b293.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891957872_8a3113af.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891957884_0bb85208.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891957892_86c75e2e.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891957907_98ca0e6f.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891957918_d9091231.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891957931_dcaf77ed.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891962931_eb1fdee2.json +0 -91
- package/packages/sdk/.trajectories/completed/traj_1771891966262_9061a93f.json +0 -91
- package/packages/sdk/.trajectories/completed/traj_1771891969915_1adaba19.json +0 -91
- package/packages/sdk/.trajectories/completed/traj_1771891973588_f08b79e9.json +0 -91
- package/packages/sdk/.trajectories/completed/traj_1771891982421_f1985bce.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891982432_e7a84163.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891982447_369b842a.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891982469_5fc45199.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891982495_454c7cb3.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891982514_08098e03.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891982526_b351d778.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891982533_fa542d83.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891982540_18ab24dc.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891982544_5b4fa163.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891982548_c13f089a.json +0 -80
- package/packages/sdk/.trajectories/completed/traj_1771891987510_23f6da1f.json +0 -91
- package/packages/sdk/.trajectories/completed/traj_1771891991466_912c2e04.json +0 -91
- package/packages/sdk/.trajectories/completed/traj_1771891994891_60604be2.json +0 -91
- package/packages/sdk/.trajectories/completed/traj_1771891998370_cfaf9b8b.json +0 -91
- package/packages/sdk/bin/agent-relay-broker +0 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"""Tests for spawn/release lifecycle hooks in the high-level relay facade."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from unittest.mock import AsyncMock
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
from agent_relay import AgentRelay, SpawnOptions
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class _FakeRelayClient:
|
|
14
|
+
def __init__(self) -> None:
|
|
15
|
+
self.spawn_error: Exception | None = None
|
|
16
|
+
self.release_error: Exception | None = None
|
|
17
|
+
self.spawn_calls: list[dict] = []
|
|
18
|
+
self.release_calls: list[tuple[str, str | None]] = []
|
|
19
|
+
|
|
20
|
+
async def spawn_pty(self, **kwargs):
|
|
21
|
+
self.spawn_calls.append(kwargs)
|
|
22
|
+
if self.spawn_error:
|
|
23
|
+
raise self.spawn_error
|
|
24
|
+
return {"name": kwargs["name"], "runtime": "pty"}
|
|
25
|
+
|
|
26
|
+
async def release(self, name: str, reason: str | None = None):
|
|
27
|
+
self.release_calls.append((name, reason))
|
|
28
|
+
if self.release_error:
|
|
29
|
+
raise self.release_error
|
|
30
|
+
return {"name": name}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@pytest.mark.asyncio
|
|
34
|
+
async def test_spawn_lifecycle_hooks_success():
|
|
35
|
+
relay = AgentRelay()
|
|
36
|
+
client = _FakeRelayClient()
|
|
37
|
+
relay._ensure_started = AsyncMock(return_value=client)
|
|
38
|
+
|
|
39
|
+
events: list[tuple[str, dict]] = []
|
|
40
|
+
options = SpawnOptions(
|
|
41
|
+
channels=["general"],
|
|
42
|
+
on_start=lambda ctx: events.append(("start", dict(ctx))),
|
|
43
|
+
on_success=lambda ctx: events.append(("success", dict(ctx))),
|
|
44
|
+
on_error=lambda ctx: events.append(("error", dict(ctx))),
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
agent = await relay.spawn("HookWorker", "claude", "Do the work", options)
|
|
48
|
+
|
|
49
|
+
assert agent.name == "HookWorker"
|
|
50
|
+
assert events[0] == (
|
|
51
|
+
"start",
|
|
52
|
+
{
|
|
53
|
+
"name": "HookWorker",
|
|
54
|
+
"cli": "claude",
|
|
55
|
+
"channels": ["general"],
|
|
56
|
+
"task": "Do the work",
|
|
57
|
+
},
|
|
58
|
+
)
|
|
59
|
+
assert events[1] == (
|
|
60
|
+
"success",
|
|
61
|
+
{
|
|
62
|
+
"name": "HookWorker",
|
|
63
|
+
"cli": "claude",
|
|
64
|
+
"channels": ["general"],
|
|
65
|
+
"task": "Do the work",
|
|
66
|
+
"runtime": "pty",
|
|
67
|
+
},
|
|
68
|
+
)
|
|
69
|
+
assert len(events) == 2
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@pytest.mark.asyncio
|
|
73
|
+
async def test_spawn_lifecycle_hooks_support_async_callbacks():
|
|
74
|
+
relay = AgentRelay()
|
|
75
|
+
client = _FakeRelayClient()
|
|
76
|
+
relay._ensure_started = AsyncMock(return_value=client)
|
|
77
|
+
|
|
78
|
+
start_done = False
|
|
79
|
+
success_done = False
|
|
80
|
+
|
|
81
|
+
async def on_start(_ctx):
|
|
82
|
+
nonlocal start_done
|
|
83
|
+
await asyncio.sleep(0)
|
|
84
|
+
start_done = True
|
|
85
|
+
|
|
86
|
+
async def on_success(_ctx):
|
|
87
|
+
nonlocal success_done
|
|
88
|
+
await asyncio.sleep(0)
|
|
89
|
+
success_done = True
|
|
90
|
+
|
|
91
|
+
options = SpawnOptions(
|
|
92
|
+
channels=["general"],
|
|
93
|
+
on_start=on_start,
|
|
94
|
+
on_success=on_success,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
await relay.spawn("AsyncHookWorker", "claude", "Do the work", options)
|
|
98
|
+
|
|
99
|
+
assert start_done is True
|
|
100
|
+
assert success_done is True
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@pytest.mark.asyncio
|
|
104
|
+
async def test_spawn_lifecycle_hooks_error():
|
|
105
|
+
relay = AgentRelay()
|
|
106
|
+
client = _FakeRelayClient()
|
|
107
|
+
client.spawn_error = RuntimeError("spawn failed")
|
|
108
|
+
relay._ensure_started = AsyncMock(return_value=client)
|
|
109
|
+
|
|
110
|
+
on_error_calls: list[dict] = []
|
|
111
|
+
options = SpawnOptions(
|
|
112
|
+
channels=["general"],
|
|
113
|
+
on_start=lambda _: None,
|
|
114
|
+
on_error=lambda ctx: on_error_calls.append(dict(ctx)),
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
with pytest.raises(RuntimeError, match="spawn failed"):
|
|
118
|
+
await relay.spawn("HookWorkerFail", "claude", "Do the work", options)
|
|
119
|
+
|
|
120
|
+
assert len(on_error_calls) == 1
|
|
121
|
+
error_ctx = on_error_calls[0]
|
|
122
|
+
assert error_ctx["name"] == "HookWorkerFail"
|
|
123
|
+
assert error_ctx["cli"] == "claude"
|
|
124
|
+
assert isinstance(error_ctx["error"], RuntimeError)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@pytest.mark.asyncio
|
|
128
|
+
async def test_shorthand_spawn_lifecycle_hooks_success():
|
|
129
|
+
relay = AgentRelay()
|
|
130
|
+
client = _FakeRelayClient()
|
|
131
|
+
relay._ensure_started = AsyncMock(return_value=client)
|
|
132
|
+
|
|
133
|
+
events: list[str] = []
|
|
134
|
+
agent = await relay.claude.spawn(
|
|
135
|
+
name="ShorthandWorker",
|
|
136
|
+
channels=["general"],
|
|
137
|
+
task="Run analysis",
|
|
138
|
+
on_start=lambda _: events.append("start"),
|
|
139
|
+
on_success=lambda _: events.append("success"),
|
|
140
|
+
on_error=lambda _: events.append("error"),
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
assert agent.name == "ShorthandWorker"
|
|
144
|
+
assert events == ["start", "success"]
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@pytest.mark.asyncio
|
|
148
|
+
async def test_shorthand_spawn_does_not_fire_start_hook_if_broker_startup_fails():
|
|
149
|
+
relay = AgentRelay()
|
|
150
|
+
relay._ensure_started = AsyncMock(side_effect=RuntimeError("broker startup failed"))
|
|
151
|
+
|
|
152
|
+
start_called = False
|
|
153
|
+
error_called = False
|
|
154
|
+
|
|
155
|
+
def _mark_called(kind: str) -> None:
|
|
156
|
+
nonlocal start_called, error_called
|
|
157
|
+
if kind == "start":
|
|
158
|
+
start_called = True
|
|
159
|
+
else:
|
|
160
|
+
error_called = True
|
|
161
|
+
|
|
162
|
+
with pytest.raises(RuntimeError, match="broker startup failed"):
|
|
163
|
+
await relay.claude.spawn(
|
|
164
|
+
name="ShorthandWorkerStartupFail",
|
|
165
|
+
channels=["general"],
|
|
166
|
+
on_start=lambda _ctx: _mark_called("start"),
|
|
167
|
+
on_error=lambda _ctx: _mark_called("error"),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
assert start_called is False
|
|
171
|
+
assert error_called is False
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@pytest.mark.asyncio
|
|
175
|
+
async def test_release_lifecycle_hooks_success_and_error():
|
|
176
|
+
relay = AgentRelay()
|
|
177
|
+
client = _FakeRelayClient()
|
|
178
|
+
relay._ensure_started = AsyncMock(return_value=client)
|
|
179
|
+
|
|
180
|
+
agent = await relay.spawn("ReleaseWorker", "claude")
|
|
181
|
+
|
|
182
|
+
success_events: list[str] = []
|
|
183
|
+
await agent.release(
|
|
184
|
+
"cleanup",
|
|
185
|
+
on_start=lambda _: success_events.append("start"),
|
|
186
|
+
on_success=lambda _: success_events.append("success"),
|
|
187
|
+
on_error=lambda _: success_events.append("error"),
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
assert client.release_calls[-1] == ("ReleaseWorker", "cleanup")
|
|
191
|
+
assert success_events == ["start", "success"]
|
|
192
|
+
|
|
193
|
+
client.release_error = RuntimeError("release failed")
|
|
194
|
+
error_calls: list[dict] = []
|
|
195
|
+
with pytest.raises(RuntimeError, match="release failed"):
|
|
196
|
+
await agent.release(
|
|
197
|
+
"cleanup-again",
|
|
198
|
+
on_error=lambda ctx: error_calls.append(dict(ctx)),
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
assert len(error_calls) == 1
|
|
202
|
+
assert error_calls[0]["name"] == "ReleaseWorker"
|
|
203
|
+
assert isinstance(error_calls[0]["error"], RuntimeError)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@pytest.mark.asyncio
|
|
207
|
+
async def test_release_lifecycle_hooks_support_async_callbacks():
|
|
208
|
+
relay = AgentRelay()
|
|
209
|
+
client = _FakeRelayClient()
|
|
210
|
+
relay._ensure_started = AsyncMock(return_value=client)
|
|
211
|
+
|
|
212
|
+
agent = await relay.spawn("ReleaseAsyncWorker", "claude")
|
|
213
|
+
|
|
214
|
+
success_done = False
|
|
215
|
+
|
|
216
|
+
async def on_success(_ctx):
|
|
217
|
+
nonlocal success_done
|
|
218
|
+
await asyncio.sleep(0)
|
|
219
|
+
success_done = True
|
|
220
|
+
|
|
221
|
+
await agent.release("cleanup", on_success=on_success)
|
|
222
|
+
|
|
223
|
+
assert success_done is True
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@pytest.mark.asyncio
|
|
227
|
+
async def test_release_does_not_fire_hooks_if_broker_startup_fails():
|
|
228
|
+
relay = AgentRelay()
|
|
229
|
+
client = _FakeRelayClient()
|
|
230
|
+
relay._ensure_started = AsyncMock(return_value=client)
|
|
231
|
+
agent = await relay.spawn("ReleaseStartupFailWorker", "claude")
|
|
232
|
+
|
|
233
|
+
relay._ensure_started = AsyncMock(side_effect=RuntimeError("broker startup failed"))
|
|
234
|
+
|
|
235
|
+
start_called = False
|
|
236
|
+
error_called = False
|
|
237
|
+
|
|
238
|
+
def mark_start(_ctx):
|
|
239
|
+
nonlocal start_called
|
|
240
|
+
start_called = True
|
|
241
|
+
|
|
242
|
+
def mark_error(_ctx):
|
|
243
|
+
nonlocal error_called
|
|
244
|
+
error_called = True
|
|
245
|
+
|
|
246
|
+
with pytest.raises(RuntimeError, match="broker startup failed"):
|
|
247
|
+
await agent.release("cleanup", on_start=mark_start, on_error=mark_error)
|
|
248
|
+
|
|
249
|
+
assert start_called is False
|
|
250
|
+
assert error_called is False
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/trajectory",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
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.0
|
|
25
|
+
"@agent-relay/config": "3.1.0"
|
|
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.0
|
|
3
|
+
"version": "3.1.0",
|
|
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.0
|
|
25
|
+
"@agent-relay/utils": "3.1.0"
|
|
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.0
|
|
3
|
+
"version": "3.1.0",
|
|
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.0
|
|
115
|
+
"@agent-relay/config": "3.1.0",
|
|
116
116
|
"compare-versions": "^6.1.1"
|
|
117
117
|
},
|
|
118
118
|
"publishConfig": {
|
package/scripts/postinstall.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Postinstall Script for agent-relay
|
|
4
4
|
*
|
|
5
5
|
* This script runs after npm install to:
|
|
6
|
-
* 1. Install relay-
|
|
6
|
+
* 1. Install agent-relay-broker binary for current platform
|
|
7
7
|
* 2. Install dashboard dependencies
|
|
8
8
|
* 3. Patch agent-trajectories CLI
|
|
9
9
|
* 4. Check for tmux availability (fallback)
|
|
@@ -148,36 +148,6 @@ function ensureSqliteDriver() {
|
|
|
148
148
|
return { ok: false, driver: 'none', statusPath, error: detail };
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
/**
|
|
152
|
-
* Get the platform-specific binary name for relay-pty
|
|
153
|
-
* Returns null if platform is not supported
|
|
154
|
-
*/
|
|
155
|
-
function getRelayPtyBinaryName() {
|
|
156
|
-
const platform = os.platform();
|
|
157
|
-
const arch = os.arch();
|
|
158
|
-
|
|
159
|
-
// Map Node.js arch to Rust target arch
|
|
160
|
-
const archMap = {
|
|
161
|
-
'arm64': 'arm64',
|
|
162
|
-
'x64': 'x64',
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
// Map Node.js platform to Rust target platform
|
|
166
|
-
const platformMap = {
|
|
167
|
-
'darwin': 'darwin',
|
|
168
|
-
'linux': 'linux',
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
const targetPlatform = platformMap[platform];
|
|
172
|
-
const targetArch = archMap[arch];
|
|
173
|
-
|
|
174
|
-
if (!targetPlatform || !targetArch) {
|
|
175
|
-
return null;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return `relay-pty-${targetPlatform}-${targetArch}`;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
151
|
/** Read the package version from package.json */
|
|
182
152
|
function getPackageVersion(pkgRoot) {
|
|
183
153
|
const packageJsonPath = path.join(pkgRoot, 'package.json');
|
|
@@ -198,7 +168,7 @@ function getPackageVersion(pkgRoot) {
|
|
|
198
168
|
* Download a file via HTTPS, following redirects
|
|
199
169
|
* Uses only built-in https module (no deps)
|
|
200
170
|
*/
|
|
201
|
-
function
|
|
171
|
+
function downloadBinary(url, destPath, maxRedirects = 5) {
|
|
202
172
|
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
203
173
|
|
|
204
174
|
const attemptDownload = (currentUrl, redirectsRemaining, resolve, reject) => {
|
|
@@ -210,7 +180,7 @@ function downloadRelayPtyBinary(url, destPath, maxRedirects = 5) {
|
|
|
210
180
|
if (isRedirect) {
|
|
211
181
|
if (redirectsRemaining <= 0) {
|
|
212
182
|
res.resume();
|
|
213
|
-
reject(new Error('Too many redirects while downloading
|
|
183
|
+
reject(new Error('Too many redirects while downloading binary'));
|
|
214
184
|
return;
|
|
215
185
|
}
|
|
216
186
|
|
|
@@ -280,83 +250,6 @@ function resignBinaryForMacOS(binaryPath) {
|
|
|
280
250
|
}
|
|
281
251
|
}
|
|
282
252
|
|
|
283
|
-
/**
|
|
284
|
-
* Install the relay-pty binary for the current platform
|
|
285
|
-
*/
|
|
286
|
-
async function installRelayPtyBinary() {
|
|
287
|
-
const pkgRoot = getPackageRoot();
|
|
288
|
-
const binaryName = getRelayPtyBinaryName();
|
|
289
|
-
|
|
290
|
-
if (!binaryName) {
|
|
291
|
-
warn(`Unsupported platform: ${os.platform()}-${os.arch()}`);
|
|
292
|
-
warn('relay-pty binary not available, will fall back to tmux mode');
|
|
293
|
-
return false;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
const sourcePath = path.join(pkgRoot, 'bin', binaryName);
|
|
297
|
-
const targetPath = path.join(pkgRoot, 'bin', 'relay-pty');
|
|
298
|
-
|
|
299
|
-
// Check if platform-specific binary exists (bundled install)
|
|
300
|
-
if (!fs.existsSync(sourcePath)) {
|
|
301
|
-
const version = getPackageVersion(pkgRoot);
|
|
302
|
-
if (!version) {
|
|
303
|
-
warn('relay-pty binary not available and package version unknown');
|
|
304
|
-
warn('Will fall back to tmux mode');
|
|
305
|
-
return false;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const downloadUrl = `https://github.com/AgentWorkforce/relay/releases/download/v${version}/${binaryName}`;
|
|
309
|
-
info(`relay-pty binary not bundled, downloading from ${downloadUrl} ...`);
|
|
310
|
-
|
|
311
|
-
try {
|
|
312
|
-
await downloadRelayPtyBinary(downloadUrl, sourcePath);
|
|
313
|
-
fs.chmodSync(sourcePath, 0o755);
|
|
314
|
-
success(`Downloaded relay-pty binary for ${os.platform()}-${os.arch()}`);
|
|
315
|
-
} catch (err) {
|
|
316
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
317
|
-
warn(`Failed to download relay-pty binary: ${message}`);
|
|
318
|
-
warn('Will fall back to tmux mode');
|
|
319
|
-
return false;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Check if already installed (and is a symlink or copy of correct binary)
|
|
324
|
-
if (fs.existsSync(targetPath)) {
|
|
325
|
-
try {
|
|
326
|
-
// Check if it's already the right binary by comparing size
|
|
327
|
-
const sourceStats = fs.statSync(sourcePath);
|
|
328
|
-
const targetStats = fs.statSync(targetPath);
|
|
329
|
-
if (sourceStats.size === targetStats.size) {
|
|
330
|
-
// Re-sign even if already installed to ensure signature is valid
|
|
331
|
-
// This fixes issues where previous installs have invalid signatures
|
|
332
|
-
resignBinaryForMacOS(targetPath);
|
|
333
|
-
info('relay-pty binary already installed');
|
|
334
|
-
return true;
|
|
335
|
-
}
|
|
336
|
-
} catch {
|
|
337
|
-
// Continue to reinstall
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// Copy the binary (symlinks don't work well across npm install)
|
|
342
|
-
try {
|
|
343
|
-
fs.copyFileSync(sourcePath, targetPath);
|
|
344
|
-
fs.chmodSync(targetPath, 0o755);
|
|
345
|
-
|
|
346
|
-
// Re-sign the binary on macOS to prevent code signature validation failures
|
|
347
|
-
// Without this, macOS may SIGKILL the process immediately on execution
|
|
348
|
-
if (resignBinaryForMacOS(targetPath)) {
|
|
349
|
-
success(`Installed relay-pty binary for ${os.platform()}-${os.arch()}`);
|
|
350
|
-
} else {
|
|
351
|
-
warn(`Installed relay-pty binary but signing failed - may not work on macOS`);
|
|
352
|
-
}
|
|
353
|
-
return true;
|
|
354
|
-
} catch (err) {
|
|
355
|
-
warn(`Failed to install relay-pty binary: ${err.message}`);
|
|
356
|
-
return false;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
253
|
/**
|
|
361
254
|
* Get the platform-specific binary name for the broker binary.
|
|
362
255
|
* The broker binary is the Rust-compiled broker (not the Bun-compiled CLI).
|
|
@@ -391,8 +284,9 @@ function getBrokerBinaryName() {
|
|
|
391
284
|
*
|
|
392
285
|
* Resolution order:
|
|
393
286
|
* 1. Already bundled at packages/sdk/bin/agent-relay-broker (e.g. from prepack)
|
|
394
|
-
* 2.
|
|
395
|
-
* 3.
|
|
287
|
+
* 2. Platform-specific binary bundled in root bin/ (e.g. bin/agent-relay-broker-linux-x64)
|
|
288
|
+
* 3. Download platform-specific standalone binary from GitHub releases
|
|
289
|
+
* 4. Fall back to the local Rust debug binary at target/debug/agent-relay-broker (dev only)
|
|
396
290
|
*/
|
|
397
291
|
async function installBrokerBinary() {
|
|
398
292
|
const pkgRoot = getPackageRoot();
|
|
@@ -420,8 +314,25 @@ async function installBrokerBinary() {
|
|
|
420
314
|
|
|
421
315
|
fs.mkdirSync(sdkBinDir, { recursive: true });
|
|
422
316
|
|
|
423
|
-
// 2. Try downloading from GitHub releases
|
|
424
317
|
const binaryName = getBrokerBinaryName();
|
|
318
|
+
|
|
319
|
+
// 2. Check for bundled platform-specific binary in root bin/
|
|
320
|
+
if (binaryName) {
|
|
321
|
+
const bundledBinary = path.join(pkgRoot, 'bin', binaryName);
|
|
322
|
+
if (fs.existsSync(bundledBinary)) {
|
|
323
|
+
try {
|
|
324
|
+
fs.copyFileSync(bundledBinary, targetPath);
|
|
325
|
+
fs.chmodSync(targetPath, 0o755);
|
|
326
|
+
resignBinaryForMacOS(targetPath);
|
|
327
|
+
success(`Installed broker binary from bundled package (${binaryName})`);
|
|
328
|
+
return true;
|
|
329
|
+
} catch (err) {
|
|
330
|
+
warn(`Failed to copy bundled broker binary: ${err.message}`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// 3. Try downloading from GitHub releases
|
|
425
336
|
if (binaryName) {
|
|
426
337
|
const version = getPackageVersion(pkgRoot);
|
|
427
338
|
if (version) {
|
|
@@ -429,7 +340,7 @@ async function installBrokerBinary() {
|
|
|
429
340
|
info(`Downloading broker binary from ${downloadUrl} ...`);
|
|
430
341
|
|
|
431
342
|
try {
|
|
432
|
-
await
|
|
343
|
+
await downloadBinary(downloadUrl, targetPath);
|
|
433
344
|
fs.chmodSync(targetPath, 0o755);
|
|
434
345
|
resignBinaryForMacOS(targetPath);
|
|
435
346
|
success(`Downloaded broker binary for ${os.platform()}-${os.arch()}`);
|
|
@@ -441,7 +352,7 @@ async function installBrokerBinary() {
|
|
|
441
352
|
}
|
|
442
353
|
}
|
|
443
354
|
|
|
444
|
-
//
|
|
355
|
+
// 4. Dev fallback — check for local Rust build (release first, then debug)
|
|
445
356
|
for (const profile of ['release', 'debug']) {
|
|
446
357
|
const localBinary = path.join(pkgRoot, 'target', profile, binaryFilename);
|
|
447
358
|
if (fs.existsSync(localBinary)) {
|
|
@@ -462,18 +373,6 @@ async function installBrokerBinary() {
|
|
|
462
373
|
return false;
|
|
463
374
|
}
|
|
464
375
|
|
|
465
|
-
/**
|
|
466
|
-
* Check if tmux is available on the system
|
|
467
|
-
*/
|
|
468
|
-
function hasSystemTmux() {
|
|
469
|
-
try {
|
|
470
|
-
execSync('which tmux', { stdio: 'pipe' });
|
|
471
|
-
return true;
|
|
472
|
-
} catch {
|
|
473
|
-
return false;
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
376
|
/**
|
|
478
377
|
* Setup workspace package symlinks for global/bundled installs.
|
|
479
378
|
*
|
|
@@ -683,7 +582,7 @@ function patchAgentTrajectories() {
|
|
|
683
582
|
success('Patched agent-trajectories to record agent on trail start');
|
|
684
583
|
}
|
|
685
584
|
|
|
686
|
-
function logPostinstallDiagnostics(
|
|
585
|
+
function logPostinstallDiagnostics(hasBrokerBinary, sqliteStatus, linkResult) {
|
|
687
586
|
// Workspace packages status (for global installs)
|
|
688
587
|
if (linkResult && linkResult.needed) {
|
|
689
588
|
if (linkResult.success) {
|
|
@@ -693,16 +592,10 @@ function logPostinstallDiagnostics(hasRelayPty, hasBrokerBinary, sqliteStatus, l
|
|
|
693
592
|
}
|
|
694
593
|
}
|
|
695
594
|
|
|
696
|
-
if (hasRelayPty) {
|
|
697
|
-
console.log('✓ relay-pty binary installed');
|
|
698
|
-
} else {
|
|
699
|
-
console.log('⚠ relay-pty binary not installed - falling back to tmux mode if available');
|
|
700
|
-
}
|
|
701
|
-
|
|
702
595
|
if (hasBrokerBinary) {
|
|
703
|
-
console.log('✓ broker binary installed
|
|
596
|
+
console.log('✓ agent-relay-broker binary installed');
|
|
704
597
|
} else {
|
|
705
|
-
console.log('⚠ broker binary not installed - AgentRelay
|
|
598
|
+
console.log('⚠ agent-relay-broker binary not installed - AgentRelay will not work');
|
|
706
599
|
}
|
|
707
600
|
|
|
708
601
|
if (sqliteStatus.ok && sqliteStatus.driver === 'better-sqlite3') {
|
|
@@ -734,10 +627,7 @@ async function main() {
|
|
|
734
627
|
}
|
|
735
628
|
}
|
|
736
629
|
|
|
737
|
-
// Install
|
|
738
|
-
const hasRelayPty = await installRelayPtyBinary();
|
|
739
|
-
|
|
740
|
-
// Install broker binary for SDK programmatic usage (AgentRelay)
|
|
630
|
+
// Install broker binary for agent spawning and SDK programmatic usage
|
|
741
631
|
const hasBrokerBinary = await installBrokerBinary();
|
|
742
632
|
|
|
743
633
|
// Ensure SQLite driver is available (better-sqlite3 or node:sqlite)
|
|
@@ -750,30 +640,13 @@ async function main() {
|
|
|
750
640
|
installDashboardDeps();
|
|
751
641
|
|
|
752
642
|
// Always print diagnostics (even in CI)
|
|
753
|
-
logPostinstallDiagnostics(
|
|
754
|
-
|
|
755
|
-
// Skip tmux check in CI environments
|
|
756
|
-
if (process.env.CI === 'true') {
|
|
757
|
-
return;
|
|
758
|
-
}
|
|
643
|
+
logPostinstallDiagnostics(hasBrokerBinary, sqliteStatus, linkResult);
|
|
759
644
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
info('
|
|
763
|
-
|
|
645
|
+
if (!hasBrokerBinary) {
|
|
646
|
+
warn('agent-relay-broker binary not available');
|
|
647
|
+
info('Agent spawning will not work without the broker binary.');
|
|
648
|
+
info('To fix: cargo build --release --bin agent-relay-broker (requires Rust toolchain)');
|
|
764
649
|
}
|
|
765
|
-
|
|
766
|
-
// Fall back to tmux check
|
|
767
|
-
if (hasSystemTmux()) {
|
|
768
|
-
info('System tmux found (fallback mode)');
|
|
769
|
-
return;
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
// Neither relay-pty nor tmux available
|
|
773
|
-
warn('Neither relay-pty nor tmux available');
|
|
774
|
-
info('Agent spawning will not work without one of:');
|
|
775
|
-
info(' 1. relay-pty binary (included for darwin-arm64, darwin-x64, linux-x64)');
|
|
776
|
-
info(' 2. tmux: brew install tmux (macOS) or apt install tmux (Linux)');
|
|
777
650
|
}
|
|
778
651
|
|
|
779
652
|
main().catch((err) => {
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "traj_1771875803391_84ca57b2",
|
|
3
|
-
"version": 1,
|
|
4
|
-
"task": {
|
|
5
|
-
"title": "default run #18889026",
|
|
6
|
-
"source": {
|
|
7
|
-
"system": "workflow-runner",
|
|
8
|
-
"id": "188890267c8264bd0f5a7422"
|
|
9
|
-
}
|
|
10
|
-
},
|
|
11
|
-
"status": "active",
|
|
12
|
-
"startedAt": "2026-02-23T19:43:23.391Z",
|
|
13
|
-
"agents": [
|
|
14
|
-
{
|
|
15
|
-
"name": "orchestrator",
|
|
16
|
-
"role": "workflow-runner",
|
|
17
|
-
"joinedAt": "2026-02-23T19:43:23.391Z"
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
"name": "agent-a",
|
|
21
|
-
"role": "agent-a",
|
|
22
|
-
"joinedAt": "2026-02-23T19:43:28.180Z"
|
|
23
|
-
}
|
|
24
|
-
],
|
|
25
|
-
"chapters": [
|
|
26
|
-
{
|
|
27
|
-
"id": "ch_ed619507",
|
|
28
|
-
"title": "Planning",
|
|
29
|
-
"agentName": "orchestrator",
|
|
30
|
-
"startedAt": "2026-02-23T19:43:23.391Z",
|
|
31
|
-
"events": [
|
|
32
|
-
{
|
|
33
|
-
"ts": 1771875803391,
|
|
34
|
-
"type": "note",
|
|
35
|
-
"content": "Workflow \"default\" started with 2 steps"
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
"ts": 1771875803391,
|
|
39
|
-
"type": "note",
|
|
40
|
-
"content": "Parsed 2 steps, 1 dependent steps, DAG validated, no cycles"
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
"ts": 1771875808180,
|
|
44
|
-
"type": "note",
|
|
45
|
-
"content": "Step \"step-1\" assigned to agent \"agent-a\""
|
|
46
|
-
}
|
|
47
|
-
]
|
|
48
|
-
}
|
|
49
|
-
]
|
|
50
|
-
}
|