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/client.ts ADDED
@@ -0,0 +1,221 @@
1
+ /**
2
+ * RPC Client for Digital Workers
3
+ *
4
+ * Provides a typed RPC client that connects to the deployed
5
+ * digital-workers worker using rpc.do for remote procedure calls.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { createDigitalWorkersClient } from 'digital-workers/client'
10
+ *
11
+ * const client = createDigitalWorkersClient('https://digital-workers.workers.dev')
12
+ * const worker = await client.spawn({ name: 'my-agent', type: 'agent' })
13
+ * await client.send(worker.id, otherWorkerId, 'task', { data: 'hello' })
14
+ * ```
15
+ *
16
+ * @packageDocumentation
17
+ */
18
+
19
+ import { RPC, http } from 'rpc.do'
20
+
21
+ // ==================== Types ====================
22
+
23
+ /** Worker instance status */
24
+ type WorkerInstanceStatus = 'spawning' | 'running' | 'paused' | 'terminated' | 'error'
25
+
26
+ /** Worker instance state */
27
+ interface WorkerInstance {
28
+ id: string
29
+ name: string
30
+ status: WorkerInstanceStatus
31
+ type: 'agent' | 'human'
32
+ tier?: string | undefined
33
+ createdAt: Date
34
+ updatedAt: Date
35
+ metadata?: Record<string, unknown> | undefined
36
+ }
37
+
38
+ /** Message sent between workers */
39
+ interface WorkerMessage<T = unknown> {
40
+ id: string
41
+ from: string
42
+ to: string
43
+ type: string
44
+ payload: T
45
+ timestamp: Date
46
+ acknowledged?: boolean
47
+ }
48
+
49
+ /** Worker spawn options */
50
+ interface SpawnOptions {
51
+ name?: string
52
+ type?: 'agent' | 'human'
53
+ tier?: string
54
+ metadata?: Record<string, unknown>
55
+ timeout?: number
56
+ }
57
+
58
+ /** Receive options */
59
+ interface ReceiveOptions {
60
+ type?: string
61
+ limit?: number
62
+ acknowledged?: boolean
63
+ }
64
+
65
+ /** List options */
66
+ interface ListOptions {
67
+ status?: WorkerInstanceStatus
68
+ type?: 'agent' | 'human'
69
+ tier?: string
70
+ limit?: number
71
+ includeTerminated?: boolean
72
+ }
73
+
74
+ /** Set state options */
75
+ interface SetStateOptions {
76
+ metadata?: Record<string, unknown>
77
+ }
78
+
79
+ /** Broadcast result */
80
+ interface BroadcastResult {
81
+ workerId: string
82
+ success: boolean
83
+ messageId?: string | undefined
84
+ error?: string | undefined
85
+ }
86
+
87
+ /** Coordination task */
88
+ interface CoordinationTask<T = unknown> {
89
+ id: string
90
+ type: 'fanout' | 'pipeline' | 'race' | 'consensus'
91
+ workers: string[]
92
+ status: 'pending' | 'running' | 'completed' | 'failed'
93
+ result?: T | undefined
94
+ errors?: Error[] | undefined
95
+ }
96
+
97
+ /** Consensus options */
98
+ interface ConsensusOptions {
99
+ quorum?: number
100
+ }
101
+
102
+ // ==================== API Type ====================
103
+
104
+ /**
105
+ * DigitalWorkersAPI - Type-safe interface matching DigitalWorkersServiceCore RPC methods
106
+ *
107
+ * This interface mirrors all public methods on DigitalWorkersServiceCore so that
108
+ * the RPC client provides full type safety when calling remote methods.
109
+ */
110
+ export interface DigitalWorkersAPI {
111
+ // Worker Lifecycle Management
112
+ spawn(options?: SpawnOptions): Promise<WorkerInstance>
113
+ terminate(workerId: string): Promise<boolean>
114
+ pause(workerId: string): Promise<boolean>
115
+ resume(workerId: string): Promise<boolean>
116
+
117
+ // Worker Communication / Messaging
118
+ send<T = unknown>(
119
+ fromId: string,
120
+ toId: string,
121
+ type: string,
122
+ payload: T
123
+ ): Promise<WorkerMessage<T>>
124
+ receive<T = unknown>(workerId: string, options?: ReceiveOptions): Promise<WorkerMessage<T>[]>
125
+ acknowledge(workerId: string, messageId: string): Promise<boolean>
126
+ broadcast<T = unknown>(
127
+ fromId: string,
128
+ toIds: string[],
129
+ type: string,
130
+ payload: T
131
+ ): Promise<BroadcastResult[]>
132
+
133
+ // Worker State Management
134
+ getState(workerId: string): Promise<WorkerInstance | null>
135
+ setState(workerId: string, options: SetStateOptions): Promise<void>
136
+ list(options?: ListOptions): Promise<WorkerInstance[]>
137
+
138
+ // Worker Coordination Patterns
139
+ fanOut<T = unknown>(
140
+ coordinatorId: string,
141
+ workerIds: string[],
142
+ type: string,
143
+ payload: T
144
+ ): Promise<CoordinationTask<T>>
145
+ pipeline<T = unknown>(workerIds: string[], type: string, payload: T): Promise<CoordinationTask<T>>
146
+ race<T = unknown>(workerIds: string[], type: string, payload: T): Promise<CoordinationTask<T>>
147
+ consensus<T = unknown>(
148
+ workerIds: string[],
149
+ type: string,
150
+ payload: T,
151
+ options?: ConsensusOptions
152
+ ): Promise<CoordinationTask<T>>
153
+ getTaskStatus<T = unknown>(taskId: string): Promise<CoordinationTask<T> | null>
154
+ }
155
+
156
+ // ==================== Client Options ====================
157
+
158
+ /**
159
+ * Options for creating a digital workers RPC client
160
+ */
161
+ export interface DigitalWorkersClientOptions {
162
+ /** Authentication token or API key */
163
+ token?: string
164
+ /** Request timeout in milliseconds */
165
+ timeout?: number
166
+ /** Custom headers to include in requests */
167
+ headers?: Record<string, string>
168
+ }
169
+
170
+ // ==================== Client Factory ====================
171
+
172
+ /** Default URL for the digital-workers worker */
173
+ const DEFAULT_URL = 'https://digital-workers.workers.dev'
174
+
175
+ /**
176
+ * Create a typed RPC client for the digital-workers worker
177
+ *
178
+ * @param url - The URL of the deployed digital-workers worker
179
+ * @param options - Optional client configuration
180
+ * @returns A typed RPC client with all DigitalWorkersServiceCore methods
181
+ *
182
+ * @example
183
+ * ```ts
184
+ * import { createDigitalWorkersClient } from 'digital-workers/client'
185
+ *
186
+ * // Connect to production
187
+ * const client = createDigitalWorkersClient('https://digital-workers.workers.dev')
188
+ *
189
+ * // Spawn and manage workers
190
+ * const agent = await client.spawn({ name: 'my-agent', type: 'agent' })
191
+ * const human = await client.spawn({ name: 'reviewer', type: 'human' })
192
+ *
193
+ * // Send messages between workers
194
+ * await client.send(agent.id, human.id, 'review-request', { content: 'Please review' })
195
+ * const messages = await client.receive(human.id, { type: 'review-request' })
196
+ *
197
+ * // Coordinate multiple workers
198
+ * const task = await client.fanOut(agent.id, [worker1.id, worker2.id], 'process', data)
199
+ * const status = await client.getTaskStatus(task.id)
200
+ * ```
201
+ */
202
+ export function createDigitalWorkersClient(
203
+ url: string = DEFAULT_URL,
204
+ options?: DigitalWorkersClientOptions
205
+ ) {
206
+ return RPC<DigitalWorkersAPI>(http(url, options?.token))
207
+ }
208
+
209
+ /**
210
+ * Default client instance connected to the production digital-workers worker
211
+ *
212
+ * @example
213
+ * ```ts
214
+ * import client from 'digital-workers/client'
215
+ *
216
+ * const worker = await client.spawn({ name: 'my-agent' })
217
+ * ```
218
+ */
219
+ const client = createDigitalWorkersClient()
220
+
221
+ export default client
package/src/decide.ts CHANGED
@@ -1,21 +1,54 @@
1
1
  /**
2
2
  * Decision-making functionality for digital workers
3
+ *
4
+ * IMPORTANT: Worker-Assisted Decisions vs LLM Judging
5
+ * ----------------------------------------------------
6
+ * This module provides structured decision-making that can involve human
7
+ * decision-makers, NOT simple LLM-based option selection.
8
+ *
9
+ * - `digital-workers.decide()` - Structured decision-making with criteria
10
+ * evaluation, confidence scoring, and optional human approval routing.
11
+ *
12
+ * - `ai-functions.decide()` - LLM as judge - compares options and picks
13
+ * the best one based on criteria (curried function pattern).
14
+ *
15
+ * Use digital-workers when you need:
16
+ * - Multi-criteria decision analysis
17
+ * - Confidence scores and alternative rankings
18
+ * - Human approval for critical decisions
19
+ * - Audit trail of decision reasoning
20
+ *
21
+ * Use ai-functions when you need:
22
+ * - Simple "pick the best" comparison
23
+ * - LLM judging between options
24
+ * - Curried decision function pattern
25
+ *
26
+ * @module
3
27
  */
