digital-workers 2.1.3 → 2.4.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 (183) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +17 -0
  3. package/README.md +2 -0
  4. package/dist/actions.d.ts.map +1 -1
  5. package/dist/actions.js +33 -21
  6. package/dist/actions.js.map +1 -1
  7. package/dist/agent-comms.d.ts.map +1 -1
  8. package/dist/agent-comms.js +36 -25
  9. package/dist/agent-comms.js.map +1 -1
  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.js +3 -3
  23. package/dist/capability-tiers.js.map +1 -1
  24. package/dist/cascade-context.d.ts +28 -28
  25. package/dist/client.d.ts +162 -0
  26. package/dist/client.d.ts.map +1 -0
  27. package/dist/client.js +64 -0
  28. package/dist/client.js.map +1 -0
  29. package/dist/decide.d.ts +42 -6
  30. package/dist/decide.d.ts.map +1 -1
  31. package/dist/decide.js +54 -11
  32. package/dist/decide.js.map +1 -1
  33. package/dist/do.d.ts +36 -7
  34. package/dist/do.d.ts.map +1 -1
  35. package/dist/do.js +82 -39
  36. package/dist/do.js.map +1 -1
  37. package/dist/error-escalation.d.ts.map +1 -1
  38. package/dist/error-escalation.js +38 -38
  39. package/dist/error-escalation.js.map +1 -1
  40. package/dist/generate.d.ts +48 -7
  41. package/dist/generate.d.ts.map +1 -1
  42. package/dist/generate.js +49 -8
  43. package/dist/generate.js.map +1 -1
  44. package/dist/goals.d.ts +10 -9
  45. package/dist/goals.d.ts.map +1 -1
  46. package/dist/goals.js +30 -24
  47. package/dist/goals.js.map +1 -1
  48. package/dist/image.d.ts +189 -0
  49. package/dist/image.d.ts.map +1 -0
  50. package/dist/image.js +528 -0
  51. package/dist/image.js.map +1 -0
  52. package/dist/index.d.ts +49 -2
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +58 -2
  55. package/dist/index.js.map +1 -1
  56. package/dist/is.d.ts +45 -10
  57. package/dist/is.d.ts.map +1 -1
  58. package/dist/is.js +56 -21
  59. package/dist/is.js.map +1 -1
  60. package/dist/kpis.d.ts +24 -15
  61. package/dist/kpis.d.ts.map +1 -1
  62. package/dist/kpis.js +16 -14
  63. package/dist/kpis.js.map +1 -1
  64. package/dist/load-balancing.d.ts.map +1 -1
  65. package/dist/load-balancing.js +124 -38
  66. package/dist/load-balancing.js.map +1 -1
  67. package/dist/logger.d.ts +76 -0
  68. package/dist/logger.d.ts.map +1 -0
  69. package/dist/logger.js +39 -0
  70. package/dist/logger.js.map +1 -0
  71. package/dist/notify.d.ts +38 -9
  72. package/dist/notify.d.ts.map +1 -1
  73. package/dist/notify.js +72 -17
  74. package/dist/notify.js.map +1 -1
  75. package/dist/role.d.ts +5 -4
  76. package/dist/role.d.ts.map +1 -1
  77. package/dist/role.js +13 -10
  78. package/dist/role.js.map +1 -1
  79. package/dist/runtime.d.ts +310 -0
  80. package/dist/runtime.d.ts.map +1 -0
  81. package/dist/runtime.js +510 -0
  82. package/dist/runtime.js.map +1 -0
  83. package/dist/team.d.ts +11 -6
  84. package/dist/team.d.ts.map +1 -1
  85. package/dist/team.js +22 -15
  86. package/dist/team.js.map +1 -1
  87. package/dist/transports/email.d.ts +318 -0
  88. package/dist/transports/email.d.ts.map +1 -0
  89. package/dist/transports/email.js +779 -0
  90. package/dist/transports/email.js.map +1 -0
  91. package/dist/transports/slack.d.ts +515 -0
  92. package/dist/transports/slack.d.ts.map +1 -0
  93. package/dist/transports/slack.js +844 -0
  94. package/dist/transports/slack.js.map +1 -0
  95. package/dist/transports.d.ts.map +1 -1
  96. package/dist/transports.js +44 -25
  97. package/dist/transports.js.map +1 -1
  98. package/dist/types.d.ts +141 -19
  99. package/dist/types.d.ts.map +1 -1
  100. package/dist/types.js +5 -0
  101. package/dist/types.js.map +1 -1
  102. package/dist/utils/id.d.ts +19 -0
  103. package/dist/utils/id.d.ts.map +1 -0
  104. package/dist/utils/id.js +21 -0
  105. package/dist/utils/id.js.map +1 -0
  106. package/dist/video.d.ts +203 -0
  107. package/dist/video.d.ts.map +1 -0
  108. package/dist/video.js +528 -0
  109. package/dist/video.js.map +1 -0
  110. package/dist/worker.d.ts +343 -0
  111. package/dist/worker.d.ts.map +1 -0
  112. package/dist/worker.js +698 -0
  113. package/dist/worker.js.map +1 -0
  114. package/package.json +32 -14
  115. package/src/actions.ts +39 -30
  116. package/src/agent-comms.ts +54 -92
  117. package/src/approve.ts +91 -20
  118. package/src/ask.ts +99 -25
  119. package/src/browse.ts +627 -0
  120. package/src/capability-tiers.ts +5 -5
  121. package/src/client.ts +221 -0
  122. package/src/decide.ts +81 -35
  123. package/src/do.ts +98 -52
  124. package/src/error-escalation.ts +55 -67
  125. package/src/generate.ts +52 -18
  126. package/src/goals.ts +36 -27
  127. package/src/image.ts +816 -0
  128. package/src/index.ts +187 -2
  129. package/src/is.ts +59 -25
  130. package/src/kpis.ts +41 -36
  131. package/src/load-balancing.ts +132 -46
  132. package/src/logger.ts +93 -0
  133. package/src/notify.ts +78 -17
  134. package/src/role.ts +30 -20
  135. package/src/runtime.ts +796 -0
  136. package/src/team.ts +24 -19
  137. package/src/transports/email.ts +1160 -0
  138. package/src/transports/slack.ts +1320 -0
  139. package/src/transports.ts +58 -43
  140. package/src/types.ts +174 -46
  141. package/src/utils/id.ts +21 -0
  142. package/src/video.ts +906 -0
  143. package/src/worker.ts +1007 -0
  144. package/test/approve.test.ts +305 -0
  145. package/test/ask.test.ts +274 -0
  146. package/test/browse.test.ts +361 -0
  147. package/test/decide.test.ts +252 -0
  148. package/test/do.test.ts +144 -0
  149. package/test/error-logging.test.ts +357 -0
  150. package/test/generate.test.ts +319 -0
  151. package/test/image.test.ts +398 -0
  152. package/test/is.test.ts +287 -0
  153. package/test/load-balancing-safety.test.ts +404 -0
  154. package/test/notify.test.ts +434 -0
  155. package/test/primitives.test.ts +320 -0
  156. package/test/runtime-integration.test.ts +892 -0
  157. package/test/transports/crypto.test.ts +230 -0
  158. package/test/transports/email.test.ts +866 -0
  159. package/test/transports/id-generation.test.ts +91 -0
  160. package/test/transports/slack.test.ts +760 -0
  161. package/test/type-safety.test.ts +834 -0
  162. package/test/types.test.ts +60 -2
  163. package/test/video.test.ts +530 -0
  164. package/test/worker.test.ts +1433 -0
  165. package/tsconfig.json +4 -1
  166. package/vitest.config.ts +42 -0
  167. package/wrangler.jsonc +36 -0
  168. package/LICENSE +0 -21
  169. package/src/actions.js +0 -436
  170. package/src/approve.js +0 -234
  171. package/src/ask.js +0 -226
  172. package/src/decide.js +0 -244
  173. package/src/do.js +0 -227
  174. package/src/generate.js +0 -298
  175. package/src/goals.js +0 -205
  176. package/src/index.js +0 -68
  177. package/src/is.js +0 -317
  178. package/src/kpis.js +0 -270
  179. package/src/notify.js +0 -219
  180. package/src/role.js +0 -110
  181. package/src/team.js +0 -130
  182. package/src/transports.js +0 -357
  183. package/src/types.js +0 -71
