@agentuity/runtime 0.0.59 → 0.0.61

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 (61) hide show
  1. package/dist/_context.d.ts +11 -7
  2. package/dist/_context.d.ts.map +1 -1
  3. package/dist/_context.js +9 -2
  4. package/dist/_context.js.map +1 -1
  5. package/dist/_server.d.ts +4 -2
  6. package/dist/_server.d.ts.map +1 -1
  7. package/dist/_server.js +71 -31
  8. package/dist/_server.js.map +1 -1
  9. package/dist/_services.d.ts +1 -1
  10. package/dist/_services.d.ts.map +1 -1
  11. package/dist/_services.js +4 -2
  12. package/dist/_services.js.map +1 -1
  13. package/dist/_waituntil.d.ts.map +1 -1
  14. package/dist/_waituntil.js +5 -2
  15. package/dist/_waituntil.js.map +1 -1
  16. package/dist/agent.d.ts +647 -19
  17. package/dist/agent.d.ts.map +1 -1
  18. package/dist/agent.js +55 -6
  19. package/dist/agent.js.map +1 -1
  20. package/dist/app.d.ts +205 -28
  21. package/dist/app.d.ts.map +1 -1
  22. package/dist/app.js +181 -13
  23. package/dist/app.js.map +1 -1
  24. package/dist/index.d.ts +41 -2
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +2 -2
  27. package/dist/index.js.map +1 -1
  28. package/dist/io/email.d.ts.map +1 -1
  29. package/dist/io/email.js +11 -3
  30. package/dist/io/email.js.map +1 -1
  31. package/dist/router.d.ts +282 -32
  32. package/dist/router.d.ts.map +1 -1
  33. package/dist/router.js +110 -35
  34. package/dist/router.js.map +1 -1
  35. package/dist/services/evalrun/http.d.ts.map +1 -1
  36. package/dist/services/evalrun/http.js +7 -5
  37. package/dist/services/evalrun/http.js.map +1 -1
  38. package/dist/services/local/_util.d.ts.map +1 -1
  39. package/dist/services/local/_util.js +3 -1
  40. package/dist/services/local/_util.js.map +1 -1
  41. package/dist/services/session/http.d.ts.map +1 -1
  42. package/dist/services/session/http.js +4 -3
  43. package/dist/services/session/http.js.map +1 -1
  44. package/dist/session.d.ts +284 -4
  45. package/dist/session.d.ts.map +1 -1
  46. package/dist/session.js +2 -2
  47. package/dist/session.js.map +1 -1
  48. package/package.json +5 -4
  49. package/src/_context.ts +37 -9
  50. package/src/_server.ts +88 -36
  51. package/src/_services.ts +9 -2
  52. package/src/_waituntil.ts +13 -2
  53. package/src/agent.ts +856 -68
  54. package/src/app.ts +238 -38
  55. package/src/index.ts +42 -2
  56. package/src/io/email.ts +23 -5
  57. package/src/router.ts +359 -83
  58. package/src/services/evalrun/http.ts +15 -4
  59. package/src/services/local/_util.ts +7 -1
  60. package/src/services/session/http.ts +5 -2
  61. package/src/session.ts +297 -4
package/src/agent.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import type {
3
- KeyValueStorage,
4
- ObjectStorage,
5
- StandardSchemaV1,
6
- StreamStorage,
7
- VectorStorage,
2
+ import {
3
+ StructuredError,
4
+ type KeyValueStorage,
5
+ type ObjectStorage,
6
+ type StandardSchemaV1,
7
+ type StreamStorage,
8
+ type VectorStorage,
8
9
  } from '@agentuity/core';
9
10
  import { context, SpanStatusCode, type Tracer, trace } from '@opentelemetry/api';
10
11
  import type { Context, MiddlewareHandler } from 'hono';
@@ -21,11 +22,12 @@ import type {
21
22
  import { internal } from './logger/internal';
22
23
  import { getApp } from './app';
23
24
  import type { Thread, Session } from './session';
24
- import { privateContext } from './_server';
25
+ import { privateContext, notifyReady } from './_server';
25
26
  import { generateId } from './session';
26
27
  import { getEvalRunEventProvider } from './_services';
27
28
  import * as runtimeConfig from './_config';
28
29
  import type { EvalRunStartEvent } from '@agentuity/core';
30
+ import type { AppState } from './index';
29
31
 
30
32
  export type AgentEventName = 'started' | 'completed' | 'errored';
31
33
 
@@ -47,29 +49,235 @@ export type AgentEventCallback<TAgent extends Agent<any, any, any>> =
47
49
  data: Error
48
50
  ) => Promise<void> | void);
49
51
 
52
+ /**
53
+ * Context object passed to every agent handler providing access to runtime services and state.
54
+ *
55
+ * @template TAgentRegistry - Registry of all available agents (auto-generated, strongly-typed)
56
+ * @template TCurrent - Current agent runner type
57
+ * @template TParent - Parent agent runner type (if called from another agent)
58
+ * @template TConfig - Agent-specific configuration type from setup function
59
+ * @template TAppState - Application-wide state type from createApp
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * const agent = createAgent({
64
+ * handler: async (ctx, input) => {
65
+ * // Logging
66
+ * ctx.logger.info('Processing request', { input });
67
+ *
68
+ * // Call another agent
69
+ * const result = await ctx.agent.otherAgent.run({ data: input });
70
+ *
71
+ * // Store data
72
+ * await ctx.kv.set('key', { value: result });
73
+ *
74
+ * // Access config from setup
75
+ * const cache = ctx.config.cache;
76
+ *
77
+ * // Background task
78
+ * ctx.waitUntil(async () => {
79
+ * await ctx.logger.info('Cleanup complete');
80
+ * });
81
+ *
82
+ * return result;
83
+ * }
84
+ * });
85
+ * ```
86
+ */
50
87
  export interface AgentContext<
51
88
  TAgentRegistry extends AgentRegistry = AgentRegistry,
52
89
  TCurrent extends AgentRunner<any, any, any> | undefined = AgentRunner<any, any, any> | undefined,
53
90
  TParent extends AgentRunner<any, any, any> | undefined = AgentRunner<any, any, any> | undefined,
