autonomous-agents 2.0.2 → 2.1.3

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.
package/src/agent.js ADDED
@@ -0,0 +1,451 @@
1
+ /**
2
+ * Agent - Create an autonomous agent
3
+ *
4
+ * Agents are autonomous AI workers that can execute tasks, make decisions,
5
+ * and collaborate with other agents and humans.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ import { generateObject } from 'ai-functions';
10
+ import { executeApproval } from './actions.js';
11
+ /**
12
+ * Create an autonomous agent
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { Agent, Role } from 'autonomous-agents'
17
+ *
18
+ * const agent = Agent({
19
+ * name: 'ProductAgent',
20
+ * role: Role({
21
+ * name: 'Product Manager',
22
+ * description: 'Manages product strategy and roadmap',
23
+ * skills: ['product strategy', 'user research', 'roadmap planning'],
24
+ * }),
25
+ * mode: 'autonomous',
26
+ * goals: [
27
+ * { id: 'g1', description: 'Define Q1 product roadmap', target: '100%' }
28
+ * ],
29
+ * })
30
+ *
31
+ * // Execute a task
32
+ * const result = await agent.do('Create a product brief for new feature X')
33
+ *
34
+ * // Ask a question
35
+ * const answer = await agent.ask('What are the top 3 customer pain points?')
36
+ *
37
+ * // Make a decision
38
+ * const decision = await agent.decide(['feature A', 'feature B', 'feature C'], 'Which should we prioritize?')
39
+ *
40
+ * // Request approval
41
+ * const approval = await agent.approve({
42
+ * title: 'Budget Request',
43
+ * description: 'Request $50k budget for user research',
44
+ * data: { amount: 50000, purpose: 'User research study' },
45
+ * approver: 'manager@company.com',
46
+ * })
47
+ * ```
48
+ */
49
+ export function Agent(config) {
50
+ // Initialize agent state
51
+ const state = config.context || {};
52
+ let status = 'idle';
53
+ const history = [];
54
+ // Default model and parameters
55
+ const model = config.model || 'sonnet';
56
+ const temperature = config.temperature ?? 0.7;
57
+ const maxIterations = config.maxIterations || 10;
58
+ /**
59
+ * Record action in history
60
+ */
61
+ function recordHistory(entry) {
62
+ history.push({
63
+ ...entry,
64
+ timestamp: new Date(),
65
+ });
66
+ }
67
+ /**
68
+ * Execute a task
69
+ */
70
+ async function doTask(task, context) {
71
+ const startTime = Date.now();
72
+ status = 'thinking';
73
+ try {
74
+ // Build system prompt with role and goals
75
+ const systemPrompt = buildSystemPrompt(config);
76
+ // For autonomous mode, use agentic loop
77
+ if (config.mode === 'autonomous') {
78
+ const result = await executeAgenticTask(task, context, systemPrompt, config.tools || [], maxIterations);
79
+ recordHistory({
80
+ type: 'task',
81
+ action: task,
82
+ input: context,
83
+ output: result,
84
+ duration: Date.now() - startTime,
85
+ });
86
+ status = 'completed';
87
+ return result;
88
+ }
89
+ // For supervised/manual mode, generate response with optional approval
90
+ const result = await generateObject({
91
+ model,
92
+ schema: {
93
+ result: 'The result of the task',
94
+ reasoning: 'Step-by-step reasoning',
95
+ needsApproval: 'Whether this needs approval (boolean)',
96
+ },
97
+ system: systemPrompt,
98
+ prompt: `Task: ${task}\n\nContext: ${JSON.stringify(context || {})}`,
99
+ temperature,
100
+ });
101
+ const response = result.object;
102
+ // Request approval if needed and agent requires approval
103
+ if (config.requiresApproval || response.needsApproval) {
104
+ const approval = await executeApproval({
105
+ title: `Task: ${task}`,
106
+ description: response.reasoning,
107
+ data: response.result,
108
+ approver: config.supervisor,
109
+ channel: 'web',
110
+ });
111
+ if (approval.status !== 'approved') {
112
+ throw new Error(`Task approval ${approval.status}: ${approval.notes || ''}`);
113
+ }
114
+ }
115
+ recordHistory({
116
+ type: 'task',
117
+ action: task,
118
+ input: context,
119
+ output: response.result,
120
+ duration: Date.now() - startTime,
121
+ });
122
+ status = 'completed';
123
+ return response.result;
124
+ }
125
+ catch (error) {
126
+ status = 'error';
127
+ recordHistory({
128
+ type: 'error',
129
+ action: task,
130
+ input: context,
131
+ error: error instanceof Error ? error.message : String(error),
132
+ duration: Date.now() - startTime,
133
+ });
134
+ throw error;
135
+ }
136
+ finally {
137
+ if (status !== 'error') {
138
+ status = 'idle';
139
+ }
140
+ }
141
+ }
142
+ /**
143
+ * Ask a question
144
+ */
145
+ async function ask(question, context) {
146
+ const startTime = Date.now();
147
+ status = 'thinking';
148
+ try {
149
+ const systemPrompt = buildSystemPrompt(config);
150
+ const result = await generateObject({
151
+ model,
152
+ schema: {
153
+ answer: 'The answer to the question',
154
+ reasoning: 'Supporting reasoning and evidence',
155
+ },
156
+ system: systemPrompt,
157
+ prompt: `Question: ${question}\n\nContext: ${JSON.stringify(context || {})}`,
158
+ temperature,
159
+ });
160
+ const response = result.object;
161
+ recordHistory({
162
+ type: 'question',
163
+ action: question,
164
+ input: context,
165
+ output: response.answer,
166
+ duration: Date.now() - startTime,
167
+ });
168
+ status = 'idle';
169
+ return response.answer;
170
+ }
171
+ catch (error) {
172
+ status = 'error';
173
+ recordHistory({
174
+ type: 'error',
175
+ action: question,
176
+ input: context,
177
+ error: error instanceof Error ? error.message : String(error),
178
+ duration: Date.now() - startTime,
179
+ });
180
+ throw error;
181
+ }
182
+ }
183
+ /**
184
+ * Make a decision
185
+ */
186
+ async function decide(options, context) {
187
+ const startTime = Date.now();
188
+ status = 'thinking';
189
+ try {
190
+ const systemPrompt = buildSystemPrompt(config);
191
+ const result = await generateObject({
192
+ model,
193
+ schema: {
194
+ decision: options.join(' | '),
195
+ reasoning: 'Reasoning for this decision',
196
+ confidence: 'Confidence level 0-100 (number)',
197
+ },
198
+ system: systemPrompt,
199
+ prompt: `Make a decision between these options:\n${options.map((o, i) => `${i + 1}. ${o}`).join('\n')}\n\nContext: ${context || 'No additional context'}`,
200
+ temperature,
201
+ });
202
+ const response = result.object;
203
+ recordHistory({
204
+ type: 'decision',
205
+ action: `Choose from: ${options.join(', ')}`,
206
+ input: context,
207
+ output: response,
208
+ duration: Date.now() - startTime,
209
+ });
210
+ status = 'idle';
211
+ return response.decision;
212
+ }
213
+ catch (error) {
214
+ status = 'error';
215
+ recordHistory({
216
+ type: 'error',
217
+ action: 'decision',
218
+ input: { options, context },
219
+ error: error instanceof Error ? error.message : String(error),
220
+ duration: Date.now() - startTime,
221
+ });
222
+ throw error;
223
+ }
224
+ }
225
+ /**
226
+ * Request approval
227
+ */
228
+ async function approve(request) {
229
+ const startTime = Date.now();
230
+ status = 'waiting';
231
+ try {
232
+ const result = await executeApproval(request);
233
+ recordHistory({
234
+ type: 'approval',
235
+ action: request.title,
236
+ input: request,
237
+ output: result,
238
+ duration: Date.now() - startTime,
239
+ });
240
+ status = 'idle';
241
+ return result;
242
+ }
243
+ catch (error) {
244
+ status = 'error';
245
+ recordHistory({
246
+ type: 'error',
247
+ action: request.title,
248
+ input: request,
249
+ error: error instanceof Error ? error.message : String(error),
250
+ duration: Date.now() - startTime,
251
+ });
252
+ throw error;
253
+ }
254
+ }
255
+ /**
256
+ * Generate content
257
+ */
258
+ async function generate(options) {
259
+ const startTime = Date.now();
260
+ status = 'acting';
261
+ try {
262
+ const systemPrompt = buildSystemPrompt(config);
263
+ const result = await generateObject({
264
+ model: options.model || model,
265
+ schema: options.schema || { result: 'Generated content' },
266
+ system: options.system || systemPrompt,
267
+ prompt: options.prompt || '',
268
+ temperature: options.temperature ?? temperature,
269
+ });
270
+ recordHistory({
271
+ type: 'task',
272
+ action: 'generate',
273
+ input: options,
274
+ output: result.object,
275
+ duration: Date.now() - startTime,
276
+ });
277
+ status = 'idle';
278
+ return result.object;
279
+ }
280
+ catch (error) {
281
+ status = 'error';
282
+ recordHistory({
283
+ type: 'error',
284
+ action: 'generate',
285
+ input: options,
286
+ error: error instanceof Error ? error.message : String(error),
287
+ duration: Date.now() - startTime,
288
+ });
289
+ throw error;
290
+ }
291
+ }
292
+ /**
293
+ * Type checking/validation
294
+ */
295
+ async function is(value, type) {
296
+ try {
297
+ const schema = typeof type === 'string'
298
+ ? { isValid: `Is this value a valid ${type}? (boolean)` }
299
+ : { isValid: 'Does this value match the schema? (boolean)' };
300
+ const result = await generateObject({
301
+ model,
302
+ schema,
303
+ system: 'You are a type validator. Determine if the value matches the expected type.',
304
+ prompt: `Value: ${JSON.stringify(value)}\n\nExpected type: ${typeof type === 'string' ? type : JSON.stringify(type)}`,
305
+ temperature: 0,
306
+ });
307
+ return result.object.isValid;
308
+ }
309
+ catch {
310
+ return false;
311
+ }
312
+ }
313
+ /**
314
+ * Send notification
315
+ */
316
+ async function notify(message, channel) {
317
+ const startTime = Date.now();
318
+ try {
319
+ // In a real implementation, this would send via the specified channel
320
+ recordHistory({
321
+ type: 'notification',
322
+ action: 'notify',
323
+ input: { message, channel },
324
+ duration: Date.now() - startTime,
325
+ });
326
+ // For now, just log
327
+ console.log(`[${config.name}] ${message}`);
328
+ }
329
+ catch (error) {
330
+ recordHistory({
331
+ type: 'error',
332
+ action: 'notify',
333
+ input: { message, channel },
334
+ error: error instanceof Error ? error.message : String(error),
335
+ duration: Date.now() - startTime,
336
+ });
337
+ throw error;
338
+ }
339
+ }
340
+ /**
341
+ * Update agent state
342
+ */
343
+ function setState(key, value) {
344
+ state[key] = value;
345
+ }
346
+ /**
347
+ * Get agent state
348
+ */
349
+ function getState(key) {
350
+ return state[key];
351
+ }
352
+ /**
353
+ * Get agent history
354
+ */
355
+ function getHistory() {
356
+ return [...history];
357
+ }
358
+ /**
359
+ * Reset agent state
360
+ */
361
+ function reset() {
362
+ Object.keys(state).forEach(key => delete state[key]);
363
+ history.length = 0;
364
+ status = 'idle';
365
+ }
366
+ return {
367
+ config,
368
+ status,
369
+ state,
370
+ do: doTask,
371
+ ask,
372
+ decide,
373
+ approve,
374
+ generate,
375
+ is,
376
+ notify,
377
+ setState,
378
+ getState,
379
+ getHistory,
380
+ reset,
381
+ };
382
+ }
383
+ /**
384
+ * Build system prompt from agent configuration
385
+ */
386
+ function buildSystemPrompt(config) {
387
+ const parts = [];
388
+ parts.push(`You are ${config.name}, an autonomous AI agent.`);
389
+ if (config.description) {
390
+ parts.push(config.description);
391
+ }
392
+ if (config.role) {
393
+ parts.push(`\nRole: ${config.role.name}`);
394
+ parts.push(config.role.description);
395
+ if (config.role.skills.length > 0) {
396
+ parts.push(`\nSkills: ${config.role.skills.join(', ')}`);
397
+ }
398
+ }
399
+ if (config.goals && config.goals.length > 0) {
400
+ parts.push('\nGoals:');
401
+ config.goals.forEach((goal, i) => {
402
+ parts.push(`${i + 1}. ${goal.description}`);
403
+ });
404
+ }
405
+ if (config.system) {
406
+ parts.push(`\n${config.system}`);
407
+ }
408
+ return parts.join('\n');
409
+ }
410
+ /**
411
+ * Execute an agentic task with tool use
412
+ */
413
+ async function executeAgenticTask(task, context, systemPrompt, tools, maxIterations) {
414
+ let iteration = 0;
415
+ const toolResults = [];
416
+ while (iteration < maxIterations) {
417
+ iteration++;
418
+ const result = await generateObject({
419
+ model: 'sonnet',
420
+ schema: {
421
+ thinking: 'Your step-by-step reasoning',
422
+ action: {
423
+ type: 'tool | done',
424
+ toolName: 'Name of tool to use (if action is tool)',
425
+ arguments: 'Arguments for the tool as JSON string',
426
+ },
427
+ result: 'The final result if done',
428
+ },
429
+ system: systemPrompt,
430
+ prompt: `Task: ${task}\n\nContext: ${JSON.stringify(context)}\n\nPrevious actions:\n${toolResults.map((r, i) => `${i + 1}. ${JSON.stringify(r)}`).join('\n') || 'None yet'}\n\nWhat should you do next?`,
431
+ });
432
+ const response = result.object;
433
+ // Check if done
434
+ if (response.action.type === 'done' || response.result) {
435
+ return response.result;
436
+ }
437
+ // Execute tool
438
+ if (response.action.type === 'tool' && response.action.toolName) {
439
+ const tool = tools.find(t => t.name === response.action.toolName);
440
+ if (tool) {
441
+ const args = JSON.parse(response.action.arguments || '{}');
442
+ const toolResult = await tool.handler(args);
443
+ toolResults.push({ tool: response.action.toolName, result: toolResult });
444
+ }
445
+ else {
446
+ toolResults.push({ error: `Tool not found: ${response.action.toolName}` });
447
+ }
448
+ }
449
+ }
450
+ throw new Error(`Agent exceeded maximum iterations (${maxIterations})`);
451
+ }