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.
Files changed (246) hide show
  1. package/bin/agent-relay-broker-darwin-arm64 +0 -0
  2. package/bin/agent-relay-broker-darwin-x64 +0 -0
  3. package/bin/agent-relay-broker-linux-arm64 +0 -0
  4. package/bin/agent-relay-broker-linux-x64 +0 -0
  5. package/dist/index.cjs +1358 -941
  6. package/dist/src/cli/commands/agent-management.d.ts +2 -2
  7. package/dist/src/cli/commands/agent-management.d.ts.map +1 -1
  8. package/dist/src/cli/commands/agent-management.js +41 -240
  9. package/dist/src/cli/commands/agent-management.js.map +1 -1
  10. package/dist/src/cli/commands/messaging.d.ts +1 -1
  11. package/dist/src/cli/commands/messaging.d.ts.map +1 -1
  12. package/dist/src/cli/commands/messaging.js +14 -5
  13. package/dist/src/cli/commands/messaging.js.map +1 -1
  14. package/dist/src/cli/lib/agent-management-listing.d.ts +4 -1
  15. package/dist/src/cli/lib/agent-management-listing.d.ts.map +1 -1
  16. package/dist/src/cli/lib/agent-management-listing.js +27 -2
  17. package/dist/src/cli/lib/agent-management-listing.js.map +1 -1
  18. package/package.json +11 -10
  19. package/packages/acp-bridge/package.json +2 -2
  20. package/packages/config/package.json +1 -1
  21. package/packages/hooks/package.json +4 -4
  22. package/packages/memory/package.json +2 -2
  23. package/packages/openclaw/package.json +2 -2
  24. package/packages/policy/package.json +2 -2
  25. package/packages/sdk/ADAPTER_REVIEW.md +109 -0
  26. package/packages/sdk/dist/client.d.ts +66 -0
  27. package/packages/sdk/dist/client.d.ts.map +1 -1
  28. package/packages/sdk/dist/client.js +230 -0
  29. package/packages/sdk/dist/client.js.map +1 -1
  30. package/packages/sdk/dist/communicate/a2a-bridge.d.ts +25 -0
  31. package/packages/sdk/dist/communicate/a2a-bridge.d.ts.map +1 -0
  32. package/packages/sdk/dist/communicate/a2a-bridge.js +89 -0
  33. package/packages/sdk/dist/communicate/a2a-bridge.js.map +1 -0
  34. package/packages/sdk/dist/communicate/a2a-server.d.ts +31 -0
  35. package/packages/sdk/dist/communicate/a2a-server.d.ts.map +1 -0
  36. package/packages/sdk/dist/communicate/a2a-server.js +220 -0
  37. package/packages/sdk/dist/communicate/a2a-server.js.map +1 -0
  38. package/packages/sdk/dist/communicate/a2a-transport.d.ts +48 -0
  39. package/packages/sdk/dist/communicate/a2a-transport.d.ts.map +1 -0
  40. package/packages/sdk/dist/communicate/a2a-transport.js +302 -0
  41. package/packages/sdk/dist/communicate/a2a-transport.js.map +1 -0
  42. package/packages/sdk/dist/communicate/a2a-types.d.ts +107 -0
  43. package/packages/sdk/dist/communicate/a2a-types.d.ts.map +1 -0
  44. package/packages/sdk/dist/communicate/a2a-types.js +209 -0
  45. package/packages/sdk/dist/communicate/a2a-types.js.map +1 -0
  46. package/packages/sdk/dist/communicate/adapters/claude-sdk.d.ts +28 -0
  47. package/packages/sdk/dist/communicate/adapters/claude-sdk.d.ts.map +1 -0
  48. package/packages/sdk/dist/communicate/adapters/claude-sdk.js +47 -0
  49. package/packages/sdk/dist/communicate/adapters/claude-sdk.js.map +1 -0
  50. package/packages/sdk/dist/communicate/adapters/crewai.d.ts +42 -0
  51. package/packages/sdk/dist/communicate/adapters/crewai.d.ts.map +1 -0
  52. package/packages/sdk/dist/communicate/adapters/crewai.js +95 -0
  53. package/packages/sdk/dist/communicate/adapters/crewai.js.map +1 -0
  54. package/packages/sdk/dist/communicate/adapters/google-adk.d.ts +53 -0
  55. package/packages/sdk/dist/communicate/adapters/google-adk.d.ts.map +1 -0
  56. package/packages/sdk/dist/communicate/adapters/google-adk.js +77 -0
  57. package/packages/sdk/dist/communicate/adapters/google-adk.js.map +1 -0
  58. package/packages/sdk/dist/communicate/adapters/index.d.ts +7 -0
  59. package/packages/sdk/dist/communicate/adapters/index.d.ts.map +1 -0
  60. package/packages/sdk/dist/communicate/adapters/index.js +7 -0
  61. package/packages/sdk/dist/communicate/adapters/index.js.map +1 -0
  62. package/packages/sdk/dist/communicate/adapters/langgraph.d.ts +40 -0
  63. package/packages/sdk/dist/communicate/adapters/langgraph.d.ts.map +1 -0
  64. package/packages/sdk/dist/communicate/adapters/langgraph.js +77 -0
  65. package/packages/sdk/dist/communicate/adapters/langgraph.js.map +1 -0
  66. package/packages/sdk/dist/communicate/adapters/openai-agents.d.ts +25 -0
  67. package/packages/sdk/dist/communicate/adapters/openai-agents.d.ts.map +1 -0
  68. package/packages/sdk/dist/communicate/adapters/openai-agents.js +70 -0
  69. package/packages/sdk/dist/communicate/adapters/openai-agents.js.map +1 -0
  70. package/packages/sdk/dist/communicate/adapters/pi.d.ts +45 -0
  71. package/packages/sdk/dist/communicate/adapters/pi.d.ts.map +1 -0
  72. package/packages/sdk/dist/communicate/adapters/pi.js +59 -0
  73. package/packages/sdk/dist/communicate/adapters/pi.js.map +1 -0
  74. package/packages/sdk/dist/communicate/core.d.ts +58 -0
  75. package/packages/sdk/dist/communicate/core.d.ts.map +1 -0
  76. package/packages/sdk/dist/communicate/core.js +128 -0
  77. package/packages/sdk/dist/communicate/core.js.map +1 -0
  78. package/packages/sdk/dist/communicate/index.d.ts +4 -0
  79. package/packages/sdk/dist/communicate/index.d.ts.map +1 -0
  80. package/packages/sdk/dist/communicate/index.js +4 -0
  81. package/packages/sdk/dist/communicate/index.js.map +1 -0
  82. package/packages/sdk/dist/communicate/transport.d.ts +36 -0
  83. package/packages/sdk/dist/communicate/transport.d.ts.map +1 -0
  84. package/packages/sdk/dist/communicate/transport.js +371 -0
  85. package/packages/sdk/dist/communicate/transport.js.map +1 -0
  86. package/packages/sdk/dist/communicate/types.d.ts +58 -0
  87. package/packages/sdk/dist/communicate/types.d.ts.map +1 -0
  88. package/packages/sdk/dist/communicate/types.js +66 -0
  89. package/packages/sdk/dist/communicate/types.js.map +1 -0
  90. package/packages/sdk/dist/workflows/builder.d.ts +35 -5
  91. package/packages/sdk/dist/workflows/builder.d.ts.map +1 -1
  92. package/packages/sdk/dist/workflows/builder.js +81 -7
  93. package/packages/sdk/dist/workflows/builder.js.map +1 -1
  94. package/packages/sdk/dist/workflows/cli.js +14 -1
  95. package/packages/sdk/dist/workflows/cli.js.map +1 -1
  96. package/packages/sdk/dist/workflows/runner.d.ts +10 -2
  97. package/packages/sdk/dist/workflows/runner.d.ts.map +1 -1
  98. package/packages/sdk/dist/workflows/runner.js +95 -1
  99. package/packages/sdk/dist/workflows/runner.js.map +1 -1
  100. package/packages/sdk/dist/workflows/types.d.ts +11 -0
  101. package/packages/sdk/dist/workflows/types.d.ts.map +1 -1
  102. package/packages/sdk/examples/communicate/claude_sdk_example.ts +5 -0
  103. package/packages/sdk/examples/communicate/pi_example.ts +8 -0
  104. package/packages/sdk/package.json +48 -2
  105. package/packages/sdk/src/__tests__/builder-deterministic.test.ts +132 -0
  106. package/packages/sdk/src/__tests__/communicate/a2a-bridge.test.ts +211 -0
  107. package/packages/sdk/src/__tests__/communicate/a2a-server.test.ts +359 -0
  108. package/packages/sdk/src/__tests__/communicate/a2a-transport.test.ts +537 -0
  109. package/packages/sdk/src/__tests__/communicate/a2a-types.test.ts +297 -0
  110. package/packages/sdk/src/__tests__/communicate/adapters/claude-sdk.test.ts +163 -0
  111. package/packages/sdk/src/__tests__/communicate/adapters/crewai.test.ts +219 -0
  112. package/packages/sdk/src/__tests__/communicate/adapters/e2e-crewai.test.ts +101 -0
  113. package/packages/sdk/src/__tests__/communicate/adapters/e2e-google-adk.test.ts +166 -0
  114. package/packages/sdk/src/__tests__/communicate/adapters/e2e-langgraph.test.ts +181 -0
  115. package/packages/sdk/src/__tests__/communicate/adapters/e2e-openai-agents.test.ts +137 -0
  116. package/packages/sdk/src/__tests__/communicate/adapters/e2e-pi.test.ts +140 -0
  117. package/packages/sdk/src/__tests__/communicate/adapters/google-adk.test.ts +200 -0
  118. package/packages/sdk/src/__tests__/communicate/adapters/langgraph.test.ts +162 -0
  119. package/packages/sdk/src/__tests__/communicate/adapters/openai-agents.test.ts +166 -0
  120. package/packages/sdk/src/__tests__/communicate/adapters/pi.test.ts +140 -0
  121. package/packages/sdk/src/__tests__/communicate/core.test.ts +574 -0
  122. package/packages/sdk/src/__tests__/communicate/integration/cross-framework.test.ts +353 -0
  123. package/packages/sdk/src/__tests__/communicate/transport.test.ts +613 -0
  124. package/packages/sdk/src/__tests__/start-from.test.ts +346 -0
  125. package/packages/sdk/src/client.ts +301 -0
  126. package/packages/sdk/src/communicate/a2a-bridge.ts +111 -0
  127. package/packages/sdk/src/communicate/a2a-server.ts +277 -0
  128. package/packages/sdk/src/communicate/a2a-transport.ts +395 -0
  129. package/packages/sdk/src/communicate/a2a-types.ts +338 -0
  130. package/packages/sdk/src/communicate/adapters/claude-sdk.ts +85 -0
  131. package/packages/sdk/src/communicate/adapters/crewai.ts +141 -0
  132. package/packages/sdk/src/communicate/adapters/google-adk.ts +139 -0
  133. package/packages/sdk/src/communicate/adapters/index.ts +6 -0
  134. package/packages/sdk/src/communicate/adapters/langgraph.ts +112 -0
  135. package/packages/sdk/src/communicate/adapters/openai-agents.ts +113 -0
  136. package/packages/sdk/src/communicate/adapters/pi.ts +105 -0
  137. package/packages/sdk/src/communicate/core.ts +157 -0
  138. package/packages/sdk/src/communicate/index.ts +3 -0
  139. package/packages/sdk/src/communicate/transport.ts +489 -0
  140. package/packages/sdk/src/communicate/types.ts +106 -0
  141. package/packages/sdk/src/examples/workflows/fix-dashboard-user-registration.yaml +182 -0
  142. package/packages/sdk/src/workflows/builder.ts +97 -9
  143. package/packages/sdk/src/workflows/cli.ts +16 -1
  144. package/packages/sdk/src/workflows/runner.ts +110 -1
  145. package/packages/sdk/src/workflows/types.ts +14 -0
  146. package/packages/sdk/tsconfig.build.json +1 -7
  147. package/packages/sdk/tsconfig.json +1 -7
  148. package/packages/sdk-py/README.md +67 -25
  149. package/packages/sdk-py/examples/communicate/agno_example.py +8 -0
  150. package/packages/sdk-py/examples/communicate/claude_sdk_example.py +6 -0
  151. package/packages/sdk-py/examples/communicate/crewai_example.py +7 -0
  152. package/packages/sdk-py/examples/communicate/google_adk_example.py +7 -0
  153. package/packages/sdk-py/examples/communicate/openai_agents_example.py +8 -0
  154. package/packages/sdk-py/examples/communicate/swarms_example.py +7 -0
  155. package/packages/sdk-py/pyproject.toml +12 -1
  156. package/packages/sdk-py/src/agent_relay/__init__.py +8 -0
  157. package/packages/sdk-py/src/agent_relay/builder.py +65 -26
  158. package/packages/sdk-py/src/agent_relay/communicate/__init__.py +6 -0
  159. package/packages/sdk-py/src/agent_relay/communicate/a2a_bridge.py +138 -0
  160. package/packages/sdk-py/src/agent_relay/communicate/a2a_server.py +242 -0
  161. package/packages/sdk-py/src/agent_relay/communicate/a2a_transport.py +366 -0
  162. package/packages/sdk-py/src/agent_relay/communicate/a2a_types.py +294 -0
  163. package/packages/sdk-py/src/agent_relay/communicate/adapters/__init__.py +10 -0
  164. package/packages/sdk-py/src/agent_relay/communicate/adapters/agno.py +74 -0
  165. package/packages/sdk-py/src/agent_relay/communicate/adapters/claude_sdk.py +78 -0
  166. package/packages/sdk-py/src/agent_relay/communicate/adapters/crewai.py +143 -0
  167. package/packages/sdk-py/src/agent_relay/communicate/adapters/google_adk.py +69 -0
  168. package/packages/sdk-py/src/agent_relay/communicate/adapters/openai_agents.py +86 -0
  169. package/packages/sdk-py/src/agent_relay/communicate/adapters/pi.py +175 -0
  170. package/packages/sdk-py/src/agent_relay/communicate/adapters/swarms.py +44 -0
  171. package/packages/sdk-py/src/agent_relay/communicate/core.py +293 -0
  172. package/packages/sdk-py/src/agent_relay/communicate/transport.py +502 -0
  173. package/packages/sdk-py/src/agent_relay/communicate/types.py +89 -0
  174. package/packages/sdk-py/src/agent_relay/types.py +2 -1
  175. package/packages/sdk-py/tests/communicate/__init__.py +0 -0
  176. package/packages/sdk-py/tests/communicate/adapters/__init__.py +0 -0
  177. package/packages/sdk-py/tests/communicate/adapters/e2e_test_agno.py +154 -0
  178. package/packages/sdk-py/tests/communicate/adapters/e2e_test_claude_sdk.py +428 -0
  179. package/packages/sdk-py/tests/communicate/adapters/e2e_test_crewai.py +234 -0
  180. package/packages/sdk-py/tests/communicate/adapters/e2e_test_google_adk.py +182 -0
  181. package/packages/sdk-py/tests/communicate/adapters/e2e_test_langgraph.py +262 -0
  182. package/packages/sdk-py/tests/communicate/adapters/e2e_test_openai_agents.py +88 -0
  183. package/packages/sdk-py/tests/communicate/adapters/e2e_test_pi.py +156 -0
  184. package/packages/sdk-py/tests/communicate/adapters/e2e_test_swarms.py +239 -0
  185. package/packages/sdk-py/tests/communicate/adapters/test_agno.py +140 -0
  186. package/packages/sdk-py/tests/communicate/adapters/test_claude_sdk.py +147 -0
  187. package/packages/sdk-py/tests/communicate/adapters/test_crewai.py +136 -0
  188. package/packages/sdk-py/tests/communicate/adapters/test_google_adk.py +125 -0
  189. package/packages/sdk-py/tests/communicate/adapters/test_openai_agents.py +99 -0
  190. package/packages/sdk-py/tests/communicate/adapters/test_pi.py +270 -0
  191. package/packages/sdk-py/tests/communicate/adapters/test_swarms.py +113 -0
  192. package/packages/sdk-py/tests/communicate/conftest.py +555 -0
  193. package/packages/sdk-py/tests/communicate/integration/__init__.py +1 -0
  194. package/packages/sdk-py/tests/communicate/integration/test_cross_framework.py +331 -0
  195. package/packages/sdk-py/tests/communicate/integration/test_end_to_end.py +151 -0
  196. package/packages/sdk-py/tests/communicate/test_a2a_bridge.py +363 -0
  197. package/packages/sdk-py/tests/communicate/test_a2a_server.py +346 -0
  198. package/packages/sdk-py/tests/communicate/test_a2a_transport.py +561 -0
  199. package/packages/sdk-py/tests/communicate/test_a2a_types.py +342 -0
  200. package/packages/sdk-py/tests/communicate/test_auto_detect.py +67 -0
  201. package/packages/sdk-py/tests/communicate/test_core.py +331 -0
  202. package/packages/sdk-py/tests/communicate/test_transport.py +373 -0
  203. package/packages/sdk-py/tests/communicate/test_types.py +285 -0
  204. package/packages/sdk-py/tests/test_builder_deterministic.py +118 -0
  205. package/packages/telemetry/package.json +1 -1
  206. package/packages/trajectory/package.json +2 -2
  207. package/packages/user-directory/package.json +2 -2
  208. package/packages/utils/package.json +2 -2
  209. package/packages/sdk/dist/__tests__/completion-pipeline.test.d.ts +0 -14
  210. package/packages/sdk/dist/__tests__/completion-pipeline.test.d.ts.map +0 -1
  211. package/packages/sdk/dist/__tests__/completion-pipeline.test.js +0 -1476
  212. package/packages/sdk/dist/__tests__/completion-pipeline.test.js.map +0 -1
  213. package/packages/sdk/dist/__tests__/contract-fixtures.test.d.ts +0 -2
  214. package/packages/sdk/dist/__tests__/contract-fixtures.test.d.ts.map +0 -1
  215. package/packages/sdk/dist/__tests__/contract-fixtures.test.js +0 -152
  216. package/packages/sdk/dist/__tests__/contract-fixtures.test.js.map +0 -1
  217. package/packages/sdk/dist/__tests__/e2e-owner-review.test.d.ts +0 -16
  218. package/packages/sdk/dist/__tests__/e2e-owner-review.test.d.ts.map +0 -1
  219. package/packages/sdk/dist/__tests__/e2e-owner-review.test.js +0 -640
  220. package/packages/sdk/dist/__tests__/e2e-owner-review.test.js.map +0 -1
  221. package/packages/sdk/dist/__tests__/facade.test.d.ts +0 -2
  222. package/packages/sdk/dist/__tests__/facade.test.d.ts.map +0 -1
  223. package/packages/sdk/dist/__tests__/facade.test.js +0 -305
  224. package/packages/sdk/dist/__tests__/facade.test.js.map +0 -1
  225. package/packages/sdk/dist/__tests__/integration.test.d.ts +0 -2
  226. package/packages/sdk/dist/__tests__/integration.test.d.ts.map +0 -1
  227. package/packages/sdk/dist/__tests__/integration.test.js +0 -205
  228. package/packages/sdk/dist/__tests__/integration.test.js.map +0 -1
  229. package/packages/sdk/dist/__tests__/pty.test.d.ts +0 -2
  230. package/packages/sdk/dist/__tests__/pty.test.d.ts.map +0 -1
  231. package/packages/sdk/dist/__tests__/pty.test.js +0 -20
  232. package/packages/sdk/dist/__tests__/pty.test.js.map +0 -1
  233. package/packages/sdk/dist/__tests__/quickstart.test.d.ts +0 -2
  234. package/packages/sdk/dist/__tests__/quickstart.test.d.ts.map +0 -1
  235. package/packages/sdk/dist/__tests__/quickstart.test.js +0 -176
  236. package/packages/sdk/dist/__tests__/quickstart.test.js.map +0 -1
  237. package/packages/sdk/dist/__tests__/spawn-from-env.test.d.ts +0 -2
  238. package/packages/sdk/dist/__tests__/spawn-from-env.test.d.ts.map +0 -1
  239. package/packages/sdk/dist/__tests__/spawn-from-env.test.js +0 -222
  240. package/packages/sdk/dist/__tests__/spawn-from-env.test.js.map +0 -1
  241. package/packages/sdk/dist/__tests__/unit.test.d.ts +0 -2
  242. package/packages/sdk/dist/__tests__/unit.test.d.ts.map +0 -1
  243. package/packages/sdk/dist/__tests__/unit.test.js +0 -357
  244. package/packages/sdk/dist/__tests__/unit.test.js.map +0 -1
  245. package/packages/sdk-py/agent_relay/__init__.py +0 -21
  246. package/packages/sdk-py/agent_relay/models.py +0 -398