91
+ TConfig = unknown,
92
+ TAppState = Record<string, never>,
54
93
  > {
55
- // email: () => Promise<Email | null>;
56
- // sms: () => Promise<SMS | null>;
57
- // cron: () => Promise<Cron | null>;
94
+ /**
95
+ * Schedule a background task that continues after the response is sent.
96
+ * Useful for cleanup, logging, or async operations that don't block the response.
97
+ *
98
+ * @param promise - Promise or function that returns void or Promise<void>
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * ctx.waitUntil(async () => {
103
+ * await ctx.kv.set('processed', Date.now());
104
+ * ctx.logger.info('Background task complete');
105
+ * });
106
+ * ```
107
+ */
58
108
  waitUntil: (promise: Promise<void> | (() => void | Promise<void>)) => void;
59
- agent: TAgentRegistry; // Will be augmented by generated code with strongly-typed agents
60
- current: TCurrent; // Current agent runner
61
- parent: TParent; // Parent agent runner (use ctx.agent.parentName for strict typing)
109
+
110
+ /**
111
+ * Registry of all agents in the application. Strongly-typed and auto-generated.
112
+ * Use to call other agents from within your handler.
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * const emailResult = await ctx.agent.email.run({ to: 'user@example.com' });
117
+ * const smsResult = await ctx.agent.sms.run({ phone: '+1234567890' });
118
+ * ```
119
+ */
120
+ agent: TAgentRegistry;
121
+
122
+ /**
123
+ * Information about the currently executing agent.
124
+ */
125
+ current: TCurrent;
126
+
127
+ /**
128
+ * Information about the parent agent (if this agent was called by another agent).
129
+ * Use ctx.agent.parentName for strongly-typed access.
130
+ */
131
+ parent: TParent;
132
+
133
+ /**
134
+ * Name of the current agent being executed.
135
+ */
62
136
  agentName: AgentName;
137
+
138
+ /**
139
+ * Structured logger with OpenTelemetry integration.
140
+ * Logs are automatically correlated with traces.
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * ctx.logger.info('Processing started', { userId: input.id });
145
+ * ctx.logger.warn('Rate limit approaching', { remaining: 10 });
146
+ * ctx.logger.error('Operation failed', { error: err.message });
147
+ * ```
148
+ */
63
149
  logger: Logger;
150
+
151
+ /**
152
+ * Unique session identifier for this request. Consistent across agent calls in the same session.
153
+ */
64
154
  sessionId: string;
155
+
156
+ /**
157
+ * OpenTelemetry tracer for creating custom spans and tracking performance.
158
+ *
159
+ * @example
160
+ * ```typescript
161
+ * const span = ctx.tracer.startSpan('database-query');
162
+ * try {
163
+ * const result = await database.query();
164
+ * span.setStatus({ code: SpanStatusCode.OK });
165
+ * return result;
166
+ * } finally {
167
+ * span.end();
168
+ * }
169
+ * ```
170
+ */
65
171
  tracer: Tracer;
172
+
173
+ /**
174
+ * Key-value storage for simple data persistence.
175
+ *
176
+ * @example
177
+ * ```typescript
178
+ * await ctx.kv.set('user:123', { name: 'Alice', age: 30 });
179
+ * const user = await ctx.kv.get('user:123');
180
+ * await ctx.kv.delete('user:123');
181
+ * const keys = await ctx.kv.list('user:*');
182
+ * ```
183
+ */
66
184
  kv: KeyValueStorage;
185
+
186
+ /**
187
+ * Object storage for files and blobs (S3-compatible).
188
+ *
189
+ * @example
190
+ * ```typescript
191
+ * await ctx.objectstore.put('images/photo.jpg', buffer);
192
+ * const file = await ctx.objectstore.get('images/photo.jpg');
193
+ * await ctx.objectstore.delete('images/photo.jpg');
194
+ * const objects = await ctx.objectstore.list('images/');
195
+ * ```
196
+ */
67
197
  objectstore: ObjectStorage;
198
+
199
+ /**
200
+ * Stream storage for real-time data streams and logs.
201
+ *
202
+ * @example
203
+ * ```typescript
204
+ * const stream = await ctx.stream.create('agent-logs');
205
+ * await ctx.stream.write(stream.id, 'Processing step 1');
206
+ * await ctx.stream.write(stream.id, 'Processing step 2');
207
+ * ```
208
+ */
68
209
  stream: StreamStorage;
210
+
211
+ /**
212
+ * Vector storage for embeddings and similarity search.
213
+ *
214
+ * @example
215
+ * ```typescript
216
+ * await ctx.vector.upsert('docs', [
217
+ * { id: '1', values: [0.1, 0.2, 0.3], metadata: { text: 'Hello' } }
218
+ * ]);
219
+ * const results = await ctx.vector.query('docs', [0.1, 0.2, 0.3], { topK: 5 });
220
+ * ```
221
+ */
69
222
  vector: VectorStorage;
223
+
224
+ /**
225
+ * In-memory state storage scoped to the current request.
226
+ * Use for passing data between middleware and handlers.
227
+ *
228
+ * @example
229
+ * ```typescript
230
+ * ctx.state.set('startTime', Date.now());
231
+ * const duration = Date.now() - (ctx.state.get('startTime') as number);
232
+ * ```
233
+ */
70
234
  state: Map<string, unknown>;
235
+
236
+ /**
237
+ * Thread information for multi-turn conversations.
238
+ */
71
239
  thread: Thread;
240
+
241
+ /**
242
+ * Session information for the current request.
243
+ */
72
244
  session: Session;
245
+
246
+ /**
247
+ * Agent-specific configuration returned from the setup function.
248
+ * Type is inferred from your setup function's return value.
249
+ *
250
+ * @example
251
+ * ```typescript
252
+ * createAgent({
253
+ * setup: async () => ({ cache: new Map(), db: await connectDB() }),
254
+ * handler: async (ctx, input) => {
255
+ * ctx.config.cache.set('key', 'value'); // Strongly typed!
256
+ * await ctx.config.db.query('SELECT * FROM users');
257
+ * }
258
+ * });
259
+ * ```
260
+ */
261
+ config: TConfig;
262
+
263
+ /**
264
+ * Application-wide state returned from createApp setup function.
265
+ * Shared across all agents in the application.
266
+ *
267
+ * @example
268
+ * ```typescript
269
+ * const app = createApp({
270
+ * setup: async () => ({ db: await connectDB(), redis: await connectRedis() })
271
+ * });
272
+ *
273
+ * // Later in any agent:
274
+ * handler: async (ctx, input) => {
275
+ * await ctx.app.db.query('SELECT 1');
276
+ * await ctx.app.redis.set('key', 'value');
277
+ * }
278
+ * ```
279
+ */
280
+ app: TAppState;
73
281
  }
