autonomous-agents 0.1.0 → 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 (51) hide show
  1. package/.turbo/turbo-build.log +5 -0
  2. package/CHANGELOG.md +9 -0
  3. package/README.md +260 -96
  4. package/dist/actions.d.ts +136 -0
  5. package/dist/actions.d.ts.map +1 -0
  6. package/dist/actions.js +303 -0
  7. package/dist/actions.js.map +1 -0
  8. package/dist/agent.d.ts +49 -0
  9. package/dist/agent.d.ts.map +1 -0
  10. package/dist/agent.js +452 -0
  11. package/dist/agent.js.map +1 -0
  12. package/dist/goals.d.ts +138 -0
  13. package/dist/goals.d.ts.map +1 -0
  14. package/dist/goals.js +342 -0
  15. package/dist/goals.js.map +1 -0
  16. package/dist/index.d.ts +55 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +60 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/metrics.d.ts +245 -0
  21. package/dist/metrics.d.ts.map +1 -0
  22. package/dist/metrics.js +436 -0
  23. package/dist/metrics.js.map +1 -0
  24. package/dist/role.d.ts +122 -0
  25. package/dist/role.d.ts.map +1 -0
  26. package/dist/role.js +393 -0
  27. package/dist/role.js.map +1 -0
  28. package/dist/team.d.ts +152 -0
  29. package/dist/team.d.ts.map +1 -0
  30. package/dist/team.js +347 -0
  31. package/dist/team.js.map +1 -0
  32. package/dist/types.d.ts +327 -0
  33. package/dist/types.d.ts.map +1 -0
  34. package/dist/types.js +8 -0
  35. package/dist/types.js.map +1 -0
  36. package/package.json +27 -36
  37. package/src/actions.ts +366 -0
  38. package/src/agent.ts +548 -0
  39. package/src/goals.ts +435 -0
  40. package/src/index.ts +135 -0
  41. package/src/metrics.ts +591 -0
  42. package/src/role.ts +422 -0
  43. package/src/team.ts +466 -0
  44. package/src/types.ts +356 -0
  45. package/test/actions.test.ts +522 -0
  46. package/test/agent.test.ts +490 -0
  47. package/test/goals.test.ts +570 -0
  48. package/test/metrics.test.ts +707 -0
  49. package/test/role.test.ts +423 -0
  50. package/test/team.test.ts +708 -0
  51. package/tsconfig.json +9 -0