@@ -0,0 +1,359 @@
1
+ import { describe, it, expect, vi, afterEach } from 'vitest';
2
+
3
+ import { A2AServer } from '../../communicate/a2a-server.js';
4
+ import type { A2AMessage } from '../../communicate/a2a-types.js';
5
+
6
+ describe('A2AServer', () => {
7
+ let server: A2AServer;
8
+
9
+ afterEach(async () => {
10
+ try {
11
+ await server?.stop();
12
+ } catch {
13
+ // ignore
14
+ }
15
+ });
16
+
17
+ describe('constructor and getAgentCard', () => {
18
+ it('creates server with defaults', () => {
19
+ server = new A2AServer('test-agent');
20
+ const card = server.getAgentCard();
21
+ expect(card.name).toBe('test-agent');
22
+ expect(card.description).toBe('Agent Relay agent: test-agent');
23
+ expect(card.version).toBe('1.0.0');
24
+ expect(card.capabilities).toEqual({ streaming: true, pushNotifications: false });
25
+ expect(card.skills).toEqual([]);
26
+ });
27
+
28
+ it('creates server with custom skills', () => {
29
+ server = new A2AServer('skilled-agent', 0, '127.0.0.1', [
30
+ { id: 's1', name: 'Billing', description: 'Handle billing' },
31
+ ]);
32
+ const card = server.getAgentCard();
33
+ expect(card.skills).toHaveLength(1);
34
+ expect(card.skills[0].id).toBe('s1');
35
+ });
36
+ });
37
+
38
+ describe('HTTP endpoints', () => {
39
+ it('serves agent card at /.well-known/agent.json', async () => {
40
+ server = new A2AServer('card-server', 0, '127.0.0.1');
41
+ await server.start();
42
+
43
+ const response = await fetch(`${server.url}/.well-known/agent.json`);
44
+ expect(response.ok).toBe(true);
45
+
46
+ const card = (await response.json()) as Record<string, unknown>;
47
+ expect(card.name).toBe('card-server');
48
+ expect(card.version).toBe('1.0.0');
49
+ expect(card.url).toBe(server.url);
50
+ });
51
+
52
+ it('returns 404 for unknown routes', async () => {
53
+ server = new A2AServer('404-server', 0, '127.0.0.1');
54
+ await server.start();
55
+
56
+ const response = await fetch(`${server.url}/unknown`);
57
+ expect(response.status).toBe(404);
58
+ });
59
+
60
+ it('returns parse error for invalid JSON', async () => {
61
+ server = new A2AServer('parse-server', 0, '127.0.0.1');
62
+ await server.start();
63
+
64
+ const response = await fetch(server.url, {
65
+ method: 'POST',
66
+ headers: { 'Content-Type': 'application/json' },
67
+ body: 'not valid json{{{',
68
+ });
69
+
70
+ expect(response.status).toBe(400);
71
+ const body = (await response.json()) as Record<string, unknown>;
72
+ const error = body.error as Record<string, unknown>;
73
+ expect(error.code).toBe(-32700);
74
+ });
75
+
76
+ it('returns method not found for unknown methods', async () => {
77
+ server = new A2AServer('method-server', 0, '127.0.0.1');
78
+ await server.start();
79
+
80
+ const response = await fetch(server.url, {
81
+ method: 'POST',
82
+ headers: { 'Content-Type': 'application/json' },
83
+ body: JSON.stringify({
84
+ jsonrpc: '2.0',
85
+ method: 'unknown/method',
86
+ params: {},
87
+ id: 'rpc-1',
88
+ }),
89
+ });
90
+
91
+ expect(response.status).toBe(400);
92
+ const body = (await response.json()) as Record<string, unknown>;
93
+ const error = body.error as Record<string, unknown>;
94
+ expect(error.code).toBe(-32601);
95
+ expect(error.message).toContain('unknown/method');
96
+ });
97
+ });
98
+
99
+ describe('message/send', () => {
100
+ it('handles message/send and creates task', async () => {
101
+ server = new A2AServer('msg-server', 0, '127.0.0.1');
102
+ await server.start();
103
+
104
+ const response = await fetch(server.url, {
105
+ method: 'POST',
106
+ headers: { 'Content-Type': 'application/json' },
107
+ body: JSON.stringify({
108
+ jsonrpc: '2.0',
109
+ method: 'message/send',
110
+ params: {
111
+ message: {
112
+ role: 'user',
113
+ parts: [{ text: 'hello server' }],
114
+ messageId: 'msg-1',
115
+ },
116
+ },
117
+ id: 'rpc-1',
118
+ }),
119
+ });
120
+
121
+ expect(response.ok).toBe(true);
122
+ const body = (await response.json()) as Record<string, unknown>;
123
+ const result = body.result as Record<string, unknown>;
124
+ expect(result.id).toBeDefined();
125
+ expect((result.status as Record<string, unknown>).state).toBe('completed');
126
+ expect((result.messages as unknown[]).length).toBeGreaterThanOrEqual(1);
127
+ });
128
+
129
+ it('invokes onMessage callback and includes response', async () => {
130
+ server = new A2AServer('cb-server', 0, '127.0.0.1');
131
+ await server.start();
132
+
133
+ const receivedMessages: A2AMessage[] = [];
134
+ server.onMessage((msg) => {
135
+ receivedMessages.push(msg);
136
+ return {
137
+ role: 'agent',
138
+ parts: [{ text: 'I got your message!' }],
139
+ messageId: 'response-1',
140
+ };
141
+ });
142
+
143
+ const response = await fetch(server.url, {
144
+ method: 'POST',
145
+ headers: { 'Content-Type': 'application/json' },
146
+ body: JSON.stringify({
147
+ jsonrpc: '2.0',
148
+ method: 'message/send',
149
+ params: {
150
+ message: {
151
+ role: 'user',
152
+ parts: [{ text: 'callback test' }],
153
+ },
154
+ },
155
+ id: 'rpc-cb',
156
+ }),
157
+ });
158
+
159
+ const body = (await response.json()) as Record<string, unknown>;
160
+ const result = body.result as Record<string, unknown>;
161
+
162
+ // Callback was invoked
163
+ expect(receivedMessages).toHaveLength(1);
164
+ expect(receivedMessages[0].parts[0].text).toBe('callback test');
165
+
166
+ // Response includes agent reply
167
+ const status = result.status as Record<string, unknown>;
168
+ expect(status.state).toBe('completed');
169
+ expect(status.message).toBeDefined();
170
+ const statusMsg = status.message as Record<string, unknown>;
171
+ expect(statusMsg.role).toBe('agent');
172
+
173
+ // Messages include both user and agent messages
174
+ const messages = result.messages as Record<string, unknown>[];
175
+ expect(messages.length).toBe(2);
176
+ });
177
+
178
+ it('invokes async onMessage callback', async () => {
179
+ server = new A2AServer('async-server', 0, '127.0.0.1');
180
+ await server.start();
181
+
182
+ server.onMessage(async (msg) => {
183
+ // Simulate async work
184
+ await new Promise((r) => setTimeout(r, 10));
185
+ return {
186
+ role: 'agent',
187
+ parts: [{ text: 'async reply' }],
188
+ messageId: 'async-r1',
189
+ };
190
+ });
191
+
192
+ const response = await fetch(server.url, {
193
+ method: 'POST',
194
+ headers: { 'Content-Type': 'application/json' },
195
+ body: JSON.stringify({
196
+ jsonrpc: '2.0',
197
+ method: 'message/send',
198
+ params: {
199
+ message: { role: 'user', parts: [{ text: 'async test' }] },
200
+ },
201
+ id: 'rpc-async',
202
+ }),
203
+ });
204
+
205
+ const body = (await response.json()) as Record<string, unknown>;
206
+ const result = body.result as Record<string, unknown>;
207
+ const status = result.status as Record<string, unknown>;
208
+ expect(status.state).toBe('completed');
209
+ expect((status.message as Record<string, unknown>).role).toBe('agent');
210
+ });
211
+
212
+ it('reuses existing task when taskId matches', async () => {
213
+ server = new A2AServer('reuse-server', 0, '127.0.0.1');
214
+ await server.start();
215
+
216
+ const sendMsg = (taskId: string, text: string) =>
217
+ fetch(server.url, {
218
+ method: 'POST',
219
+ headers: { 'Content-Type': 'application/json' },
220
+ body: JSON.stringify({
221
+ jsonrpc: '2.0',
222
+ method: 'message/send',
223
+ params: {
224
+ message: { role: 'user', parts: [{ text }], taskId },
225
+ },
226
+ id: `rpc-${text}`,
227
+ }),
228
+ });
229
+
230
+ await sendMsg('shared-task', 'first message');
231
+ const response = await sendMsg('shared-task', 'second message');
232
+
233
+ const body = (await response.json()) as Record<string, unknown>;
234
+ const result = body.result as Record<string, unknown>;
235
+ const messages = result.messages as unknown[];
236
+ expect(messages.length).toBe(2);
237
+ });
238
+ });
239
+
240
+ describe('tasks/get', () => {
241
+ it('returns task by ID', async () => {
242
+ server = new A2AServer('get-server', 0, '127.0.0.1');
243
+ await server.start();
244
+
245
+ // Create a task first
246
+ await fetch(server.url, {
247
+ method: 'POST',
248
+ headers: { 'Content-Type': 'application/json' },
249
+ body: JSON.stringify({
250
+ jsonrpc: '2.0',
251
+ method: 'message/send',
252
+ params: {
253
+ message: {
254
+ role: 'user',
255
+ parts: [{ text: 'create' }],
256
+ taskId: 'get-task',
257
+ },
258
+ },
259
+ id: 'rpc-create',
260
+ }),
261
+ });
262
+
263
+ // Get the task
264
+ const response = await fetch(server.url, {
265
+ method: 'POST',
266
+ headers: { 'Content-Type': 'application/json' },
267
+ body: JSON.stringify({
268
+ jsonrpc: '2.0',
269
+ method: 'tasks/get',
270
+ params: { id: 'get-task' },
271
+ id: 'rpc-get',
272
+ }),
273
+ });
274
+
275
+ expect(response.ok).toBe(true);
276
+ const body = (await response.json()) as Record<string, unknown>;
277
+ const result = body.result as Record<string, unknown>;
278
+ expect(result.id).toBe('get-task');
279
+ });
280
+
281
+ it('returns error for non-existent task', async () => {
282
+ server = new A2AServer('noget-server', 0, '127.0.0.1');
283
+ await server.start();
284
+
285
+ const response = await fetch(server.url, {
286
+ method: 'POST',
287
+ headers: { 'Content-Type': 'application/json' },
288
+ body: JSON.stringify({
289
+ jsonrpc: '2.0',
290
+ method: 'tasks/get',
291
+ params: { id: 'nonexistent' },
292
+ id: 'rpc-noget',
293
+ }),
294
+ });
295
+
296
+ expect(response.status).toBe(404);
297
+ const body = (await response.json()) as Record<string, unknown>;
298
+ expect(body.error).toBeDefined();
299
+ });
300
+ });
301
+
302
+ describe('tasks/cancel', () => {
303
+ it('cancels a task', async () => {
304
+ server = new A2AServer('cancel-server', 0, '127.0.0.1');
305
+ await server.start();
306
+
307
+ // Manually add a working task
308
+ server.tasks.set('cancel-task', {
309
+ id: 'cancel-task',
310
+ contextId: 'ctx-1',
311
+ status: { state: 'working', timestamp: new Date().toISOString() },
312
+ messages: [],
313
+ artifacts: [],
314
+ });
315
+
316
+ const response = await fetch(server.url, {
317
+ method: 'POST',
318
+ headers: { 'Content-Type': 'application/json' },
319
+ body: JSON.stringify({
320
+ jsonrpc: '2.0',
321
+ method: 'tasks/cancel',
322
+ params: { id: 'cancel-task' },
323
+ id: 'rpc-cancel',
324
+ }),
325
+ });
326
+
327
+ expect(response.ok).toBe(true);
328
+ const body = (await response.json()) as Record<string, unknown>;
329
+ const result = body.result as Record<string, unknown>;
330
+ expect((result.status as Record<string, unknown>).state).toBe('canceled');
331
+ });
332
+ });
333
+
334
+ describe('start / stop', () => {
335
+ it('starts and stops cleanly', async () => {
336
+ server = new A2AServer('lifecycle-server', 0, '127.0.0.1');
337
+ await server.start();
338
+
339
+ // Server is running
340
+ const response = await fetch(`${server.url}/.well-known/agent.json`);
341
+ expect(response.ok).toBe(true);
342
+
343
+ await server.stop();
344
+
345
+ // Server is stopped
346
+ await expect(fetch(`${server.url}/.well-known/agent.json`)).rejects.toThrow();
347
+ });
348
+
349
+ it('url updates after start with port 0', async () => {
350
+ server = new A2AServer('port-server', 0, '127.0.0.1');
351
+ await server.start();
352
+
353
+ // URL should contain the actual assigned port
354
+ expect(server.url).toMatch(/http:\/\/127\.0\.0\.1:\d+/);
355
+ const port = parseInt(server.url.split(':').pop()!, 10);
356
+ expect(port).toBeGreaterThan(0);
357
+ });
358
+ });
359
+ });