package/src/approve.ts CHANGED
@@ -1,5 +1,28 @@
1
1
  /**
2
2
  * Approval request functionality for digital workers
3
+ *
4
+ * IMPORTANT: Worker Routing vs LLM Content Generation
5
+ * ---------------------------------------------------
6
+ * This module provides real approval workflows, NOT LLM-generated approval content.
7
+ *
8
+ * - `digital-workers.approve()` - Routes approval requests to Workers (Humans
9
+ * or AI Agents) via communication channels and waits for actual approval.
10
+ *
11
+ * - `ai-functions.approve()` - Generates approval request content via LLM
12
+ * for use in human interaction UIs.
13
+ *
14
+ * Use digital-workers when you need:
15
+ * - Real approval workflows with actual approvers
16
+ * - Routing to specific people via Slack, email, SMS
17
+ * - Approval audit trails (who, when, via what channel)
18
+ * - Multi-approver workflows (any/all must approve)
19
+ *
20
+ * Use ai-functions when you need:
21
+ * - LLM-generated approval request content
22
+ * - Generating UI/UX for approval flows
23
+ * - Approval request templates
24
+ *
25
+ * @module
3
26
  */
4
27
 
5
28
  import type {
@@ -14,32 +37,41 @@ import type {
14
37
  } from './types.js'
15
38
 
16
39
  /**
17
- * Request approval from a worker or team
40
+ * Route an approval request to a Worker (Human or AI Agent) and wait for response.
18
41
  *
19
- * Routes approval requests through the specified channel and waits for a response.
42
+ * **Key Difference from ai-functions.approve():**
43
+ * Unlike `ai-functions.approve()` which generates approval request content
44
+ * using the LLM, this function routes the request to an actual approver via
45
+ * real communication channels (Slack, email, SMS) and waits for their response.
46
+ *
47
+ * This is a **workflow approval primitive**, not a content generation primitive.
20
48
  *
21
49
  * @param request - What is being requested for approval
22
- * @param target - The worker or team to request approval from
23
- * @param options - Approval options
24
- * @returns Promise resolving to approval result
50
+ * @param target - The worker or team to request approval from (routes to their channels)
51
+ * @param options - Approval options including channel, timeout, and context
52
+ * @returns Promise resolving to approval result with metadata (who approved, when, notes)
25
53
  *
26
54
  * @example
27
55
  * ```ts
28
- * // Request approval from a worker
56
+ * // Request approval from a human via Slack
29
57
  * const result = await approve('Expense: $500 for AWS', manager, {
30
58
  * via: 'slack',
31
59
  * context: { amount: 500, category: 'Infrastructure' },
32
60
  * })
33
61
  *
34
62
  * if (result.approved) {
35
- * console.log(`Approved by ${result.approvedBy?.name}`)
63
+ * console.log(`Approved by ${result.approvedBy?.name} at ${result.approvedAt}`)
36
64
  * }
37
65
  *
38
- * // Request approval from a team
66
+ * // Request approval from a team (routes to lead or available member)
39
67
  * const result = await approve('Deploy v2.1.0 to production', opsTeam, {
40
68
  * via: 'slack',
41
69
  * })
42
70
  * ```
71
+ *
72
+ * @see {@link ai-functions#approve} for LLM-generated approval request content
73
+ * @see {@link approve.all} for multi-approver workflows (all must approve)
74
+ * @see {@link approve.any} for multi-approver workflows (any can approve)
43
75
  */
44
76
  export async function approve(
45
77
  request: string,
@@ -60,8 +92,8 @@ export async function approve(
60
92
 
61
93
  // Send the approval request and wait for response
62
94
  const response = await sendApprovalRequest(channel, request, contacts, {
63
- timeout,
64
- context,
95
+ ...(timeout !== undefined && { timeout }),
96
+ ...(context !== undefined && { context }),
65
97
  approver,
66
98
  escalate,
67
99
  })
@@ -70,7 +102,7 @@ export async function approve(
70
102
  approved: response.approved,
71
103
  approvedBy: approver,
72
104
  approvedAt: new Date(),
73
- notes: response.notes,
105
+ ...(response.notes !== undefined && { notes: response.notes }),
74
106
  via: channel,
75
107
  }
76
108
  }
@@ -300,18 +332,57 @@ async function sendApprovalRequest(
300
332
  throw new Error(`No ${channel} contact configured`)
301
333
  }
302
334
 
303
- // In a real implementation, this would:
304
- // 1. Format the request for the channel (Slack blocks, email HTML, etc.)
305
- // 2. Send via the appropriate API
306
- // 3. Wait for response (polling, webhook, interactive message, etc.)
307
- // 4. Handle timeout and escalation
335
+ // Import transport functions dynamically to avoid circular dependencies
336
+ const { channelToTransport, sendViaTransport, hasTransport, resolveAddress } = await import(
337
+ './transports.js'
338
+ )
308
339
 
309
- // For now, simulate a pending response
310
- await new Promise((resolve) => setTimeout(resolve, 10))
340
+ const transport = channelToTransport(channel)
341
+ const address = resolveAddress(contacts, channel)
342
+
343
+ // If transport is registered, use it for real delivery
344
+ if (hasTransport(transport) && address) {
345
+ const { generateRequestId } = await import('./utils/id.js')
346
+ const requestId = generateRequestId('apr')
347
+
348
+ const payload = {
349
+ to: address.value,
350
+ body: request,
351
+ type: 'approval' as const,
352
+ priority: 'normal' as const,
353
+ threadId: requestId,
354
+ actions: [
355
+ { id: 'approve', label: 'Approve', style: 'primary' as const, value: true },
356
+ { id: 'reject', label: 'Reject', style: 'danger' as const, value: false },
357
+ ],
358
+ metadata: {
359
+ ...options.context,
360
+ approver: options.approver,
361
+ escalate: options.escalate,
362
+ },
363
+ ...(options.timeout !== undefined && { timeout: options.timeout }),
364
+ }
365
+
366
+ const result = await sendViaTransport(transport, payload)
367
+
368
+ if (result.success) {
369
+ // For real transports, we need to wait for a response
370
+ // This would integrate with the runtime's HumanRequestProcessor
371
+ // For now, return pending state - the response comes via webhook
372
+ return {
373
+ approved: false,
374
+ notes: `Approval request sent via ${transport}. Awaiting response. Request ID: ${requestId}`,
375
+ }
376
+ } else {
377
+ throw new Error(`Failed to send approval request via ${transport}: ${result.error}`)
378
+ }
379
+ }
311
380
 
312
- // Return a placeholder - real impl would wait for actual response
381
+ // No transport registered - return pending state
382
+ // In a real workflow, this would be processed when a transport is configured
383
+ // or the runtime handles the request via HumanRequestProcessor
313
384
  return {
314
385
  approved: false,
315
- notes: 'Approval pending - waiting for response',
386
+ notes: `Approval request pending - no transport registered for ${channel}. Configure a transport handler to enable real delivery.`,
316
387
  }
317
388
  }
package/src/ask.ts CHANGED
@@ -1,5 +1,27 @@
1
1
  /**
2
2
  * Question/answer functionality for digital workers
3
+ *
4
+ * IMPORTANT: Worker Routing vs Direct LLM Calls
5
+ * ---------------------------------------------
6
+ * This module provides worker-routed question handling, NOT direct LLM queries.
7
+ *
8
+ * - `digital-workers.ask()` - Routes questions to Workers (AI Agents or Humans)
9
+ * via communication channels (Slack, email, SMS, etc.) and waits for response.
10
+ *
11
+ * - `ai-functions.ask()` - Generates content for human interaction via LLM.
12
+ *
13
+ * Use digital-workers when you need:
14
+ * - To ask a specific person or team via real channels
15
+ * - Human responses with accountability (who answered)
16
+ * - Channel-based communication (Slack, email, SMS)
17
+ * - Team coordination and escalation
18
+ *
19
+ * Use ai-functions when you need:
20
+ * - LLM-generated question/answer content
21
+ * - Generating UI for human input
22
+ * - Direct AI text generation
23
+ *
24
+ * @module
3
25
  */
4
26
 
5
27
  import { generateObject } from 'ai-functions'
@@ -16,24 +38,31 @@ import type {
16
38
  } from './types.js'
17
39
 
18
40
  /**
19
- * Ask a question to a worker or team
41
+ * Route a question to a Worker (AI Agent or Human) via communication channels.
20
42
  *
21
- * Routes questions through the specified channel and waits for a response.
43
+ * **Key Difference from ai-functions.ask():**
44
+ * Unlike `ai-functions.ask()` which generates content for human interaction
45
+ * using the LLM, this function routes the question to an actual Worker (person
46
+ * or AI agent) via real communication channels (Slack, email, SMS, etc.) and
47
+ * waits for their response.
22
48
  *
23
- * @param target - The worker or team to ask
49
+ * This is a **worker communication primitive**, not a direct LLM primitive.
50
+ *
51
+ * @param target - The worker or team to ask (routes to their configured channels)
24
52
  * @param question - The question to ask
25
- * @param options - Ask options
26
- * @returns Promise resolving to the answer
53
+ * @param options - Ask options including channel, schema, and timeout
54
+ * @returns Promise resolving to the answer with metadata (who answered, when, via what channel)
27
55
  *
28
56
  * @example
29
57
  * ```ts
30
- * // Ask a simple question
58
+ * // Ask a human via Slack
31
59
  * const result = await ask(alice, 'What is the company holiday policy?', {
32
60
  * via: 'slack',
33
61
  * })
34
62
  * console.log(result.answer)
63
+ * console.log(`Answered by ${result.answeredBy.name} at ${result.answeredAt}`)
35
64
  *
36
- * // Ask with structured response
65
+ * // Ask with structured response schema
37
66
  * const result = await ask(ceo, 'What are our Q1 priorities?', {
38
67
  * via: 'email',
39
68
  * schema: {
@@ -42,6 +71,8 @@ import type {
42
71
  * },
43
72
  * })
44
73
  * ```
74
+ *
75
+ * @see {@link ai-functions#ask} for LLM-generated human interaction content
45
76
  */
46
77
  export async function ask<T = string>(
47
78
  target: ActionTarget,
@@ -62,9 +93,9 @@ export async function ask<T = string>(
62
93
 
63
94
  // Send the question and wait for response
64
95
  const response = await sendQuestion<T>(channel, question, contacts, {
65
- schema,
66
- timeout,
67
- context,
96
+ ...(schema !== undefined && { schema }),
97
+ ...(timeout !== undefined && { timeout }),
98
+ ...(context !== undefined && { context }),
68
99
  recipient,
69
100
  })
70
101
 
@@ -97,9 +128,13 @@ ask.ai = async <T = string>(
97
128
  model: 'sonnet',
98
129
  schema,
99
130
  prompt: question,
100
- system: context
101
- ? `Use the following context to answer the question:\n\n${JSON.stringify(context, null, 2)}`
102
- : undefined,
131
+ ...(context !== undefined && {
132
+ system: `Use the following context to answer the question:\n\n${JSON.stringify(
133
+ context,
134
+ null,
135
+ 2
136
+ )}`,
137
+ }),
103
138
  })
104
139
  return result.object as T
105
140
  }
@@ -108,9 +143,13 @@ ask.ai = async <T = string>(
108
143
  model: 'sonnet',
109
144
  schema: { answer: 'The answer to the question' },
110
145
  prompt: question,
111
- system: context
112
- ? `Use the following context to answer the question:\n\n${JSON.stringify(context, null, 2)}`
113
- : undefined,
146
+ ...(context !== undefined && {
147
+ system: `Use the following context to answer the question:\n\n${JSON.stringify(
148
+ context,
149
+ null,
150
+ 2
151
+ )}`,
152
+ }),
114
153
  })
