@agent-relay/sdk 2.3.14 → 2.4.1

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 (179) hide show
  1. package/README.md +68 -838
  2. package/bin/agent-relay-broker +0 -0
  3. package/dist/__tests__/contract-fixtures.test.d.ts +2 -0
  4. package/dist/__tests__/contract-fixtures.test.d.ts.map +1 -0
  5. package/dist/__tests__/contract-fixtures.test.js +85 -0
  6. package/dist/__tests__/contract-fixtures.test.js.map +1 -0
  7. package/dist/__tests__/facade.test.d.ts +2 -0
  8. package/dist/__tests__/facade.test.d.ts.map +1 -0
  9. package/dist/__tests__/facade.test.js +305 -0
  10. package/dist/__tests__/facade.test.js.map +1 -0
  11. package/dist/__tests__/integration.test.d.ts +2 -0
  12. package/dist/__tests__/integration.test.d.ts.map +1 -0
  13. package/dist/__tests__/integration.test.js +169 -0
  14. package/dist/__tests__/integration.test.js.map +1 -0
  15. package/dist/__tests__/pty.test.d.ts +2 -0
  16. package/dist/__tests__/pty.test.d.ts.map +1 -0
  17. package/dist/__tests__/pty.test.js +20 -0
  18. package/dist/__tests__/pty.test.js.map +1 -0
  19. package/dist/__tests__/quickstart.test.d.ts +2 -0
  20. package/dist/__tests__/quickstart.test.d.ts.map +1 -0
  21. package/dist/__tests__/quickstart.test.js +176 -0
  22. package/dist/__tests__/quickstart.test.js.map +1 -0
  23. package/dist/__tests__/spawn-from-env.test.d.ts +2 -0
  24. package/dist/__tests__/spawn-from-env.test.d.ts.map +1 -0
  25. package/dist/__tests__/spawn-from-env.test.js +206 -0
  26. package/dist/__tests__/spawn-from-env.test.js.map +1 -0
  27. package/dist/__tests__/unit.test.d.ts +2 -0
  28. package/dist/__tests__/unit.test.d.ts.map +1 -0
  29. package/dist/__tests__/unit.test.js +347 -0
  30. package/dist/__tests__/unit.test.js.map +1 -0
  31. package/dist/browser.d.ts +16 -0
  32. package/dist/browser.d.ts.map +1 -0
  33. package/dist/browser.js +19 -0
  34. package/dist/browser.js.map +1 -0
  35. package/dist/client.d.ts +140 -526
  36. package/dist/client.d.ts.map +1 -1
  37. package/dist/client.js +416 -1509
  38. package/dist/client.js.map +1 -1
  39. package/dist/consensus-helpers.d.ts +103 -0
  40. package/dist/consensus-helpers.d.ts.map +1 -0
  41. package/dist/consensus-helpers.js +147 -0
  42. package/dist/consensus-helpers.js.map +1 -0
  43. package/dist/consensus.d.ts +72 -0
  44. package/dist/consensus.d.ts.map +1 -0
  45. package/dist/consensus.js +378 -0
  46. package/dist/consensus.js.map +1 -0
  47. package/dist/examples/demo.d.ts +2 -0
  48. package/dist/examples/demo.d.ts.map +1 -0
  49. package/dist/examples/demo.js +63 -0
  50. package/dist/examples/demo.js.map +1 -0
  51. package/dist/examples/example.d.ts +2 -0
  52. package/dist/examples/example.d.ts.map +1 -0
  53. package/dist/examples/example.js +80 -0
  54. package/dist/examples/example.js.map +1 -0
  55. package/dist/examples/quickstart.d.ts +2 -0
  56. package/dist/examples/quickstart.d.ts.map +1 -0
  57. package/dist/examples/quickstart.js +56 -0
  58. package/dist/examples/quickstart.js.map +1 -0
  59. package/dist/examples/ralph-loop.d.ts +2 -0
  60. package/dist/examples/ralph-loop.d.ts.map +1 -0
  61. package/dist/examples/ralph-loop.js +281 -0
  62. package/dist/examples/ralph-loop.js.map +1 -0
  63. package/dist/examples/workflow-superiority.d.ts +32 -0
  64. package/dist/examples/workflow-superiority.d.ts.map +1 -0
  65. package/dist/examples/workflow-superiority.js +1421 -0
  66. package/dist/examples/workflow-superiority.js.map +1 -0
  67. package/dist/index.d.ts +13 -20
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +12 -26
  70. package/dist/index.js.map +1 -1
  71. package/dist/logs.d.ts +70 -25
  72. package/dist/logs.d.ts.map +1 -1
  73. package/dist/logs.js +238 -42
  74. package/dist/logs.js.map +1 -1
  75. package/dist/models.d.ts +9 -0
  76. package/dist/models.d.ts.map +1 -0
  77. package/dist/models.js +17 -0
  78. package/dist/models.js.map +1 -0
  79. package/dist/protocol.d.ts +366 -0
  80. package/dist/protocol.d.ts.map +1 -0
  81. package/dist/protocol.js +2 -0
  82. package/dist/protocol.js.map +1 -0
  83. package/dist/pty.d.ts +8 -0
  84. package/dist/pty.d.ts.map +1 -0
  85. package/dist/pty.js +26 -0
  86. package/dist/pty.js.map +1 -0
  87. package/dist/relay-adapter.d.ts +139 -0
  88. package/dist/relay-adapter.d.ts.map +1 -0
  89. package/dist/relay-adapter.js +210 -0
  90. package/dist/relay-adapter.js.map +1 -0
  91. package/dist/relay.d.ts +304 -0
  92. package/dist/relay.d.ts.map +1 -0
  93. package/dist/relay.js +910 -0
  94. package/dist/relay.js.map +1 -0
  95. package/dist/shadow.d.ts +101 -0
  96. package/dist/shadow.d.ts.map +1 -0
  97. package/dist/shadow.js +174 -0
  98. package/dist/shadow.js.map +1 -0
  99. package/dist/spawn-from-env.d.ts +77 -0
  100. package/dist/spawn-from-env.d.ts.map +1 -0
  101. package/dist/spawn-from-env.js +172 -0
  102. package/dist/spawn-from-env.js.map +1 -0
  103. package/dist/workflows/barrier.d.ts +72 -0
  104. package/dist/workflows/barrier.d.ts.map +1 -0
  105. package/dist/workflows/barrier.js +162 -0
  106. package/dist/workflows/barrier.js.map +1 -0
  107. package/dist/workflows/builder.d.ts +114 -0
  108. package/dist/workflows/builder.d.ts.map +1 -0
  109. package/dist/workflows/builder.js +201 -0
  110. package/dist/workflows/builder.js.map +1 -0
  111. package/dist/workflows/cli.d.ts +11 -0
  112. package/dist/workflows/cli.d.ts.map +1 -0
  113. package/dist/workflows/cli.js +144 -0
  114. package/dist/workflows/cli.js.map +1 -0
  115. package/dist/workflows/coordinator.d.ts +73 -0
  116. package/dist/workflows/coordinator.d.ts.map +1 -0
  117. package/dist/workflows/coordinator.js +647 -0
  118. package/dist/workflows/coordinator.js.map +1 -0
  119. package/dist/workflows/custom-steps.d.ts +73 -0
  120. package/dist/workflows/custom-steps.d.ts.map +1 -0
  121. package/dist/workflows/custom-steps.js +321 -0
  122. package/dist/workflows/custom-steps.js.map +1 -0
  123. package/dist/workflows/dry-run-format.d.ts +6 -0
  124. package/dist/workflows/dry-run-format.d.ts.map +1 -0
  125. package/dist/workflows/dry-run-format.js +68 -0
  126. package/dist/workflows/dry-run-format.js.map +1 -0
  127. package/dist/workflows/file-db.d.ts +33 -0
  128. package/dist/workflows/file-db.d.ts.map +1 -0
  129. package/dist/workflows/file-db.js +108 -0
  130. package/dist/workflows/file-db.js.map +1 -0
  131. package/dist/workflows/index.d.ts +15 -0
  132. package/dist/workflows/index.d.ts.map +1 -0
  133. package/dist/workflows/index.js +15 -0
  134. package/dist/workflows/index.js.map +1 -0
  135. package/dist/workflows/memory-db.d.ts +17 -0
  136. package/dist/workflows/memory-db.d.ts.map +1 -0
  137. package/dist/workflows/memory-db.js +33 -0
  138. package/dist/workflows/memory-db.js.map +1 -0
  139. package/dist/workflows/run.d.ts +38 -0
  140. package/dist/workflows/run.d.ts.map +1 -0
  141. package/dist/workflows/run.js +25 -0
  142. package/dist/workflows/run.js.map +1 -0
  143. package/dist/workflows/runner.d.ts +320 -0
  144. package/dist/workflows/runner.d.ts.map +1 -0
  145. package/dist/workflows/runner.js +2821 -0
  146. package/dist/workflows/runner.js.map +1 -0
  147. package/dist/workflows/state.d.ts +77 -0
  148. package/dist/workflows/state.d.ts.map +1 -0
  149. package/dist/workflows/state.js +140 -0
  150. package/dist/workflows/state.js.map +1 -0
  151. package/dist/workflows/templates.d.ts +47 -0
  152. package/dist/workflows/templates.d.ts.map +1 -0
  153. package/dist/workflows/templates.js +405 -0
  154. package/dist/workflows/templates.js.map +1 -0
  155. package/dist/workflows/trajectory.d.ts +87 -0
  156. package/dist/workflows/trajectory.d.ts.map +1 -0
  157. package/dist/workflows/trajectory.js +441 -0
  158. package/dist/workflows/trajectory.js.map +1 -0
  159. package/dist/workflows/types.d.ts +306 -0
  160. package/dist/workflows/types.d.ts.map +1 -0
  161. package/dist/workflows/types.js +23 -0
  162. package/dist/workflows/types.js.map +1 -0
  163. package/dist/workflows/validator.d.ts +11 -0
  164. package/dist/workflows/validator.d.ts.map +1 -0
  165. package/dist/workflows/validator.js +128 -0
  166. package/dist/workflows/validator.js.map +1 -0
  167. package/package.json +59 -53
  168. package/dist/discovery.d.ts +0 -10
  169. package/dist/discovery.d.ts.map +0 -1
  170. package/dist/discovery.js +0 -22
  171. package/dist/discovery.js.map +0 -1
  172. package/dist/errors.d.ts +0 -9
  173. package/dist/errors.d.ts.map +0 -1
  174. package/dist/errors.js +0 -9
  175. package/dist/errors.js.map +0 -1
  176. package/dist/protocol/index.d.ts +0 -8
  177. package/dist/protocol/index.d.ts.map +0 -1
  178. package/dist/protocol/index.js +0 -8
  179. package/dist/protocol/index.js.map +0 -1
