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,101 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import test from 'node:test';
|
|
4
|
+
|
|
5
|
+
import { Relay } from '../../../communicate/core.js';
|
|
6
|
+
import { onRelay, onCrewRelay } from '../../../communicate/adapters/crewai.js';
|
|
7
|
+
|
|
8
|
+
const AGENT_NAME = `e2e-crewai-${randomUUID().slice(0, 8)}`;
|
|
9
|
+
|
|
10
|
+
function createAgent(role: string) {
|
|
11
|
+
return {
|
|
12
|
+
role,
|
|
13
|
+
tools: [] as any[],
|
|
14
|
+
step_callback: null as ((step: any) => void) | null,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
test('CrewAI adapter e2e: onRelay tools work against live Relaycast', async () => {
|
|
19
|
+
const relay = new Relay(AGENT_NAME, { autoCleanup: false });
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const agent = createAgent(AGENT_NAME);
|
|
23
|
+
const { unsubscribe } = onRelay(agent, relay);
|
|
24
|
+
|
|
25
|
+
const findTool = (name: string) => {
|
|
26
|
+
const tool = agent.tools.find((t: any) => t.tool_name === name);
|
|
27
|
+
assert.ok(tool, `Tool ${name} not found`);
|
|
28
|
+
return tool;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const relaySend = findTool('relay_send');
|
|
32
|
+
const relayInbox = findTool('relay_inbox');
|
|
33
|
+
const relayPost = findTool('relay_post');
|
|
34
|
+
const relayAgents = findTool('relay_agents');
|
|
35
|
+
|
|
36
|
+
// relay_agents: should list at least our agent
|
|
37
|
+
const agentsResult = await relayAgents.execute({});
|
|
38
|
+
console.log('relay_agents result:', agentsResult);
|
|
39
|
+
assert.ok(agentsResult.includes(AGENT_NAME), `Expected agent list to include ${AGENT_NAME}`);
|
|
40
|
+
|
|
41
|
+
// relay_send: send a DM to ourselves
|
|
42
|
+
const sendResult = await relaySend.execute({ to: AGENT_NAME, text: 'e2e self-ping' });
|
|
43
|
+
console.log('relay_send result:', sendResult);
|
|
44
|
+
assert.ok(sendResult.includes('Sent relay message'), 'Expected send confirmation');
|
|
45
|
+
|
|
46
|
+
// relay_post: post to general channel
|
|
47
|
+
const postResult = await relayPost.execute({ channel: 'general', text: `e2e crewai test from ${AGENT_NAME}` });
|
|
48
|
+
console.log('relay_post result:', postResult);
|
|
49
|
+
assert.ok(postResult.includes('Posted relay message'), 'Expected post confirmation');
|
|
50
|
+
|
|
51
|
+
// relay_inbox: drain inbox
|
|
52
|
+
const inboxResult = await relayInbox.execute({});
|
|
53
|
+
console.log('relay_inbox result:', inboxResult);
|
|
54
|
+
assert.ok(typeof inboxResult === 'string', 'Expected inbox text string');
|
|
55
|
+
|
|
56
|
+
// unsubscribe: stop message routing
|
|
57
|
+
unsubscribe();
|
|
58
|
+
console.log('onRelay unsubscribe called successfully.');
|
|
59
|
+
|
|
60
|
+
console.log('All CrewAI onRelay e2e checks passed.');
|
|
61
|
+
} finally {
|
|
62
|
+
await relay.close();
|
|
63
|
+
console.log('Relay connection closed.');
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('CrewAI adapter e2e: onCrewRelay adds tools to all crew agents', async () => {
|
|
68
|
+
const crewAgentName = `e2e-crew-${randomUUID().slice(0, 8)}`;
|
|
69
|
+
const relay = new Relay(crewAgentName, { autoCleanup: false });
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const agent1 = createAgent('researcher');
|
|
73
|
+
const agent2 = createAgent('writer');
|
|
74
|
+
const crew = { agents: [agent1, agent2], task_callback: null };
|
|
75
|
+
|
|
76
|
+
const { unsubscribe } = onCrewRelay(crew, relay);
|
|
77
|
+
|
|
78
|
+
// Both agents should have all 4 relay tools
|
|
79
|
+
for (const agent of [agent1, agent2]) {
|
|
80
|
+
const toolNames = agent.tools.map((t: any) => t.tool_name);
|
|
81
|
+
assert.ok(toolNames.includes('relay_send'), `${agent.role} missing relay_send`);
|
|
82
|
+
assert.ok(toolNames.includes('relay_inbox'), `${agent.role} missing relay_inbox`);
|
|
83
|
+
assert.ok(toolNames.includes('relay_post'), `${agent.role} missing relay_post`);
|
|
84
|
+
assert.ok(toolNames.includes('relay_agents'), `${agent.role} missing relay_agents`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Use a tool from one of the crew agents against live API
|
|
88
|
+
const agentsTool = agent1.tools.find((t: any) => t.tool_name === 'relay_agents');
|
|
89
|
+
const agentsResult = await agentsTool.execute({});
|
|
90
|
+
console.log('onCrewRelay relay_agents result:', agentsResult);
|
|
91
|
+
assert.ok(agentsResult.includes(crewAgentName), `Expected crew agent ${crewAgentName} in list`);
|
|
92
|
+
|
|
93
|
+
unsubscribe();
|
|
94
|
+
console.log('onCrewRelay unsubscribe called successfully.');
|
|
95
|
+
|
|
96
|
+
console.log('All CrewAI onCrewRelay e2e checks passed.');
|
|
97
|
+
} finally {
|
|
98
|
+
await relay.close();
|
|
99
|
+
console.log('Crew relay connection closed.');
|
|
100
|
+
}
|
|
101
|
+
});
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import test from 'node:test';
|
|
4
|
+
|
|
5
|
+
import { onRelay } from '../../../communicate/adapters/google-adk.js';
|
|
6
|
+
import type { Message, MessageCallback } from '../../../communicate/types.js';
|
|
7
|
+
|
|
8
|
+
const API_KEY = process.env.RELAY_API_KEY!;
|
|
9
|
+
const BASE_URL = (process.env.RELAY_BASE_URL ?? 'https://api.relaycast.dev').replace(/\/+$/, '');
|
|
10
|
+
const AGENT_NAME = `e2e-adk-${randomUUID().slice(0, 8)}`;
|
|
11
|
+
|
|
12
|
+
class LiveRelay {
|
|
13
|
+
private agentToken?: string;
|
|
14
|
+
private agentId?: string;
|
|
15
|
+
private callbacks: MessageCallback[] = [];
|
|
16
|
+
|
|
17
|
+
constructor(private readonly name: string) {}
|
|
18
|
+
|
|
19
|
+
async register(): Promise<void> {
|
|
20
|
+
const res = await fetch(`${BASE_URL}/v1/agents`, {
|
|
21
|
+
method: 'POST',
|
|
22
|
+
headers: { authorization: `Bearer ${API_KEY}`, 'content-type': 'application/json' },
|
|
23
|
+
body: JSON.stringify({ name: this.name, type: 'agent' }),
|
|
24
|
+
});
|
|
25
|
+
const body = (await res.json()) as any;
|
|
26
|
+
if (!body.ok) throw new Error(`Register failed: ${JSON.stringify(body)}`);
|
|
27
|
+
this.agentId = body.data.id;
|
|
28
|
+
this.agentToken = body.data.token;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async send(to: string, text: string): Promise<void> {
|
|
32
|
+
const res = await fetch(`${BASE_URL}/v1/dm`, {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: { authorization: `Bearer ${this.agentToken}`, 'content-type': 'application/json' },
|
|
35
|
+
body: JSON.stringify({ to, text }),
|
|
36
|
+
});
|
|
37
|
+
const body = (await res.json()) as any;
|
|
38
|
+
if (!body.ok) throw new Error(`DM failed: ${JSON.stringify(body)}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async post(channel: string, text: string): Promise<void> {
|
|
42
|
+
const res = await fetch(`${BASE_URL}/v1/channels/${encodeURIComponent(channel)}/messages`, {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
headers: { authorization: `Bearer ${this.agentToken}`, 'content-type': 'application/json' },
|
|
45
|
+
body: JSON.stringify({ text }),
|
|
46
|
+
});
|
|
47
|
+
const body = (await res.json()) as any;
|
|
48
|
+
if (!body.ok) throw new Error(`Post failed: ${JSON.stringify(body)}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async inbox(): Promise<Message[]> {
|
|
52
|
+
const res = await fetch(`${BASE_URL}/v1/inbox`, {
|
|
53
|
+
headers: { authorization: `Bearer ${this.agentToken}` },
|
|
54
|
+
});
|
|
55
|
+
const body = (await res.json()) as any;
|
|
56
|
+
if (!body.ok) throw new Error(`Inbox failed: ${JSON.stringify(body)}`);
|
|
57
|
+
const msgs: Message[] = [];
|
|
58
|
+
for (const dm of body.data.unread_dms ?? []) {
|
|
59
|
+
msgs.push({ sender: dm.from ?? 'unknown', text: dm.text ?? '' });
|
|
60
|
+
}
|
|
61
|
+
return msgs;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async agents(): Promise<string[]> {
|
|
65
|
+
const res = await fetch(`${BASE_URL}/v1/agents`, {
|
|
66
|
+
headers: { authorization: `Bearer ${API_KEY}` },
|
|
67
|
+
});
|
|
68
|
+
const body = (await res.json()) as any;
|
|
69
|
+
if (!body.ok) throw new Error(`Agents failed: ${JSON.stringify(body)}`);
|
|
70
|
+
return (body.data as any[]).map((a: any) => a.name);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
onMessage(callback: MessageCallback): () => void {
|
|
74
|
+
this.callbacks.push(callback);
|
|
75
|
+
return () => {
|
|
76
|
+
this.callbacks = this.callbacks.filter((c) => c !== callback);
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async close(): Promise<void> {
|
|
81
|
+
if (!this.agentId) return;
|
|
82
|
+
try {
|
|
83
|
+
await fetch(`${BASE_URL}/v1/agents/${this.agentId}`, {
|
|
84
|
+
method: 'DELETE',
|
|
85
|
+
headers: { authorization: `Bearer ${API_KEY}` },
|
|
86
|
+
});
|
|
87
|
+
} catch {
|
|
88
|
+
/* ignore */
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
test('Google ADK adapter e2e: relay tools work against live Relaycast', async () => {
|
|
94
|
+
const relay = new LiveRelay(AGENT_NAME);
|
|
95
|
+
await relay.register();
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
const mockAgent = { name: 'test-adk-agent', tools: [] as any[] };
|
|
99
|
+
const { agent, tools, unsubscribe } = onRelay(AGENT_NAME, { agent: mockAgent }, relay);
|
|
100
|
+
|
|
101
|
+
// Verify tools were injected into the agent
|
|
102
|
+
assert.ok(agent.tools.length >= 4, `Expected >= 4 relay tools on agent, got ${agent.tools.length}`);
|
|
103
|
+
|
|
104
|
+
const findTool = (name: string) => {
|
|
105
|
+
const tool = tools.find((t: any) => t.name === name);
|
|
106
|
+
assert.ok(tool, `Tool ${name} not found`);
|
|
107
|
+
return tool;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const relayAgents = findTool('relay_agents');
|
|
111
|
+
const relaySend = findTool('relay_send');
|
|
112
|
+
const relayPost = findTool('relay_post');
|
|
113
|
+
const relayInbox = findTool('relay_inbox');
|
|
114
|
+
|
|
115
|
+
// 1. relay_agents — should include our freshly registered agent
|
|
116
|
+
const agentsResult = await (relayAgents as any).execute({});
|
|
117
|
+
const agentsText = agentsResult.result as string;
|
|
118
|
+
console.log('relay_agents:', agentsText.split('\n').length, 'agents');
|
|
119
|
+
assert.ok(agentsText.includes(AGENT_NAME), `Agent list must include ${AGENT_NAME}`);
|
|
120
|
+
|
|
121
|
+
// 2. relay_send — DM to self
|
|
122
|
+
const sendResult = await (relaySend as any).execute({ to: AGENT_NAME, text: 'e2e self-ping' });
|
|
123
|
+
console.log('relay_send:', sendResult.result);
|
|
124
|
+
assert.ok((sendResult.result as string).includes('Sent relay message'));
|
|
125
|
+
|
|
126
|
+
// 3. relay_post — post to general channel
|
|
127
|
+
const postResult = await (relayPost as any).execute({
|
|
128
|
+
channel: 'general',
|
|
129
|
+
text: `e2e ADK test from ${AGENT_NAME}`,
|
|
130
|
+
});
|
|
131
|
+
console.log('relay_post:', postResult.result);
|
|
132
|
+
assert.ok((postResult.result as string).includes('Posted relay message'));
|
|
133
|
+
|
|
134
|
+
// 4. relay_inbox — drain inbox
|
|
135
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
136
|
+
const inboxResult = await (relayInbox as any).execute({});
|
|
137
|
+
console.log('relay_inbox:', inboxResult.result);
|
|
138
|
+
assert.ok(typeof inboxResult.result === 'string');
|
|
139
|
+
|
|
140
|
+
// 5. Verify unsubscribe stops routing
|
|
141
|
+
let routerFired = false;
|
|
142
|
+
const mockRunner = {
|
|
143
|
+
async *runAsync() {
|
|
144
|
+
routerFired = true;
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
const { unsubscribe: unsub2 } = onRelay(
|
|
148
|
+
`${AGENT_NAME}-sub`,
|
|
149
|
+
{ agent: { name: 'sub-agent', tools: [] }, runner: mockRunner },
|
|
150
|
+
relay,
|
|
151
|
+
);
|
|
152
|
+
unsub2();
|
|
153
|
+
// After unsubscribe, incoming messages should NOT invoke the runner
|
|
154
|
+
await (relaySend as any).execute({ to: AGENT_NAME, text: 'should-not-route' });
|
|
155
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
156
|
+
assert.ok(!routerFired, 'Expected runner NOT to fire after unsubscribe');
|
|
157
|
+
|
|
158
|
+
// Also test that unsubscribe from the main config works
|
|
159
|
+
unsubscribe();
|
|
160
|
+
|
|
161
|
+
console.log('All Google ADK adapter e2e checks passed.');
|
|
162
|
+
} finally {
|
|
163
|
+
await relay.close();
|
|
164
|
+
console.log('Relay closed.');
|
|
165
|
+
}
|
|
166
|
+
});
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import test from 'node:test';
|
|
4
|
+
import { setTimeout as sleep } from 'node:timers/promises';
|
|
5
|
+
|
|
6
|
+
import { onRelay, type RelayToolDef } from '../../../communicate/adapters/langgraph.js';
|
|
7
|
+
import type { Message, MessageCallback } from '../../../communicate/types.js';
|
|
8
|
+
|
|
9
|
+
const API_KEY = process.env.RELAY_API_KEY!;
|
|
10
|
+
const BASE_URL = (process.env.RELAY_BASE_URL ?? 'https://api.relaycast.dev').replace(/\/+$/, '');
|
|
11
|
+
const AGENT_NAME = `e2e-lg-${randomUUID().slice(0, 8)}`;
|
|
12
|
+
const CHANNEL = 'general';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Thin RelayLike wrapper that talks to the real Relaycast API using correct
|
|
16
|
+
* endpoints (POST /v1/agents, POST /v1/channels/{name}/messages, etc.).
|
|
17
|
+
* The SDK's Relay/RelayTransport uses legacy routes that 404 on the live API,
|
|
18
|
+
* so we bypass it for this e2e test.
|
|
19
|
+
*/
|
|
20
|
+
class LiveRelay {
|
|
21
|
+
private agentToken?: string;
|
|
22
|
+
private agentId?: string;
|
|
23
|
+
private callbacks = new Set<MessageCallback>();
|
|
24
|
+
|
|
25
|
+
constructor(private name: string) {}
|
|
26
|
+
|
|
27
|
+
async register(): Promise<void> {
|
|
28
|
+
const res = await fetch(`${BASE_URL}/v1/agents`, {
|
|
29
|
+
method: 'POST',
|
|
30
|
+
headers: { authorization: `Bearer ${API_KEY}`, 'content-type': 'application/json' },
|
|
31
|
+
body: JSON.stringify({ name: this.name }),
|
|
32
|
+
});
|
|
33
|
+
if (!res.ok) throw new Error(`register failed: ${res.status} ${await res.text()}`);
|
|
34
|
+
const body = (await res.json()) as any;
|
|
35
|
+
this.agentToken = body.data.token;
|
|
36
|
+
this.agentId = body.data.id;
|
|
37
|
+
|
|
38
|
+
// join channel
|
|
39
|
+
await fetch(`${BASE_URL}/v1/channels/${CHANNEL}/join`, {
|
|
40
|
+
method: 'POST',
|
|
41
|
+
headers: { authorization: `Bearer ${this.agentToken}` },
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async send(to: string, text: string): Promise<void> {
|
|
46
|
+
const res = await fetch(`${BASE_URL}/v1/dm`, {
|
|
47
|
+
method: 'POST',
|
|
48
|
+
headers: { authorization: `Bearer ${this.agentToken}`, 'content-type': 'application/json' },
|
|
49
|
+
body: JSON.stringify({ to, text }),
|
|
50
|
+
});
|
|
51
|
+
if (!res.ok) throw new Error(`send failed: ${res.status} ${await res.text()}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async post(channel: string, text: string): Promise<void> {
|
|
55
|
+
const res = await fetch(`${BASE_URL}/v1/channels/${encodeURIComponent(channel)}/messages`, {
|
|
56
|
+
method: 'POST',
|
|
57
|
+
headers: { authorization: `Bearer ${this.agentToken}`, 'content-type': 'application/json' },
|
|
58
|
+
body: JSON.stringify({ text }),
|
|
59
|
+
});
|
|
60
|
+
if (!res.ok) throw new Error(`post failed: ${res.status} ${await res.text()}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async inbox(): Promise<Message[]> {
|
|
64
|
+
const res = await fetch(`${BASE_URL}/v1/inbox`, {
|
|
65
|
+
headers: { authorization: `Bearer ${this.agentToken}` },
|
|
66
|
+
});
|
|
67
|
+
if (!res.ok) throw new Error(`inbox failed: ${res.status} ${await res.text()}`);
|
|
68
|
+
const body = (await res.json()) as any;
|
|
69
|
+
const msgs: Message[] = [];
|
|
70
|
+
for (const dm of body.data?.unread_dms ?? []) {
|
|
71
|
+
msgs.push({ sender: dm.from ?? '', text: dm.text ?? '' });
|
|
72
|
+
}
|
|
73
|
+
return msgs;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async agents(): Promise<string[]> {
|
|
77
|
+
const res = await fetch(`${BASE_URL}/v1/agents`, {
|
|
78
|
+
headers: { authorization: `Bearer ${API_KEY}` },
|
|
79
|
+
});
|
|
80
|
+
if (!res.ok) throw new Error(`agents failed: ${res.status} ${await res.text()}`);
|
|
81
|
+
const body = (await res.json()) as any;
|
|
82
|
+
return (body.data ?? []).map((a: any) => a.name as string);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
onMessage(callback: MessageCallback): () => void {
|
|
86
|
+
this.callbacks.add(callback);
|
|
87
|
+
return () => { this.callbacks.delete(callback); };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async simulateIncoming(message: Message): Promise<void> {
|
|
91
|
+
for (const cb of [...this.callbacks]) await cb(message);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async disconnect(): Promise<void> {
|
|
95
|
+
if (!this.agentToken) return;
|
|
96
|
+
await fetch(`${BASE_URL}/v1/agents/disconnect`, {
|
|
97
|
+
method: 'POST',
|
|
98
|
+
headers: { authorization: `Bearer ${this.agentToken}` },
|
|
99
|
+
}).catch(() => {});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function createFakeGraph() {
|
|
104
|
+
return {
|
|
105
|
+
nodes: {},
|
|
106
|
+
invokeCalls: [] as Array<{ input: Record<string, unknown> }>,
|
|
107
|
+
async invoke(input: Record<string, unknown>) {
|
|
108
|
+
this.invokeCalls.push({ input });
|
|
109
|
+
return { messages: [] };
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
test('LangGraph e2e: full lifecycle against live Relaycast', async () => {
|
|
115
|
+
const relay = new LiveRelay(AGENT_NAME);
|
|
116
|
+
await relay.register();
|
|
117
|
+
console.log(`[e2e] Registered agent: ${AGENT_NAME}`);
|
|
118
|
+
|
|
119
|
+
const graph = createFakeGraph();
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
// 1. onRelay returns tools and unsubscribe handle
|
|
123
|
+
const result = onRelay(graph, relay as any);
|
|
124
|
+
assert.ok(result.tools, 'tools should be defined');
|
|
125
|
+
assert.ok(typeof result.unsubscribe === 'function', 'unsubscribe should be a function');
|
|
126
|
+
|
|
127
|
+
const toolMap = new Map<string, RelayToolDef>();
|
|
128
|
+
for (const t of result.tools) toolMap.set(t.name, t);
|
|
129
|
+
|
|
130
|
+
assert.ok(toolMap.has('relay_send'), 'should have relay_send');
|
|
131
|
+
assert.ok(toolMap.has('relay_inbox'), 'should have relay_inbox');
|
|
132
|
+
assert.ok(toolMap.has('relay_post'), 'should have relay_post');
|
|
133
|
+
assert.ok(toolMap.has('relay_agents'), 'should have relay_agents');
|
|
134
|
+
console.log('[e2e] onRelay returned 4 tools + unsubscribe');
|
|
135
|
+
|
|
136
|
+
// 2. relay_agents returns a real agent list that includes ourselves
|
|
137
|
+
const agentsOutput = await toolMap.get('relay_agents')!.invoke({});
|
|
138
|
+
assert.ok(typeof agentsOutput === 'string', 'agents output should be a string');
|
|
139
|
+
const agentNames = agentsOutput.split('\n');
|
|
140
|
+
assert.ok(agentNames.includes(AGENT_NAME), `agents list should include "${AGENT_NAME}"`);
|
|
141
|
+
console.log(`[e2e] relay_agents OK: ${agentNames.length} agent(s), self found`);
|
|
142
|
+
|
|
143
|
+
// 3. relay_post sends a message to a channel (real HTTP 201)
|
|
144
|
+
const postOutput = await toolMap.get('relay_post')!.invoke({ channel: CHANNEL, text: `e2e post from ${AGENT_NAME}` });
|
|
145
|
+
assert.equal(postOutput, `Posted relay message to #${CHANNEL}.`);
|
|
146
|
+
console.log('[e2e] relay_post OK');
|
|
147
|
+
|
|
148
|
+
// 4. relay_send sends a DM to ourselves (real HTTP 201)
|
|
149
|
+
const sendOutput = await toolMap.get('relay_send')!.invoke({ to: AGENT_NAME, text: 'e2e self-DM' });
|
|
150
|
+
assert.equal(sendOutput, `Sent relay message to ${AGENT_NAME}.`);
|
|
151
|
+
console.log('[e2e] relay_send OK');
|
|
152
|
+
|
|
153
|
+
// 5. relay_inbox drains messages (real HTTP 200)
|
|
154
|
+
await sleep(500);
|
|
155
|
+
const inboxOutput = await toolMap.get('relay_inbox')!.invoke({});
|
|
156
|
+
assert.ok(typeof inboxOutput === 'string', 'inbox output should be a string');
|
|
157
|
+
console.log('[e2e] relay_inbox OK:', inboxOutput.slice(0, 120));
|
|
158
|
+
|
|
159
|
+
// 6. Test message routing into graph via simulateIncoming
|
|
160
|
+
await relay.simulateIncoming({ sender: 'test-peer', text: 'routed msg', messageId: 'msg-sim-1' });
|
|
161
|
+
assert.equal(graph.invokeCalls.length, 1, 'graph should receive 1 routed message');
|
|
162
|
+
const call = graph.invokeCalls[0];
|
|
163
|
+
assert.ok(Array.isArray(call.input.messages));
|
|
164
|
+
const routedMsg = (call.input.messages as any[])[0];
|
|
165
|
+
assert.equal(routedMsg.role, 'user');
|
|
166
|
+
assert.match(routedMsg.content, /test-peer/);
|
|
167
|
+
assert.match(routedMsg.content, /routed msg/);
|
|
168
|
+
console.log('[e2e] message routing into graph OK');
|
|
169
|
+
|
|
170
|
+
// 7. unsubscribe stops routing
|
|
171
|
+
result.unsubscribe();
|
|
172
|
+
await relay.simulateIncoming({ sender: 'test-peer', text: 'should not route', messageId: 'msg-sim-2' });
|
|
173
|
+
assert.equal(graph.invokeCalls.length, 1, 'graph should NOT receive messages after unsubscribe');
|
|
174
|
+
console.log('[e2e] unsubscribe OK');
|
|
175
|
+
|
|
176
|
+
console.log('[e2e] All LangGraph e2e checks passed.');
|
|
177
|
+
} finally {
|
|
178
|
+
await relay.disconnect();
|
|
179
|
+
console.log('[e2e] Agent disconnected.');
|
|
180
|
+
}
|
|
181
|
+
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
|
|
5
|
+
import { onRelay } from '../../../communicate/adapters/openai-agents.js';
|
|
6
|
+
import type { Message, MessageCallback } from '../../../communicate/types.js';
|
|
7
|
+
|
|
8
|
+
const API_KEY = process.env.RELAY_API_KEY!;
|
|
9
|
+
const BASE_URL = process.env.RELAY_BASE_URL!;
|
|
10
|
+
const AGENT_NAME = `e2e-oai-${randomUUID().slice(0, 8)}`;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Lightweight relay implementation that speaks the real Relaycast API surface
|
|
14
|
+
* (the SDK transport endpoints are currently misaligned).
|
|
15
|
+
*/
|
|
16
|
+
class LiveRelay {
|
|
17
|
+
private agentToken?: string;
|
|
18
|
+
private agentId?: string;
|
|
19
|
+
private callbacks = new Set<MessageCallback>();
|
|
20
|
+
private pending: Message[] = [];
|
|
21
|
+
|
|
22
|
+
constructor(private name: string) {}
|
|
23
|
+
|
|
24
|
+
async register(): Promise<void> {
|
|
25
|
+
const res = await fetch(`${BASE_URL}/v1/agents`, {
|
|
26
|
+
method: 'POST',
|
|
27
|
+
headers: { Authorization: `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
|
|
28
|
+
body: JSON.stringify({ name: this.name, type: 'agent' }),
|
|
29
|
+
});
|
|
30
|
+
const body = await res.json() as any;
|
|
31
|
+
if (!body.ok) throw new Error(`register failed: ${JSON.stringify(body)}`);
|
|
32
|
+
this.agentId = body.data.id;
|
|
33
|
+
this.agentToken = body.data.token;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async send(to: string, text: string): Promise<void> {
|
|
37
|
+
const res = await fetch(`${BASE_URL}/v1/dm`, {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
headers: { Authorization: `Bearer ${this.agentToken}`, 'Content-Type': 'application/json' },
|
|
40
|
+
body: JSON.stringify({ to, text }),
|
|
41
|
+
});
|
|
42
|
+
const body = await res.json() as any;
|
|
43
|
+
if (!body.ok) throw new Error(`send failed: ${JSON.stringify(body)}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async post(channel: string, text: string): Promise<void> {
|
|
47
|
+
const res = await fetch(`${BASE_URL}/v1/channels/${encodeURIComponent(channel)}/messages`, {
|
|
48
|
+
method: 'POST',
|
|
49
|
+
headers: { Authorization: `Bearer ${this.agentToken}`, 'Content-Type': 'application/json' },
|
|
50
|
+
body: JSON.stringify({ text }),
|
|
51
|
+
});
|
|
52
|
+
const body = await res.json() as any;
|
|
53
|
+
if (!body.ok) throw new Error(`post failed: ${JSON.stringify(body)}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async inbox(): Promise<Message[]> {
|
|
57
|
+
const msgs = [...this.pending];
|
|
58
|
+
this.pending = [];
|
|
59
|
+
return msgs;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async agents(): Promise<string[]> {
|
|
63
|
+
const res = await fetch(`${BASE_URL}/v1/agents`, {
|
|
64
|
+
headers: { Authorization: `Bearer ${API_KEY}` },
|
|
65
|
+
});
|
|
66
|
+
const body = await res.json() as any;
|
|
67
|
+
if (!body.ok) throw new Error(`agents failed: ${JSON.stringify(body)}`);
|
|
68
|
+
return (body.data as any[]).map((a) => a.name);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
onMessage(callback: MessageCallback): () => void {
|
|
72
|
+
this.callbacks.add(callback);
|
|
73
|
+
return () => { this.callbacks.delete(callback); };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async cleanup(): Promise<void> {
|
|
77
|
+
if (!this.name) return;
|
|
78
|
+
await fetch(`${BASE_URL}/v1/agents/${encodeURIComponent(this.name)}`, {
|
|
79
|
+
method: 'PATCH',
|
|
80
|
+
headers: { Authorization: `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
|
|
81
|
+
body: JSON.stringify({ status: 'offline' }),
|
|
82
|
+
}).catch(() => {});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function createAgent(name: string) {
|
|
87
|
+
return {
|
|
88
|
+
name,
|
|
89
|
+
instructions: 'You are an e2e test agent.',
|
|
90
|
+
tools: [] as any[],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
test('OpenAI Agents adapter e2e against live Relaycast', async (t) => {
|
|
95
|
+
const relay = new LiveRelay(AGENT_NAME);
|
|
96
|
+
await relay.register();
|
|
97
|
+
|
|
98
|
+
const agent = createAgent(AGENT_NAME);
|
|
99
|
+
const { agent: augmented, cleanup } = onRelay(agent, relay);
|
|
100
|
+
|
|
101
|
+
await t.test('onRelay injects 4 relay tools', () => {
|
|
102
|
+
const names = augmented.tools.map((tool: any) => tool.name);
|
|
103
|
+
assert.deepEqual(names, ['relay_send', 'relay_inbox', 'relay_post', 'relay_agents']);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
await t.test('relay_agents returns live agent list including ours', async () => {
|
|
107
|
+
const tool = augmented.tools.find((t: any) => t.name === 'relay_agents')!;
|
|
108
|
+
const result: string = await tool.invoke(null, '{}');
|
|
109
|
+
assert.ok(result.includes(AGENT_NAME), `Expected agent list to contain "${AGENT_NAME}", got: ${result.slice(0, 200)}`);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
await t.test('relay_send delivers a DM without error', async () => {
|
|
113
|
+
const tool = augmented.tools.find((t: any) => t.name === 'relay_send')!;
|
|
114
|
+
const result: string = await tool.invoke(null, JSON.stringify({ to: AGENT_NAME, text: 'e2e self-ping' }));
|
|
115
|
+
assert.match(result, /Sent relay message to/);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
await t.test('relay_post posts to channel without error', async () => {
|
|
119
|
+
const tool = augmented.tools.find((t: any) => t.name === 'relay_post')!;
|
|
120
|
+
const result: string = await tool.invoke(null, JSON.stringify({ channel: 'general', text: `e2e from ${AGENT_NAME}` }));
|
|
121
|
+
assert.match(result, /Posted relay message to #general/);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
await t.test('relay_inbox returns string result', async () => {
|
|
125
|
+
const tool = augmented.tools.find((t: any) => t.name === 'relay_inbox')!;
|
|
126
|
+
const result: string = await tool.invoke(null, '{}');
|
|
127
|
+
assert.ok(typeof result === 'string');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
await t.test('cleanup restores agent and removes tools', () => {
|
|
131
|
+
cleanup();
|
|
132
|
+
assert.equal(augmented.tools.length, 0);
|
|
133
|
+
assert.equal(augmented.instructions, 'You are an e2e test agent.');
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
await relay.cleanup();
|
|
137
|
+
});
|