digital-workers 2.1.1 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +136 -180
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +34 -21
  5. package/dist/actions.js.map +1 -1
  6. package/dist/agent-comms.d.ts +438 -0
  7. package/dist/agent-comms.d.ts.map +1 -0
  8. package/dist/agent-comms.js +677 -0
  9. package/dist/agent-comms.js.map +1 -0
  10. package/dist/approve.d.ts +40 -8
  11. package/dist/approve.d.ts.map +1 -1
  12. package/dist/approve.js +86 -20
  13. package/dist/approve.js.map +1 -1
  14. package/dist/ask.d.ts +38 -7
  15. package/dist/ask.d.ts.map +1 -1
  16. package/dist/ask.js +85 -25
  17. package/dist/ask.js.map +1 -1
  18. package/dist/browse.d.ts +223 -0
  19. package/dist/browse.d.ts.map +1 -0
  20. package/dist/browse.js +392 -0
  21. package/dist/browse.js.map +1 -0
  22. package/dist/capability-tiers.d.ts +230 -0
  23. package/dist/capability-tiers.d.ts.map +1 -0
  24. package/dist/capability-tiers.js +388 -0
  25. package/dist/capability-tiers.js.map +1 -0
  26. package/dist/cascade-context.d.ts +523 -0
  27. package/dist/cascade-context.d.ts.map +1 -0
  28. package/dist/cascade-context.js +494 -0
  29. package/dist/cascade-context.js.map +1 -0
  30. package/dist/client.d.ts +162 -0
  31. package/dist/client.d.ts.map +1 -0
  32. package/dist/client.js +64 -0
  33. package/dist/client.js.map +1 -0
  34. package/dist/decide.d.ts +42 -6
  35. package/dist/decide.d.ts.map +1 -1
  36. package/dist/decide.js +54 -11
  37. package/dist/decide.js.map +1 -1
  38. package/dist/do.d.ts +36 -7
  39. package/dist/do.d.ts.map +1 -1
  40. package/dist/do.js +82 -39
  41. package/dist/do.js.map +1 -1
  42. package/dist/error-escalation.d.ts +416 -0
  43. package/dist/error-escalation.d.ts.map +1 -0
  44. package/dist/error-escalation.js +656 -0
  45. package/dist/error-escalation.js.map +1 -0
  46. package/dist/generate.d.ts +48 -7
  47. package/dist/generate.d.ts.map +1 -1
  48. package/dist/generate.js +49 -8
  49. package/dist/generate.js.map +1 -1
  50. package/dist/goals.d.ts +10 -9
  51. package/dist/goals.d.ts.map +1 -1
  52. package/dist/goals.js +30 -24
  53. package/dist/goals.js.map +1 -1
  54. package/dist/image.d.ts +189 -0
  55. package/dist/image.d.ts.map +1 -0
  56. package/dist/image.js +528 -0
  57. package/dist/image.js.map +1 -0
  58. package/dist/index.d.ts +59 -2
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +92 -2
  61. package/dist/index.js.map +1 -1
  62. package/dist/is.d.ts +45 -10
  63. package/dist/is.d.ts.map +1 -1
  64. package/dist/is.js +56 -21
  65. package/dist/is.js.map +1 -1
  66. package/dist/kpis.d.ts +24 -15
  67. package/dist/kpis.d.ts.map +1 -1
  68. package/dist/kpis.js +16 -14
  69. package/dist/kpis.js.map +1 -1
  70. package/dist/load-balancing.d.ts +395 -0
  71. package/dist/load-balancing.d.ts.map +1 -0
  72. package/dist/load-balancing.js +991 -0
  73. package/dist/load-balancing.js.map +1 -0
  74. package/dist/logger.d.ts +76 -0
  75. package/dist/logger.d.ts.map +1 -0
  76. package/dist/logger.js +39 -0
  77. package/dist/logger.js.map +1 -0
  78. package/dist/notify.d.ts +38 -9
  79. package/dist/notify.d.ts.map +1 -1
  80. package/dist/notify.js +72 -17
  81. package/dist/notify.js.map +1 -1
  82. package/dist/role.d.ts +5 -4
  83. package/dist/role.d.ts.map +1 -1
  84. package/dist/role.js +13 -10
  85. package/dist/role.js.map +1 -1
  86. package/dist/runtime.d.ts +310 -0
  87. package/dist/runtime.d.ts.map +1 -0
  88. package/dist/runtime.js +510 -0
  89. package/dist/runtime.js.map +1 -0
  90. package/dist/team.d.ts +11 -6
  91. package/dist/team.d.ts.map +1 -1
  92. package/dist/team.js +22 -15
  93. package/dist/team.js.map +1 -1
  94. package/dist/transports/email.d.ts +318 -0
  95. package/dist/transports/email.d.ts.map +1 -0
  96. package/dist/transports/email.js +779 -0
  97. package/dist/transports/email.js.map +1 -0
  98. package/dist/transports/slack.d.ts +515 -0
  99. package/dist/transports/slack.d.ts.map +1 -0
  100. package/dist/transports/slack.js +844 -0
  101. package/dist/transports/slack.js.map +1 -0
  102. package/dist/transports.d.ts.map +1 -1
  103. package/dist/transports.js +44 -25
  104. package/dist/transports.js.map +1 -1
  105. package/dist/types.d.ts +149 -19
  106. package/dist/types.d.ts.map +1 -1
  107. package/dist/types.js +6 -0
  108. package/dist/types.js.map +1 -1
  109. package/dist/utils/id.d.ts +19 -0
  110. package/dist/utils/id.d.ts.map +1 -0
  111. package/dist/utils/id.js +21 -0
  112. package/dist/utils/id.js.map +1 -0
  113. package/dist/video.d.ts +203 -0
  114. package/dist/video.d.ts.map +1 -0
  115. package/dist/video.js +528 -0
  116. package/dist/video.js.map +1 -0
  117. package/dist/worker.d.ts +343 -0
  118. package/dist/worker.d.ts.map +1 -0
  119. package/dist/worker.js +698 -0
  120. package/dist/worker.js.map +1 -0
  121. package/package.json +24 -5
  122. package/src/actions.ts +48 -38
  123. package/src/agent-comms.ts +1200 -0
  124. package/src/approve.ts +91 -20
  125. package/src/ask.ts +99 -25
  126. package/src/browse.ts +627 -0
  127. package/src/capability-tiers.ts +545 -0
  128. package/src/cascade-context.ts +648 -0
  129. package/src/client.ts +221 -0
  130. package/src/decide.ts +81 -35
  131. package/src/do.ts +98 -52
  132. package/src/error-escalation.ts +1123 -0
  133. package/src/generate.ts +52 -18
  134. package/src/goals.ts +36 -27
  135. package/src/image.ts +816 -0
  136. package/src/index.ts +410 -2
  137. package/src/is.ts +59 -25
  138. package/src/kpis.ts +41 -36
  139. package/src/load-balancing.ts +1467 -0
  140. package/src/logger.ts +93 -0
  141. package/src/notify.ts +78 -17
  142. package/src/role.ts +30 -20
  143. package/src/runtime.ts +796 -0
  144. package/src/team.ts +24 -19
  145. package/src/transports/email.ts +1160 -0
  146. package/src/transports/slack.ts +1320 -0
  147. package/src/transports.ts +58 -43
  148. package/src/types.ts +182 -46
  149. package/src/utils/id.ts +21 -0
  150. package/src/video.ts +906 -0
  151. package/src/worker.ts +1007 -0
  152. package/test/agent-comms.test.ts +1397 -0
  153. package/test/approve.test.ts +305 -0
  154. package/test/ask.test.ts +274 -0
  155. package/test/browse.test.ts +361 -0
  156. package/test/capability-tiers.test.ts +631 -0
  157. package/test/cascade-context.test.ts +692 -0
  158. package/test/decide.test.ts +252 -0
  159. package/test/do.test.ts +144 -0
  160. package/test/error-escalation.test.ts +1205 -0
  161. package/test/error-logging.test.ts +357 -0
  162. package/test/generate.test.ts +319 -0
  163. package/test/image.test.ts +398 -0
  164. package/test/is.test.ts +287 -0
  165. package/test/load-balancing-safety.test.ts +404 -0
  166. package/test/load-balancing-thread-safety.test.ts +464 -0
  167. package/test/load-balancing.test.ts +1145 -0
  168. package/test/notify.test.ts +434 -0
  169. package/test/primitives.test.ts +320 -0
  170. package/test/runtime-integration.test.ts +892 -0
  171. package/test/transports/crypto.test.ts +230 -0
  172. package/test/transports/email.test.ts +866 -0
  173. package/test/transports/id-generation.test.ts +91 -0
  174. package/test/transports/slack.test.ts +760 -0
  175. package/test/type-safety.test.ts +834 -0
  176. package/test/types.test.ts +95 -2
  177. package/test/video.test.ts +530 -0
  178. package/test/worker.test.ts +1433 -0
  179. package/tsconfig.json +4 -1
  180. package/vitest.config.ts +42 -0
  181. package/wrangler.jsonc +36 -0
  182. package/.turbo/turbo-build.log +0 -5
  183. package/src/actions.js +0 -436
  184. package/src/approve.js +0 -234
  185. package/src/ask.js +0 -226
  186. package/src/decide.js +0 -244
  187. package/src/do.js +0 -227
  188. package/src/generate.js +0 -298
  189. package/src/goals.js +0 -205
  190. package/src/index.js +0 -68
  191. package/src/is.js +0 -317
  192. package/src/kpis.js +0 -270
  193. package/src/notify.js +0 -219
  194. package/src/role.js +0 -110
  195. package/src/team.js +0 -130
  196. package/src/transports.js +0 -357
  197. package/src/types.js +0 -71