package/README.md CHANGED
@@ -1,867 +1,97 @@
1
- # @agent-relay/sdk
1
+ # `@agent-relay/sdk`
2
2
 
3
- **Primitives for building powerful multi-agent swarms.**
3
+ TypeScript SDK for driving `agent-relay-broker init` over stdio.
4
4
 
5
- Unlike frameworks that impose specific orchestration patterns, Agent Relay provides flexible communication primitives that let you build any swarm architecture. Whether you want hierarchical coordination, parallel fan-out, consensus-based decisions, or self-organizing agents—the SDK gives you the building blocks.
5
+ ## Status
6
+ - Broker lifecycle + request/response protocol client implemented.
7
+ - Spawn/list/release/shutdown APIs implemented.
8
+ - Event subscription for broker `event` frames implemented.
9
+ - Agent idle detection — configurable silence threshold with `onAgentIdle` hook and `waitForIdle()`.
6
10
 
7
- ## Why Agent Relay for Swarms?
11
+ ## Bundled binary
12
+ - The SDK package bundles `agent-relay-broker` inside `bin/` during `npm run build` and `npm pack`.
13
+ - By default, `AgentRelayClient` uses the bundled binary path when present.
14
+ - You can still override with `binaryPath` in `AgentRelayClient.start(...)`.
8
15
 
