digital-workers 0.1.1 → 2.0.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 (83) hide show
  1. package/.turbo/turbo-build.log +5 -0
  2. package/CHANGELOG.md +9 -0
  3. package/README.md +290 -106
  4. package/dist/actions.d.ts +95 -0
  5. package/dist/actions.d.ts.map +1 -0
  6. package/dist/actions.js +437 -0
  7. package/dist/actions.js.map +1 -0
  8. package/dist/approve.d.ts +49 -0
  9. package/dist/approve.d.ts.map +1 -0
  10. package/dist/approve.js +235 -0
  11. package/dist/approve.js.map +1 -0
  12. package/dist/ask.d.ts +42 -0
  13. package/dist/ask.d.ts.map +1 -0
  14. package/dist/ask.js +227 -0
  15. package/dist/ask.js.map +1 -0
  16. package/dist/decide.d.ts +62 -0
  17. package/dist/decide.d.ts.map +1 -0
  18. package/dist/decide.js +245 -0
  19. package/dist/decide.js.map +1 -0
  20. package/dist/do.d.ts +63 -0
  21. package/dist/do.d.ts.map +1 -0
  22. package/dist/do.js +228 -0
  23. package/dist/do.js.map +1 -0
  24. package/dist/generate.d.ts +61 -0
  25. package/dist/generate.d.ts.map +1 -0
  26. package/dist/generate.js +299 -0
  27. package/dist/generate.js.map +1 -0
  28. package/dist/goals.d.ts +89 -0
  29. package/dist/goals.d.ts.map +1 -0
  30. package/dist/goals.js +206 -0
  31. package/dist/goals.js.map +1 -0
  32. package/dist/index.d.ts +68 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +69 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/is.d.ts +54 -0
  37. package/dist/is.d.ts.map +1 -0
  38. package/dist/is.js +318 -0
  39. package/dist/is.js.map +1 -0
  40. package/dist/kpis.d.ts +103 -0
  41. package/dist/kpis.d.ts.map +1 -0
  42. package/dist/kpis.js +271 -0
  43. package/dist/kpis.js.map +1 -0
  44. package/dist/notify.d.ts +47 -0
  45. package/dist/notify.d.ts.map +1 -0
  46. package/dist/notify.js +220 -0
  47. package/dist/notify.js.map +1 -0
  48. package/dist/role.d.ts +53 -0
  49. package/dist/role.d.ts.map +1 -0
  50. package/dist/role.js +111 -0
  51. package/dist/role.js.map +1 -0
  52. package/dist/team.d.ts +61 -0
  53. package/dist/team.d.ts.map +1 -0
  54. package/dist/team.js +131 -0
  55. package/dist/team.js.map +1 -0
  56. package/dist/transports.d.ts +164 -0
  57. package/dist/transports.d.ts.map +1 -0
  58. package/dist/transports.js +358 -0
  59. package/dist/transports.js.map +1 -0
  60. package/dist/types.d.ts +693 -0
  61. package/dist/types.d.ts.map +1 -0
  62. package/dist/types.js +72 -0
  63. package/dist/types.js.map +1 -0
  64. package/package.json +27 -61
  65. package/src/actions.ts +615 -0
  66. package/src/approve.ts +317 -0
  67. package/src/ask.ts +304 -0
  68. package/src/decide.ts +295 -0
  69. package/src/do.ts +275 -0
  70. package/src/generate.ts +364 -0
  71. package/src/goals.ts +220 -0
  72. package/src/index.ts +118 -0
  73. package/src/is.ts +372 -0
  74. package/src/kpis.ts +348 -0
  75. package/src/notify.ts +303 -0
  76. package/src/role.ts +116 -0
  77. package/src/team.ts +142 -0
  78. package/src/transports.ts +504 -0
  79. package/src/types.ts +843 -0
  80. package/test/actions.test.ts +546 -0
  81. package/test/standalone.test.ts +299 -0
  82. package/test/types.test.ts +460 -0
  83. package/tsconfig.json +9 -0