@@ -0,0 +1,677 @@
1
+ /**
2
+ * Agent-to-Agent Communication Layer
3
+ *
4
+ * Provides structured messaging, coordination patterns, and handoff protocols
5
+ * for communication between agents in the digital-workers system.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ // =============================================================================
10
+ // AgentMessageBus Implementation
11
+ // =============================================================================
12
+ /**
13
+ * Agent message bus for routing messages between agents
14
+ */
15
+ export class AgentMessageBus {
16
+ subscriptions = new Map();
17
+ pendingAcks = new Map();
18
+ messageStatus = new Map();
19
+ handoffs = new Map();
20
+ storedMessages = [];
21
+ messageQueue = new Map();
22
+ processingAgent = new Set();
23
+ disposed = false;
24
+ options;
25
+ constructor(options = {}) {
26
+ this.options = {
27
+ persistence: options.persistence ?? false,
28
+ defaultTtl: options.defaultTtl ?? 30000,
29
+ maxQueueSize: options.maxQueueSize ?? 1000,
30
+ };
31
+ }
32
+ /**
33
+ * Send a message to an agent
34
+ */
35
+ async send(message) {
36
+ if (this.disposed) {
37
+ return this.createFailedEnvelope(message, 'Message bus disposed');
38
+ }
39
+ const envelope = {
40
+ message,
41
+ deliveryAttempts: 1,
42
+ firstAttemptAt: new Date(),
43
+ lastAttemptAt: new Date(),
44
+ status: 'pending',
45
+ };
46
+ // Store message if persistence is enabled
47
+ if (this.options.persistence) {
48
+ this.storedMessages.push({ envelope: envelope, storedAt: new Date() });
49
+ }
50
+ // Get subscriptions for recipient
51
+ const subs = this.subscriptions.get(message.recipient);
52
+ if (!subs || subs.length === 0) {
53
+ envelope.status = 'failed';
54
+ envelope.error = `Agent '${message.recipient}' not found`;
55
+ this.messageStatus.set(message.id, 'failed');
56
+ return envelope;
57
+ }
58
+ // Track pending acknowledgment
59
+ if (message.type === 'request') {
60
+ const senderAcks = this.pendingAcks.get(message.sender) ?? new Set();
61
+ senderAcks.add(message.id);
62
+ this.pendingAcks.set(message.sender, senderAcks);
63
+ }
64
+ // Setup TTL expiration
65
+ if (message.ttl) {
66
+ setTimeout(() => {
67
+ if (this.messageStatus.get(message.id) !== 'acknowledged') {
68
+ this.messageStatus.set(message.id, 'expired');
69
+ }
70
+ }, message.ttl);
71
+ }
72
+ // Deliver to matching subscribers
73
+ try {
74
+ await this.deliverMessage(message, subs);
75
+ envelope.status = 'delivered';
76
+ this.messageStatus.set(message.id, 'delivered');
77
+ }
78
+ catch (error) {
79
+ envelope.status = 'failed';
80
+ envelope.error = error instanceof Error ? error.message : String(error);
81
+ this.messageStatus.set(message.id, 'failed');
82
+ }
83
+ return envelope;
84
+ }
85
+ /**
86
+ * Deliver message to subscribers with queue handling
87
+ */
88
+ async deliverMessage(message, subs) {
89
+ const matchingSubs = subs.filter((sub) => this.matchesFilter(message, sub.options));
90
+ if (matchingSubs.length === 0) {
91
+ return;
92
+ }
93
+ // Queue messages if agent is busy
94
+ if (this.processingAgent.has(message.recipient)) {
95
+ const queue = this.messageQueue.get(message.recipient) ?? [];
96
+ queue.push(message);
97
+ this.messageQueue.set(message.recipient, queue);
98
+ return;
99
+ }
100
+ this.processingAgent.add(message.recipient);
101
+ try {
102
+ await Promise.all(matchingSubs.map((sub) => sub.handler(message)));
103
+ }
104
+ finally {
105
+ this.processingAgent.delete(message.recipient);
106
+ await this.processQueue(message.recipient, subs);
107
+ }
108
+ }
109
+ /**
110
+ * Process queued messages
111
+ */
112
+ async processQueue(agentId, subs) {
113
+ const queue = this.messageQueue.get(agentId);
114
+ if (!queue || queue.length === 0)
115
+ return;
116
+ const nextMessage = queue.shift();
117
+ this.messageQueue.set(agentId, queue);
118
+ await this.deliverMessage(nextMessage, subs);
119
+ }
120
+ /**
121
+ * Check if message matches subscription filter
122
+ */
123
+ matchesFilter(message, options) {
124
+ if (!options)
125
+ return true;
126
+ if (options.types && !options.types.includes(message.type))
127
+ return false;
128
+ if (options.from && message.sender !== options.from)
129
+ return false;
130
+ return true;
131
+ }
132
+ /**
133
+ * Subscribe to messages for an agent
134
+ */
135
+ subscribe(agentId, handler, options) {
136
+ const subs = this.subscriptions.get(agentId) ?? [];
137
+ const subscription = { handler, ...(options !== undefined && { options }) };
138
+ subs.push(subscription);
139
+ this.subscriptions.set(agentId, subs);
140
+ return () => {
141
+ const currentSubs = this.subscriptions.get(agentId) ?? [];
142
+ const index = currentSubs.indexOf(subscription);
143
+ if (index !== -1) {
144
+ currentSubs.splice(index, 1);
145
+ this.subscriptions.set(agentId, currentSubs);
146
+ }
147
+ };
148
+ }
149
+ /**
150
+ * Acknowledge a message
151
+ */
152
+ async acknowledge(messageId, agentId, status) {
153
+ this.messageStatus.set(messageId, 'acknowledged');
154
+ // Remove from pending acks
155
+ for (const entry of Array.from(this.pendingAcks.entries())) {
156
+ const [sender, acks] = entry;
157
+ if (acks.has(messageId)) {
158
+ acks.delete(messageId);
159
+ this.pendingAcks.set(sender, acks);
160
+ break;
161
+ }
162
+ }
163
+ }
164
+ /**
165
+ * Get pending acknowledgments for a sender
166
+ */
167
+ getPendingAcks(senderId) {
168
+ const acks = this.pendingAcks.get(senderId);
169
+ return acks ? Array.from(acks) : [];
170
+ }
171
+ /**
172
+ * Get message delivery status
173
+ */
174
+ getMessageStatus(messageId) {
175
+ return this.messageStatus.get(messageId);
176
+ }
177
+ /**
178
+ * Get handoff status
179
+ */
180
+ getHandoffStatus(handoffId) {
181
+ return this.handoffs.get(handoffId)?.status;
182
+ }
183
+ /**
184
+ * Get handoff history (previous attempts)
185
+ */
186
+ getHandoffHistory(handoffId) {
187
+ return this.handoffs.get(handoffId)?.previousAttempts ?? [];
188
+ }
189
+ /**
190
+ * Register a handoff
191
+ */
192
+ registerHandoff(request, originalMessage) {
193
+ const state = {
194
+ request,
195
+ status: 'pending',
196
+ previousAttempts: request.previousAttempt ? [request.previousAttempt] : [],
197
+ ...(originalMessage !== undefined && { originalMessage }),
198
+ };
199
+ // Setup timeout if specified
200
+ if (request.timeout) {
201
+ state.timeoutId = setTimeout(() => {
202
+ const currentState = this.handoffs.get(request.id);
203
+ if (currentState && currentState.status === 'pending') {
204
+ currentState.status = 'expired';
205
+ this.handoffs.set(request.id, currentState);
206
+ // Notify initiator of timeout
207
+ if (request.onTimeout) {
208
+ const timeoutMsg = {
209
+ id: `timeout_${request.id}`,
210
+ type: 'handoff',
211
+ sender: 'system',
212
+ recipient: request.fromAgent,
213
+ payload: { action: 'timeout', handoffId: request.id },
214
+ timestamp: new Date(),
215
+ };
216
+ request.onTimeout(timeoutMsg);
217
+ }
218
+ }
219
+ }, request.timeout);
220
+ }
221
+ this.handoffs.set(request.id, state);
222
+ }
223
+ /**
224
+ * Get handoff request info
225
+ */
226
+ getHandoffRequest(handoffId) {
227
+ return this.handoffs.get(handoffId)?.request;
228
+ }
229
+ /**
230
+ * Update handoff status
231
+ */
232
+ updateHandoffStatus(handoffId, status) {
233
+ const state = this.handoffs.get(handoffId);
234
+ if (state) {
235
+ // Clear timeout if pending timeout exists
236
+ if (state.timeoutId) {
237
+ clearTimeout(state.timeoutId);
238
+ }
239
+ state.status = status;
240
+ this.handoffs.set(handoffId, state);
241
+ }
242
+ }
243
+ /**
244
+ * Get stored messages (for persistence)
245
+ */
246
+ getStoredMessages() {
247
+ return this.storedMessages.map((s) => s.envelope);
248
+ }
249
+ /**
250
+ * Get message history for an agent
251
+ */
252
+ getMessageHistory(agentId, options) {
253
+ let messages = this.storedMessages
254
+ .filter((s) => s.envelope.message.recipient === agentId || s.envelope.message.sender === agentId)
255
+ .map((s) => s.envelope);
256
+ if (options?.from) {
257
+ messages = messages.filter((m) => m.message.timestamp >= options.from);
258
+ }
259
+ if (options?.to) {
260
+ messages = messages.filter((m) => m.message.timestamp <= options.to);
261
+ }
262
+ if (options?.limit) {
263
+ messages = messages.slice(-options.limit);
264
+ }
265
+ return messages;
266
+ }
267
+ /**
268
+ * Clear old messages
269
+ */
270
+ clearMessages(options) {
271
+ if (options?.olderThan) {
272
+ this.storedMessages = this.storedMessages.filter((s) => s.storedAt >= options.olderThan);
273
+ }
274
+ else {
275
+ this.storedMessages = [];
276
+ }
277
+ }
278
+ /**
279
+ * Dispose the message bus
280
+ */
281
+ dispose() {
282
+ this.disposed = true;
283
+ this.subscriptions.clear();
284
+ this.pendingAcks.clear();
285
+ this.messageStatus.clear();
286
+ this.messageQueue.clear();
287
+ this.processingAgent.clear();
288
+ // Clear all handoff timeouts
289
+ for (const state of Array.from(this.handoffs.values())) {
290
+ if (state.timeoutId) {
291
+ clearTimeout(state.timeoutId);
292
+ }
293
+ }
294
+ this.handoffs.clear();
295
+ }
296
+ /**
297
+ * Create a failed envelope
298
+ */
299
+ createFailedEnvelope(message, error) {
300
+ return {
301
+ message,
302
+ deliveryAttempts: 0,
303
+ firstAttemptAt: new Date(),
304
+ lastAttemptAt: new Date(),
305
+ status: 'failed',
306
+ error,
307
+ };
308
+ }
309
+ }
310
+ // =============================================================================
311
+ // Factory Function
312
+ // =============================================================================
313
+ /**
314
+ * Create a new message bus instance
315
+ */
316
+ export function createMessageBus(options) {
317
+ return new AgentMessageBus(options);
318
+ }
319
+ // =============================================================================
320
+ // Helper Functions
321
+ // =============================================================================
322
+ /**
323
+ * Generate a unique message ID
324
+ */
325
+ function generateMessageId() {
326
+ return `msg_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
327
+ }
328
+ /**
329
+ * Generate a unique handoff ID
330
+ */
331
+ function generateHandoffId() {
332
+ return `handoff_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
333
+ }
334
+ /**
335
+ * Generate a unique correlation ID
336
+ */
337
+ function generateCorrelationId() {
338
+ return `corr_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
339
+ }
340
+ /**
341
+ * Resolve agent ID from Worker, WorkerRef, or string
342
+ */
343
+ function resolveAgentId(agent) {
344
+ if (typeof agent === 'string')
345
+ return agent;
346
+ return agent.id;
347
+ }
348
+ /**
349
+ * Send a message to a specific agent
350
+ */
351
+ export async function sendToAgent(bus, sender, recipient, payload, options) {
352
+ const message = {
353
+ id: generateMessageId(),
354
+ type: 'notification',
355
+ sender: resolveAgentId(sender),
356
+ recipient: resolveAgentId(recipient),
357
+ payload,
358
+ timestamp: new Date(),
359
+ ...(options?.priority !== undefined && { priority: options.priority }),
360
+ ...(options?.ttl !== undefined && { ttl: options.ttl }),
361
+ ...(options?.correlationId !== undefined && { correlationId: options.correlationId }),
362
+ ...(options?.metadata !== undefined && { metadata: options.metadata }),
363
+ };
364
+ return bus.send(message);
365
+ }
366
+ /**
367
+ * Broadcast a message to multiple agents
368
+ */
369
+ export async function broadcastToGroup(bus, sender, recipients, payload, options) {
370
+ const correlationId = options?.correlationId ?? generateCorrelationId();
371
+ const results = await Promise.all(recipients.map((recipient) => sendToAgent(bus, sender, recipient, payload, {
372
+ ...options,
373
+ correlationId,
374
+ })));
375
+ return results;
376
+ }
377
+ /**
378
+ * Send a request to an agent and await response
379
+ */
380
+ export async function requestFromAgent(bus, sender, recipient, payload, options) {
381
+ const senderId = resolveAgentId(sender);
382
+ const recipientId = resolveAgentId(recipient);
383
+ const messageId = generateMessageId();
384
+ const timeout = options?.timeout ?? 30000;
385
+ return new Promise((resolve, reject) => {
386
+ let timeoutId;
387
+ let unsubscribe;
388
+ // Setup response handler
389
+ unsubscribe = bus.subscribe(senderId, (response) => {
390
+ if (response.correlationId === messageId) {
391
+ clearTimeout(timeoutId);
392
+ unsubscribe?.();
393
+ if (response.type === 'error') {
394
+ const errorPayload = response.payload;
395
+ reject(new Error(errorPayload.error ?? 'Request failed'));
396
+ }
397
+ else {
398
+ resolve(response);
399
+ }
400
+ }
401
+ });
402
+ // Setup timeout
403
+ timeoutId = setTimeout(() => {
404
+ unsubscribe?.();
405
+ reject(new Error('Request timeout'));
406
+ }, timeout);
407
+ // Send request
408
+ const message = {
409
+ id: messageId,
410
+ type: 'request',
411
+ sender: senderId,
412
+ recipient: recipientId,
413
+ payload,
414
+ timestamp: new Date(),
415
+ correlationId: messageId,
416
+ replyTo: senderId,
417
+ ...(options?.priority !== undefined && { priority: options.priority }),
418
+ ...(options?.ttl !== undefined && { ttl: options.ttl }),
419
+ ...(options?.metadata !== undefined && { metadata: options.metadata }),
420
+ };
421
+ bus
422
+ .send(message)
423
+ .then((envelope) => {
424
+ // Fail fast if delivery failed (recipient not found, etc.)
425
+ if (envelope.status === 'failed') {
426
+ clearTimeout(timeoutId);
427
+ unsubscribe?.();
428
+ reject(new Error(envelope.error ?? 'Message delivery failed'));
429
+ }
430
+ })
431
+ .catch((error) => {
432
+ clearTimeout(timeoutId);
433
+ unsubscribe?.();
434
+ reject(error);
435
+ });
436
+ });
437
+ }
438
+ /**
439
+ * Register a message handler for an agent
440
+ */
441
+ export function onMessage(bus, agentId, handler, options) {
442
+ return bus.subscribe(agentId, handler, {
443
+ ...(options?.from !== undefined && { from: options.from }),
444
+ ...(options?.types !== undefined && { types: options.types }),
445
+ });
446
+ }
447
+ /**
448
+ * Send an acknowledgment for a received message
449
+ */
450
+ export async function acknowledge(bus, message, status, result) {
451
+ // Send ack message to original sender
452
+ const ackMessage = {
453
+ id: generateMessageId(),
454
+ type: 'ack',
455
+ sender: message.recipient,
456
+ recipient: message.sender,
457
+ payload: {
458
+ messageId: message.id,
459
+ status,
460
+ timestamp: new Date(),
461
+ agentId: message.recipient,
462
+ result,
463
+ },
464
+ timestamp: new Date(),
465
+ correlationId: message.id,
466
+ };
467
+ await bus.send(ackMessage);
468
+ await bus.acknowledge(message.id, message.recipient, status);
469
+ }
470
+ /**
471
+ * Execute request-response pattern
472
+ */
473
+ export async function requestResponse(bus, options) {
474
+ return requestFromAgent(bus, options.from, options.to, options.payload, {
475
+ ...(options.timeout !== undefined && { timeout: options.timeout }),
476
+ ...(options.priority !== undefined && { priority: options.priority }),
477
+ });
478
+ }
479
+ /**
480
+ * Execute fan-out pattern - distribute work to multiple agents
481
+ */
482
+ export async function fanOut(bus, options) {
483
+ const correlationId = generateCorrelationId();
484
+ const timeout = options.timeout ?? 30000;
485
+ const fromId = resolveAgentId(options.from);
486
+ const results = await Promise.all(options.to.map(async (agent) => {
487
+ const agentId = resolveAgentId(agent);
488
+ try {
489
+ const response = await requestFromAgent(bus, fromId, agentId, options.payload, {
490
+ timeout,
491
+ correlationId,
492
+ });
493
+ return {
494
+ agentId,
495
+ success: true,
496
+ payload: response.payload,
497
+ };
498
+ }
499
+ catch (error) {
500
+ if (!options.continueOnError) {
501
+ throw error;
502
+ }
503
+ return {
504
+ agentId,
505
+ success: false,
506
+ error: error instanceof Error ? error.message : String(error),
507
+ };
508
+ }
509
+ }));
510
+ return results;
511
+ }
512
+ /**
513
+ * Execute fan-in pattern - collect responses from multiple agents
514
+ */
515
+ export async function fanIn(bus, options) {
516
+ const results = await Promise.all(options.sources.map((source) => {
517
+ const sourceId = resolveAgentId(source);
518
+ return options.onSourceMessage(sourceId);
519
+ }));
520
+ return results;
521
+ }
522
+ /**
523
+ * Execute pipeline pattern - chain agents in sequence
524
+ */
525
+ export async function pipeline(bus, options) {
526
+ const initiatorId = resolveAgentId(options.initiator);
527
+ let currentPayload = options.input;
528
+ let lastResponse;
529
+ for (const stage of options.stages) {
530
+ const stageId = resolveAgentId(stage);
531
+ lastResponse = await requestFromAgent(bus, initiatorId, stageId, currentPayload, {
532
+ ...(options.stageTimeout !== undefined && { timeout: options.stageTimeout }),
533
+ });
534
+ currentPayload = lastResponse.payload;
535
+ }
536
+ return lastResponse;
537
+ }
538
+ /**
539
+ * Initiate a handoff to another agent
540
+ */
541
+ export async function initiateHandoff(bus, options) {
542
+ const handoffId = generateHandoffId();
543
+ const fromAgentId = resolveAgentId(options.fromAgent);
544
+ const toAgentId = resolveAgentId(options.toAgent);
545
+ const request = {
546
+ id: handoffId,
547
+ fromAgent: fromAgentId,
548
+ toAgent: toAgentId,
549
+ context: options.context,
550
+ timestamp: new Date(),
551
+ ...(options.reason !== undefined && { reason: options.reason }),
552
+ ...(options.timeout !== undefined && { timeout: options.timeout }),
553
+ ...(options.previousAttempt !== undefined && { previousAttempt: options.previousAttempt }),
554
+ ...(options.onTimeout !== undefined && { onTimeout: options.onTimeout }),
555
+ };
556
+ // Register handoff in bus
557
+ bus.registerHandoff(request);
558
+ // Send handoff request message
559
+ const message = {
560
+ id: generateMessageId(),
561
+ type: 'handoff',
562
+ sender: fromAgentId,
563
+ recipient: toAgentId,
564
+ payload: {
565
+ action: 'request',
566
+ handoffId,
567
+ context: options.context,
568
+ reason: options.reason,
569
+ },
570
+ timestamp: new Date(),
571
+ correlationId: handoffId,
572
+ };
573
+ await bus.send(message);
574
+ return { handoffId, status: 'pending' };
575
+ }
576
+ /**
577
+ * Accept a pending handoff
578
+ */
579
+ export async function acceptHandoff(bus, handoffId, agentId) {
580
+ const status = bus.getHandoffStatus(handoffId);
581
+ if (!status) {
582
+ throw new Error('Handoff not found');
583
+ }
584
+ if (status !== 'pending') {
585
+ throw new Error(`Cannot accept handoff in '${status}' state`);
586
+ }
587
+ bus.updateHandoffStatus(handoffId, 'accepted');
588
+ // Get the handoff request to find the initiating agent
589
+ const request = bus.getHandoffRequest(handoffId);
590
+ if (request) {
591
+ const acceptMessage = {
592
+ id: generateMessageId(),
593
+ type: 'handoff',
594
+ sender: agentId,
595
+ recipient: request.fromAgent,
596
+ payload: {
597
+ action: 'accepted',
598
+ handoffId,
599
+ },
600
+ timestamp: new Date(),
601
+ correlationId: handoffId,
602
+ };
603
+ await bus.send(acceptMessage);
604
+ }
605
+ return { handoffId, status: 'accepted' };
606
+ }
607
+ /**
608
+ * Reject a pending handoff
609
+ */
610
+ export async function rejectHandoff(bus, handoffId, agentId, options) {
611
+ const status = bus.getHandoffStatus(handoffId);
612
+ if (!status) {
613
+ throw new Error('Handoff not found');
614
+ }
615
+ bus.updateHandoffStatus(handoffId, 'rejected');
616
+ // Get the handoff request to find the initiating agent
617
+ const request = bus.getHandoffRequest(handoffId);
618
+ if (request) {
619
+ const rejectMessage = {
620
+ id: generateMessageId(),
621
+ type: 'handoff',
622
+ sender: agentId,
623
+ recipient: request.fromAgent,
624
+ payload: {
625
+ action: 'rejected',
626
+ handoffId,
627
+ ...(options?.reason !== undefined && { reason: options.reason }),
628
+ },
629
+ timestamp: new Date(),
630
+ correlationId: handoffId,
631
+ };
632
+ await bus.send(rejectMessage);
633
+ }
634
+ return {
635
+ handoffId,
636
+ status: 'rejected',
637
+ ...(options?.reason !== undefined && { reason: options.reason }),
638
+ };
639
+ }
640
+ /**
641
+ * Complete a handoff (mark work as done)
642
+ */
643
+ export async function completeHandoff(bus, handoffId, agentId, options) {
644
+ const status = bus.getHandoffStatus(handoffId);
645
+ if (!status) {
646
+ throw new Error('Handoff not found');
647
+ }
648
+ if (status !== 'accepted') {
649
+ throw new Error('Handoff not accepted');
650
+ }
651
+ bus.updateHandoffStatus(handoffId, 'completed');
652
+ // Get the handoff request to find the initiating agent
653
+ const request = bus.getHandoffRequest(handoffId);
654
+ if (request) {
655
+ const completeMessage = {
656
+ id: generateMessageId(),
657
+ type: 'handoff',
658
+ sender: agentId,
659
+ recipient: request.fromAgent,
660
+ payload: {
661
+ action: 'completed',
662
+ handoffId,
663
+ result: options?.result,
664
+ },
665
+ timestamp: new Date(),
666
+ correlationId: handoffId,
667
+ };
668
+ await bus.send(completeMessage);
669
+ }
670
+ return {
671
+ handoffId,
672
+ status: 'completed',
673
+ result: options?.result,
674
+ completedAt: new Date(),
675
+ };
676
+ }
677
+ //# sourceMappingURL=agent-comms.js.map