4
28
 
5
29
  import { generateObject } from 'ai-functions'
6
30
  import type { Decision, DecideOptions } from './types.js'
7
31
 
8
32
  /**
9
- * Make a decision from a set of options
33
+ * Make a structured decision with criteria evaluation and optional human routing.
34
+ *
35
+ * **Key Difference from ai-functions.decide():**
36
+ * Unlike `ai-functions.decide()` which is a curried LLM judge function
37
+ * (e.g., `decide\`criteria\`(optionA, optionB)`), this function provides
38
+ * comprehensive decision analysis with:
39
+ * - Multi-criteria scoring
40
+ * - Confidence levels
41
+ * - Alternative rankings
42
+ * - Optional human approval routing via `decide.withApproval()`
10
43
  *
11
- * Uses AI to evaluate options and make a reasoned decision,
12
- * or can route to human decision-makers for critical choices.
44
+ * This is a **decision framework**, not a simple LLM comparison primitive.
13
45
  *
14
- * @param options - Decision options with configuration
15
- * @returns Promise resolving to decision result
46
+ * @param options - Decision options including choices, context, and criteria
47
+ * @returns Promise resolving to decision with choice, reasoning, confidence, and alternatives
16
48
  *
17
49
  * @example
18
50
  * ```ts
51
+ * // Structured decision with criteria evaluation
19
52
  * const decision = await decide({
20
53
  * options: ['Option A', 'Option B', 'Option C'],
21
54
  * context: 'We need to choose a technology stack for our new project',
@@ -29,7 +62,8 @@ import type { Decision, DecideOptions } from './types.js'
29
62
  *
30
63
  * console.log(`Decision: ${decision.choice}`)
31
64
  * console.log(`Reasoning: ${decision.reasoning}`)
32
- * console.log(`Confidence: ${decision.confidence}`)
65
+ * console.log(`Confidence: ${decision.confidence}`) // 0-1 score
66
+ * console.log(`Alternatives:`, decision.alternatives)
33
67
  * ```
34
68
  *
35
69
  * @example
@@ -50,23 +84,19 @@ import type { Decision, DecideOptions } from './types.js'
50
84
  * criteria: ['Cost', 'Time to market', 'Risk', 'Scalability'],
51
85
  * })
52
86
  * ```
87
+ *
88
+ * @see {@link ai-functions#decide} for LLM-as-judge option comparison
53
89
  */