9
- | Framework | Approach | Limitation |
10
- |-----------|----------|------------|
11
- | OpenAI Agents | Handoff-based routing | Prescriptive flow control |
12
- | Swarms.ai | Pre-built swarm types | Configuration-heavy |
13
- | Strands | Self-organizing swarms | AWS ecosystem lock-in |
14
- | **Agent Relay** | **Communication primitives** | **You design the orchestration** |
16
+ ## Quick example
17
+ ```ts
18
+ import { AgentRelayClient } from "@agent-relay/sdk";
15
19
 
16
- ### What You Can Build
17
-
18
- - **Hierarchical swarms** - Lead + specialist workers with task delegation
19
- - **Parallel execution** - Fan-out to workers, fan-in results
20
- - **Pipeline workflows** - Sequential processing across agents
21
- - **Consensus decisions** - Group voting on critical choices
22
- - **Self-organizing teams** - Dynamic task claiming via channels
23
- - **Supervised agents** - Shadow monitoring for QA and oversight
24
-
25
- See [examples/SWARM_PATTERNS.md](./examples/SWARM_PATTERNS.md) for detailed patterns and [examples/SWARM_CAPABILITIES.md](./examples/SWARM_CAPABILITIES.md) for how primitives map to swarm capabilities.
26
-
27
- ## Install
28
-
29
- ```bash
30
- npm install @agent-relay/sdk
31
- ```
32
-
33
- For standalone mode (in-process daemon), also install:
34
-
35
- ```bash
36
- npm install @agent-relay/daemon
37
- ```
38
-
39
- ## Quick Start
40
-
41
- ### Standalone Mode (Zero Config)
42
-
43
- ```typescript
44
- import { createRelay } from '@agent-relay/sdk';
45
-
46
- // Start a relay (in-process daemon - no setup required)
47
- const relay = await createRelay();
48
-
49
- // Create agents
50
- const alice = await relay.client('Alice');
51
- const bob = await relay.client('Bob');
52
-
53
- // Bob listens for messages
54
- bob.onMessage = (from, { body }) => {
55
- console.log(`${from}: ${body}`);
56
- };
57
-
58
- // Alice sends a message
59
- alice.sendMessage('Bob', 'Hello!');
60
-
61
- // When done
62
- await relay.stop();
63
- ```
64
-
65
- ### Even Simpler: Two Agents
66
-
67
- ```typescript
68
- import { createPair } from '@agent-relay/sdk';
69
-
70
- const { alice, bob, stop } = await createPair('alice', 'bob');
71
-
72
- bob.onMessage = (from, { body }) => console.log(`${from}: ${body}`);
73
- alice.sendMessage('bob', 'Hey!');
74
-
75
- await stop();
76
- ```
77
-
78
- ### External Daemon Mode
79
-
80
- If you're running `agent-relay up` separately:
81
-
82
- ```typescript
83
- import { RelayClient } from '@agent-relay/sdk';
84
-
85
- const client = new RelayClient({
86
- agentName: 'MyAgent',
87
- socketPath: '/tmp/agent-relay.sock', // optional, this is the default
88
- });
89
-
90
- await client.connect();
91
- client.sendMessage('OtherAgent', 'Hello!');
92
- ```
93
-
94
- ## Features
95
-
96
- | Feature | Description |
97
- |---------|-------------|
98
- | **Zero config** | Just import and go with standalone mode |
99
- | **Auto-reconnect** | Handles disconnections automatically |
100
- | **Message deduplication** | No duplicate deliveries |
101
- | **Sync messaging** | Wait for acknowledgment with `sendAndWait()` |
102
- | **Broadcast** | Send to all agents with `*` |
103
- | **Channels** | Group messaging with `#channel` |
104
- | **Pub/Sub** | Topic-based subscriptions |
105
- | **Agent spawning** | Spawn, release, list, and send input to worker agents |
106
- | **Shadow agents** | Monitor another agent's communication |
107
- | **Consensus** | Distributed decision-making (external daemon only) |
108
- | **Monitoring** | Health, metrics, agent discovery |
109
-
110
- ## API Reference
111
-
112
- ### Connection
113
-
114
- #### connect()
115
-
116
- Connect to the relay daemon. Returns a Promise that resolves when the connection is ready.
117
-
118
- ```typescript
119
- const client = new RelayClient({ agentName: 'MyAgent' });
120
- await client.connect();
121
- // Client is now ready to send/receive messages
122
- ```
123
-
124
- #### disconnect()
125
-
126
- Gracefully disconnect from the daemon. The client can reconnect later.
127
-
128
- ```typescript
129
- client.disconnect();
130
- ```
131
-
132
- #### destroy()
133
-
134
- Permanently destroy the client. Prevents automatic reconnection.
135
-
136
- ```typescript
137
- client.destroy();
138
- ```
139
-
140
- #### Properties
141
-
142
- ```typescript
143
- client.state; // 'DISCONNECTED' | 'CONNECTING' | 'HANDSHAKING' | 'READY' | 'BACKOFF'
144
- client.agentName; // The agent's name
145
- client.currentSessionId; // Current session ID (undefined if not connected)
146
- ```
147
-
148
- ### Core Messaging
149
-
150
- #### sendMessage(to, body, kind?, data?, thread?)
151
-
152
- Send a message to another agent.
153
-
154
- ```typescript
155
- // Simple message
156
- client.sendMessage('Bob', 'Hello!');
157
-
158
- // With message kind and data
159
- client.sendMessage('Bob', 'Task complete', 'action', { taskId: 123 });
160
-
161
- // In a thread
162
- client.sendMessage('Bob', 'Follow-up', 'message', undefined, 'thread-123');
163
- ```
164
-
165
- #### sendAndWait(to, body, options?)
166
-
167
- Send and wait for acknowledgment. Useful for ensuring delivery.
168
-
169
- ```typescript
170
- const ack = await client.sendAndWait('Bob', 'Important message', {
171
- timeoutMs: 5000, // default: 30000
172
- });
173
- console.log('Acknowledged:', ack);
174
- ```
175
-
176
- #### broadcast(body, kind?, data?)
177
-
178
- Send to all connected agents.
179
-
180
- ```typescript
181
- client.broadcast('System notice to everyone');
182
- ```
183
-
184
- #### request(to, body, options?)
185
-
186
- Send a request and wait for a response from the target agent. This implements a request/response pattern where the target agent can respond with `respond()`.
187
-
188
- ```typescript
189
- // Simple request
190
- const response = await client.request('Worker', 'Process this task');
191
- console.log(response.body); // Worker's response
192
- console.log(response.from); // 'Worker'
193
-
194
- // With options
195
- const response = await client.request('Worker', 'Process task', {
196
- timeout: 60000, // default: 30000ms
197
- data: { taskId: '123', priority: 'high' },
198
- thread: 'task-thread-1',
199
- kind: 'action', // default: 'message'
200
- });
201
- ```
202
-
203
- **RequestResponse type:**
204
- ```typescript
205
- interface RequestResponse {
206
- from: string; // sender of the response
207
- body: string; // response text
208
- data?: Record<string, unknown>;
209
- correlationId: string;
210
- thread?: string;
211
- payload: SendPayload; // full payload for advanced use
212
- }
213
- ```
214
-
215
- #### respond(correlationId, to, body, data?)
216
-
217
- Respond to a request from another agent. Use when you receive a message with a correlation ID.
218
-
219
- ```typescript
220
- client.onMessage = (from, payload, messageId, meta) => {
221
- const correlationId = meta?.replyTo || payload.data?._correlationId;
222
- if (correlationId) {
223
- // This is a request - send a response
224
- client.respond(correlationId, from, 'Task completed!', { result: 42 });
225
- }
226
- };
227
- ```
228
-
229
- #### sendAck(payload)
230
-
231
- Send an ACK for a delivered message. Used internally, but available for custom acknowledgment flows.
232
-
233
- ```typescript
234
- client.sendAck({
235
- ack_id: messageId,
236
- seq: 123,
237
- correlationId: 'optional-correlation-id',
238
- });
239
- ```
240
-
241
- #### onMessage
242
-
243
- Callback for incoming messages.
244
-
245
- ```typescript
246
- client.onMessage = (from, payload, messageId, meta, originalTo) => {
247
- console.log(`${from}: ${payload.body}`);
248
-
249
- // payload.kind: 'message' | 'action' | 'state' | 'thinking'
250
- // payload.data: optional structured data
251
- // payload.thread: optional thread ID
252
-
253
- // Check if it was a broadcast
254
- if (originalTo === '*') {
255
- console.log('This was a broadcast');
256
- }
257
- };
258
- ```
259
-
260
- ### Channels
261
-
262
- #### joinChannel(channel, displayName?)
263
-
264
- ```typescript
265
- client.joinChannel('#general');
266
- client.joinChannel('#team', 'Alice (Lead)'); // with display name
267
- ```
268
-
269
- #### leaveChannel(channel, reason?)
270
-
271
- ```typescript
272
- client.leaveChannel('#general');
273
- client.leaveChannel('#team', 'Signing off');
274
- ```
275
-
276
- #### sendChannelMessage(channel, body, options?)
277
-
278
- ```typescript
279
- // Simple message
280
- client.sendChannelMessage('#general', 'Hello team!');
281
-
282
- // With mentions and thread
283
- client.sendChannelMessage('#general', 'Check this out', {
284
- thread: 'discussion-123',
285
- mentions: ['Bob', 'Charlie'],
286
- attachments: [{ type: 'file', name: 'report.pdf', url: '...' }],
287
- });
288
- ```
289
-
290
- #### onChannelMessage
291
-
292
- ```typescript
293
- client.onChannelMessage = (from, channel, body, envelope) => {
294
- console.log(`[${channel}] ${from}: ${body}`);
295
- // envelope contains full message details (thread, mentions, etc.)
296
- };
297
- ```
298
-
299
- #### Admin Channel Operations
300
-
301
- ```typescript
302
- // Add a member to a channel (they don't need to be connected)
303
- client.adminJoinChannel('#team', 'NewMember');
304
-
305
- // Remove a member from a channel
306
- client.adminRemoveMember('#team', 'FormerMember');
307
- ```
308
-
309
- ### Pub/Sub
310
-
311
- Subscribe to topics for filtered message delivery.
312
-
313
- ```typescript
314
- // Subscribe to a topic
315
- client.subscribe('builds');
316
- client.subscribe('deployments');
317
-
318
- // Messages to that topic will be delivered via onMessage
319
- client.onMessage = (from, payload) => {
320
- if (payload.data?.topic === 'builds') {
321
- console.log('Build notification:', payload.body);
322
- }
323
- };
324
-
325
- // Unsubscribe
326
- client.unsubscribe('builds');
327
- ```
328
-
329
- ### Agent Spawning
330
-
331
- Spawn and manage worker agents programmatically.
332
-
333
- ```typescript
334
- // Spawn a new agent
335
- const result = await client.spawn({
336
- name: 'Worker1',
337
- cli: 'claude', // claude, codex, gemini, etc.
338
- task: 'Process the data files',
339
- cwd: '/path/to/workdir', // optional
20
+ const client = await AgentRelayClient.start({
21
+ binaryPath: "/absolute/path/to/agent-relay-broker",
22
+ env: process.env,
340
23
  });
341
24
 
342
- if (result.success) {
343
- console.log('Worker spawned!');
344
- }
345
-
346
- // Spawn and wait for the agent to be ready
347
- const result = await client.spawn({
348
- name: 'Worker2',
349
- cli: 'claude',
350
- task: 'Process data',
351
- waitForReady: true, // wait for agent to connect
352
- readyTimeoutMs: 60000, // timeout for ready (default: 60000)
25
+ await client.spawnPty({
26
+ name: "Worker1",
27
+ cli: "codex",
28
+ args: ["--model", "gpt-5"],
29
+ channels: ["general"],
353
30
  });
354
31
 
355
- if (result.ready) {
356
- console.log('Worker is ready:', result.readyInfo);
357
- }
358
-
359
- // Release (terminate) an agent
360
- const releaseResult = await client.release('Worker1');
361
- const releaseWithReason = await client.release('Worker2', 'Task complete');
362
-
363
- // Send input to a spawned agent's PTY
364
- const inputResult = await client.sendWorkerInput('Worker1', 'yes\n');
365
- console.log(inputResult.success); // true
366
-
367
- // List active spawned workers
368
- const workers = await client.listWorkers();
369
- for (const w of workers.workers) {
370
- console.log(`${w.name} (${w.cli}) spawned at ${new Date(w.spawnedAt)}`);
371
- }
372
- ```
373
-
374
- #### waitForAgentReady(name, timeoutMs?)
375
-
376
- Wait for an agent to become ready (complete HELLO/WELCOME handshake). Useful when waiting for an agent spawned through another mechanism.
377
-
378
- ```typescript
379
- try {
380
- const readyInfo = await client.waitForAgentReady('Worker', 30000);
381
- console.log(`Worker is ready, using ${readyInfo.cli}`);
382
- } catch (err) {
383
- console.error('Worker did not become ready in time');
384
- }
385
- ```
386
-
387
- #### onAgentReady
388
-
389
- Callback when any agent becomes ready (completes connection handshake).
390
-
391
- ```typescript
392
- client.onAgentReady = (info) => {
393
- console.log(`Agent ${info.name} is now ready (cli: ${info.cli})`);
394
- };
395
- ```
396
-
397
- #### sendWorkerInput(name, data, timeoutMs?)
398
-
399
- Send input data to a spawned agent's PTY. Useful for answering prompts or sending commands to interactive agents.
400
-
401
- ```typescript
402
- const result = await client.sendWorkerInput('Worker1', 'yes\n');
403
- if (result.success) {
404
- console.log('Input sent');
405
- }
406
- ```
407
-
408
- #### listWorkers(timeoutMs?)
409
-
410
- List all active spawned workers managed by the daemon's SpawnManager.
411
-
412
- ```typescript
413
- const result = await client.listWorkers();
414
- for (const worker of result.workers) {
415
- console.log(`${worker.name} (${worker.cli}) - team: ${worker.team || 'none'}`);
416
- }
417
- ```
418
-
419
- **ListWorkersResultPayload:**
420
- ```typescript
421
- interface ListWorkersResultPayload {
422
- replyTo: string;
423
- workers: Array<{
424
- name: string;
425
- cli: string;
426
- task: string;
427
- team?: string;
428
- spawnerName?: string;
429
- spawnedAt: number;
430
- pid?: number;
431
- }>;
432
- error?: string;
433
- }
434
- ```
435
-
436
- #### Spawn as Shadow
437
-
438
- ```typescript
439
- // Spawn an agent that shadows another
440
- await client.spawn({
441
- name: 'Reviewer',
442
- cli: 'claude',
443
- task: 'Review code changes',
444
- shadowOf: 'Developer',
445
- shadowSpeakOn: ['error', 'complete'],
446
- });
447
- ```
448
-
449
- ### Shadow Agents
450
-
451
- Monitor another agent's communication without them knowing.
452
-
453
- ```typescript
454
- // Bind as shadow to see all messages to/from PrimaryAgent
455
- client.bindAsShadow('PrimaryAgent', {
456
- receiveIncoming: true, // see messages sent TO the primary
457
- receiveOutgoing: true, // see messages sent BY the primary
458
- speakOn: ['error'], // triggers that allow shadow to speak
459
- });
460
-
461
- // Unbind
462
- client.unbindAsShadow('PrimaryAgent');
463
- ```
464
-
465
- ### Logging
466
-
467
- Stream logs to the daemon (for dashboard display).
468
-
469
- ```typescript
470
- // Send log output
471
- client.sendLog('Starting task...');
472
- client.sendLog('Processing file 1 of 10');
473
- client.sendLog('Error: File not found');
474
- ```
475
-
476
- ### Consensus (External Daemon Only)
477
-
478
- Distributed decision-making across agents. **Note:** Consensus requires an external daemon - it's disabled in standalone mode.
479
-
480
- #### Consensus Types
481
-
482
- | Type | Description |
483
- |------|-------------|
484
- | `majority` | >50% agreement (default) |
485
- | `supermajority` | >=2/3 agreement (configurable) |
486
- | `unanimous` | 100% agreement required |
487
- | `weighted` | Votes weighted by role/expertise |
488
- | `quorum` | Minimum participation + majority |
489
-
490
- #### Create a Proposal
491
-
492
- ```typescript
493
- client.createProposal({
494
- title: 'Approve API design',
495
- description: 'Should we proceed with the REST API design?',
496
- participants: ['Developer', 'Reviewer', 'Lead'],
497
- consensusType: 'majority',
498
- timeoutMs: 300000, // 5 minutes, optional
499
- threshold: 0.67, // for supermajority, optional
500
- quorum: 2, // minimum votes, optional
501
- });
502
- ```
503
-
504
- #### Vote on a Proposal
505
-
506
- ```typescript
507
- client.vote({
508
- proposalId: 'prop_123_abc',
509
- value: 'approve', // or 'reject', 'abstain'
510
- reason: 'Looks good to me', // optional
511
- });
512
- ```
513
-
514
- #### Receiving Proposals and Results
515
-
516
- ```typescript
517
- client.onMessage = (from, payload) => {
518
- if (payload.data?._isConsensusMessage) {
519
- console.log('Consensus message:', payload.body);
520
- }
521
- };
522
- ```
523
-
524
- ### Monitoring & Discovery
525
-
526
- #### List Online Agents
527
-
528
- ```typescript
529
32
  const agents = await client.listAgents();
530
- for (const agent of agents) {
531
- console.log(`${agent.name} (${agent.cli}) - ${agent.idle ? 'idle' : 'active'}`);
532
- }
33
+ console.log(agents);
533
34
 
534
- // Filter options
535
- const activeOnly = await client.listAgents({ includeIdle: false });
536
- const projectAgents = await client.listAgents({ project: 'myproject' });
35
+ await client.release("Worker1");
36
+ await client.shutdown();
537
37
  ```