package/src/decide.ts ADDED
@@ -0,0 +1,295 @@
1
+ /**
2
+ * Decision-making functionality for digital workers
3
+ */
4
+
5
+ import { generateObject } from 'ai-functions'
6
+ import type { Decision, DecideOptions } from './types.js'
7
+
8
+ /**
9
+ * Make a decision from a set of options
10
+ *
11
+ * Uses AI to evaluate options and make a reasoned decision,
12
+ * or can route to human decision-makers for critical choices.
13
+ *
14
+ * @param options - Decision options with configuration
15
+ * @returns Promise resolving to decision result
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const decision = await decide({
20
+ * options: ['Option A', 'Option B', 'Option C'],
21
+ * context: 'We need to choose a technology stack for our new project',
22
+ * criteria: [
23
+ * 'Developer experience',
24
+ * 'Performance',
25
+ * 'Community support',
26
+ * 'Long-term viability',
27
+ * ],
28
+ * })
29
+ *
30
+ * console.log(`Decision: ${decision.choice}`)
31
+ * console.log(`Reasoning: ${decision.reasoning}`)
32
+ * console.log(`Confidence: ${decision.confidence}`)
33
+ * ```
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * // Complex decision with structured options
38
+ * const decision = await decide({
39
+ * options: [
40
+ * { id: 'migrate', label: 'Migrate to new platform' },
41
+ * { id: 'refactor', label: 'Refactor existing system' },
42
+ * { id: 'rebuild', label: 'Rebuild from scratch' },
43
+ * ],
44
+ * context: {
45
+ * budget: '$500k',
46
+ * timeline: '6 months',
47
+ * teamSize: 5,
48
+ * currentSystem: 'Legacy monolith',
49
+ * },
50
+ * criteria: ['Cost', 'Time to market', 'Risk', 'Scalability'],
51
+ * })
52
+ * ```
53
+ */
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
63
+
64
+ // 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'
70
+
71
+ // Format choices for the prompt
72
+ const choicesStr = choices
73
+ .map((choice, i) => {
74
+ if (typeof choice === 'object' && choice !== null) {
75
+ return `${i + 1}. ${JSON.stringify(choice)}`
76
+ }
77
+ return `${i + 1}. ${choice}`
78
+ })
79
+ .join('\n')
80
+
81
+ const result = await generateObject({
82
+ model: 'sonnet',
83
+ schema: {
84
+ choice: 'The chosen option (must be one of the provided options)',
85
+ reasoning: includeReasoning
86
+ ? 'Detailed reasoning explaining why this choice is best'
87
+ : 'Brief explanation of the choice',
88
+ confidence: 'Confidence level in this decision as a decimal (number)',
89
+ alternatives: [
90
+ {
91
+ option: 'An alternative option that was considered',
92
+ score: 'Score for this alternative from 0-100 (number)',
93
+ },
94
+ ],
95
+ criteriaScores: criteria.length > 0
96
+ ? 'Scores for each criterion as object mapping criterion name to score (0-100)'
97
+ : undefined,
98
+ },
99
+ system: `You are a decision-making expert. Analyze the options carefully and make the best choice based on the context and criteria provided.
100
+
101
+ ${criteria.length > 0 ? `Evaluation Criteria:\n${criteria.map((c, i) => `${i + 1}. ${c}`).join('\n')}` : ''}`,
102
+ prompt: `Make a decision based on the following:
103
+
104
+ Context:
105
+ ${contextStr}
106
+
107
+ Options:
108
+ ${choicesStr}
109
+
110
+ ${criteria.length > 0 ? `\nEvaluate each option against these criteria:\n${criteria.join(', ')}` : ''}
111
+
112
+ Provide your decision with clear reasoning.`,
113
+ })
114
+
115
+ const response = result.object as unknown as {
116
+ choice: T
117
+ reasoning: string
118
+ confidence: number
119
+ alternatives: Array<{ option: T; score: number }>
120
+ criteriaScores?: Record<string, number>
121
+ }
122
+
123
+ return {
124
+ choice: response.choice,
125
+ reasoning: response.reasoning,
126
+ confidence: Math.min(1, Math.max(0, response.confidence)),
127
+ alternatives: response.alternatives,
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Make a binary (yes/no) decision
133
+ *
134
+ * @param question - The yes/no question
135
+ * @param context - Context for the decision
136
+ * @returns Promise resolving to decision
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * const decision = await decide.yesNo(
141
+ * 'Should we proceed with the deployment?',
142
+ * {
143
+ * tests: 'All passing',
144
+ * codeReview: 'Approved',
145
+ * loadTests: 'Within acceptable range',
146
+ * }
147
+ * )
148
+ *
149
+ * if (decision.choice === 'yes') {
150
+ * // Proceed with deployment
151
+ * }
152
+ * ```
153
+ */
154
+ decide.yesNo = async (
155
+ question: string,
156
+ context?: string | Record<string, unknown>
157
+ ): Promise<Decision<'yes' | 'no'>> => {
158
+ return decide({
159
+ 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) : ''}`,
163
+ })
164
+ }
165
+
166
+ /**
167
+ * Make a prioritization decision
168
+ *
169
+ * Ranks options by priority instead of choosing one.
170
+ *
171
+ * @param items - Items to prioritize
172
+ * @param context - Context for prioritization
173
+ * @param criteria - Criteria for prioritization
174
+ * @returns Promise resolving to prioritized list
175
+ *
176
+ * @example
177
+ * ```ts
178
+ * const prioritized = await decide.prioritize(
179
+ * ['Feature A', 'Bug fix B', 'Tech debt C', 'Feature D'],
180
+ * 'Sprint planning for next 2 weeks',
181
+ * ['User impact', 'Urgency', 'Effort required']
182
+ * )
183
+ *
184
+ * console.log('Priority order:', prioritized.map(p => p.choice))
185
+ * ```
186
+ */
187
+ decide.prioritize = async <T = string>(
188
+ items: T[],
189
+ context?: string | Record<string, unknown>,
190
+ criteria: string[] = []
191
+ ): 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'
197
+
198
+ const itemsStr = items
199
+ .map((item, i) => `${i + 1}. ${typeof item === 'object' ? JSON.stringify(item) : item}`)
200
+ .join('\n')
201
+
202
+ const result = await generateObject({
203
+ model: 'sonnet',
204
+ schema: {
205
+ prioritized: [
206
+ {
207
+ item: 'The item',
208
+ rank: 'Priority rank (1 = highest) (number)',
209
+ reasoning: 'Why this priority',
210
+ confidence: 'Confidence in this ranking (number)',
211
+ },
212
+ ],
213
+ },
214
+ system: `You are a prioritization expert. Rank items by priority based on the context and criteria.
215
+
216
+ ${criteria.length > 0 ? `Prioritization Criteria:\n${criteria.map((c, i) => `${i + 1}. ${c}`).join('\n')}` : ''}`,
217
+ prompt: `Prioritize the following items:
218
+
219
+ Context:
220
+ ${contextStr}
221
+
222
+ Items:
223
+ ${itemsStr}
224
+
225
+ ${criteria.length > 0 ? `\nConsider these criteria when prioritizing:\n${criteria.join(', ')}` : ''}
226
+
227
+ Rank all items from highest to lowest priority.`,
228
+ })
229
+
230
+ const response = result.object as unknown as {
231
+ prioritized: Array<{
232
+ item: T
233
+ rank: number
234
+ reasoning: string
235
+ confidence: number
236
+ }>
237
+ }
238
+
239
+ return response.prioritized.map((p) => ({
240
+ choice: p.item,
241
+ rank: p.rank,
242
+ reasoning: p.reasoning,
243
+ confidence: Math.min(1, Math.max(0, p.confidence)),
244
+ }))
245
+ }
246
+
247
+ /**
248
+ * Make a decision with approval required
249
+ *
250
+ * Makes a recommendation but requires human approval before finalizing.
251
+ *
252
+ * @param options - Decision options
253
+ * @param approver - Who should approve the decision
254
+ * @returns Promise resolving to approved decision
255
+ *
256
+ * @example
257
+ * ```ts
258
+ * const decision = await decide.withApproval(
259
+ * {
260
+ * options: ['Rollback', 'Fix forward', 'Wait'],
261
+ * context: 'Production incident - 500 errors on checkout',
262
+ * criteria: ['User impact', 'Recovery time', 'Risk'],
263
+ * },
264
+ * 'oncall-engineer@company.com'
265
+ * )
266
+ * ```
267
+ */
268
+ decide.withApproval = async <T = string>(
269
+ options: DecideOptions<T>,
270
+ approver: string
271
+ ): Promise<Decision<T> & { approved: boolean; approvedBy?: string }> => {
272
+ // First, make the decision
273
+ const decision = await decide(options)
274
+
275
+ // Then request approval
276
+ const { approve } = await import('./approve.js')
277
+ const approval = await approve(
278
+ `Approve decision: ${decision.choice}`,
279
+ { id: approver },
280
+ {
281
+ via: 'slack',
282
+ context: {
283
+ decision,
284
+ options: options.options,
285
+ context: options.context,
286
+ },
287
+ }
288
+ )
289
+
290
+ return {
291
+ ...decision,
292
+ approved: approval.approved,
293
+ approvedBy: approval.approvedBy?.id ?? approval.approvedBy?.name,
294
+ }
295
+ }
package/src/do.ts ADDED
@@ -0,0 +1,275 @@
1
+ /**
2
+ * Task execution functionality for digital workers
3
+ */
4
+
5
+ import { define } from 'ai-functions'
6
+ import type { TaskResult, DoOptions } from './types.js'
7
+
8
+ /**
9
+ * Execute a task
10
+ *
11
+ * Routes tasks to appropriate workers (AI or human) based on complexity
12
+ * and requirements. Handles retries, timeouts, and background execution.
13
+ *
14
+ * @param task - Description of the task to execute
15
+ * @param options - Execution options (retries, timeout, background, etc.)
16
+ * @returns Promise resolving to task result
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * // Execute a simple task
21
+ * const result = await do('Generate monthly sales report', {
22
+ * timeout: 60000, // 1 minute
23
+ * context: {
24
+ * month: 'January',
25
+ * year: 2024,
26
+ * },
27
+ * })
28
+ *
29
+ * if (result.success) {
30
+ * console.log('Report:', result.result)
31
+ * }
32
+ * ```
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * // Execute with retries
37
+ * const result = await do('Sync data to backup server', {
38
+ * maxRetries: 3,
39
+ * timeout: 30000,
40
+ * context: {
41
+ * source: 'primary-db',
42
+ * destination: 'backup-db',
43
+ * },
44
+ * })
45
+ * ```
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * // Execute in background
50
+ * const result = await do('Process large dataset', {
51
+ * background: true,
52
+ * context: {
53
+ * dataset: 'customer_transactions.csv',
54
+ * outputFormat: 'parquet',
55
+ * },
56
+ * })
57
+ * ```
58
+ */
59
+ export async function doTask<T = unknown>(
60
+ task: string,
61
+ options: DoOptions = {}
62
+ ): Promise<TaskResult<T>> {
63
+ const {
64
+ maxRetries = 0,
65
+ timeout,
66
+ background = false,
67
+ context,
68
+ } = options
69
+
70
+ const startTime = Date.now()
71
+ const steps: TaskResult<T>['steps'] = []
72
+
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
+ let retries = 0
98
+ let lastError: Error | undefined
99
+
100
+ while (retries <= maxRetries) {
101
+ try {
102
+ const response = await Promise.race([
103
+ taskFn.call({ task, contextInfo: context ? JSON.stringify(context) : '' }),
104
+ timeout
105
+ ? new Promise((_, reject) =>
106
+ setTimeout(() => reject(new Error('Task timeout')), timeout)
107
+ )
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 }> }
112
+
113
+ // Track steps if provided
114
+ if (typedResponse.steps) {
115
+ steps.push(
116
+ ...typedResponse.steps.map((step) => ({
117
+ ...step,
118
+ timestamp: new Date(),
119
+ }))
120
+ )
121
+ }
122
+
123
+ const duration = Date.now() - startTime
124
+
125
+ return {
126
+ result: typedResponse.result,
127
+ success: true,
128
+ duration,
129
+ steps,
130
+ }
131
+ } catch (error) {
132
+ lastError = error as Error
133
+ retries++
134
+
135
+ if (retries <= maxRetries) {
136
+ steps.push({
137
+ action: `Retry attempt ${retries}`,
138
+ result: { error: lastError.message },
139
+ timestamp: new Date(),
140
+ })
141
+
142
+ // Exponential backoff
143
+ await new Promise((resolve) =>
144
+ setTimeout(resolve, Math.pow(2, retries) * 1000)
145
+ )
146
+ }
147
+ }
148
+ }
149
+
150
+ const duration = Date.now() - startTime
151
+
152
+ return {
153
+ result: undefined as T,
154
+ success: false,
155
+ error: lastError?.message || 'Task failed',
156
+ duration,
157
+ steps,
158
+ }
159
+ }
160
+
161
+ // Export as 'do' with proper typing
162
+ export { doTask as do }
163
+
164
+ /**
165
+ * Execute multiple tasks in parallel
166
+ *
167
+ * @param tasks - Array of tasks to execute
168
+ * @param options - Execution options
169
+ * @returns Promise resolving to array of task results
170
+ *
171
+ * @example
172
+ * ```ts
173
+ * const results = await do.parallel([
174
+ * 'Generate sales report',
175
+ * 'Generate marketing report',
176
+ * 'Generate finance report',
177
+ * ], {
178
+ * timeout: 60000,
179
+ * })
180
+ *
181
+ * const successful = results.filter(r => r.success)
182
+ * console.log(`Completed ${successful.length} of ${results.length} tasks`)
183
+ * ```
184
+ */
185
+ doTask.parallel = async <T = unknown>(
186
+ tasks: string[],
187
+ options: DoOptions = {}
188
+ ): Promise<Array<TaskResult<T>>> => {
189
+ return Promise.all(tasks.map((task) => doTask<T>(task, options)))
190
+ }
191
+
192
+ /**
193
+ * Execute tasks in sequence
194
+ *
195
+ * @param tasks - Array of tasks to execute sequentially
196
+ * @param options - Execution options
197
+ * @returns Promise resolving to array of task results
198
+ *
199
+ * @example
200
+ * ```ts
201
+ * const results = await do.sequence([
202
+ * 'Backup database',
203
+ * 'Run migrations',
204
+ * 'Restart application',
205
+ * ], {
206
+ * maxRetries: 1,
207
+ * })
208
+ * ```
209
+ */
210
+ doTask.sequence = async <T = unknown>(
211
+ tasks: string[],
212
+ options: DoOptions = {}
213
+ ): Promise<Array<TaskResult<T>>> => {
214
+ const results: Array<TaskResult<T>> = []
215
+
216
+ for (const task of tasks) {
217
+ const result = await doTask<T>(task, options)
218
+ results.push(result)
219
+
220
+ // Stop if a task fails (unless we're continuing on error)
221
+ if (!result.success && !options.context?.continueOnError) {
222
+ break
223
+ }
224
+ }
225
+
226
+ return results
227
+ }
228
+
229
+ /**
230
+ * Execute a task with specific dependencies
231
+ *
232
+ * @param task - The task to execute
233
+ * @param dependencies - Tasks that must complete first
234
+ * @param options - Execution options
235
+ * @returns Promise resolving to task result
236
+ *
237
+ * @example
238
+ * ```ts
239
+ * const result = await do.withDependencies(
240
+ * 'Deploy application',
241
+ * ['Run tests', 'Build artifacts', 'Get approval'],
242
+ * { maxRetries: 1 }
243
+ * )
244
+ * ```
245
+ */
246
+ doTask.withDependencies = async <T = unknown>(
247
+ task: string,
248
+ dependencies: string[],
249
+ options: DoOptions = {}
250
+ ): Promise<TaskResult<T>> => {
251
+ // Execute dependencies in sequence
252
+ const depResults = await doTask.sequence(dependencies, options)
253
+
254
+ // Check if all dependencies succeeded
255
+ const allSuccessful = depResults.every((r) => r.success)
256
+
257
+ if (!allSuccessful) {
258
+ const failed = depResults.filter((r) => !r.success)
259
+ return {
260
+ result: undefined as T,
261
+ success: false,
262
+ error: `Dependencies failed: ${failed.map((r) => r.error).join(', ')}`,
263
+ duration: 0,
264
+ }
265
+ }
266
+
267
+ // Execute main task with dependency results as context
268
+ return doTask<T>(task, {
269
+ ...options,
270
+ context: {
271
+ ...options.context,
272
+ dependencies: depResults.map((r) => r.result),
273
+ },
274
+ })
275
+ }