54
- export async function decide<T = string>(
55
- options: DecideOptions<T>
56
- ): Promise<Decision<T>> {
57
- const {
58
- options: choices,
59
- context,
60
- criteria = [],
61
- includeReasoning = true,
62
- } = options
90
+ export async function decide<T = string>(options: DecideOptions<T>): Promise<Decision<T>> {
91
+ const { options: choices, context, criteria = [], includeReasoning = true } = options
63
92
 
64
93
  // Format context for the prompt
65
- const contextStr = typeof context === 'string'
66
- ? context
67
- : context
68
- ? JSON.stringify(context, null, 2)
69
- : 'No additional context provided'
94
+ const contextStr =
95
+ typeof context === 'string'
96
+ ? context
97
+ : context
98
+ ? JSON.stringify(context, null, 2)
99
+ : 'No additional context provided'
70
100
 
71
101
  // Format choices for the prompt
72
102
  const choicesStr = choices
@@ -92,13 +122,18 @@ export async function decide<T = string>(
92
122
  score: 'Score for this alternative from 0-100 (number)',
93
123
  },
94
124
  ],
95
- criteriaScores: criteria.length > 0
96
- ? 'Scores for each criterion as object mapping criterion name to score (0-100)'
97
- : undefined,
125
+ criteriaScores:
126
+ criteria.length > 0
127
+ ? 'Scores for each criterion as object mapping criterion name to score (0-100)'
128
+ : undefined,
98
129
  },