538
38
 
539
- #### Get System Health
540
-
541
- ```typescript
542
- const health = await client.getHealth();
543
- console.log(`Health score: ${health.healthScore}/100`);
544
- console.log(`Issues: ${health.issues.length}`);
545
- console.log(`Recommendations:`, health.recommendations);
546
-
547
- // Options
548
- const health = await client.getHealth({
549
- includeCrashes: true, // include crash history
550
- includeAlerts: true, // include alerts
551
- });
552
- ```
39
+ ### High-level API with idle detection
40
+ ```ts
41
+ import { AgentRelay } from "@agent-relay/sdk";
553
42
 
554
- #### Get Resource Metrics
43
+ const relay = new AgentRelay();
555
44
 
556
- ```typescript
557
- const metrics = await client.getMetrics();
558
-
559
- // System overview
560
- console.log(`Heap used: ${metrics.system.heapUsed}`);
561
- console.log(`Free memory: ${metrics.system.freeMemory}`);
562
-
563
- // Per-agent metrics
564
- for (const agent of metrics.agents) {
565
- console.log(`${agent.name}: ${agent.rssBytes} bytes, ${agent.cpuPercent}% CPU`);
566
- }
567
-
568
- // Filter to specific agent
569
- const workerMetrics = await client.getMetrics({ agent: 'Worker1' });
570
- ```
571
-
572
- #### Get Daemon Status
573
-
574
- ```typescript
575
- const status = await client.getStatus();
576
- console.log(`Version: ${status.version}`);
577
- console.log(`Uptime: ${status.uptime}ms`);
578
- console.log(`Agents: ${status.agentCount}`);
579
- ```
580
-
581
- #### Get Inbox Messages
582
-
583
- ```typescript
584
- const messages = await client.getInbox({ limit: 10 });
585
- for (const msg of messages) {
586
- console.log(`From ${msg.from}: ${msg.body}`);
587
- }
588
-
589
- // Filter options
590
- const unread = await client.getInbox({ unreadOnly: true });
591
- const fromAlice = await client.getInbox({ from: 'Alice' });
592
- const channelMsgs = await client.getInbox({ channel: '#general' });
593
- ```
594
-
595
- #### Query All Messages
596
-
597
- Query all messages (not filtered by recipient). Useful for dashboards or analytics.
598
-
599
- ```typescript
600
- const messages = await client.queryMessages({
601
- limit: 100, // default: 100
602
- sinceTs: Date.now() - 3600000, // last hour
603
- from: 'Alice', // filter by sender
604
- to: 'Bob', // filter by recipient
605
- thread: 'thread-123', // filter by thread
606
- order: 'desc', // 'asc' or 'desc' (default: 'desc')
607
- });
608
-
609
- for (const msg of messages) {
610
- console.log(`${msg.from} -> ${msg.to}: ${msg.body}`);
611
- }
612
- ```
613
-
614
- #### List Connected Agents Only
615
-
616
- Unlike `listAgents()` which includes historical/registered agents, this only returns agents currently connected.
617
-
618
- ```typescript
619
- const connected = await client.listConnectedAgents();
620
- for (const agent of connected) {
621
- console.log(`${agent.name} is connected right now`);
622
- }
623
-
624
- // Filter by project
625
- const projectAgents = await client.listConnectedAgents({ project: 'myproject' });
626
- ```
627
-
628
- #### Remove Agent
629
-
630
- Remove a stale agent from the registry (sessions, agents.json). Use to clean up agents no longer needed.
631
-
632
- ```typescript
633
- const result = await client.removeAgent('OldWorker');
634
- if (result.success) {
635
- console.log('Agent removed');
636
- }
637
-
638
- // Also remove all messages from/to this agent
639
- await client.removeAgent('OldWorker', { removeMessages: true });
640
- ```
641
-
642
- #### Read Agent Logs (File-based)
643
-
644
- Logs are stored locally and can be read without a connection:
645
-
646
- ```typescript
647
- import { getLogs, listLoggedAgents } from '@agent-relay/sdk';
648
-
649
- // List agents with logs
650
- const agents = await listLoggedAgents();
651
-
652
- // Get last 100 lines of a specific agent's logs
653
- const result = await getLogs('Worker1', { lines: 100 });
654
- if (result.found) {
655
- console.log(result.content);
656
- }
657
- ```
658
-
659
- ### Connection Management
660
-
661
- #### State Changes
662
-
663
- ```typescript
664
- client.onStateChange = (state) => {
665
- // 'DISCONNECTED' | 'CONNECTING' | 'HANDSHAKING' | 'READY' | 'BACKOFF'
666
- console.log('State:', state);
45
+ // Listen for idle events
46
+ relay.onAgentIdle = ({ name, idleSecs }) => {
47
+ console.log(`${name} has been idle for ${idleSecs}s`);
667
48
  };
668
- ```
669
-
670
- #### Error Handling
671
-
672
- ```typescript
673
- client.onError = (error) => {
674
- console.error('Client error:', error.message);
675
- };
676
- ```
677
-
678
- #### Manual Connection Control
679
-
680
- ```typescript
681
- // Disconnect gracefully
682
- client.disconnect();
683
-
684
- // Permanently destroy (prevents reconnection)
685
- client.destroy();
686
-
687
- // Check current state
688
- console.log(client.state); // 'READY', 'DISCONNECTED', etc.
689
- console.log(client.agentName);
690
- console.log(client.currentSessionId);
691
- ```
692
49
 
693
- ## Standalone vs External Daemon
694
-
695
- | Feature | Standalone | External Daemon |
696
- |---------|------------|-----------------|
697
- | Setup required | None | Run `agent-relay up` |
698
- | Consensus | No | Yes |
699
- | Cloud sync | No | Yes |
700
- | Dashboard | No | Yes |
701
- | Best for | Testing, simple scripts | Production, multi-machine |
702
-
703
- ## Configuration Reference
704
-
705
- ### RelayClient Options
706
-
707
- ```typescript
708
- const client = new RelayClient({
709
- // Required
710
- agentName: 'MyAgent',
711
-
712
- // Optional
713
- socketPath: '/tmp/agent-relay.sock', // default
714
- entityType: 'agent', // 'agent' or 'user'
715
- cli: 'claude', // CLI identifier
716
- program: 'my-app', // program identifier
717
- model: 'claude-3', // model identifier
718
- task: 'My task', // task description
719
- workingDirectory: '/path/to/work',
720
- displayName: 'Alice', // for human users
721
- avatarUrl: 'https://...',
722
- quiet: false, // suppress console logs
723
- reconnect: true, // auto-reconnect
724
- maxReconnectAttempts: 10,
725
- reconnectDelayMs: 1000,
726
- reconnectMaxDelayMs: 30000,
50
+ const agent = await relay.spawnPty({
51
+ name: "Worker1",
52
+ cli: "claude",
53
+ channels: ["general"],
54
+ idleThresholdSecs: 30, // emit agent_idle after 30s of silence (default), 0 to disable
727
55
  });
728
- ```
729
56
 
730
- ### createRelay Options
57
+ // Wait for the agent to go idle (e.g. after finishing its task)
58
+ const result = await agent.waitForIdle(120_000); // 2 min timeout
59
+ if (result === "idle") {
60
+ console.log("Agent finished work");
61
+ } else if (result === "exited") {
62
+ console.log("Agent exited");
63
+ } else {
64
+ console.log("Timed out waiting for idle");
65
+ }
731
66
 
732
- ```typescript
733
- const relay = await createRelay({
734
- socketPath: '/tmp/my-relay.sock', // optional
735
- quiet: true, // suppress logs (default: true)
736
- });
67
+ await relay.shutdown();
737
68
  ```
738
69
 
739
- ## TypeScript Types
740
-
741
- All types are exported for TypeScript users:
742
-
743
- ```typescript
744
- import type {
745
- // Client
746
- ClientState,
747
- ClientConfig,
748
- SyncOptions,
749
- RequestOptions,
750
- RequestResponse,
751
-
752
- // Messages
753
- SendPayload,
754
- SendMeta,
755
- PayloadKind,
756
- AckPayload,
757
-
758
- // Channels
759
- ChannelMessagePayload,
760
- MessageAttachment,
761
-
762
- // Spawning
763
- SpawnPayload,
764
- SpawnResult,
765
- SpawnResultPayload,
766
- ReleaseResultPayload,
767
- AgentReadyPayload,
768
-
769
- // Worker Management
770
- SendInputPayload,
771
- SendInputResultPayload,
772
- ListWorkersPayload,
773
- ListWorkersResultPayload,
774
-
775
- // Monitoring
776
- AgentInfo,
777
- AgentMetrics,
778
- HealthResponsePayload,
779
- MetricsResponsePayload,
780
- StatusResponsePayload,
781
- InboxMessage,
782
- MessagesResponsePayload,
783
- RemoveAgentResponsePayload,
784
-
785
- // Consensus
786
- ConsensusType,
787
- VoteValue,
788
- CreateProposalOptions,
789
- VoteOptions,
790
- } from '@agent-relay/sdk';
70
+ ## Tic-tac-toe demo script
71
+ After build, run:
72
+ ```bash
73
+ npm --prefix packages/sdk run example
791
74
  ```
792
75
 
793
- ## Building Swarms
76
+ Optional env:
77
+ - `CODEX_CMD` (default: `codex`)
78
+ - `CODEX_ARGS` (space-separated CLI args)
79
+ - `AGENT_X_NAME` / `AGENT_O_NAME`
80
+ - `RELAY_CHANNEL` (default: `general`)
81
+ - `AGENT_RELAY_BIN` (override bundled binary path)
794
82
 
795
- The SDK provides primitives that map directly to swarm capabilities:
796
-
797
- | SDK Primitive | Swarm Capability |
798
- |---------------|------------------|
799
- | `sendMessage()` | **Handoffs** - Transfer tasks between agents |
800
- | `sendAndWait()` | **Synchronous handoffs** - Wait for task completion |
801
- | `getInbox()` + session resume | **Continuity** - Recover state across disconnections |
802
- | `createProposal()` / `vote()` | **Consensus** - Group decision-making |
803
- | `channels` + state payloads | **Shared memory** - Distributed state |
804
- | `listAgents()` | **Discovery** - Find available workers |
805
- | `getMetrics()` / `getHealth()` | **Monitoring** - Auto-scaling decisions |
806
- | `bindAsShadow()` | **Observation** - QA and oversight |
807
- | `spawn()` / `release()` | **Dynamic teams** - Scale workers on demand |
808
- | `sendWorkerInput()` | **Interactive control** - Send input to agent PTYs |
809
- | `listWorkers()` | **Fleet visibility** - Enumerate active workers |
810
-
811
- ### Example: Hierarchical Swarm
812
-
813
- ```typescript
814
- const lead = new RelayClient({ agentName: 'Lead' });
815
- await lead.connect();
816
-
817
- // Spawn specialized workers
818
- for (const role of ['Frontend', 'Backend', 'Tests']) {
819
- await lead.spawn({
820
- name: `${role}Worker`,
821
- cli: 'claude',
822
- task: `You are a ${role} specialist. Wait for tasks from Lead.`,
823
- });
824
- }
825
-
826
- // Delegate work
827
- lead.sendMessage('FrontendWorker', 'Build the login page UI');
828
- lead.sendMessage('BackendWorker', 'Create the /auth API endpoint');
829
- lead.sendMessage('TestsWorker', 'Write integration tests for auth');
830
-
831
- // Collect results
832
- const results = new Map();
833
- lead.onMessage = (from, { body }) => {
834
- results.set(from, body);
835
- if (results.size === 3) console.log('All workers complete!');
836
- };
83
+ ## Integration test
84
+ ```bash
85
+ cargo build
86
+ npm --prefix packages/sdk install
87
+ npm --prefix packages/sdk run build
88
+ AGENT_RELAY_BIN="$(pwd)/target/debug/agent-relay-broker" npm --prefix packages/sdk run test:integration
837
89
  ```
838
90
 
839
- ### Example: Consensus Decision
91
+ Integration tests require Relaycast credentials in environment (`RELAY_API_KEY`).
840
92
 
841
- ```typescript
842
- // Create a proposal for group decision
843
- client.createProposal({
844
- title: 'API Design Choice',
845
- description: 'Should we use GraphQL or REST?',
846
- participants: ['Architect', 'FrontendLead', 'BackendLead'],
847
- consensusType: 'majority',
848
- timeoutMs: 300000,
849
- });
850
-
851
- // Participants vote
852
- client.vote({
853
- proposalId: 'prop_123',
854
- value: 'approve',
855
- reason: 'GraphQL fits our needs better',
856
- });
93
+ ## Package tarball
94
+ ```bash
95
+ npm --prefix packages/sdk pack
857
96
  ```
858
-
859
- ### Learn More
860
-
861
- - **[Swarm Patterns](./examples/SWARM_PATTERNS.md)** - 8 detailed patterns with code
862
- - **[Swarm Capabilities](./examples/SWARM_CAPABILITIES.md)** - How primitives enable swarm features
863
- - **[AgentSwarm](https://github.com/AgentWorkforce/agentswarm)** - Production orchestrator built on Agent Relay
864
-
865
- ## License
866
-
867
- MIT
97
+ The generated tarball includes `dist/` and `bin/agent-relay-broker` (or `bin/agent-relay-broker.exe` on Windows).