115
154
 
116
155
  return (result.object as { answer: T }).answer
@@ -288,17 +327,52 @@ async function sendQuestion<T>(
288
327
  throw new Error(`No ${channel} contact configured`)
289
328
  }
290
329
 
291
- // In a real implementation, this would:
292
- // 1. Format the question for the channel
293
- // 2. Send via the appropriate API
294
- // 3. Wait for response (polling, webhook, etc.)
295
- // 4. Parse and validate the response
330
+ // Import transport functions dynamically to avoid circular dependencies
331
+ const { channelToTransport, sendViaTransport, hasTransport, resolveAddress } = await import(
332
+ './transports.js'
333
+ )
296
334
 
297
- // For now, simulate a pending response
298
- await new Promise((resolve) => setTimeout(resolve, 10))
335
+ const transport = channelToTransport(channel)
336
+ const address = resolveAddress(contacts, channel)
337
+
338
+ // If transport is registered, use it for real delivery
339
+ if (hasTransport(transport) && address) {
340
+ const { generateRequestId } = await import('./utils/id.js')
341
+ const requestId = generateRequestId('ask')
342
+
343
+ const payload = {
344
+ to: address.value,
345
+ body: question,
346
+ type: 'question' as const,
347
+ priority: 'normal' as const,
348
+ threadId: requestId,
349
+ ...(options.schema !== undefined && { schema: options.schema }),
350
+ ...(options.timeout !== undefined && { timeout: options.timeout }),
351
+ metadata: {
352
+ ...options.context,
353
+ recipient: options.recipient,
354
+ },
355
+ }
356
+
357
+ const result = await sendViaTransport(transport, payload)
358
+
359
+ if (result.success) {
360
+ // For real transports, we need to wait for a response
361
+ // This would integrate with the runtime's HumanRequestProcessor
362
+ // For now, return pending state - the response comes via webhook
363
+ return {
364
+ answer: `Question sent via ${transport}. Awaiting response. Request ID: ${requestId}` as T,
365
+ }
366
+ } else {
367
+ throw new Error(`Failed to send question via ${transport}: ${result.error}`)
368
+ }
369
+ }
299
370
 
300
- // Return a placeholder - real impl would wait for actual response
371
+ // No transport registered - return pending state
372
+ // In a real workflow, this would be processed when a transport is configured
373
+ // or the runtime handles the request via HumanRequestProcessor
301
374
  return {
302
- answer: 'Waiting for response...' as T,
375
+ answer:
376
+ `Question pending - no transport registered for ${channel}. Configure a transport handler to enable real delivery.` as T,
303
377
  }
304
378
  }