99
130
  system: `You are a decision-making expert. Analyze the options carefully and make the best choice based on the context and criteria provided.
100
131
 
101
- ${criteria.length > 0 ? `Evaluation Criteria:\n${criteria.map((c, i) => `${i + 1}. ${c}`).join('\n')}` : ''}`,
132
+ ${
133
+ criteria.length > 0
134
+ ? `Evaluation Criteria:\n${criteria.map((c, i) => `${i + 1}. ${c}`).join('\n')}`
135
+ : ''
136
+ }`,
102
137
  prompt: `Make a decision based on the following:
103
138
 
104
139
  Context:
@@ -107,7 +142,11 @@ ${contextStr}
107
142
  Options:
108
143
  ${choicesStr}
109
144
 
110
- ${criteria.length > 0 ? `\nEvaluate each option against these criteria:\n${criteria.join(', ')}` : ''}
145
+ ${
146
+ criteria.length > 0
147
+ ? `\nEvaluate each option against these criteria:\n${criteria.join(', ')}`
148
+ : ''
149
+ }
111
150
 
112
151
  Provide your decision with clear reasoning.`,
113
152
  })
@@ -157,9 +196,10 @@ decide.yesNo = async (
157
196
  ): Promise<Decision<'yes' | 'no'>> => {
158
197
  return decide({
159
198
  options: ['yes', 'no'] as const,
160
- context: typeof context === 'string'
161
- ? `${question}\n\n${context}`
162
- : `${question}\n\n${context ? JSON.stringify(context, null, 2) : ''}`,
199
+ context:
200
+ typeof context === 'string'
201
+ ? `${question}\n\n${context}`
202
+ : `${question}\n\n${context ? JSON.stringify(context, null, 2) : ''}`,
163
203
  })
164
204
  }
165
205
 
@@ -189,11 +229,12 @@ decide.prioritize = async <T = string>(
189
229
  context?: string | Record<string, unknown>,
190
230
  criteria: string[] = []
191
231
  ): Promise<Array<Decision<T> & { rank: number }>> => {
192
- const contextStr = typeof context === 'string'
193
- ? context
194
- : context
195
- ? JSON.stringify(context, null, 2)
196
- : 'No additional context provided'
232
+ const contextStr =
233
+ typeof context === 'string'
234
+ ? context
235
+ : context
236
+ ? JSON.stringify(context, null, 2)
237
+ : 'No additional context provided'
197
238
 
198
239
  const itemsStr = items
199
240
  .map((item, i) => `${i + 1}. ${typeof item === 'object' ? JSON.stringify(item) : item}`)
@@ -213,7 +254,11 @@ decide.prioritize = async <T = string>(
213
254
  },
214
255
  system: `You are a prioritization expert. Rank items by priority based on the context and criteria.
215
256
 