@@ -0,0 +1,522 @@
1
+ /**
2
+ * Tests for Actions functionality
3
+ *
4
+ * Covers standalone action functions.
5
+ * Uses mocks since these functions require AI calls.
6
+ */
7
+
8
+ import { describe, it, expect, vi, beforeEach } from 'vitest'
9
+ import {
10
+ doAction,
11
+ ask,
12
+ decide,
13
+ approve,
14
+ generate,
15
+ is,
16
+ notify,
17
+ } from '../src/actions.js'
18
+
19
+ // Mock the ai-functions module
20
+ vi.mock('ai-functions', () => ({
21
+ generateObject: vi.fn(),
22
+ }))
23
+
24
+ import { generateObject } from 'ai-functions'
25
+
26
+ const mockGenerateObject = vi.mocked(generateObject)
27
+
28
+ describe('Actions', () => {
29
+ beforeEach(() => {
30
+ vi.clearAllMocks()
31
+ })
32
+
33
+ describe('doAction', () => {
34
+ it('executes a task and returns result', async () => {
35
+ mockGenerateObject.mockResolvedValueOnce({
36
+ object: { result: 'Task completed successfully' },
37
+ })
38
+
39
+ const result = await doAction('Complete the task')
40
+
41
+ expect(result).toBe('Task completed successfully')
42
+ expect(mockGenerateObject).toHaveBeenCalledTimes(1)
43
+ })
44
+
45
+ it('passes context to the task', async () => {
46
+ mockGenerateObject.mockResolvedValueOnce({
47
+ object: { result: 'Analyzed' },
48
+ })
49
+
50
+ await doAction('Analyze data', { data: [1, 2, 3] })
51
+
52
+ expect(mockGenerateObject).toHaveBeenCalledWith(
53
+ expect.objectContaining({
54
+ prompt: expect.stringContaining('data'),
55
+ })
56
+ )
57
+ })
58
+
59
+ it('uses custom options', async () => {
60
+ mockGenerateObject.mockResolvedValueOnce({
61
+ object: { result: 'Done' },
62
+ })
63
+
64
+ await doAction('Task', {}, { model: 'opus', temperature: 0.5 })
65
+
66
+ expect(mockGenerateObject).toHaveBeenCalledWith(
67
+ expect.objectContaining({
68
+ model: 'opus',
69
+ temperature: 0.5,
70
+ })
71
+ )
72
+ })
73
+
74
+ it('uses default model when not specified', async () => {
75
+ mockGenerateObject.mockResolvedValueOnce({
76
+ object: { result: 'Done' },
77
+ })
78
+
79
+ await doAction('Task')
80
+
81
+ expect(mockGenerateObject).toHaveBeenCalledWith(
82
+ expect.objectContaining({
83
+ model: 'sonnet',
84
+ })
85
+ )
86
+ })
87
+ })
88
+
89
+ describe('ask', () => {
90
+ it('asks a question and returns answer', async () => {
91
+ mockGenerateObject.mockResolvedValueOnce({
92
+ object: { answer: 'Paris', reasoning: 'Paris is the capital of France' },
93
+ })
94
+
95
+ const result = await ask('What is the capital of France?')
96
+
97
+ expect(result).toBe('Paris')
98
+ })
99
+
100
+ it('passes context to the question', async () => {
101
+ mockGenerateObject.mockResolvedValueOnce({
102
+ object: { answer: 'TypeScript', reasoning: 'Based on the context' },
103
+ })
104
+
105
+ await ask('What language?', { project: 'web' })
106
+
107
+ expect(mockGenerateObject).toHaveBeenCalledWith(
108
+ expect.objectContaining({
109
+ prompt: expect.stringContaining('project'),
110
+ })
111
+ )
112
+ })
113
+
114
+ it('uses custom options', async () => {
115
+ mockGenerateObject.mockResolvedValueOnce({
116
+ object: { answer: 'Answer', reasoning: 'Reason' },
117
+ })
118
+
119
+ await ask('Question', {}, { model: 'opus' })
120
+
121
+ expect(mockGenerateObject).toHaveBeenCalledWith(
122
+ expect.objectContaining({
123
+ model: 'opus',
124
+ })
125
+ )
126
+ })
127
+ })
128
+
129
+ describe('decide', () => {
130
+ it('makes a decision between options', async () => {
131
+ mockGenerateObject.mockResolvedValueOnce({
132
+ object: {
133
+ decision: 'option A',
134
+ reasoning: 'Best choice',
135
+ confidence: 85,
136
+ },
137
+ })
138
+
139
+ const result = await decide(['option A', 'option B', 'option C'])
140
+
141
+ expect(result).toBe('option A')
142
+ })
143
+
144
+ it('includes context in decision', async () => {
145
+ mockGenerateObject.mockResolvedValueOnce({
146
+ object: {
147
+ decision: 'React',
148
+ reasoning: 'Best for the use case',
149
+ confidence: 90,
150
+ },
151
+ })
152
+
153
+ await decide(['React', 'Vue', 'Angular'], 'Building a dashboard')
154
+
155
+ expect(mockGenerateObject).toHaveBeenCalledWith(
156
+ expect.objectContaining({
157
+ prompt: expect.stringContaining('dashboard'),
158
+ })
159
+ )
160
+ })
161
+
162
+ it('uses custom model settings', async () => {
163
+ mockGenerateObject.mockResolvedValueOnce({
164
+ object: {
165
+ decision: 'A',
166
+ reasoning: 'Reason',
167
+ confidence: 75,
168
+ },
169
+ })
170
+
171
+ await decide(['A', 'B'], 'Context', { model: 'opus' })
172
+
173
+ expect(mockGenerateObject).toHaveBeenCalledWith(
174
+ expect.objectContaining({
175
+ model: 'opus',
176
+ })
177
+ )
178
+ })
179
+ })
180
+
181
+ describe('approve', () => {
182
+ it('creates an approval request', async () => {
183
+ mockGenerateObject.mockResolvedValueOnce({
184
+ object: {
185
+ component: 'ApprovalForm',
186
+ schema: { approved: 'boolean' },
187
+ },
188
+ })
189
+
190
+ const result = await approve({
191
+ title: 'Budget Request',
192
+ description: 'Request $50k for marketing',
193
+ data: { amount: 50000 },
194
+ })
195
+
196
+ expect(result.status).toBe('pending')
197
+ expect(result.timestamp).toBeInstanceOf(Date)
198
+ })
199
+
200
+ it('includes approver in request', async () => {
201
+ mockGenerateObject.mockResolvedValueOnce({
202
+ object: { component: 'Form', schema: {} },
203
+ })
204
+
205
+ await approve({
206
+ title: 'Request',
207
+ description: 'Description',
208
+ data: {},
209
+ approver: 'manager@example.com',
210
+ })
211
+
212
+ expect(mockGenerateObject).toHaveBeenCalledWith(
213
+ expect.objectContaining({
214
+ prompt: expect.stringContaining('manager@example.com'),
215
+ })
216
+ )
217
+ })
218
+
219
+ it('includes priority in request', async () => {
220
+ mockGenerateObject.mockResolvedValueOnce({
221
+ object: { component: 'Form', schema: {} },
222
+ })
223
+
224
+ await approve({
225
+ title: 'Urgent Request',
226
+ description: 'Needs immediate attention',
227
+ data: {},
228
+ priority: 'high',
229
+ })
230
+
231
+ expect(mockGenerateObject).toHaveBeenCalledWith(
232
+ expect.objectContaining({
233
+ prompt: expect.stringContaining('high'),
234
+ })
235
+ )
236
+ })
237
+
238
+ it('supports different channels', async () => {
239
+ mockGenerateObject.mockResolvedValueOnce({
240
+ object: { blocks: [], text: 'Approval needed' },
241
+ })
242
+
243
+ await approve({
244
+ title: 'Request',
245
+ description: 'Description',
246
+ data: {},
247
+ channel: 'slack',
248
+ })
249
+
250
+ expect(mockGenerateObject).toHaveBeenCalledWith(
251
+ expect.objectContaining({
252
+ system: expect.stringContaining('slack'),
253
+ })
254
+ )
255
+ })
256
+ })
257
+
258
+ describe('generate', () => {
259
+ it('generates content with schema', async () => {
260
+ mockGenerateObject.mockResolvedValueOnce({
261
+ object: {
262
+ title: 'AI in Healthcare',
263
+ content: 'AI is transforming healthcare...',
264
+ tags: ['AI', 'healthcare'],
265
+ },
266
+ })
267
+
268
+ const result = await generate({
269
+ schema: {
270
+ title: 'Blog post title',
271
+ content: 'Blog post content',
272
+ tags: ['List of tags'],
273
+ },
274
+ prompt: 'Write a blog post about AI in healthcare',
275
+ })
276
+
277
+ expect(result).toHaveProperty('title')
278
+ expect(result).toHaveProperty('content')
279
+ })
280
+
281
+ it('uses custom model', async () => {
282
+ mockGenerateObject.mockResolvedValueOnce({
283
+ object: { result: 'Generated' },
284
+ })
285
+
286
+ await generate({
287
+ model: 'opus',
288
+ prompt: 'Generate content',
289
+ })
290
+
291
+ expect(mockGenerateObject).toHaveBeenCalledWith(
292
+ expect.objectContaining({
293
+ model: 'opus',
294
+ })
295
+ )
296
+ })
297
+
298
+ it('uses higher temperature by default', async () => {
299
+ mockGenerateObject.mockResolvedValueOnce({
300
+ object: { result: 'Creative content' },
301
+ })
302
+
303
+ await generate({
304
+ prompt: 'Be creative',
305
+ })
306
+
307
+ expect(mockGenerateObject).toHaveBeenCalledWith(
308
+ expect.objectContaining({
309
+ temperature: 0.8,
310
+ })
311
+ )
312
+ })
313
+
314
+ it('allows custom temperature', async () => {
315
+ mockGenerateObject.mockResolvedValueOnce({
316
+ object: { result: 'Focused content' },
317
+ })
318
+
319
+ await generate({
320
+ prompt: 'Be precise',
321
+ temperature: 0.2,
322
+ })
323
+
324
+ expect(mockGenerateObject).toHaveBeenCalledWith(
325
+ expect.objectContaining({
326
+ temperature: 0.2,
327
+ })
328
+ )
329
+ })
330
+ })
331
+
332
+ describe('is', () => {
333
+ it('validates a type with string descriptor', async () => {
334
+ mockGenerateObject.mockResolvedValueOnce({
335
+ object: { isValid: true, reason: 'Valid email format' },
336
+ })
337
+
338
+ const result = await is('test@example.com', 'valid email address')
339
+
340
+ expect(result).toBe(true)
341
+ })
342
+
343
+ it('validates a type with schema', async () => {
344
+ mockGenerateObject.mockResolvedValueOnce({
345
+ object: { isValid: true, reason: 'Matches schema' },
346
+ })
347
+
348
+ const result = await is(
349
+ { name: 'John', age: 30 },
350
+ { name: 'string', age: 'number' }
351
+ )
352
+
353
+ expect(result).toBe(true)
354
+ })
355
+
356
+ it('returns false for invalid type', async () => {
357
+ mockGenerateObject.mockResolvedValueOnce({
358
+ object: { isValid: false, reason: 'Not a valid email' },
359
+ })
360
+
361
+ const result = await is('not-an-email', 'valid email address')
362
+
363
+ expect(result).toBe(false)
364
+ })
365
+
366
+ it('uses low temperature for validation', async () => {
367
+ mockGenerateObject.mockResolvedValueOnce({
368
+ object: { isValid: true, reason: 'Valid' },
369
+ })
370
+
371
+ await is('value', 'type')
372
+
373
+ expect(mockGenerateObject).toHaveBeenCalledWith(
374
+ expect.objectContaining({
375
+ temperature: 0,
376
+ })
377
+ )
378
+ })
379
+ })
380
+
381
+ describe('notify', () => {
382
+ it('sends a notification', async () => {
383
+ mockGenerateObject.mockResolvedValueOnce({
384
+ object: {
385
+ title: 'Notification',
386
+ message: 'Task completed',
387
+ type: 'success',
388
+ },
389
+ })
390
+
391
+ // Should not throw
392
+ await expect(
393
+ notify({
394
+ message: 'Task completed successfully!',
395
+ })
396
+ ).resolves.toBeUndefined()
397
+ })
398
+
399
+ it('uses specified channel', async () => {
400
+ mockGenerateObject.mockResolvedValueOnce({
401
+ object: { blocks: [], text: 'Notification' },
402
+ })
403
+
404
+ await notify({
405
+ message: 'Alert',
406
+ channel: 'slack',
407
+ })
408
+
409
+ expect(mockGenerateObject).toHaveBeenCalledWith(
410
+ expect.objectContaining({
411
+ system: expect.stringContaining('slack'),
412
+ })
413
+ )
414
+ })
415
+
416
+ it('includes recipients', async () => {
417
+ mockGenerateObject.mockResolvedValueOnce({
418
+ object: { text: 'Notification' },
419
+ })
420
+
421
+ await notify({
422
+ message: 'Update',
423
+ recipients: ['#team', '@user'],
424
+ })
425
+
426
+ expect(mockGenerateObject).toHaveBeenCalledWith(
427
+ expect.objectContaining({
428
+ prompt: expect.stringContaining('#team'),
429
+ })
430
+ )
431
+ })
432
+
433
+ it('includes priority', async () => {
434
+ mockGenerateObject.mockResolvedValueOnce({
435
+ object: { text: 'Alert' },
436
+ })
437
+
438
+ await notify({
439
+ message: 'Critical alert',
440
+ priority: 'high',
441
+ })
442
+
443
+ expect(mockGenerateObject).toHaveBeenCalledWith(
444
+ expect.objectContaining({
445
+ prompt: expect.stringContaining('high'),
446
+ })
447
+ )
448
+ })
449
+
450
+ it('includes additional data', async () => {
451
+ mockGenerateObject.mockResolvedValueOnce({
452
+ object: { text: 'Notification' },
453
+ })
454
+
455
+ await notify({
456
+ message: 'Task done',
457
+ data: { taskId: '123', duration: '5 minutes' },
458
+ })
459
+
460
+ expect(mockGenerateObject).toHaveBeenCalledWith(
461
+ expect.objectContaining({
462
+ prompt: expect.stringContaining('taskId'),
463
+ })
464
+ )
465
+ })
466
+ })
467
+ })
468
+
469
+ describe('Action scenarios', () => {
470
+ beforeEach(() => {
471
+ vi.clearAllMocks()
472
+ })
473
+
474
+ it('chains multiple actions', async () => {
475
+ // Mock ask
476
+ mockGenerateObject.mockResolvedValueOnce({
477
+ object: { answer: 'TypeScript', reasoning: 'Best for this project' },
478
+ })
479
+
480
+ // Mock decide
481
+ mockGenerateObject.mockResolvedValueOnce({
482
+ object: { decision: 'React', reasoning: 'Popular choice', confidence: 90 },
483
+ })
484
+
485
+ // Mock doAction
486
+ mockGenerateObject.mockResolvedValueOnce({
487
+ object: { result: 'Project created' },
488
+ })
489
+
490
+ // Execute chain
491
+ const language = await ask('What language should we use?')
492
+ const framework = await decide(['React', 'Vue', 'Angular'])
493
+ const result = await doAction('Create project', { language, framework })
494
+
495
+ expect(language).toBe('TypeScript')
496
+ expect(framework).toBe('React')
497
+ expect(result).toBe('Project created')
498
+ expect(mockGenerateObject).toHaveBeenCalledTimes(3)
499
+ })
500
+
501
+ it('validates before processing', async () => {
502
+ // Mock is
503
+ mockGenerateObject.mockResolvedValueOnce({
504
+ object: { isValid: true, reason: 'Valid email' },
505
+ })
506
+
507
+ // Mock doAction
508
+ mockGenerateObject.mockResolvedValueOnce({
509
+ object: { result: 'Email sent' },
510
+ })
511
+
512
+ const email = 'user@example.com'
513
+ const isValid = await is(email, 'valid email address')
514
+
515
+ if (isValid) {
516
+ const result = await doAction('Send email', { to: email })
517
+ expect(result).toBe('Email sent')
518
+ }
519
+
520
+ expect(mockGenerateObject).toHaveBeenCalledTimes(2)
521
+ })
522
+ })