74
282
 
75
283
  type InternalAgentMetadata = {
@@ -108,77 +316,281 @@ type ExternalAgentMetadata = {
108
316
 
109
317
  type AgentMetadata = InternalAgentMetadata & ExternalAgentMetadata;
110
318
 
111
- // Type for createEval method
112
- type CreateEvalMethod<
319
+ /**
320
+ * Configuration object for creating an agent evaluation function.
321
+ *
322
+ * @template TInput - Input schema type from the parent agent
323
+ * @template TOutput - Output schema type from the parent agent
324
+ */
325
+ export interface CreateEvalConfig<
113
326
  TInput extends StandardSchemaV1 | undefined = any,
114
327
  TOutput extends StandardSchemaV1 | undefined = any,
115
- > = (config: {
328
+ > {
329
+ /**
330
+ * Optional metadata for the evaluation function.
331
+ *
332
+ * @example
333
+ * ```typescript
334
+ * metadata: {
335
+ * name: 'Validate positive output',
336
+ * description: 'Ensures output is greater than zero'
337
+ * }
338
+ * ```
339
+ */
116
340
  metadata?: Partial<ExternalEvalMetadata>;
341
+
342
+ /**
343
+ * Evaluation handler function that tests the agent's behavior.
344
+ * Return true if the evaluation passes, false if it fails.
345
+ *
346
+ * @param run - Evaluation run context containing input and metadata
347
+ * @param result - The output from the agent handler
348
+ * @returns Boolean indicating pass/fail, or evaluation result object
349
+ *
350
+ * @example
351
+ * ```typescript
352
+ * handler: async (run, result) => {
353
+ * // Assert that output is positive
354
+ * if (result <= 0) {
355
+ * return false; // Evaluation failed
356
+ * }
357
+ * return true; // Evaluation passed
358
+ * }
359
+ * ```
360
+ *
361
+ * @example
362
+ * ```typescript
363
+ * // With detailed result
364
+ * handler: async (run, result) => {
365
+ * const passed = result.length > 5;
366
+ * return {
367
+ * passed,
368
+ * score: passed ? 1.0 : 0.0,
369
+ * message: passed ? 'Output length is valid' : 'Output too short'
370
+ * };
371
+ * }
372
+ * ```
373
+ */
117
374
  handler: EvalFunction<
118
375
  TInput extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<TInput> : undefined,
119
376
  TOutput extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<TOutput> : undefined
120
377
  >;
121
- }) => Eval<TInput, TOutput>;
378
+ }
379
+
380
+ // Type for createEval method
381
+ type CreateEvalMethod<
382
+ TInput extends StandardSchemaV1 | undefined = any,
383
+ TOutput extends StandardSchemaV1 | undefined = any,
384
+ > = (config: CreateEvalConfig<TInput, TOutput>) => Eval<TInput, TOutput>;
122
385
 
123
386
  /**
124
- * The Agent handler interface.
387
+ * Agent instance type returned by createAgent().
388
+ * Represents a fully configured agent with metadata, handler, lifecycle hooks, and event listeners.
389
+ *
390
+ * @template TInput - Input schema type (StandardSchemaV1 or undefined)
391
+ * @template TOutput - Output schema type (StandardSchemaV1 or undefined)
392
+ * @template TStream - Whether the agent returns a stream (true/false)
393
+ * @template TConfig - Agent-specific configuration type from setup function
394
+ * @template TAppState - Application state type from createApp
395
+ *
396
+ * @example
397
+ * ```typescript
398
+ * const agent = createAgent({
399
+ * metadata: { name: 'My Agent' },
400
+ * schema: { input: z.string(), output: z.number() },
401
+ * handler: async (ctx, input) => input.length
402
+ * });
403
+ *
404
+ * // Access agent properties
405
+ * console.log(agent.metadata.name); // "My Agent"
406
+ *
407
+ * // Add event listeners
408
+ * agent.addEventListener('started', (eventName, agent, ctx) => {
409
+ * console.log('Agent started:', ctx.sessionId);
410
+ * });
411
+ *
412
+ * // Create evals for testing
413
+ * const eval1 = agent.createEval({
414
+ * handler: async (run, result) => {
415
+ * return result > 5; // Assert output is greater than 5
416
+ * }
417
+ * });
418
+ * ```
125
419
  */
126
420
  export type Agent<
127
421
  TInput extends StandardSchemaV1 | undefined = any,
128
422
  TOutput extends StandardSchemaV1 | undefined = any,
129
423
  TStream extends boolean = false,
424
+ TConfig = unknown,
425
+ TAppState = Record<string, never>,
130
426
  > = {
427
+ /**
428
+ * Agent metadata including name, description, id, version, and filename.
429
+ */
131
430
  metadata: AgentMetadata;
132
- handler: (ctx: AgentContext<any, any, any>, ...args: any[]) => any | Promise<any>;
431
+
432
+ /**
433
+ * The main handler function that processes agent requests.
434
+ * Receives AgentContext and validated input, returns output or stream.
435
+ */
436
+ handler: (
437
+ ctx: AgentContext<any, any, any, TConfig, TAppState>,
438
+ ...args: any[]
439
+ ) => any | Promise<any>;
440
+
441
+ /**
442
+ * Array of evaluation functions created via agent.createEval().
443
+ * Used for testing and validating agent behavior.
444
+ */
133
445
  evals?: Eval[];
446
+
447
+ /**
448
+ * Create an evaluation function for testing this agent.
449
+ * Evals can assert correctness of agent input/output during test runs.
450
+ *
451
+ * @param config - Eval configuration
452
+ * @param config.metadata - Optional eval metadata (name, description)
453
+ * @param config.handler - Eval handler function receiving run context and result
454
+ *
455
+ * @example
456
+ * ```typescript
457
+ * const agent = createAgent({
458
+ * schema: { input: z.string(), output: z.number() },
459
+ * handler: async (ctx, input) => input.length
460
+ * });
461
+ *
462
+ * // Create eval to validate output
463
+ * agent.createEval({
464
+ * metadata: { name: 'Check positive output' },
465
+ * handler: async (run, result) => {
466
+ * return result > 0; // Assert output is positive
467
+ * }
468
+ * });
469
+ * ```
470
+ */
134
471
  createEval: CreateEvalMethod<TInput, TOutput>;
472
+
473
+ /**
474
+ * Optional setup function called once when app starts.
475
+ * Returns agent-specific configuration available via ctx.config.
476
+ */
477
+ setup?: (app: TAppState) => Promise<TConfig> | TConfig;
478
+
479
+ /**
480
+ * Optional shutdown function called when app stops.
481
+ * Receives app state and agent config for cleanup.
482
+ */
483
+ shutdown?: (app: TAppState, config: TConfig) => Promise<void> | void;
484
+
485
+ /**
486
+ * Register an event listener for when the agent starts execution.
487
+ *
488
+ * @param eventName - Must be 'started'
489
+ * @param callback - Function called when agent execution begins
490
+ *
491
+ * @example
492
+ * ```typescript
493
+ * agent.addEventListener('started', (eventName, agent, ctx) => {
494
+ * console.log(`${agent.metadata.name} started at ${new Date()}`);
495
+ * });
496
+ * ```
497
+ */
135
498
  addEventListener(
136
499
  eventName: 'started',
137
500
  callback: (
138
501
  eventName: 'started',
139
- agent: Agent<TInput, TOutput, TStream>,
140
- context: AgentContext<any, any, any>
502
+ agent: Agent<TInput, TOutput, TStream, TConfig, TAppState>,
503
+ context: AgentContext<any, any, any, TConfig, TAppState>
141
504
  ) => Promise<void> | void
142
505
  ): void;
506
+
507
+ /**
508
+ * Register an event listener for when the agent completes successfully.
509
+ *
510
+ * @param eventName - Must be 'completed'
511
+ * @param callback - Function called when agent execution completes
512
+ *
513
+ * @example
514
+ * ```typescript
515
+ * agent.addEventListener('completed', (eventName, agent, ctx) => {
516
+ * console.log(`${agent.metadata.name} completed successfully`);
517
+ * });
518
+ * ```
519
+ */
143
520
  addEventListener(
144
521
  eventName: 'completed',
145
522
  callback: (
146
523
  eventName: 'completed',
147
- agent: Agent<TInput, TOutput, TStream>,
148
- context: AgentContext<any, any, any>
524
+ agent: Agent<TInput, TOutput, TStream, TConfig, TAppState>,
525
+ context: AgentContext<any, any, any, TConfig, TAppState>
149
526
  ) => Promise<void> | void
150
527
  ): void;
528
+
529
+ /**
530
+ * Register an event listener for when the agent throws an error.
531
+ *
532
+ * @param eventName - Must be 'errored'
533
+ * @param callback - Function called when agent execution fails
534
+ *
535
+ * @example
536
+ * ```typescript
537
+ * agent.addEventListener('errored', (eventName, agent, ctx, error) => {
538
+ * console.error(`${agent.metadata.name} failed:`, error.message);
539
+ * });
540
+ * ```
541
+ */
151
542
  addEventListener(
152
543
  eventName: 'errored',
153
544
  callback: (
154
545
  eventName: 'errored',
155
- agent: Agent<TInput, TOutput, TStream>,
156
- context: AgentContext<any, any, any>,
546
+ agent: Agent<TInput, TOutput, TStream, TConfig, TAppState>,
547
+ context: AgentContext<any, any, any, TConfig, TAppState>,
157
548
  data: Error
158
549
  ) => Promise<void> | void
159
550
  ): void;
551
+
552
+ /**
553
+ * Remove a previously registered 'started' event listener.
554
+ *
555
+ * @param eventName - Must be 'started'
556
+ * @param callback - The callback function to remove
557
+ */
160
558
  removeEventListener(
161
559
  eventName: 'started',
162
560
  callback: (
163
561
  eventName: 'started',
164
- agent: Agent<TInput, TOutput, TStream>,
165
- context: AgentContext<any, any, any>
562
+ agent: Agent<TInput, TOutput, TStream, TConfig, TAppState>,
563
+ context: AgentContext<any, any, any, TConfig, TAppState>
166
564
  ) => Promise<void> | void
167
565
  ): void;
566
+
567
+ /**
568
+ * Remove a previously registered 'completed' event listener.
569
+ *
570
+ * @param eventName - Must be 'completed'
571
+ * @param callback - The callback function to remove
572
+ */
168
573
  removeEventListener(
169
574
  eventName: 'completed',
170
575
  callback: (
171
576
  eventName: 'completed',
172
- agent: Agent<TInput, TOutput, TStream>,
173
- context: AgentContext<any, any, any>
577
+ agent: Agent<TInput, TOutput, TStream, TConfig, TAppState>,
578
+ context: AgentContext<any, any, any, TConfig, TAppState>
174
579
  ) => Promise<void> | void
175
580
  ): void;
581
+
582
+ /**
583
+ * Remove a previously registered 'errored' event listener.
584
+ *
585
+ * @param eventName - Must be 'errored'
586
+ * @param callback - The callback function to remove
587
+ */
176
588
  removeEventListener(
177
589
  eventName: 'errored',
178
590
  callback: (
179
591
  eventName: 'errored',
180
- agent: Agent<TInput, TOutput, TStream>,
181
- context: AgentContext<any, any, any>,
592
+ agent: Agent<TInput, TOutput, TStream, TConfig, TAppState>,
593
+ context: AgentContext<any, any, any, TConfig, TAppState>,
182
594
  data: Error
183
595
  ) => Promise<void> | void
184
596
  ): void;
@@ -196,6 +608,143 @@ type InferStreamOutput<TOutput, TStream extends boolean> = TStream extends true
196
608
  ? StandardSchemaV1.InferOutput<TOutput>
197
609
  : void;
198
610
 
611
+ type SchemaInput<TSchema> = TSchema extends { input: infer I } ? I : undefined;
612
+ type SchemaOutput<TSchema> = TSchema extends { output: infer O } ? O : undefined;
613
+ type SchemaStream<TSchema> = TSchema extends { stream: infer S }
614
+ ? S extends boolean
615
+ ? S
616
+ : false
617
+ : false;
618
+
619
+ type SchemaHandlerReturn<TSchema> =
620
+ SchemaStream<TSchema> extends true
621
+ ? SchemaOutput<TSchema> extends StandardSchemaV1
622
+ ? ReadableStream<StandardSchemaV1.InferOutput<SchemaOutput<TSchema>>>
623
+ : ReadableStream<unknown>
624
+ : SchemaOutput<TSchema> extends StandardSchemaV1
625
+ ? StandardSchemaV1.InferOutput<SchemaOutput<TSchema>>
626
+ : void;
627
+
628
+ // Handler signature based on schema + setup result (no self-reference)
629
+ type AgentHandlerFromConfig<TSchema, TSetupReturn, TAppState = AppState> =
630
+ SchemaInput<TSchema> extends infer I
631
+ ? I extends StandardSchemaV1
632
+ ? (
633
+ ctx: AgentContext<any, any, any, TSetupReturn, TAppState>,
634
+ input: StandardSchemaV1.InferOutput<I>
635
+ ) => Promise<SchemaHandlerReturn<TSchema>> | SchemaHandlerReturn<TSchema>
636
+ : (
637
+ ctx: AgentContext<any, any, any, TSetupReturn, TAppState>
638
+ ) => Promise<SchemaHandlerReturn<TSchema>> | SchemaHandlerReturn<TSchema>
639
+ : (
640
+ ctx: AgentContext<any, any, any, TSetupReturn, TAppState>
641
+ ) => Promise<SchemaHandlerReturn<TSchema>> | SchemaHandlerReturn<TSchema>;
642
+
643
+ /**
644
+ * Configuration object for creating an agent with automatic type inference.
645
+ *
646
+ * @template TSchema - Schema definition object containing optional input, output, and stream properties
647
+ * @template TConfig - Function type that returns agent-specific configuration from setup
648
+ */
649
+ export interface CreateAgentConfig<
650
+ TSchema extends
651
+ | {
652
+ input?: StandardSchemaV1;
653
+ output?: StandardSchemaV1;
654
+ stream?: boolean;
655
+ }
656
+ | undefined = undefined,
657
+ TConfig extends (app: AppState) => any = any,
658
+ > {
659
+ /**
660
+ * Optional schema validation using Zod or any StandardSchemaV1 compatible library.
661
+ *
662
+ * @example
663
+ * ```typescript
664
+ * schema: {
665
+ * input: z.object({ name: z.string(), age: z.number() }),
666
+ * output: z.string(),
667
+ * stream: false
668
+ * }
669
+ * ```
670
+ */
671
+ schema?: TSchema;
672
+
673
+ /**
674
+ * Agent metadata visible in the Agentuity platform.
675
+ *
676
+ * @example
677
+ * ```typescript
678
+ * metadata: {
679
+ * name: 'Greeting Agent',
680
+ * description: 'Returns personalized greetings'
681
+ * }
682
+ * ```
683
+ */
684
+ metadata: ExternalAgentMetadata;
685
+
686
+ /**
687
+ * Optional async function called once on app startup to initialize agent-specific resources.
688
+ * The returned value is available in the handler via `ctx.config`.
689
+ *
690
+ * @param app - Application state from createApp setup function
691
+ * @returns Agent-specific configuration object
692
+ *
693
+ * @example
694
+ * ```typescript
695
+ * setup: async (app) => {
696
+ * const cache = new Map();
697
+ * const db = await connectDB();
698
+ * return { cache, db };
699
+ * }
700
+ * ```
701
+ */
702
+ setup?: TConfig;
703
+
704
+ /**
705
+ * The main agent logic that processes requests.
706
+ * Receives AgentContext and validated input (if schema.input is defined), returns output or stream.
707
+ *
708
+ * @param ctx - Agent context with logger, storage, and other runtime services
709
+ * @param input - Validated input (only present if schema.input is defined)
710
+ * @returns Output matching schema.output type, or ReadableStream if schema.stream is true
711
+ *
712
+ * @example
713
+ * ```typescript
714
+ * handler: async (ctx, { name, age }) => {
715
+ * ctx.logger.info(`Processing for ${name}`);
716
+ * await ctx.kv.set('lastUser', name);
717
+ * return `Hello, ${name}! You are ${age} years old.`;
718
+ * }
719
+ * ```
720
+ */
721
+ handler: AgentHandlerFromConfig<
722
+ TSchema,
723
+ TConfig extends (app: AppState) => infer R ? Awaited<R> : undefined,
724
+ AppState
725
+ >;
726
+
727
+ /**
728
+ * Optional async cleanup function called on app shutdown.
729
+ * Use this to close connections, flush buffers, etc.
730
+ *
731
+ * @param app - Application state from createApp
732
+ * @param config - Agent config returned from setup function
733
+ *
734
+ * @example
735
+ * ```typescript
736
+ * shutdown: async (app, config) => {
737
+ * await config.db.close();
738
+ * config.cache.clear();
739
+ * }
740
+ * ```
741
+ */
742
+ shutdown?: (
743
+ app: AppState,
744
+ config: TConfig extends (app: AppState) => infer R ? Awaited<R> : undefined
745
+ ) => Promise<void> | void;
746
+ }
747
+
199
748
  export interface AgentRunner<
200
749
  TInput extends StandardSchemaV1 | undefined = any,
201
750
  TOutput extends StandardSchemaV1 | undefined = any,
@@ -210,30 +759,33 @@ export interface AgentRunner<
210
759
  }
211
760
 
212
761
  // Will be populated at runtime with strongly typed agents
213
- const agents = new Map<string, Agent>();
762
+ const agents = new Map<string, Agent<any, any, any, any, any>>();
214
763
 
215
764
  // WeakMap to store event listeners for each agent instance (truly private)
216
765
  const agentEventListeners = new WeakMap<
217
- Agent<any, any, any>,
766
+ Agent<any, any, any, any, any>,
218
767
  Map<AgentEventName, Set<AgentEventCallback<any>>>
219
768
  >();
220
769
 
770
+ // Map to store agent configs returned from setup (keyed by agent name)
771
+ const agentConfigs = new Map<string, unknown>();
772
+
221
773
  // Helper to fire event listeners sequentially, abort on first error
222
774
  async function fireAgentEvent(
223
- agent: Agent<any, any, any>,
775
+ agent: Agent<any, any, any, any, any>,
224
776
  eventName: 'started' | 'completed',
225
- context: AgentContext<any, any, any>
777
+ context: AgentContext<any, any, any, any, any>
226
778
  ): Promise<void>;
227
779
  async function fireAgentEvent(
228
- agent: Agent<any, any, any>,
780
+ agent: Agent<any, any, any, any, any>,
229
781
  eventName: 'errored',
230
- context: AgentContext<any, any, any>,
782
+ context: AgentContext<any, any, any, any, any>,
231
783
  data: Error
232
784
  ): Promise<void>;
233
785
  async function fireAgentEvent(
234
- agent: Agent<any, any, any>,
786
+ agent: Agent<any, any, any, any, any>,
235
787
  eventName: AgentEventName,
236
- context: AgentContext<any, any, any>,
788
+ context: AgentContext<any, any, any, any, any>,
237
789
  data?: Error
238
790
  ): Promise<void> {
239
791
  // Fire agent-level listeners
@@ -278,63 +830,265 @@ export type AgentName = keyof AgentRegistry extends never ? string : keyof Agent
278
830
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
279
831
  export interface AgentRegistry {}
280
832
 
281
- export const registerAgent = (name: AgentName, agent: Agent): void => {
833
+ export const registerAgent = (name: AgentName, agent: Agent<any, any, any, any, any>): void => {
282
834
  agents.set(name, agent);
283
835
  };
284
836
 
285
- export function createAgent<
837
+ export const setAgentConfig = (name: AgentName, config: unknown): void => {
838
+ agentConfigs.set(name, config);
839
+ };
840
+
841
+ export const getAgentConfig = (name: AgentName): unknown => {
842
+ return agentConfigs.get(name);
843
+ };
844
+
845
+ const ValidationError = StructuredError('ValidationError')<{
846
+ issues: readonly StandardSchemaV1.Issue[];
847
+ }>();
848
+
849
+ /**
850
+ * Configuration object for creating an agent with explicit type parameters.
851
+ *
852
+ * @template TInput - Input schema type (StandardSchemaV1 or undefined)
853
+ * @template TOutput - Output schema type (StandardSchemaV1 or undefined)
854
+ * @template TStream - Whether agent returns a stream (true/false)
855
+ * @template TConfig - Type returned by setup function
856
+ * @template TAppState - Custom app state type from createApp
857
+ */
858
+ export interface CreateAgentConfigExplicit<
286
859
  TInput extends StandardSchemaV1 | undefined = undefined,
287
860
  TOutput extends StandardSchemaV1 | undefined = undefined,
288
861
  TStream extends boolean = false,
289
- >(config: {
862
+ TConfig = unknown,
863
+ TAppState = AppState,
864
+ > {
865
+ /**
866
+ * Optional schema validation.
867
+ *
868
+ * @example
869
+ * ```typescript
870
+ * schema: {
871
+ * input: z.object({ name: z.string() }),
872
+ * output: z.string(),
873
+ * stream: false
874
+ * }
875
+ * ```
876
+ */
290
877
  schema?: {
878
+ /** Input validation schema */
291
879
  input?: TInput;
880
+ /** Output validation schema */
292
881
  output?: TOutput;
882
+ /** Whether the agent returns a ReadableStream */
293
883
  stream?: TStream;
294
884
  };
885
+
886
+ /**
887
+ * Agent metadata.
888
+ *
889
+ * @example
890
+ * ```typescript
891
+ * metadata: {
892
+ * name: 'My Agent',
893
+ * description: 'Does something useful'
894
+ * }
895
+ * ```
896
+ */
295
897
  metadata: ExternalAgentMetadata;
898
+
899
+ /**
900
+ * Optional setup function receiving app state, returns agent config.
901
+ * The returned value is available in the handler via `ctx.config`.
902
+ *
903
+ * @param app - Application state from createApp
904
+ * @returns Agent-specific configuration
905
+ *
906
+ * @example
907
+ * ```typescript
908
+ * setup: async (app) => ({ cache: new Map() })
909
+ * ```
910
+ */
911
+ setup?: (app: TAppState) => Promise<TConfig> | TConfig;
912
+
913
+ /**
914
+ * Optional cleanup function called on app shutdown.
915
+ *
916
+ * @param app - Application state from createApp
917
+ * @param config - Agent config returned from setup
918
+ *
919
+ * @example
920
+ * ```typescript
921
+ * shutdown: async (app, config) => {
922
+ * config.cache.clear();
923
+ * }
924
+ * ```
925
+ */
926
+ shutdown?: (app: TAppState, config: TConfig) => Promise<void> | void;
927
+
928
+ /**
929
+ * Agent handler function.
930
+ * Type is automatically inferred based on schema definitions.
931
+ *
932
+ * @param ctx - Agent context
933
+ * @param input - Validated input (only present if schema.input is defined)
934
+ * @returns Output or ReadableStream based on schema
935
+ *
936
+ * @example
937
+ * ```typescript
938
+ * handler: async (ctx, input) => {
939
+ * return `Hello, ${input.name}!`;
940
+ * }
941
+ * ```
942
+ */
296
943
  handler: TInput extends StandardSchemaV1
297
944
  ? TStream extends true
298
945
  ? TOutput extends StandardSchemaV1
299
946
  ? (
300
- c: AgentContext<any, any, any>,
947
+ c: AgentContext<any, any, any, TConfig, TAppState>,
301
948
  input: StandardSchemaV1.InferOutput<TInput>
302
949
  ) =>
303
950
  | Promise<ReadableStream<StandardSchemaV1.InferOutput<TOutput>>>
304
951
  | ReadableStream<StandardSchemaV1.InferOutput<TOutput>>
305
952
  : (
306
- c: AgentContext<any, any, any>,
953
+ c: AgentContext<any, any, any, TConfig, TAppState>,
307
954
  input: StandardSchemaV1.InferOutput<TInput>
308
955
  ) => Promise<ReadableStream<unknown>> | ReadableStream<unknown>
309
956
  : TOutput extends StandardSchemaV1
310
957
  ? (
311
- c: AgentContext<any, any, any>,
958
+ c: AgentContext<any, any, any, TConfig, TAppState>,
312
959
  input: StandardSchemaV1.InferOutput<TInput>
313
960
  ) =>
314
961
  | Promise<StandardSchemaV1.InferOutput<TOutput>>
315
962
  | StandardSchemaV1.InferOutput<TOutput>
316
963
  : (
317
- c: AgentContext<any, any, any>,
964
+ c: AgentContext<any, any, any, TConfig, TAppState>,
318
965
  input: StandardSchemaV1.InferOutput<TInput>
319
966
  ) => Promise<void> | void
320
967
  : TStream extends true
321
968
  ? TOutput extends StandardSchemaV1
322
969
  ? (
323
- c: AgentContext<any, any, any>
970
+ c: AgentContext<any, any, any, TConfig, TAppState>
324
971
  ) =>
325
972
  | Promise<ReadableStream<StandardSchemaV1.InferOutput<TOutput>>>
326
973
  | ReadableStream<StandardSchemaV1.InferOutput<TOutput>>
327
974
  : (
328
- c: AgentContext<any, any, any>
975
+ c: AgentContext<any, any, any, TConfig, TAppState>
329
976
  ) => Promise<ReadableStream<unknown>> | ReadableStream<unknown>
330
977
  : TOutput extends StandardSchemaV1
331
978
  ? (
332
- c: AgentContext<any, any, any>
979
+ c: AgentContext<any, any, any, TConfig, TAppState>
333
980
  ) =>
334
981
  | Promise<StandardSchemaV1.InferOutput<TOutput>>
335
982
  | StandardSchemaV1.InferOutput<TOutput>
336
- : (c: AgentContext<any, any, any>) => Promise<void> | void;
337
- }): Agent<TInput, TOutput, TStream> {
983
+ : (c: AgentContext<any, any, any, TConfig, TAppState>) => Promise<void> | void;
984
+ }
985
+
986
+ /**
987
+ * Creates an agent with schema validation and lifecycle hooks.
988
+ *
989
+ * This is the recommended way to create agents with automatic type inference from schemas.
990
+ *
991
+ * @template TSchema - Schema definition object containing optional input, output, and stream properties
992
+ * @template TConfig - Function type that returns agent-specific configuration from setup
993
+ *
994
+ * @param config - Agent configuration object
995
+ *
996
+ * @returns Agent instance that can be registered with the runtime
997
+ *
998
+ * @example
999
+ * ```typescript
1000
+ * const agent = createAgent({
1001
+ * metadata: {
1002
+ * name: 'Greeting Agent',
1003
+ * description: 'Returns personalized greetings'
1004
+ * },
1005
+ * schema: {
1006
+ * input: z.object({ name: z.string(), age: z.number() }),
1007
+ * output: z.string()
1008
+ * },
1009
+ * handler: async (ctx, { name, age }) => {
1010
+ * ctx.logger.info(`Processing greeting for ${name}`);
1011
+ * return `Hello, ${name}! You are ${age} years old.`;
1012
+ * }
1013
+ * });
1014
+ * ```
1015
+ */
1016
+ export function createAgent<
1017
+ TSchema extends
1018
+ | {
1019
+ input?: StandardSchemaV1;
1020
+ output?: StandardSchemaV1;
1021
+ stream?: boolean;
1022
+ }
1023
+ | undefined = undefined,
1024
+ TConfig extends (app: AppState) => any = any,
1025
+ >(
1026
+ config: CreateAgentConfig<TSchema, TConfig>
1027
+ ): Agent<
1028
+ SchemaInput<TSchema>,
1029
+ SchemaOutput<TSchema>,
1030
+ SchemaStream<TSchema>,
1031
+ TConfig extends (app: AppState) => infer R ? Awaited<R> : undefined,
1032
+ AppState
1033
+ >;
1034
+
1035
+ /**
1036
+ * Creates an agent with explicit generic type parameters.
1037
+ *
1038
+ * Use this overload when you need explicit control over types or working with custom app state.
1039
+ *
1040
+ * @template TInput - Input schema type (StandardSchemaV1 or undefined)
1041
+ * @template TOutput - Output schema type (StandardSchemaV1 or undefined)
1042
+ * @template TStream - Whether agent returns a stream (true/false)
1043
+ * @template TConfig - Type returned by setup function
1044
+ * @template TAppState - Custom app state type from createApp
1045
+ *
1046
+ * @param config - Agent configuration object
1047
+ *
1048
+ * @returns Agent instance with explicit types
1049
+ *
1050
+ * @example
1051
+ * ```typescript
1052
+ * interface MyAppState { db: Database }
1053
+ * interface MyConfig { cache: Map<string, any> }
1054
+ *
1055
+ * const agent = createAgent<
1056
+ * z.ZodObject<any>, // TInput
1057
+ * z.ZodString, // TOutput
1058
+ * false, // TStream
1059
+ * MyConfig, // TConfig
1060
+ * MyAppState // TAppState
1061
+ * >({
1062
+ * metadata: { name: 'Custom Agent' },
1063
+ * setup: async (app) => ({ cache: new Map() }),
1064
+ * handler: async (ctx, input) => {
1065
+ * const db = ctx.app.db;
1066
+ * const cache = ctx.config.cache;
1067
+ * return 'result';
1068
+ * }
1069
+ * });
1070
+ * ```
1071
+ */
1072
+ export function createAgent<
1073
+ TInput extends StandardSchemaV1 | undefined = undefined,
1074
+ TOutput extends StandardSchemaV1 | undefined = undefined,
1075
+ TStream extends boolean = false,
1076
+ TConfig = unknown,
1077
+ TAppState = AppState,
1078
+ >(
1079
+ config: CreateAgentConfigExplicit<TInput, TOutput, TStream, TConfig, TAppState>
1080
+ ): Agent<TInput, TOutput, TStream, TConfig, TAppState>;
1081
+
1082
+ // Implementation
1083
+ export function createAgent<
1084
+ TInput extends StandardSchemaV1 | undefined = undefined,
1085
+ TOutput extends StandardSchemaV1 | undefined = undefined,
1086
+ TStream extends boolean = false,
1087
+ TConfig = unknown,
1088
+ TAppState = AppState,
1089
+ >(
1090
+ config: CreateAgentConfigExplicit<TInput, TOutput, TStream, TConfig, TAppState>
1091
+ ): Agent<TInput, TOutput, TStream, TConfig, TAppState> {
338
1092
  const inputSchema = config.schema?.input;
339
1093
  const outputSchema = config.schema?.output;
340
1094
 
@@ -348,14 +1102,15 @@ export function createAgent<
348
1102
  if (inputSchema) {
349
1103
  const inputResult = await inputSchema['~standard'].validate(input);
350
1104
  if (inputResult.issues) {
351
- throw new Error(
352
- `Input validation failed: ${inputResult.issues.map((i: any) => i.message).join(', ')}`
353
- );
1105
+ throw new ValidationError({
1106
+ issues: inputResult.issues,
1107
+ message: `Input validation failed: ${inputResult.issues.map((i: any) => i.message).join(', ')}`,
1108
+ });
354
1109
  }
355
1110
  validatedInput = inputResult.value;
356
1111
  }
357
1112
 
358
- const agentCtx = getAgentContext();
1113
+ const agentCtx = getAgentContext() as AgentContext<any, any, any, TConfig, TAppState>;
359
1114
 
360
1115
  // Get the agent instance from the agents Map to fire events
361
1116
  // The agent will be registered in the agents Map before the handler is called
@@ -376,9 +1131,10 @@ export function createAgent<
376
1131
  if (outputSchema) {
377
1132
  const outputResult = await outputSchema['~standard'].validate(result);
378
1133
  if (outputResult.issues) {
379
- throw new Error(
380
- `Output validation failed: ${outputResult.issues.map((i: any) => i.message).join(', ')}`
381
- );
1134
+ throw new ValidationError({
1135
+ issues: outputResult.issues,
1136
+ message: `Output validation failed: ${outputResult.issues.map((i: any) => i.message).join(', ')}`,
1137
+ });
382
1138
  }
383
1139
  validatedOutput = outputResult.value;
384
1140
  }
@@ -501,6 +1257,8 @@ export function createAgent<
501
1257
  metadata: config.metadata,
502
1258
  evals: evalsArray,
503
1259
  createEval,
1260
+ setup: config.setup,
1261
+ shutdown: config.shutdown,
504
1262
  };
505
1263
 
506
1264
  // Add event listener methods
@@ -627,9 +1385,10 @@ export function createAgent<
627
1385
  const evalInputResult =
628
1386
  await evalItem.inputSchema['~standard'].validate(validatedInput);
629
1387
  if (evalInputResult.issues) {
630
- throw new Error(
631
- `Eval input validation failed: ${evalInputResult.issues.map((i: any) => i.message).join(', ')}`
632
- );
1388
+ throw new ValidationError({
1389
+ issues: evalInputResult.issues,
1390
+ message: `Eval input validation failed: ${evalInputResult.issues.map((i: any) => i.message).join(', ')}`,
1391
+ });
633
1392
  }
634
1393
  evalValidatedInput = evalInputResult.value;
635
1394
  }
@@ -640,9 +1399,10 @@ export function createAgent<
640
1399
  const evalOutputResult =
641
1400
  await evalItem.outputSchema['~standard'].validate(validatedOutput);
642
1401
  if (evalOutputResult.issues) {
643
- throw new Error(
644
- `Eval output validation failed: ${evalOutputResult.issues.map((i: any) => i.message).join(', ')}`
645
- );
1402
+ throw new ValidationError({
1403
+ issues: evalOutputResult.issues,
1404
+ message: `Eval output validation failed: ${evalOutputResult.issues.map((i: any) => i.message).join(', ')}`,
1405
+ });
646
1406
  }
647
1407
  evalValidatedOutput = evalOutputResult.value;
648
1408
  }
@@ -765,7 +1525,7 @@ export function createAgent<
765
1525
  agent.stream = config.schema.stream;
766
1526
  }
767
1527
 
768
- return agent as Agent<TInput, TOutput, TStream>;
1528
+ return agent as Agent<TInput, TOutput, TStream, TConfig, TAppState>;
769
1529
  }
770
1530
 
771
1531
  const runWithSpan = async <
@@ -877,11 +1637,14 @@ export const populateAgentsRegistry = (ctx: Context): any => {
877
1637
  return agentsObj;
878
1638
  };
879
1639
 
880
- export const createAgentMiddleware = (agentName: AgentName): MiddlewareHandler => {
1640
+ export const createAgentMiddleware = (agentName: AgentName | ''): MiddlewareHandler => {
881
1641
  return async (ctx, next) => {
882
1642
  // Populate agents object with strongly-typed keys
883
1643
  const agentsObj = populateAgentsRegistry(ctx);
884
1644
 
1645
+ // Set agent registry on context for access via c.var.agent
1646
+ ctx.set('agent', agentsObj);
1647
+
885
1648
  // Determine current and parent agents
886
1649
  let currentAgent: AgentRunner | undefined;
887
1650
  let parentAgent: AgentRunner | undefined;
@@ -910,22 +1673,28 @@ export const createAgentMiddleware = (agentName: AgentName): MiddlewareHandler =
910
1673
  const sessionId = ctx.var.sessionId;
911
1674
  const thread = ctx.var.thread;
912
1675
  const session = ctx.var.session;
1676
+ const config = agentName ? getAgentConfig(agentName as AgentName) : undefined;
1677
+ const app = ctx.var.app;
913
1678
 
914
1679
  const args: RequestAgentContextArgs<
915
1680
  AgentRegistry,
916
1681
  AgentRunner | undefined,
917
- AgentRunner | undefined
1682
+ AgentRunner | undefined,
1683
+ unknown,
1684
+ unknown
918
1685
  > = {
919
1686
  agent: agentsObj,
920
1687
  current: currentAgent,
921
1688
  parent: parentAgent,
922
- agentName,
1689
+ agentName: agentName as AgentName,
923
1690
  logger: ctx.var.logger.child({ agent: agentName }),
924
1691
  tracer: ctx.var.tracer,
925
1692
  sessionId,
926
1693
  session,
927
1694
  thread,
928
1695
  handler: ctx.var.waitUntilHandler,
1696
+ config: config || {},
1697
+ app: app || {},
929
1698
  };
930
1699
 
931
1700
  return runInAgentContext(ctx as unknown as Record<string, unknown>, args, next);
@@ -933,3 +1702,22 @@ export const createAgentMiddleware = (agentName: AgentName): MiddlewareHandler =
933
1702
  };
934
1703
 
935
1704
  export const getAgents = () => agents;
1705
+
1706
+ export const runAgentSetups = async (appState: AppState): Promise<void> => {
1707
+ for (const [name, agent] of agents.entries()) {
1708
+ if (agent.setup) {
1709
+ const config = await agent.setup(appState);
1710
+ setAgentConfig(name as AgentName, config);
1711
+ }
1712
+ }
1713
+ await notifyReady();
1714
+ };
1715
+
1716
+ export const runAgentShutdowns = async (appState: AppState): Promise<void> => {
1717
+ for (const [name, agent] of agents.entries()) {
1718
+ if (agent.shutdown) {
1719
+ const config = getAgentConfig(name as AgentName);
1720
+ await agent.shutdown(appState, config);
1721
+ }
1722
+ }
1723
+ };