216
- ${criteria.length > 0 ? `Prioritization Criteria:\n${criteria.map((c, i) => `${i + 1}. ${c}`).join('\n')}` : ''}`,
257
+ ${
258
+ criteria.length > 0
259
+ ? `Prioritization Criteria:\n${criteria.map((c, i) => `${i + 1}. ${c}`).join('\n')}`
260
+ : ''
261
+ }`,
217
262
  prompt: `Prioritize the following items:
218
263
 
219
264
  Context:
@@ -287,9 +332,10 @@ decide.withApproval = async <T = string>(
287
332
  }
288
333
  )
289
334
 
335
+ const approvedBy = approval.approvedBy?.id ?? approval.approvedBy?.name
290
336
  return {
291
337
  ...decision,
292
338
  approved: approval.approved,
293
- approvedBy: approval.approvedBy?.id ?? approval.approvedBy?.name,
339
+ ...(approvedBy !== undefined && { approvedBy }),
294
340
  }
295
341
  }
package/src/do.ts CHANGED
@@ -1,23 +1,49 @@
1
1
  /**
2
2
  * Task execution functionality for digital workers
3
+ *
4
+ * IMPORTANT: Worker Routing vs Direct LLM Calls
5
+ * ---------------------------------------------
6
+ * This module provides worker-routed task execution, NOT direct LLM calls.
7
+ *
8
+ * - `digital-workers.do()` - Routes tasks to Workers (AI Agents or Humans)
9
+ * based on capability matching, load balancing, and escalation policies.
10
+ *
11
+ * - `ai-functions.do()` - Directly calls the LLM to describe task execution.
12
+ *
13
+ * Use digital-workers when you need:
14
+ * - Task routing to appropriate workers
15
+ * - Human-in-the-loop escalation
16
+ * - Capability-based worker selection
17
+ * - Retry and timeout handling across workers
18
+ *
19
+ * Use ai-functions when you need:
20
+ * - Direct LLM text generation
21
+ * - Simple AI task description
22
+ * - No worker coordination required
23
+ *
24
+ * @module
3
25
  */
4
26
 
5
- import { define } from 'ai-functions'
6
27
  import type { TaskResult, DoOptions } from './types.js'
7
28
 
8
29
  /**
9
- * Execute a task
30
+ * Execute a task by routing to an appropriate Worker (AI Agent or Human).
10
31
  *
11
- * Routes tasks to appropriate workers (AI or human) based on complexity
12
- * and requirements. Handles retries, timeouts, and background execution.
32
+ * **Key Difference from ai-functions.do():**
33
+ * Unlike `ai-functions.do()` which directly calls the LLM to describe what
34
+ * actions would be taken, this function routes the task to a Worker (Agent
35
+ * or Human) based on capability matching. The Worker then executes the task
36
+ * using their specific tools and capabilities.
37
+ *
38
+ * This is a **worker coordination primitive**, not a direct LLM primitive.
13
39
  *
14
40
  * @param task - Description of the task to execute
15
41
  * @param options - Execution options (retries, timeout, background, etc.)
16
- * @returns Promise resolving to task result
42
+ * @returns Promise resolving to task result with execution details
17
43
  *
18
44
  * @example
19
45
  * ```ts
20
- * // Execute a simple task
46
+ * // Route task to appropriate worker based on capability
21
47
  * const result = await do('Generate monthly sales report', {
22
48
  * timeout: 60000, // 1 minute
23
49
  * context: {
@@ -33,7 +59,7 @@ import type { TaskResult, DoOptions } from './types.js'
33
59
  *
34
60
  * @example
35
61
  * ```ts
36
- * // Execute with retries
62
+ * // Execute with retries across workers
37
63
  * const result = await do('Sync data to backup server', {
38
64
  * maxRetries: 3,
39
65
  * timeout: 30000,
@@ -46,7 +72,7 @@ import type { TaskResult, DoOptions } from './types.js'
46
72
  *
47
73
  * @example
48
74
  * ```ts
49
- * // Execute in background
75
+ * // Execute in background (worker handles async)
50
76
  * const result = await do('Process large dataset', {
51
77
  * background: true,
52
78
  * context: {
@@ -55,66 +81,88 @@ import type { TaskResult, DoOptions } from './types.js'
55
81
  * },
56
82
  * })
57
83
  * ```
84
+ *
85
+ * @see {@link ai-functions#do} for direct LLM task description
58
86
  */
59
87
  export async function doTask<T = unknown>(
60
88
  task: string,
61
89
  options: DoOptions = {}
62
90
  ): Promise<TaskResult<T>> {
63
- const {
64
- maxRetries = 0,
65
- timeout,
66
- background = false,
67
- context,
68
- } = options
91
+ const { maxRetries = 0, timeout, background = false, context } = options
69
92
 
70
93
  const startTime = Date.now()
71
94
  const steps: TaskResult<T>['steps'] = []
72
95
 
73
- // Use agentic function for complex tasks
74
- const taskFn = define.agentic({
75
- name: 'executeTask',
76
- description: 'Execute a task using available tools and capabilities',
77
- args: {
78
- task: 'Description of the task to execute',
79
- contextInfo: 'Additional context and parameters for the task',
80
- },
81
- returnType: {
82
- result: 'The result of executing the task',
83
- steps: ['List of steps taken to complete the task'],
84
- },
85
- instructions: `Execute the following task:
86
-
87
- ${task}
88
-
89
- ${context ? `Context: ${JSON.stringify(context, null, 2)}` : ''}
90
-
91
- Work step-by-step to complete the task. Use available tools as needed.
92
- Document each step you take for transparency.`,
93
- maxIterations: 10,
94
- tools: [], // Tools would be provided by the execution environment
95
- })
96
-
97
96
  let retries = 0
98
97
  let lastError: Error | undefined
99
98
 
100
99
  while (retries <= maxRetries) {
101
100
  try {
101
+ // Use generateObject directly for more reliable execution
102
+ const { generateObject } = await import('ai-functions')
103
+
104
+ const executeTask = async (): Promise<{
105
+ result: T
106
+ steps: Array<{ action: string; result: unknown }>
107
+ }> => {
108
+ const response = await generateObject({
109
+ model: 'sonnet',
110
+ schema: {
111
+ result: 'The result of executing the task - provide a detailed response',
112
+ steps: [
113
+ {
114
+ action: 'Description of what was done in this step',
115
+ result: 'Outcome of this step',
116
+ },
117
+ ],
118
+ success: 'Whether the task was completed successfully (boolean)',
119
+ },
120
+ system: `You are a capable AI worker executing tasks. You have access to the following context:
121
+
122
+ ${context ? JSON.stringify(context, null, 2) : 'No additional context provided.'}
123
+
124
+ Execute the task step-by-step, documenting each action you take.
125
+ Provide detailed, actionable results that can be used directly.`,
126
+ prompt: `Execute the following task and provide the result:
127
+
128
+ Task: ${task}
129
+
130
+ Work through this task carefully:
131
+ 1. Analyze what needs to be done
132
+ 2. Execute each step
133
+ 3. Provide the final result
134
+
135
+ Return a detailed result that fulfills the task requirements.`,
136
+ })
137
+
138
+ const obj = response.object as unknown as {
139
+ result: T
140
+ steps: Array<{ action: string; result: unknown }>
141
+ success: boolean
142
+ }
143
+
144
+ if (!obj.success) {
145
+ throw new Error('Task execution failed')
146
+ }
147
+
148
+ return { result: obj.result, steps: obj.steps }
149
+ }
150
+
102
151
  const response = await Promise.race([
103
- taskFn.call({ task, contextInfo: context ? JSON.stringify(context) : '' }),
152
+ executeTask(),
104
153
  timeout
105
- ? new Promise((_, reject) =>
154
+ ? new Promise<never>((_, reject) =>
106
155
  setTimeout(() => reject(new Error('Task timeout')), timeout)
107
156
  )
108
- : new Promise(() => {}), // Never resolves if no timeout
109
- ]) as unknown
110
-
111
- const typedResponse = response as { result: T; steps?: Array<{ action: string; result: unknown }> }
157
+ : new Promise<never>(() => {}), // Never resolves if no timeout
158
+ ])
112
159
 
113
160
  // Track steps if provided
114
- if (typedResponse.steps) {
161
+ if (response.steps) {
115
162
  steps.push(
116
- ...typedResponse.steps.map((step) => ({
117
- ...step,
163
+ ...response.steps.map((step) => ({
164
+ action: String(step.action),
165
+ result: step.result,
118
166
  timestamp: new Date(),
119
167
  }))
120
168
  )
@@ -123,7 +171,7 @@ Document each step you take for transparency.`,
123
171
  const duration = Date.now() - startTime
124
172
 
125
173
  return {
126
- result: typedResponse.result,
174
+ result: response.result,
127
175
  success: true,
128
176
  duration,
129
177
  steps,
@@ -140,9 +188,7 @@ Document each step you take for transparency.`,
140
188
  })
141
189
 
142
190
  // Exponential backoff
143
- await new Promise((resolve) =>
144
- setTimeout(resolve, Math.pow(2, retries) * 1000)
145
- )
191
+ await new Promise((resolve) => setTimeout(resolve, Math.pow(2, retries) * 1000))
146
192
  }
147
193
  }
148
194
  }
@@ -218,7 +264,7 @@ doTask.sequence = async <T = unknown>(
218
264
  results.push(result)
219
265
 
220
266
  // Stop if a task fails (unless we're continuing on error)
221
- if (!result.success && !options.context?.continueOnError) {
267
+ if (!result.success && !options.context?.['continueOnError']) {
222
268
  break
223
269
  }
224
270
  }