@agentuity/runtime 2.0.11 → 3.0.0-alpha.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.
- package/dist/index.d.ts +37 -65
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +59 -61
- package/dist/index.js.map +1 -1
- package/package.json +9 -38
- package/src/index.ts +58 -259
- package/AGENTS.md +0 -116
- package/dist/_config.d.ts +0 -100
- package/dist/_config.d.ts.map +0 -1
- package/dist/_config.js +0 -147
- package/dist/_config.js.map +0 -1
- package/dist/_context.d.ts +0 -80
- package/dist/_context.d.ts.map +0 -1
- package/dist/_context.js +0 -160
- package/dist/_context.js.map +0 -1
- package/dist/_events.d.ts +0 -64
- package/dist/_events.d.ts.map +0 -1
- package/dist/_events.js +0 -92
- package/dist/_events.js.map +0 -1
- package/dist/_globals.d.ts +0 -58
- package/dist/_globals.d.ts.map +0 -1
- package/dist/_globals.js +0 -71
- package/dist/_globals.js.map +0 -1
- package/dist/_idle.d.ts +0 -7
- package/dist/_idle.d.ts.map +0 -1
- package/dist/_idle.js +0 -10
- package/dist/_idle.js.map +0 -1
- package/dist/_metadata.d.ts +0 -117
- package/dist/_metadata.d.ts.map +0 -1
- package/dist/_metadata.js +0 -268
- package/dist/_metadata.js.map +0 -1
- package/dist/_process-protection.d.ts +0 -27
- package/dist/_process-protection.d.ts.map +0 -1
- package/dist/_process-protection.js +0 -56
- package/dist/_process-protection.js.map +0 -1
- package/dist/_server.d.ts +0 -50
- package/dist/_server.d.ts.map +0 -1
- package/dist/_server.js +0 -89
- package/dist/_server.js.map +0 -1
- package/dist/_services.d.ts +0 -25
- package/dist/_services.d.ts.map +0 -1
- package/dist/_services.js +0 -286
- package/dist/_services.js.map +0 -1
- package/dist/_standalone.d.ts +0 -212
- package/dist/_standalone.d.ts.map +0 -1
- package/dist/_standalone.js +0 -556
- package/dist/_standalone.js.map +0 -1
- package/dist/_tokens.d.ts +0 -12
- package/dist/_tokens.d.ts.map +0 -1
- package/dist/_tokens.js +0 -97
- package/dist/_tokens.js.map +0 -1
- package/dist/_util.d.ts +0 -16
- package/dist/_util.d.ts.map +0 -1
- package/dist/_util.js +0 -54
- package/dist/_util.js.map +0 -1
- package/dist/_validation.d.ts +0 -89
- package/dist/_validation.d.ts.map +0 -1
- package/dist/_validation.js +0 -29
- package/dist/_validation.js.map +0 -1
- package/dist/_waituntil.d.ts +0 -32
- package/dist/_waituntil.d.ts.map +0 -1
- package/dist/_waituntil.js +0 -156
- package/dist/_waituntil.js.map +0 -1
- package/dist/agent.d.ts +0 -1262
- package/dist/agent.d.ts.map +0 -1
- package/dist/agent.js +0 -981
- package/dist/agent.js.map +0 -1
- package/dist/app.d.ts +0 -514
- package/dist/app.d.ts.map +0 -1
- package/dist/app.js +0 -228
- package/dist/app.js.map +0 -1
- package/dist/bootstrap.d.ts +0 -44
- package/dist/bootstrap.d.ts.map +0 -1
- package/dist/bootstrap.js +0 -259
- package/dist/bootstrap.js.map +0 -1
- package/dist/bun-s3-patch.d.ts +0 -37
- package/dist/bun-s3-patch.d.ts.map +0 -1
- package/dist/bun-s3-patch.js +0 -142
- package/dist/bun-s3-patch.js.map +0 -1
- package/dist/cors.d.ts +0 -42
- package/dist/cors.d.ts.map +0 -1
- package/dist/cors.js +0 -117
- package/dist/cors.js.map +0 -1
- package/dist/dev-patches/aisdk.d.ts +0 -17
- package/dist/dev-patches/aisdk.d.ts.map +0 -1
- package/dist/dev-patches/aisdk.js +0 -160
- package/dist/dev-patches/aisdk.js.map +0 -1
- package/dist/dev-patches/gateway.d.ts +0 -16
- package/dist/dev-patches/gateway.d.ts.map +0 -1
- package/dist/dev-patches/gateway.js +0 -54
- package/dist/dev-patches/gateway.js.map +0 -1
- package/dist/dev-patches/index.d.ts +0 -21
- package/dist/dev-patches/index.d.ts.map +0 -1
- package/dist/dev-patches/index.js +0 -33
- package/dist/dev-patches/index.js.map +0 -1
- package/dist/dev-patches/otel-llm.d.ts +0 -12
- package/dist/dev-patches/otel-llm.d.ts.map +0 -1
- package/dist/dev-patches/otel-llm.js +0 -352
- package/dist/dev-patches/otel-llm.js.map +0 -1
- package/dist/devmode.d.ts +0 -3
- package/dist/devmode.d.ts.map +0 -1
- package/dist/devmode.js +0 -167
- package/dist/devmode.js.map +0 -1
- package/dist/eval.d.ts +0 -91
- package/dist/eval.d.ts.map +0 -1
- package/dist/eval.js +0 -16
- package/dist/eval.js.map +0 -1
- package/dist/handlers/_route-meta.d.ts +0 -22
- package/dist/handlers/_route-meta.d.ts.map +0 -1
- package/dist/handlers/_route-meta.js +0 -25
- package/dist/handlers/_route-meta.js.map +0 -1
- package/dist/handlers/cron.d.ts +0 -73
- package/dist/handlers/cron.d.ts.map +0 -1
- package/dist/handlers/cron.js +0 -43
- package/dist/handlers/cron.js.map +0 -1
- package/dist/handlers/index.d.ts +0 -6
- package/dist/handlers/index.d.ts.map +0 -1
- package/dist/handlers/index.js +0 -6
- package/dist/handlers/index.js.map +0 -1
- package/dist/handlers/sse.d.ts +0 -163
- package/dist/handlers/sse.d.ts.map +0 -1
- package/dist/handlers/sse.js +0 -175
- package/dist/handlers/sse.js.map +0 -1
- package/dist/handlers/stream.d.ts +0 -52
- package/dist/handlers/stream.d.ts.map +0 -1
- package/dist/handlers/stream.js +0 -108
- package/dist/handlers/stream.js.map +0 -1
- package/dist/handlers/webrtc.d.ts +0 -49
- package/dist/handlers/webrtc.d.ts.map +0 -1
- package/dist/handlers/webrtc.js +0 -109
- package/dist/handlers/webrtc.js.map +0 -1
- package/dist/handlers/websocket.d.ts +0 -88
- package/dist/handlers/websocket.d.ts.map +0 -1
- package/dist/handlers/websocket.js +0 -161
- package/dist/handlers/websocket.js.map +0 -1
- package/dist/logger/console.d.ts +0 -70
- package/dist/logger/console.d.ts.map +0 -1
- package/dist/logger/console.js +0 -278
- package/dist/logger/console.js.map +0 -1
- package/dist/logger/index.d.ts +0 -3
- package/dist/logger/index.d.ts.map +0 -1
- package/dist/logger/index.js +0 -3
- package/dist/logger/index.js.map +0 -1
- package/dist/logger/internal.d.ts +0 -79
- package/dist/logger/internal.d.ts.map +0 -1
- package/dist/logger/internal.js +0 -133
- package/dist/logger/internal.js.map +0 -1
- package/dist/logger/logger.d.ts +0 -41
- package/dist/logger/logger.d.ts.map +0 -1
- package/dist/logger/logger.js +0 -2
- package/dist/logger/logger.js.map +0 -1
- package/dist/logger/user.d.ts +0 -8
- package/dist/logger/user.d.ts.map +0 -1
- package/dist/logger/user.js +0 -7
- package/dist/logger/user.js.map +0 -1
- package/dist/logger/util.d.ts +0 -11
- package/dist/logger/util.d.ts.map +0 -1
- package/dist/logger/util.js +0 -77
- package/dist/logger/util.js.map +0 -1
- package/dist/middleware.d.ts +0 -105
- package/dist/middleware.d.ts.map +0 -1
- package/dist/middleware.js +0 -763
- package/dist/middleware.js.map +0 -1
- package/dist/otel/config.d.ts +0 -19
- package/dist/otel/config.d.ts.map +0 -1
- package/dist/otel/config.js +0 -26
- package/dist/otel/config.js.map +0 -1
- package/dist/otel/console.d.ts +0 -33
- package/dist/otel/console.d.ts.map +0 -1
- package/dist/otel/console.js +0 -86
- package/dist/otel/console.js.map +0 -1
- package/dist/otel/exporters/index.d.ts +0 -4
- package/dist/otel/exporters/index.d.ts.map +0 -1
- package/dist/otel/exporters/index.js +0 -4
- package/dist/otel/exporters/index.js.map +0 -1
- package/dist/otel/exporters/jsonl-log-exporter.d.ts +0 -36
- package/dist/otel/exporters/jsonl-log-exporter.d.ts.map +0 -1
- package/dist/otel/exporters/jsonl-log-exporter.js +0 -103
- package/dist/otel/exporters/jsonl-log-exporter.js.map +0 -1
- package/dist/otel/exporters/jsonl-metric-exporter.d.ts +0 -40
- package/dist/otel/exporters/jsonl-metric-exporter.d.ts.map +0 -1
- package/dist/otel/exporters/jsonl-metric-exporter.js +0 -104
- package/dist/otel/exporters/jsonl-metric-exporter.js.map +0 -1
- package/dist/otel/exporters/jsonl-trace-exporter.d.ts +0 -36
- package/dist/otel/exporters/jsonl-trace-exporter.d.ts.map +0 -1
- package/dist/otel/exporters/jsonl-trace-exporter.js +0 -111
- package/dist/otel/exporters/jsonl-trace-exporter.js.map +0 -1
- package/dist/otel/fetch.d.ts +0 -12
- package/dist/otel/fetch.d.ts.map +0 -1
- package/dist/otel/fetch.js +0 -82
- package/dist/otel/fetch.js.map +0 -1
- package/dist/otel/http.d.ts +0 -16
- package/dist/otel/http.d.ts.map +0 -1
- package/dist/otel/http.js +0 -44
- package/dist/otel/http.js.map +0 -1
- package/dist/otel/logger.d.ts +0 -37
- package/dist/otel/logger.d.ts.map +0 -1
- package/dist/otel/logger.js +0 -265
- package/dist/otel/logger.js.map +0 -1
- package/dist/otel/otel.d.ts +0 -68
- package/dist/otel/otel.d.ts.map +0 -1
- package/dist/otel/otel.js +0 -245
- package/dist/otel/otel.js.map +0 -1
- package/dist/otel/tracestate.d.ts +0 -44
- package/dist/otel/tracestate.d.ts.map +0 -1
- package/dist/otel/tracestate.js +0 -84
- package/dist/otel/tracestate.js.map +0 -1
- package/dist/router.d.ts +0 -66
- package/dist/router.d.ts.map +0 -1
- package/dist/router.js +0 -44
- package/dist/router.js.map +0 -1
- package/dist/services/evalrun/composite.d.ts +0 -21
- package/dist/services/evalrun/composite.d.ts.map +0 -1
- package/dist/services/evalrun/composite.js +0 -26
- package/dist/services/evalrun/composite.js.map +0 -1
- package/dist/services/evalrun/http.d.ts +0 -24
- package/dist/services/evalrun/http.d.ts.map +0 -1
- package/dist/services/evalrun/http.js +0 -115
- package/dist/services/evalrun/http.js.map +0 -1
- package/dist/services/evalrun/index.d.ts +0 -5
- package/dist/services/evalrun/index.d.ts.map +0 -1
- package/dist/services/evalrun/index.js +0 -5
- package/dist/services/evalrun/index.js.map +0 -1
- package/dist/services/evalrun/json.d.ts +0 -21
- package/dist/services/evalrun/json.d.ts.map +0 -1
- package/dist/services/evalrun/json.js +0 -38
- package/dist/services/evalrun/json.js.map +0 -1
- package/dist/services/evalrun/local.d.ts +0 -19
- package/dist/services/evalrun/local.d.ts.map +0 -1
- package/dist/services/evalrun/local.js +0 -22
- package/dist/services/evalrun/local.js.map +0 -1
- package/dist/services/local/_db.d.ts +0 -4
- package/dist/services/local/_db.d.ts.map +0 -1
- package/dist/services/local/_db.js +0 -281
- package/dist/services/local/_db.js.map +0 -1
- package/dist/services/local/_router.d.ts +0 -3
- package/dist/services/local/_router.d.ts.map +0 -1
- package/dist/services/local/_router.js +0 -28
- package/dist/services/local/_router.js.map +0 -1
- package/dist/services/local/_util.d.ts +0 -18
- package/dist/services/local/_util.d.ts.map +0 -1
- package/dist/services/local/_util.js +0 -44
- package/dist/services/local/_util.js.map +0 -1
- package/dist/services/local/email.d.ts +0 -24
- package/dist/services/local/email.d.ts.map +0 -1
- package/dist/services/local/email.js +0 -58
- package/dist/services/local/email.js.map +0 -1
- package/dist/services/local/index.d.ts +0 -10
- package/dist/services/local/index.d.ts.map +0 -1
- package/dist/services/local/index.js +0 -10
- package/dist/services/local/index.js.map +0 -1
- package/dist/services/local/keyvalue.d.ts +0 -17
- package/dist/services/local/keyvalue.d.ts.map +0 -1
- package/dist/services/local/keyvalue.js +0 -133
- package/dist/services/local/keyvalue.js.map +0 -1
- package/dist/services/local/queue.d.ts +0 -10
- package/dist/services/local/queue.d.ts.map +0 -1
- package/dist/services/local/queue.js +0 -96
- package/dist/services/local/queue.js.map +0 -1
- package/dist/services/local/stream.d.ts +0 -12
- package/dist/services/local/stream.d.ts.map +0 -1
- package/dist/services/local/stream.js +0 -266
- package/dist/services/local/stream.js.map +0 -1
- package/dist/services/local/task.d.ts +0 -55
- package/dist/services/local/task.d.ts.map +0 -1
- package/dist/services/local/task.js +0 -1248
- package/dist/services/local/task.js.map +0 -1
- package/dist/services/local/vector.d.ts +0 -17
- package/dist/services/local/vector.d.ts.map +0 -1
- package/dist/services/local/vector.js +0 -303
- package/dist/services/local/vector.js.map +0 -1
- package/dist/services/sandbox/http.d.ts +0 -23
- package/dist/services/sandbox/http.d.ts.map +0 -1
- package/dist/services/sandbox/http.js +0 -327
- package/dist/services/sandbox/http.js.map +0 -1
- package/dist/services/sandbox/index.d.ts +0 -2
- package/dist/services/sandbox/index.d.ts.map +0 -1
- package/dist/services/sandbox/index.js +0 -2
- package/dist/services/sandbox/index.js.map +0 -1
- package/dist/services/session/composite.d.ts +0 -21
- package/dist/services/session/composite.d.ts.map +0 -1
- package/dist/services/session/composite.js +0 -26
- package/dist/services/session/composite.js.map +0 -1
- package/dist/services/session/http.d.ts +0 -34
- package/dist/services/session/http.d.ts.map +0 -1
- package/dist/services/session/http.js +0 -124
- package/dist/services/session/http.js.map +0 -1
- package/dist/services/session/index.d.ts +0 -5
- package/dist/services/session/index.d.ts.map +0 -1
- package/dist/services/session/index.js +0 -5
- package/dist/services/session/index.js.map +0 -1
- package/dist/services/session/json.d.ts +0 -22
- package/dist/services/session/json.d.ts.map +0 -1
- package/dist/services/session/json.js +0 -35
- package/dist/services/session/json.js.map +0 -1
- package/dist/services/session/local.d.ts +0 -19
- package/dist/services/session/local.d.ts.map +0 -1
- package/dist/services/session/local.js +0 -23
- package/dist/services/session/local.js.map +0 -1
- package/dist/services/thread/local.d.ts +0 -20
- package/dist/services/thread/local.d.ts.map +0 -1
- package/dist/services/thread/local.js +0 -158
- package/dist/services/thread/local.js.map +0 -1
- package/dist/session.d.ts +0 -734
- package/dist/session.d.ts.map +0 -1
- package/dist/session.js +0 -1140
- package/dist/session.js.map +0 -1
- package/dist/signature.d.ts +0 -22
- package/dist/signature.d.ts.map +0 -1
- package/dist/signature.js +0 -63
- package/dist/signature.js.map +0 -1
- package/dist/validator.d.ts +0 -142
- package/dist/validator.d.ts.map +0 -1
- package/dist/validator.js +0 -149
- package/dist/validator.js.map +0 -1
- package/dist/version-check.d.ts +0 -20
- package/dist/version-check.d.ts.map +0 -1
- package/dist/version-check.js +0 -157
- package/dist/version-check.js.map +0 -1
- package/dist/web.d.ts +0 -8
- package/dist/web.d.ts.map +0 -1
- package/dist/web.js +0 -67
- package/dist/web.js.map +0 -1
- package/dist/webrtc-signaling.d.ts +0 -80
- package/dist/webrtc-signaling.d.ts.map +0 -1
- package/dist/webrtc-signaling.js +0 -237
- package/dist/webrtc-signaling.js.map +0 -1
- package/dist/workbench.d.ts +0 -17
- package/dist/workbench.d.ts.map +0 -1
- package/dist/workbench.js +0 -605
- package/dist/workbench.js.map +0 -1
- package/src/_config.ts +0 -163
- package/src/_context.ts +0 -240
- package/src/_events.ts +0 -142
- package/src/_globals.ts +0 -92
- package/src/_idle.ts +0 -10
- package/src/_metadata.ts +0 -407
- package/src/_process-protection.ts +0 -71
- package/src/_server.ts +0 -109
- package/src/_services.ts +0 -379
- package/src/_standalone.ts +0 -710
- package/src/_tokens.ts +0 -114
- package/src/_util.ts +0 -62
- package/src/_validation.ts +0 -119
- package/src/_waituntil.ts +0 -188
- package/src/agent.ts +0 -2739
- package/src/app.ts +0 -769
- package/src/bootstrap.ts +0 -321
- package/src/bun-s3-patch.ts +0 -224
- package/src/cors.ts +0 -137
- package/src/dev-patches/aisdk.ts +0 -169
- package/src/dev-patches/gateway.ts +0 -68
- package/src/dev-patches/index.ts +0 -37
- package/src/dev-patches/otel-llm.ts +0 -405
- package/src/devmode.ts +0 -171
- package/src/eval.ts +0 -109
- package/src/globals.d.ts +0 -28
- package/src/handlers/_route-meta.ts +0 -33
- package/src/handlers/cron.ts +0 -141
- package/src/handlers/index.ts +0 -18
- package/src/handlers/sse.ts +0 -358
- package/src/handlers/stream.ts +0 -121
- package/src/handlers/webrtc.ts +0 -125
- package/src/handlers/websocket.ts +0 -203
- package/src/logger/console.ts +0 -323
- package/src/logger/index.ts +0 -2
- package/src/logger/internal.ts +0 -165
- package/src/logger/logger.ts +0 -44
- package/src/logger/user.ts +0 -15
- package/src/logger/util.ts +0 -80
- package/src/middleware.ts +0 -1095
- package/src/otel/config.ts +0 -47
- package/src/otel/console.ts +0 -91
- package/src/otel/exporters/README.md +0 -217
- package/src/otel/exporters/index.ts +0 -3
- package/src/otel/exporters/jsonl-log-exporter.ts +0 -113
- package/src/otel/exporters/jsonl-metric-exporter.ts +0 -120
- package/src/otel/exporters/jsonl-trace-exporter.ts +0 -121
- package/src/otel/fetch.ts +0 -105
- package/src/otel/http.ts +0 -53
- package/src/otel/logger.ts +0 -293
- package/src/otel/otel.ts +0 -354
- package/src/otel/tracestate.ts +0 -108
- package/src/router.ts +0 -75
- package/src/services/evalrun/composite.ts +0 -34
- package/src/services/evalrun/http.ts +0 -167
- package/src/services/evalrun/index.ts +0 -4
- package/src/services/evalrun/json.ts +0 -46
- package/src/services/evalrun/local.ts +0 -28
- package/src/services/local/README.md +0 -1576
- package/src/services/local/_db.ts +0 -353
- package/src/services/local/_router.ts +0 -40
- package/src/services/local/_util.ts +0 -55
- package/src/services/local/email.ts +0 -91
- package/src/services/local/index.ts +0 -9
- package/src/services/local/keyvalue.ts +0 -174
- package/src/services/local/queue.ts +0 -145
- package/src/services/local/stream.ts +0 -358
- package/src/services/local/task.ts +0 -1711
- package/src/services/local/vector.ts +0 -438
- package/src/services/sandbox/http.ts +0 -522
- package/src/services/sandbox/index.ts +0 -1
- package/src/services/session/composite.ts +0 -33
- package/src/services/session/http.ts +0 -167
- package/src/services/session/index.ts +0 -4
- package/src/services/session/json.ts +0 -42
- package/src/services/session/local.ts +0 -33
- package/src/services/thread/local.ts +0 -199
- package/src/session.ts +0 -1960
- package/src/signature.ts +0 -82
- package/src/validator.ts +0 -283
- package/src/version-check.ts +0 -184
- package/src/web.ts +0 -76
- package/src/webrtc-signaling.ts +0 -288
- package/src/workbench.ts +0 -725
package/src/agent.ts
DELETED
|
@@ -1,2739 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import {
|
|
3
|
-
StructuredError,
|
|
4
|
-
type KeyValueStorage,
|
|
5
|
-
type StandardSchemaV1,
|
|
6
|
-
type StreamStorage,
|
|
7
|
-
type VectorStorage,
|
|
8
|
-
type SandboxService,
|
|
9
|
-
type QueueService,
|
|
10
|
-
type EmailService,
|
|
11
|
-
type ScheduleService,
|
|
12
|
-
type TaskStorage,
|
|
13
|
-
type InferInput,
|
|
14
|
-
type InferOutput,
|
|
15
|
-
toCamelCase,
|
|
16
|
-
} from '@agentuity/core';
|
|
17
|
-
import { context, SpanStatusCode, type Tracer, trace } from '@opentelemetry/api';
|
|
18
|
-
import { enrichContextWithTraceState } from './otel/tracestate';
|
|
19
|
-
import type { Context, MiddlewareHandler } from 'hono';
|
|
20
|
-
import type { Handler } from 'hono/types';
|
|
21
|
-
import { validator } from 'hono/validator';
|
|
22
|
-
import { AGENT_RUNTIME, INTERNAL_AGENT, CURRENT_AGENT, AGENT_IDS } from './_config';
|
|
23
|
-
import {
|
|
24
|
-
getAgentContext,
|
|
25
|
-
inHTTPContext,
|
|
26
|
-
getHTTPContext,
|
|
27
|
-
setupRequestAgentContext,
|
|
28
|
-
getAgentAsyncLocalStorage,
|
|
29
|
-
type RequestAgentContextArgs,
|
|
30
|
-
} from './_context';
|
|
31
|
-
import type { Logger } from './logger';
|
|
32
|
-
import type { Eval, EvalHandlerResult, EvalRunResult, EvalFunction } from './eval';
|
|
33
|
-
import { internal } from './logger/internal';
|
|
34
|
-
import { fireEvent } from './_events';
|
|
35
|
-
import type { Thread, Session } from './session';
|
|
36
|
-
import { privateContext } from './_server';
|
|
37
|
-
import { generateId } from './session';
|
|
38
|
-
import { getEvalRunEventProvider } from './_services';
|
|
39
|
-
import * as runtimeConfig from './_config';
|
|
40
|
-
import type { AppState } from './index';
|
|
41
|
-
import { validateSchema, formatValidationIssues } from './_validation';
|
|
42
|
-
import { getAgentMetadataByName, getEvalMetadata } from './_metadata';
|
|
43
|
-
|
|
44
|
-
export type AgentEventName = 'started' | 'completed' | 'errored';
|
|
45
|
-
|
|
46
|
-
export type AgentEventCallback<TAgent extends Agent<any, any, any>> =
|
|
47
|
-
| ((
|
|
48
|
-
eventName: 'started',
|
|
49
|
-
agent: TAgent,
|
|
50
|
-
context: AgentContext<any, any, any>
|
|
51
|
-
) => Promise<void> | void)
|
|
52
|
-
| ((
|
|
53
|
-
eventName: 'completed',
|
|
54
|
-
agent: TAgent,
|
|
55
|
-
context: AgentContext<any, any, any>
|
|
56
|
-
) => Promise<void> | void)
|
|
57
|
-
| ((
|
|
58
|
-
eventName: 'errored',
|
|
59
|
-
agent: TAgent,
|
|
60
|
-
context: AgentContext<any, any, any>,
|
|
61
|
-
data: Error
|
|
62
|
-
) => Promise<void> | void);
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Runtime state container for agents and event listeners.
|
|
66
|
-
* Isolates global state into context for better testing.
|
|
67
|
-
*/
|
|
68
|
-
export interface AgentRuntimeState {
|
|
69
|
-
agents: Map<string, Agent<any, any, any, any, any>>;
|
|
70
|
-
agentConfigs: Map<string, unknown>;
|
|
71
|
-
agentEventListeners: WeakMap<
|
|
72
|
-
Agent<any, any, any, any, any>,
|
|
73
|
-
Map<AgentEventName, Set<AgentEventCallback<any>>>
|
|
74
|
-
>;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Context object passed to every agent handler providing access to runtime services and state.
|
|
79
|
-
*
|
|
80
|
-
* @template TAgentRegistry - Registry of all available agents (auto-generated, strongly-typed)
|
|
81
|
-
* @template TConfig - Agent-specific configuration type from setup function
|
|
82
|
-
* @template TAppState - Application-wide state type from createApp
|
|
83
|
-
*
|
|
84
|
-
* @example
|
|
85
|
-
* ```typescript
|
|
86
|
-
* const agent = createAgent('my-agent', {
|
|
87
|
-
* handler: async (ctx, input) => {
|
|
88
|
-
* // Logging
|
|
89
|
-
* ctx.logger.info('Processing request', { input });
|
|
90
|
-
*
|
|
91
|
-
* // Call another agent (import it directly)
|
|
92
|
-
* import otherAgent from './other-agent';
|
|
93
|
-
* const result = await otherAgent.run({ data: input });
|
|
94
|
-
*
|
|
95
|
-
* // Store data
|
|
96
|
-
* await ctx.kv.set('key', { value: result });
|
|
97
|
-
*
|
|
98
|
-
* // Access config from setup
|
|
99
|
-
* const cache = ctx.config.cache;
|
|
100
|
-
*
|
|
101
|
-
* // Background task
|
|
102
|
-
* ctx.waitUntil(async () => {
|
|
103
|
-
* await ctx.logger.info('Cleanup complete');
|
|
104
|
-
* });
|
|
105
|
-
*
|
|
106
|
-
* return result;
|
|
107
|
-
* }
|
|
108
|
-
* });
|
|
109
|
-
* ```
|
|
110
|
-
*/
|
|
111
|
-
export interface AgentContext<
|
|
112
|
-
_TAgentRegistry extends AgentRegistry = AgentRegistry,
|
|
113
|
-
TConfig = unknown,
|
|
114
|
-
TAppState = Record<string, never>,
|
|
115
|
-
> {
|
|
116
|
-
/**
|
|
117
|
-
* Internal runtime state (agents, configs, event listeners).
|
|
118
|
-
* Stored with Symbol key to prevent accidental access.
|
|
119
|
-
* Use getAgentRuntime(ctx) to access.
|
|
120
|
-
* @internal
|
|
121
|
-
*/
|
|
122
|
-
[AGENT_RUNTIME]: AgentRuntimeState;
|
|
123
|
-
/**
|
|
124
|
-
* Schedule a background task that continues after the response is sent.
|
|
125
|
-
* Useful for cleanup, logging, or async operations that don't block the response.
|
|
126
|
-
*
|
|
127
|
-
* @param promise - Promise or function that returns void or Promise<void>
|
|
128
|
-
*
|
|
129
|
-
* @example
|
|
130
|
-
* ```typescript
|
|
131
|
-
* ctx.waitUntil(async () => {
|
|
132
|
-
* await ctx.kv.set('processed', Date.now());
|
|
133
|
-
* ctx.logger.info('Background task complete');
|
|
134
|
-
* });
|
|
135
|
-
* ```
|
|
136
|
-
*/
|
|
137
|
-
waitUntil: (promise: Promise<void> | (() => void | Promise<void>)) => void;
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Structured logger with OpenTelemetry integration.
|
|
141
|
-
* Logs are automatically correlated with traces.
|
|
142
|
-
*
|
|
143
|
-
* @example
|
|
144
|
-
* ```typescript
|
|
145
|
-
* ctx.logger.info('Processing started', { userId: input.id });
|
|
146
|
-
* ctx.logger.warn('Rate limit approaching', { remaining: 10 });
|
|
147
|
-
* ctx.logger.error('Operation failed', { error: err.message });
|
|
148
|
-
* ```
|
|
149
|
-
*/
|
|
150
|
-
logger: Logger;
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Unique session identifier for this request. Consistent across agent calls in the same session.
|
|
154
|
-
*/
|
|
155
|
-
sessionId: string;
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* OpenTelemetry tracer for creating custom spans and tracking performance.
|
|
159
|
-
*
|
|
160
|
-
* @example
|
|
161
|
-
* ```typescript
|
|
162
|
-
* const span = ctx.tracer.startSpan('database-query');
|
|
163
|
-
* try {
|
|
164
|
-
* const result = await database.query();
|
|
165
|
-
* span.setStatus({ code: SpanStatusCode.OK });
|
|
166
|
-
* return result;
|
|
167
|
-
* } finally {
|
|
168
|
-
* span.end();
|
|
169
|
-
* }
|
|
170
|
-
* ```
|
|
171
|
-
*/
|
|
172
|
-
tracer: Tracer;
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Key-value storage for simple data persistence.
|
|
176
|
-
*
|
|
177
|
-
* @example
|
|
178
|
-
* ```typescript
|
|
179
|
-
* await ctx.kv.set('user:123', { name: 'Alice', age: 30 });
|
|
180
|
-
* const user = await ctx.kv.get('user:123');
|
|
181
|
-
* await ctx.kv.delete('user:123');
|
|
182
|
-
* const keys = await ctx.kv.list('user:*');
|
|
183
|
-
* ```
|
|
184
|
-
*/
|
|
185
|
-
kv: KeyValueStorage;
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Stream storage for real-time data streams and logs.
|
|
189
|
-
*
|
|
190
|
-
* @example
|
|
191
|
-
* ```typescript
|
|
192
|
-
* const stream = await ctx.stream.create('agent-logs');
|
|
193
|
-
* await ctx.stream.write(stream.id, 'Processing step 1');
|
|
194
|
-
* await ctx.stream.write(stream.id, 'Processing step 2');
|
|
195
|
-
* ```
|
|
196
|
-
*/
|
|
197
|
-
stream: StreamStorage;
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Vector storage for embeddings and similarity search.
|
|
201
|
-
*
|
|
202
|
-
* @example
|
|
203
|
-
* ```typescript
|
|
204
|
-
* await ctx.vector.upsert('docs', [
|
|
205
|
-
* { id: '1', values: [0.1, 0.2, 0.3], metadata: { text: 'Hello' } }
|
|
206
|
-
* ]);
|
|
207
|
-
* const results = await ctx.vector.query('docs', [0.1, 0.2, 0.3], { topK: 5 });
|
|
208
|
-
* ```
|
|
209
|
-
*/
|
|
210
|
-
vector: VectorStorage;
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Sandbox service for creating and running isolated code execution environments.
|
|
214
|
-
*
|
|
215
|
-
* @example
|
|
216
|
-
* ```typescript
|
|
217
|
-
* // One-shot execution
|
|
218
|
-
* const result = await ctx.sandbox.run({
|
|
219
|
-
* command: {
|
|
220
|
-
* exec: ['bun', 'run', 'index.ts'],
|
|
221
|
-
* files: [{ path: 'index.ts', content: Buffer.from('console.log("hello")') }]
|
|
222
|
-
* }
|
|
223
|
-
* });
|
|
224
|
-
* console.log('Exit:', result.exitCode);
|
|
225
|
-
*
|
|
226
|
-
* // Interactive sandbox
|
|
227
|
-
* const sandbox = await ctx.sandbox.create({
|
|
228
|
-
* resources: { memory: '1Gi', cpu: '1000m' }
|
|
229
|
-
* });
|
|
230
|
-
* await sandbox.execute({ command: ['bun', 'init'] });
|
|
231
|
-
* await sandbox.execute({ command: ['bun', 'add', 'zod'] });
|
|
232
|
-
* await sandbox.destroy();
|
|
233
|
-
* ```
|
|
234
|
-
*/
|
|
235
|
-
sandbox: SandboxService;
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Queue service for publishing messages to queues.
|
|
239
|
-
*
|
|
240
|
-
* @example
|
|
241
|
-
* ```typescript
|
|
242
|
-
* const result = await ctx.queue.publish('my-queue', { event: 'user.created', userId: '123' });
|
|
243
|
-
* console.log('Message ID:', result.id);
|
|
244
|
-
* ```
|
|
245
|
-
*/
|
|
246
|
-
queue: QueueService;
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Email service for managing email addresses, destinations, and sending/receiving emails.
|
|
250
|
-
*
|
|
251
|
-
* @example
|
|
252
|
-
* ```typescript
|
|
253
|
-
* // Create an email address
|
|
254
|
-
* const address = await ctx.email.createAddress('support');
|
|
255
|
-
*
|
|
256
|
-
* // Send an email
|
|
257
|
-
* const result = await ctx.email.send({
|
|
258
|
-
* from: address.email,
|
|
259
|
-
* to: ['user@example.com'],
|
|
260
|
-
* subject: 'Hello!',
|
|
261
|
-
* text: 'Welcome to our platform.',
|
|
262
|
-
* });
|
|
263
|
-
*
|
|
264
|
-
* // List inbound emails
|
|
265
|
-
* const inbound = await ctx.email.listInbound(address.id);
|
|
266
|
-
* ```
|
|
267
|
-
*/
|
|
268
|
-
email: EmailService;
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Schedule service for managing cron-based scheduled tasks with
|
|
272
|
-
* destinations and delivery tracking.
|
|
273
|
-
*
|
|
274
|
-
* @see https://agentuity.dev/services/schedule
|
|
275
|
-
*/
|
|
276
|
-
schedule: ScheduleService;
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Task service for agent-driven issue tracking.
|
|
280
|
-
*
|
|
281
|
-
* @example
|
|
282
|
-
* ```typescript
|
|
283
|
-
* const task = await ctx.task.create({
|
|
284
|
-
* title: 'Investigate API error',
|
|
285
|
-
* type: 'bug',
|
|
286
|
-
* created_id: ctx.current.id,
|
|
287
|
-
* });
|
|
288
|
-
* await ctx.task.update(task.id, { status: 'in_progress' });
|
|
289
|
-
* ```
|
|
290
|
-
*/
|
|
291
|
-
task: TaskStorage;
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* In-memory state storage scoped to the current request.
|
|
295
|
-
* Use for passing data between middleware and handlers.
|
|
296
|
-
*
|
|
297
|
-
* @example
|
|
298
|
-
* ```typescript
|
|
299
|
-
* ctx.state.set('startTime', Date.now());
|
|
300
|
-
* const duration = Date.now() - (ctx.state.get('startTime') as number);
|
|
301
|
-
* ```
|
|
302
|
-
*/
|
|
303
|
-
state: Map<string, unknown>;
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Thread information for multi-turn conversations.
|
|
307
|
-
*/
|
|
308
|
-
thread: Thread;
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Session information for the current request.
|
|
312
|
-
*/
|
|
313
|
-
session: Session;
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Agent-specific configuration returned from the setup function.
|
|
317
|
-
* Type is inferred from your setup function's return value.
|
|
318
|
-
*
|
|
319
|
-
* @example
|
|
320
|
-
* ```typescript
|
|
321
|
-
* createAgent({
|
|
322
|
-
* setup: async () => ({ cache: new Map(), db: await connectDB() }),
|
|
323
|
-
* handler: async (ctx, input) => {
|
|
324
|
-
* ctx.config.cache.set('key', 'value'); // Strongly typed!
|
|
325
|
-
* await ctx.config.db.query('SELECT * FROM users');
|
|
326
|
-
* }
|
|
327
|
-
* });
|
|
328
|
-
* ```
|
|
329
|
-
*/
|
|
330
|
-
config: TConfig;
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* Application-wide state returned from createApp setup function.
|
|
334
|
-
* Shared across all agents in the application.
|
|
335
|
-
*
|
|
336
|
-
* @example
|
|
337
|
-
* ```typescript
|
|
338
|
-
* const app = createApp({
|
|
339
|
-
* setup: async () => ({ db: await connectDB(), redis: await connectRedis() })
|
|
340
|
-
* });
|
|
341
|
-
*
|
|
342
|
-
* // Later in any agent:
|
|
343
|
-
* handler: async (ctx, input) => {
|
|
344
|
-
* await ctx.app.db.query('SELECT 1');
|
|
345
|
-
* await ctx.app.redis.set('key', 'value');
|
|
346
|
-
* }
|
|
347
|
-
* ```
|
|
348
|
-
*/
|
|
349
|
-
app: TAppState;
|
|
350
|
-
|
|
351
|
-
/**
|
|
352
|
-
* Metadata about the currently executing agent.
|
|
353
|
-
* Provides access to the agent's id, name, and other properties.
|
|
354
|
-
*
|
|
355
|
-
* @example
|
|
356
|
-
* ```typescript
|
|
357
|
-
* handler: async (ctx, input) => {
|
|
358
|
-
* // Use agent ID for namespaced state keys
|
|
359
|
-
* const stateKey = `${ctx.current.id}_counter`;
|
|
360
|
-
* await ctx.thread.state.set(stateKey, value);
|
|
361
|
-
*
|
|
362
|
-
* // Log agent info
|
|
363
|
-
* ctx.logger.info('Running agent', { name: ctx.current.name });
|
|
364
|
-
* }
|
|
365
|
-
* ```
|
|
366
|
-
*/
|
|
367
|
-
current: AgentMetadata;
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* Authentication context when request is authenticated.
|
|
371
|
-
* Available when auth middleware is configured on the Hono app.
|
|
372
|
-
*
|
|
373
|
-
* Will be `null` for:
|
|
374
|
-
* - Unauthenticated requests
|
|
375
|
-
* - Cron jobs
|
|
376
|
-
* - Agent-to-agent calls without auth propagation
|
|
377
|
-
*
|
|
378
|
-
* @example
|
|
379
|
-
* ```typescript
|
|
380
|
-
* handler: async (ctx, input) => {
|
|
381
|
-
* if (!ctx.auth) {
|
|
382
|
-
* return { error: 'Please sign in' };
|
|
383
|
-
* }
|
|
384
|
-
*
|
|
385
|
-
* // Access user info
|
|
386
|
-
* const user = await ctx.auth.getUser();
|
|
387
|
-
* ctx.logger.info(`Request from ${user.email}`);
|
|
388
|
-
*
|
|
389
|
-
* // Check organization role
|
|
390
|
-
* if (await ctx.auth.hasOrgRole('admin')) {
|
|
391
|
-
* // Admin-only logic
|
|
392
|
-
* }
|
|
393
|
-
*
|
|
394
|
-
* return { userId: user.id };
|
|
395
|
-
* }
|
|
396
|
-
* ```
|
|
397
|
-
*/
|
|
398
|
-
auth: import('@agentuity/core').AuthInterface | null;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
type InternalAgentMetadata = {
|
|
402
|
-
/**
|
|
403
|
-
* the unique name for the agent (user-provided).
|
|
404
|
-
*/
|
|
405
|
-
name: string;
|
|
406
|
-
/**
|
|
407
|
-
* the unique identifier for this project, agent and deployment.
|
|
408
|
-
*/
|
|
409
|
-
id: string;
|
|
410
|
-
/**
|
|
411
|
-
* the unique identifier for this agent across multiple deployments
|
|
412
|
-
*/
|
|
413
|
-
agentId: string;
|
|
414
|
-
/**
|
|
415
|
-
* the relative path to the agent from the root project directory.
|
|
416
|
-
*/
|
|
417
|
-
filename: string;
|
|
418
|
-
/**
|
|
419
|
-
* a unique version for the agent. computed as the SHA256 contents of the file.
|
|
420
|
-
*/
|
|
421
|
-
version: string;
|
|
422
|
-
|
|
423
|
-
/**
|
|
424
|
-
* the source code for the input schema.
|
|
425
|
-
*/
|
|
426
|
-
inputSchemaCode?: string;
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
* the source code for the output schema.
|
|
430
|
-
*/
|
|
431
|
-
outputSchemaCode?: string;
|
|
432
|
-
};
|
|
433
|
-
|
|
434
|
-
type ExternalAgentMetadata = {
|
|
435
|
-
/**
|
|
436
|
-
* the human readable description for the agent
|
|
437
|
-
*/
|
|
438
|
-
description?: string;
|
|
439
|
-
};
|
|
440
|
-
|
|
441
|
-
export type AgentMetadata = InternalAgentMetadata & ExternalAgentMetadata;
|
|
442
|
-
|
|
443
|
-
/**
|
|
444
|
-
* Configuration object for creating an agent evaluation function.
|
|
445
|
-
*
|
|
446
|
-
* @template TInput - Input schema type from the agent
|
|
447
|
-
* @template TOutput - Output schema type from the agent
|
|
448
|
-
*/
|
|
449
|
-
export interface CreateEvalConfig<
|
|
450
|
-
TInput extends StandardSchemaV1 | undefined = any,
|
|
451
|
-
TOutput extends StandardSchemaV1 | undefined = any,
|
|
452
|
-
> {
|
|
453
|
-
/**
|
|
454
|
-
* Optional description of what this evaluation does.
|
|
455
|
-
*
|
|
456
|
-
* @example
|
|
457
|
-
* ```typescript
|
|
458
|
-
* description: 'Ensures output is greater than zero'
|
|
459
|
-
* ```
|
|
460
|
-
*/
|
|
461
|
-
description?: string;
|
|
462
|
-
|
|
463
|
-
/**
|
|
464
|
-
* Evaluation handler function that tests the agent's behavior.
|
|
465
|
-
* Return true if the evaluation passes, false if it fails.
|
|
466
|
-
*
|
|
467
|
-
* @param run - Evaluation run context containing input and metadata
|
|
468
|
-
* @param result - The output from the agent handler
|
|
469
|
-
* @returns Boolean indicating pass/fail, or evaluation result object
|
|
470
|
-
*
|
|
471
|
-
* @example
|
|
472
|
-
* ```typescript
|
|
473
|
-
* handler: async (run, result) => {
|
|
474
|
-
* // Assert that output is positive
|
|
475
|
-
* if (result <= 0) {
|
|
476
|
-
* return false; // Evaluation failed
|
|
477
|
-
* }
|
|
478
|
-
* return true; // Evaluation passed
|
|
479
|
-
* }
|
|
480
|
-
* ```
|
|
481
|
-
*
|
|
482
|
-
* @example
|
|
483
|
-
* ```typescript
|
|
484
|
-
* // With detailed result
|
|
485
|
-
* handler: async (run, result) => {
|
|
486
|
-
* const passed = result.length > 5;
|
|
487
|
-
* return {
|
|
488
|
-
* passed,
|
|
489
|
-
* score: passed ? 1.0 : 0.0,
|
|
490
|
-
* message: passed ? 'Output length is valid' : 'Output too short'
|
|
491
|
-
* };
|
|
492
|
-
* }
|
|
493
|
-
* ```
|
|
494
|
-
*/
|
|
495
|
-
handler: EvalFunction<
|
|
496
|
-
TInput extends StandardSchemaV1 ? InferOutput<TInput> : undefined,
|
|
497
|
-
TOutput extends StandardSchemaV1 ? InferOutput<TOutput> : undefined
|
|
498
|
-
>;
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
export type PresetEvalConfig<
|
|
502
|
-
TInput extends StandardSchemaV1 | undefined = any,
|
|
503
|
-
TOutput extends StandardSchemaV1 | undefined = any,
|
|
504
|
-
> = CreateEvalConfig<TInput, TOutput> & { name: string };
|
|
505
|
-
|
|
506
|
-
type CreateEvalMethod<
|
|
507
|
-
TInput extends StandardSchemaV1 | undefined = any,
|
|
508
|
-
TOutput extends StandardSchemaV1 | undefined = any,
|
|
509
|
-
> = {
|
|
510
|
-
(config: PresetEvalConfig<TInput, TOutput>): Eval<TInput, TOutput>;
|
|
511
|
-
(name: string, config: CreateEvalConfig<TInput, TOutput>): Eval<TInput, TOutput>;
|
|
512
|
-
};
|
|
513
|
-
|
|
514
|
-
/**
|
|
515
|
-
* Validator function type with method overloads for different validation scenarios.
|
|
516
|
-
* Provides type-safe validation middleware that integrates with Hono's type system.
|
|
517
|
-
*
|
|
518
|
-
* This validator automatically validates incoming JSON request bodies using StandardSchema-compatible
|
|
519
|
-
* schemas (Zod, Valibot, ArkType, etc.) and provides full TypeScript type inference for validated data
|
|
520
|
-
* accessible via `c.req.valid('json')`.
|
|
521
|
-
*
|
|
522
|
-
* The validator returns 400 Bad Request with validation error details if validation fails.
|
|
523
|
-
*
|
|
524
|
-
* @template TInput - Agent's input schema type (StandardSchemaV1 or undefined)
|
|
525
|
-
* @template _TOutput - Agent's output schema type (reserved for future output validation)
|
|
526
|
-
*
|
|
527
|
-
* @example Basic usage with agent's schema
|
|
528
|
-
* ```typescript
|
|
529
|
-
* router.post('/', agent.validator(), async (c) => {
|
|
530
|
-
* const data = c.req.valid('json'); // Fully typed from agent's input schema
|
|
531
|
-
* return c.json(data);
|
|
532
|
-
* });
|
|
533
|
-
* ```
|
|
534
|
-
*
|
|
535
|
-
* @example Override with custom input schema
|
|
536
|
-
* ```typescript
|
|
537
|
-
* router.post('/custom', agent.validator({ input: z.object({ id: z.string() }) }), async (c) => {
|
|
538
|
-
* const data = c.req.valid('json'); // Typed as { id: string }
|
|
539
|
-
* return c.json(data);
|
|
540
|
-
* });
|
|
541
|
-
* ```
|
|
542
|
-
*/
|
|
543
|
-
export interface AgentValidator<
|
|
544
|
-
TInput extends StandardSchemaV1 | undefined,
|
|
545
|
-
_TOutput extends StandardSchemaV1 | undefined,
|
|
546
|
-
> {
|
|
547
|
-
/**
|
|
548
|
-
* Validates using the agent's input schema (no override).
|
|
549
|
-
* Returns Hono middleware handler that validates JSON request body.
|
|
550
|
-
*
|
|
551
|
-
* @returns Middleware handler with type inference for validated data
|
|
552
|
-
*
|
|
553
|
-
* @example
|
|
554
|
-
* ```typescript
|
|
555
|
-
* // Agent has schema: { input: z.object({ name: z.string() }) }
|
|
556
|
-
* router.post('/', agent.validator(), async (c) => {
|
|
557
|
-
* const data = c.req.valid('json'); // { name: string }
|
|
558
|
-
* return c.json({ received: data.name });
|
|
559
|
-
* });
|
|
560
|
-
* ```
|
|
561
|
-
*/
|
|
562
|
-
(): TInput extends StandardSchemaV1
|
|
563
|
-
? Handler<
|
|
564
|
-
any,
|
|
565
|
-
any,
|
|
566
|
-
{
|
|
567
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
568
|
-
in: {};
|
|
569
|
-
out: { json: InferInput<TInput> };
|
|
570
|
-
}
|
|
571
|
-
>
|
|
572
|
-
: Handler<any, any, any>;
|
|
573
|
-
|
|
574
|
-
/**
|
|
575
|
-
* Output-only validation override.
|
|
576
|
-
* Validates only the response body (no input validation).
|
|
577
|
-
*
|
|
578
|
-
* Useful for GET routes or routes where input validation is handled elsewhere.
|
|
579
|
-
* The middleware validates the JSON response body and throws 500 Internal Server Error
|
|
580
|
-
* if validation fails.
|
|
581
|
-
*
|
|
582
|
-
* @template TOverrideOutput - Custom output schema type
|
|
583
|
-
* @param override - Object containing output schema
|
|
584
|
-
* @returns Middleware handler that validates response output
|
|
585
|
-
*
|
|
586
|
-
* @example GET route with output validation
|
|
587
|
-
* ```typescript
|
|
588
|
-
* router.get('/', agent.validator({ output: z.array(z.object({ id: z.string() })) }), async (c) => {
|
|
589
|
-
* // Returns array of objects - validated against schema
|
|
590
|
-
* return c.json([{ id: '123' }, { id: '456' }]);
|
|
591
|
-
* });
|
|
592
|
-
* ```
|
|
593
|
-
*/
|
|
594
|
-
<TOverrideOutput extends StandardSchemaV1>(override: {
|
|
595
|
-
output: TOverrideOutput;
|
|
596
|
-
}): Handler<
|
|
597
|
-
any,
|
|
598
|
-
any,
|
|
599
|
-
{
|
|
600
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
601
|
-
in: {};
|
|
602
|
-
out: { json: InferOutput<TOverrideOutput> };
|
|
603
|
-
}
|
|
604
|
-
>;
|
|
605
|
-
|
|
606
|
-
/**
|
|
607
|
-
* Validates with custom input and optional output schemas (POST/PUT/PATCH/DELETE).
|
|
608
|
-
* Overrides the agent's schema for this specific route.
|
|
609
|
-
*
|
|
610
|
-
* @template TOverrideInput - Custom input schema type
|
|
611
|
-
* @template TOverrideOutput - Optional custom output schema type
|
|
612
|
-
* @param override - Object containing input (required) and output (optional) schemas
|
|
613
|
-
* @returns Middleware handler with type inference from custom schemas
|
|
614
|
-
*
|
|
615
|
-
* @example Custom input schema
|
|
616
|
-
* ```typescript
|
|
617
|
-
* router.post('/users', agent.validator({
|
|
618
|
-
* input: z.object({ email: z.string().email(), name: z.string() })
|
|
619
|
-
* }), async (c) => {
|
|
620
|
-
* const data = c.req.valid('json'); // { email: string, name: string }
|
|
621
|
-
* return c.json({ id: '123', ...data });
|
|
622
|
-
* });
|
|
623
|
-
* ```
|
|
624
|
-
*
|
|
625
|
-
* @example Custom input and output schemas
|
|
626
|
-
* ```typescript
|
|
627
|
-
* router.post('/convert', agent.validator({
|
|
628
|
-
* input: z.string(),
|
|
629
|
-
* output: z.number()
|
|
630
|
-
* }), async (c) => {
|
|
631
|
-
* const data = c.req.valid('json'); // string
|
|
632
|
-
* return c.json(123);
|
|
633
|
-
* });
|
|
634
|
-
* ```
|
|
635
|
-
*/
|
|
636
|
-
<
|
|
637
|
-
TOverrideInput extends StandardSchemaV1,
|
|
638
|
-
TOverrideOutput extends StandardSchemaV1 | undefined = undefined,
|
|
639
|
-
>(override: {
|
|
640
|
-
input: TOverrideInput;
|
|
641
|
-
output?: TOverrideOutput;
|
|
642
|
-
}): Handler<
|
|
643
|
-
any,
|
|
644
|
-
any,
|
|
645
|
-
{
|
|
646
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
647
|
-
in: {};
|
|
648
|
-
out: {
|
|
649
|
-
json: InferInput<TOverrideInput>;
|
|
650
|
-
};
|
|
651
|
-
}
|
|
652
|
-
>;
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
/**
|
|
656
|
-
* Agent instance type returned by createAgent().
|
|
657
|
-
* Represents a fully configured agent with metadata, handler, lifecycle hooks, and event listeners.
|
|
658
|
-
*
|
|
659
|
-
* @template TInput - Input schema type (StandardSchemaV1 or undefined)
|
|
660
|
-
* @template TOutput - Output schema type (StandardSchemaV1 or undefined)
|
|
661
|
-
* @template TStream - Whether the agent returns a stream (true/false)
|
|
662
|
-
* @template TConfig - Agent-specific configuration type from setup function
|
|
663
|
-
* @template TAppState - Application state type from createApp
|
|
664
|
-
*
|
|
665
|
-
* @example
|
|
666
|
-
* ```typescript
|
|
667
|
-
* const agent = createAgent({
|
|
668
|
-
* metadata: { name: 'My Agent' },
|
|
669
|
-
* schema: { input: z.string(), output: z.number() },
|
|
670
|
-
* handler: async (ctx, input) => input.length
|
|
671
|
-
* });
|
|
672
|
-
*
|
|
673
|
-
* // Access agent properties
|
|
674
|
-
* console.log(agent.metadata.name); // "My Agent"
|
|
675
|
-
*
|
|
676
|
-
* // Add event listeners
|
|
677
|
-
* agent.addEventListener('started', (eventName, agent, ctx) => {
|
|
678
|
-
* console.log('Agent started:', ctx.sessionId);
|
|
679
|
-
* });
|
|
680
|
-
*
|
|
681
|
-
* // Create evals for testing
|
|
682
|
-
* const eval1 = agent.createEval('check-positive', {
|
|
683
|
-
* description: 'Ensures result is greater than 5',
|
|
684
|
-
* handler: async (run, result) => {
|
|
685
|
-
* return result > 5; // Assert output is greater than 5
|
|
686
|
-
* }
|
|
687
|
-
* });
|
|
688
|
-
* ```
|
|
689
|
-
*/
|
|
690
|
-
export type Agent<
|
|
691
|
-
TInput extends StandardSchemaV1 | undefined = any,
|
|
692
|
-
TOutput extends StandardSchemaV1 | undefined = any,
|
|
693
|
-
TStream extends boolean = false,
|
|
694
|
-
TConfig = unknown,
|
|
695
|
-
TAppState = Record<string, never>,
|
|
696
|
-
> = {
|
|
697
|
-
/**
|
|
698
|
-
* Agent metadata including name, description, id, version, and filename.
|
|
699
|
-
*/
|
|
700
|
-
metadata: AgentMetadata;
|
|
701
|
-
|
|
702
|
-
/**
|
|
703
|
-
* The main handler function that processes agent requests.
|
|
704
|
-
* Gets AgentContext from AsyncLocalStorage, receives validated input, returns output or stream.
|
|
705
|
-
*/
|
|
706
|
-
handler: (...args: any[]) => any | Promise<any>;
|
|
707
|
-
|
|
708
|
-
/**
|
|
709
|
-
* Creates a type-safe validation middleware for routes using StandardSchema validation.
|
|
710
|
-
*
|
|
711
|
-
* This method validates incoming JSON request bodies against the agent's **input schema**
|
|
712
|
-
* and optionally validates outgoing JSON responses against the **output schema**.
|
|
713
|
-
* Provides full TypeScript type inference for validated input data accessible via `c.req.valid('json')`.
|
|
714
|
-
*
|
|
715
|
-
* **Validation behavior:**
|
|
716
|
-
* - **Input**: Validates request JSON body, returns 400 Bad Request on failure
|
|
717
|
-
* - **Output**: Validates response JSON body (if output schema provided), throws 500 on failure
|
|
718
|
-
* - Passes validated input data to handler via `c.req.valid('json')`
|
|
719
|
-
* - Full TypeScript type inference for validated input data
|
|
720
|
-
*
|
|
721
|
-
* **Supported schema libraries:**
|
|
722
|
-
* - Zod (z.object, z.string, etc.)
|
|
723
|
-
* - Valibot (v.object, v.string, etc.)
|
|
724
|
-
* - ArkType (type({ ... }))
|
|
725
|
-
* - Any StandardSchema-compatible library
|
|
726
|
-
*
|
|
727
|
-
* **Method overloads:**
|
|
728
|
-
* - `agent.validator()` - Validates using agent's input/output schemas
|
|
729
|
-
* - `agent.validator({ output: schema })` - Output-only validation (no input validation)
|
|
730
|
-
* - `agent.validator({ input: schema })` - Custom input schema override (skips agent's output validation)
|
|
731
|
-
* - `agent.validator({ input: schema, output: schema })` - Both input and output validated
|
|
732
|
-
*
|
|
733
|
-
* @returns Hono middleware handler with proper type inference
|
|
734
|
-
*
|
|
735
|
-
* @example Automatic validation using agent's schema
|
|
736
|
-
* ```typescript
|
|
737
|
-
* // Agent defined with: schema: { input: z.object({ name: z.string(), age: z.number() }) }
|
|
738
|
-
* router.post('/', agent.validator(), async (c) => {
|
|
739
|
-
* const data = c.req.valid('json'); // Fully typed: { name: string, age: number }
|
|
740
|
-
* return c.json({ greeting: `Hello ${data.name}, age ${data.age}` });
|
|
741
|
-
* });
|
|
742
|
-
* ```
|
|
743
|
-
*
|
|
744
|
-
* @example Override with custom schema per-route
|
|
745
|
-
* ```typescript
|
|
746
|
-
* router.post('/email', agent.validator({
|
|
747
|
-
* input: z.object({ email: z.string().email() })
|
|
748
|
-
* }), async (c) => {
|
|
749
|
-
* const data = c.req.valid('json'); // Typed as { email: string }
|
|
750
|
-
* return c.json({ sent: data.email });
|
|
751
|
-
* });
|
|
752
|
-
* ```
|
|
753
|
-
*
|
|
754
|
-
* @example Works with any StandardSchema library
|
|
755
|
-
* ```typescript
|
|
756
|
-
* import * as v from 'valibot';
|
|
757
|
-
*
|
|
758
|
-
* router.post('/valibot', agent.validator({
|
|
759
|
-
* input: v.object({ count: v.number() })
|
|
760
|
-
* }), async (c) => {
|
|
761
|
-
* const data = c.req.valid('json'); // Typed correctly
|
|
762
|
-
* return c.json({ count: data.count });
|
|
763
|
-
* });
|
|
764
|
-
* ```
|
|
765
|
-
*
|
|
766
|
-
* @example Validation error response (400)
|
|
767
|
-
* ```typescript
|
|
768
|
-
* // Request: { "name": "Bob" } (missing 'age')
|
|
769
|
-
* // Response: {
|
|
770
|
-
* // "error": "Validation failed",
|
|
771
|
-
* // "message": "age: Invalid input: expected number, received undefined",
|
|
772
|
-
* // "issues": [{ "message": "...", "path": ["age"] }]
|
|
773
|
-
* // }
|
|
774
|
-
* ```
|
|
775
|
-
*/
|
|
776
|
-
validator: AgentValidator<TInput, TOutput>;
|
|
777
|
-
|
|
778
|
-
/**
|
|
779
|
-
* Array of evaluation functions created via agent.createEval().
|
|
780
|
-
* Used for testing and validating agent behavior.
|
|
781
|
-
*/
|
|
782
|
-
evals?: Eval[];
|
|
783
|
-
|
|
784
|
-
/**
|
|
785
|
-
* Create an evaluation function for testing this agent.
|
|
786
|
-
* Evals can assert correctness of agent input/output during test runs.
|
|
787
|
-
*
|
|
788
|
-
* @param config - Eval configuration
|
|
789
|
-
* @param config.metadata - Optional eval metadata (name, description)
|
|
790
|
-
* @param config.handler - Eval handler function receiving run context and result
|
|
791
|
-
*
|
|
792
|
-
* @example
|
|
793
|
-
* ```typescript
|
|
794
|
-
* const agent = createAgent({
|
|
795
|
-
* schema: { input: z.string(), output: z.number() },
|
|
796
|
-
* handler: async (ctx, input) => input.length
|
|
797
|
-
* });
|
|
798
|
-
*
|
|
799
|
-
* // Create eval to validate output
|
|
800
|
-
* agent.createEval('check-positive', {
|
|
801
|
-
* description: 'Ensures output is a positive number',
|
|
802
|
-
* handler: async (run, result) => {
|
|
803
|
-
* return result > 0; // Assert output is positive
|
|
804
|
-
* }
|
|
805
|
-
* });
|
|
806
|
-
* ```
|
|
807
|
-
*/
|
|
808
|
-
createEval: CreateEvalMethod<TInput, TOutput>;
|
|
809
|
-
|
|
810
|
-
/**
|
|
811
|
-
* Optional setup function called once when app starts.
|
|
812
|
-
* Returns agent-specific configuration available via ctx.config.
|
|
813
|
-
*/
|
|
814
|
-
setup?: (app: TAppState) => Promise<TConfig> | TConfig;
|
|
815
|
-
|
|
816
|
-
/**
|
|
817
|
-
* Optional shutdown function called when app stops.
|
|
818
|
-
* Receives app state and agent config for cleanup.
|
|
819
|
-
*/
|
|
820
|
-
shutdown?: (app: TAppState, config: TConfig) => Promise<void> | void;
|
|
821
|
-
|
|
822
|
-
/**
|
|
823
|
-
* Register an event listener for when the agent starts execution.
|
|
824
|
-
*
|
|
825
|
-
* @param eventName - Must be 'started'
|
|
826
|
-
* @param callback - Function called when agent execution begins
|
|
827
|
-
*
|
|
828
|
-
* @example
|
|
829
|
-
* ```typescript
|
|
830
|
-
* agent.addEventListener('started', (eventName, agent, ctx) => {
|
|
831
|
-
* console.log(`${agent.metadata.name} started at ${new Date()}`);
|
|
832
|
-
* });
|
|
833
|
-
* ```
|
|
834
|
-
*/
|
|
835
|
-
addEventListener(
|
|
836
|
-
eventName: 'started',
|
|
837
|
-
callback: (
|
|
838
|
-
eventName: 'started',
|
|
839
|
-
agent: Agent<TInput, TOutput, TStream, TConfig, TAppState>,
|
|
840
|
-
context: AgentContext<any, TConfig, TAppState>
|
|
841
|
-
) => Promise<void> | void
|
|
842
|
-
): void;
|
|
843
|
-
|
|
844
|
-
/**
|
|
845
|
-
* Register an event listener for when the agent completes successfully.
|
|
846
|
-
*
|
|
847
|
-
* @param eventName - Must be 'completed'
|
|
848
|
-
* @param callback - Function called when agent execution completes
|
|
849
|
-
*
|
|
850
|
-
* @example
|
|
851
|
-
* ```typescript
|
|
852
|
-
* agent.addEventListener('completed', (eventName, agent, ctx) => {
|
|
853
|
-
* console.log(`${agent.metadata.name} completed successfully`);
|
|
854
|
-
* });
|
|
855
|
-
* ```
|
|
856
|
-
*/
|
|
857
|
-
addEventListener(
|
|
858
|
-
eventName: 'completed',
|
|
859
|
-
callback: (
|
|
860
|
-
eventName: 'completed',
|
|
861
|
-
agent: Agent<TInput, TOutput, TStream, TConfig, TAppState>,
|
|
862
|
-
context: AgentContext<any, TConfig, TAppState>
|
|
863
|
-
) => Promise<void> | void
|
|
864
|
-
): void;
|
|
865
|
-
|
|
866
|
-
/**
|
|
867
|
-
* Register an event listener for when the agent throws an error.
|
|
868
|
-
*
|
|
869
|
-
* @param eventName - Must be 'errored'
|
|
870
|
-
* @param callback - Function called when agent execution fails
|
|
871
|
-
*
|
|
872
|
-
* @example
|
|
873
|
-
* ```typescript
|
|
874
|
-
* agent.addEventListener('errored', (eventName, agent, ctx, error) => {
|
|
875
|
-
* console.error(`${agent.metadata.name} failed:`, error.message);
|
|
876
|
-
* });
|
|
877
|
-
* ```
|
|
878
|
-
*/
|
|
879
|
-
addEventListener(
|
|
880
|
-
eventName: 'errored',
|
|
881
|
-
callback: (
|
|
882
|
-
eventName: 'errored',
|
|
883
|
-
agent: Agent<TInput, TOutput, TStream, TConfig, TAppState>,
|
|
884
|
-
context: AgentContext<any, TConfig, TAppState>,
|
|
885
|
-
data: Error
|
|
886
|
-
) => Promise<void> | void
|
|
887
|
-
): void;
|
|
888
|
-
|
|
889
|
-
/**
|
|
890
|
-
* Remove a previously registered 'started' event listener.
|
|
891
|
-
*
|
|
892
|
-
* @param eventName - Must be 'started'
|
|
893
|
-
* @param callback - The callback function to remove
|
|
894
|
-
*/
|
|
895
|
-
removeEventListener(
|
|
896
|
-
eventName: 'started',
|
|
897
|
-
callback: (
|
|
898
|
-
eventName: 'started',
|
|
899
|
-
agent: Agent<TInput, TOutput, TStream, TConfig, TAppState>,
|
|
900
|
-
context: AgentContext<any, TConfig, TAppState>
|
|
901
|
-
) => Promise<void> | void
|
|
902
|
-
): void;
|
|
903
|
-
|
|
904
|
-
/**
|
|
905
|
-
* Remove a previously registered 'completed' event listener.
|
|
906
|
-
*
|
|
907
|
-
* @param eventName - Must be 'completed'
|
|
908
|
-
* @param callback - The callback function to remove
|
|
909
|
-
*/
|
|
910
|
-
removeEventListener(
|
|
911
|
-
eventName: 'completed',
|
|
912
|
-
callback: (
|
|
913
|
-
eventName: 'completed',
|
|
914
|
-
agent: Agent<TInput, TOutput, TStream, TConfig, TAppState>,
|
|
915
|
-
context: AgentContext<any, TConfig, TAppState>
|
|
916
|
-
) => Promise<void> | void
|
|
917
|
-
): void;
|
|
918
|
-
|
|
919
|
-
/**
|
|
920
|
-
* Remove a previously registered 'errored' event listener.
|
|
921
|
-
*
|
|
922
|
-
* @param eventName - Must be 'errored'
|
|
923
|
-
* @param callback - The callback function to remove
|
|
924
|
-
*/
|
|
925
|
-
removeEventListener(
|
|
926
|
-
eventName: 'errored',
|
|
927
|
-
callback: (
|
|
928
|
-
eventName: 'errored',
|
|
929
|
-
agent: Agent<TInput, TOutput, TStream, TConfig, TAppState>,
|
|
930
|
-
context: AgentContext<any, TConfig, TAppState>,
|
|
931
|
-
data: Error
|
|
932
|
-
) => Promise<void> | void
|
|
933
|
-
): void;
|
|
934
|
-
} & (TInput extends StandardSchemaV1 ? { inputSchema: TInput } : { inputSchema?: never }) &
|
|
935
|
-
(TOutput extends StandardSchemaV1 ? { outputSchema: TOutput } : { outputSchema?: never }) &
|
|
936
|
-
(TStream extends true ? { stream: true } : { stream?: false });
|
|
937
|
-
|
|
938
|
-
type InferSchemaInput<T> = T extends StandardSchemaV1 ? InferOutput<T> : never;
|
|
939
|
-
|
|
940
|
-
type InferStreamOutput<TOutput, TStream extends boolean> = TStream extends true
|
|
941
|
-
? TOutput extends StandardSchemaV1
|
|
942
|
-
? ReadableStream<InferOutput<TOutput>>
|
|
943
|
-
: ReadableStream<unknown>
|
|
944
|
-
: TOutput extends StandardSchemaV1
|
|
945
|
-
? InferOutput<TOutput>
|
|
946
|
-
: void;
|
|
947
|
-
|
|
948
|
-
type SchemaInput<TSchema> = TSchema extends { input: infer I } ? I : undefined;
|
|
949
|
-
type SchemaOutput<TSchema> = TSchema extends { output: infer O } ? O : undefined;
|
|
950
|
-
type SchemaStream<TSchema> = TSchema extends { stream: infer S }
|
|
951
|
-
? S extends boolean
|
|
952
|
-
? S
|
|
953
|
-
: false
|
|
954
|
-
: false;
|
|
955
|
-
|
|
956
|
-
type SchemaHandlerReturn<TSchema> =
|
|
957
|
-
SchemaStream<TSchema> extends true
|
|
958
|
-
? SchemaOutput<TSchema> extends StandardSchemaV1
|
|
959
|
-
? ReadableStream<InferOutput<SchemaOutput<TSchema>>>
|
|
960
|
-
: ReadableStream<unknown>
|
|
961
|
-
: SchemaOutput<TSchema> extends StandardSchemaV1
|
|
962
|
-
? InferOutput<SchemaOutput<TSchema>>
|
|
963
|
-
: void;
|
|
964
|
-
|
|
965
|
-
// Handler signature based on schema + setup result (no self-reference)
|
|
966
|
-
type AgentHandlerFromConfig<TSchema, TSetupReturn, TAppState = AppState> =
|
|
967
|
-
SchemaInput<TSchema> extends infer I
|
|
968
|
-
? I extends StandardSchemaV1
|
|
969
|
-
? (
|
|
970
|
-
ctx: AgentContext<any, TSetupReturn, TAppState>,
|
|
971
|
-
input: InferOutput<I>
|
|
972
|
-
) => Promise<SchemaHandlerReturn<TSchema>> | SchemaHandlerReturn<TSchema>
|
|
973
|
-
: (
|
|
974
|
-
ctx: AgentContext<any, TSetupReturn, TAppState>
|
|
975
|
-
) => Promise<SchemaHandlerReturn<TSchema>> | SchemaHandlerReturn<TSchema>
|
|
976
|
-
: (
|
|
977
|
-
ctx: AgentContext<any, TSetupReturn, TAppState>
|
|
978
|
-
) => Promise<SchemaHandlerReturn<TSchema>> | SchemaHandlerReturn<TSchema>;
|
|
979
|
-
|
|
980
|
-
/**
|
|
981
|
-
* Configuration object for creating an agent with automatic type inference.
|
|
982
|
-
*
|
|
983
|
-
* Passed as the second parameter to createAgent(name, config).
|
|
984
|
-
*
|
|
985
|
-
* @template TSchema - Schema definition object containing optional input, output, and stream properties
|
|
986
|
-
* @template TConfig - Function type that returns agent-specific configuration from setup
|
|
987
|
-
*
|
|
988
|
-
* @example
|
|
989
|
-
* ```typescript
|
|
990
|
-
* const agent = createAgent('greeting', {
|
|
991
|
-
* description: 'Generates personalized greetings',
|
|
992
|
-
* schema: {
|
|
993
|
-
* input: z.object({ name: z.string(), age: z.number() }),
|
|
994
|
-
* output: z.string()
|
|
995
|
-
* },
|
|
996
|
-
* handler: async (ctx, { name, age }) => {
|
|
997
|
-
* return `Hello, ${name}! You are ${age} years old.`;
|
|
998
|
-
* }
|
|
999
|
-
* });
|
|
1000
|
-
* ```
|
|
1001
|
-
*/
|
|
1002
|
-
export interface CreateAgentConfig<
|
|
1003
|
-
TSchema extends
|
|
1004
|
-
| {
|
|
1005
|
-
input?: StandardSchemaV1;
|
|
1006
|
-
output?: StandardSchemaV1;
|
|
1007
|
-
stream?: boolean;
|
|
1008
|
-
}
|
|
1009
|
-
| undefined = undefined,
|
|
1010
|
-
TConfig extends (app: AppState) => any = any,
|
|
1011
|
-
> {
|
|
1012
|
-
/**
|
|
1013
|
-
* Optional schema validation using Zod or any StandardSchemaV1 compatible library.
|
|
1014
|
-
*
|
|
1015
|
-
* @example
|
|
1016
|
-
* ```typescript
|
|
1017
|
-
* schema: {
|
|
1018
|
-
* input: z.object({ name: z.string(), age: z.number() }),
|
|
1019
|
-
* output: z.string(),
|
|
1020
|
-
* stream: false
|
|
1021
|
-
* }
|
|
1022
|
-
* ```
|
|
1023
|
-
*/
|
|
1024
|
-
schema?: TSchema;
|
|
1025
|
-
|
|
1026
|
-
/**
|
|
1027
|
-
* Optional description of what this agent does, visible in the Agentuity platform.
|
|
1028
|
-
*
|
|
1029
|
-
* @example
|
|
1030
|
-
* ```typescript
|
|
1031
|
-
* description: 'Returns personalized greetings'
|
|
1032
|
-
* ```
|
|
1033
|
-
*/
|
|
1034
|
-
description?: string;
|
|
1035
|
-
|
|
1036
|
-
/**
|
|
1037
|
-
* Optional metadata object (typically injected by build plugin during compilation).
|
|
1038
|
-
* Contains agent identification and versioning information.
|
|
1039
|
-
*
|
|
1040
|
-
* @internal - Usually populated by build tooling, not manually set
|
|
1041
|
-
*/
|
|
1042
|
-
metadata?: Partial<AgentMetadata>;
|
|
1043
|
-
|
|
1044
|
-
/**
|
|
1045
|
-
* Optional async function called once on app startup to initialize agent-specific resources.
|
|
1046
|
-
* The returned value is available in the handler via `ctx.config`.
|
|
1047
|
-
*
|
|
1048
|
-
* @param app - Application state from createApp setup function
|
|
1049
|
-
* @returns Agent-specific configuration object
|
|
1050
|
-
*
|
|
1051
|
-
* @example
|
|
1052
|
-
* ```typescript
|
|
1053
|
-
* setup: async (app) => {
|
|
1054
|
-
* const cache = new Map();
|
|
1055
|
-
* const db = await connectDB();
|
|
1056
|
-
* return { cache, db };
|
|
1057
|
-
* }
|
|
1058
|
-
* ```
|
|
1059
|
-
*/
|
|
1060
|
-
setup?: TConfig;
|
|
1061
|
-
|
|
1062
|
-
/**
|
|
1063
|
-
* The main agent logic that processes requests.
|
|
1064
|
-
* Receives AgentContext and validated input (if schema.input is defined), returns output or stream.
|
|
1065
|
-
*
|
|
1066
|
-
* @param ctx - Agent context with logger, storage, and other runtime services
|
|
1067
|
-
* @param input - Validated input (only present if schema.input is defined)
|
|
1068
|
-
* @returns Output matching schema.output type, or ReadableStream if schema.stream is true
|
|
1069
|
-
*
|
|
1070
|
-
* @example
|
|
1071
|
-
* ```typescript
|
|
1072
|
-
* handler: async (ctx, { name, age }) => {
|
|
1073
|
-
* ctx.logger.info(`Processing for ${name}`);
|
|
1074
|
-
* await ctx.kv.set('lastUser', name);
|
|
1075
|
-
* return `Hello, ${name}! You are ${age} years old.`;
|
|
1076
|
-
* }
|
|
1077
|
-
* ```
|
|
1078
|
-
*/
|
|
1079
|
-
handler: AgentHandlerFromConfig<
|
|
1080
|
-
TSchema,
|
|
1081
|
-
TConfig extends (app: AppState) => infer R ? Awaited<R> : undefined,
|
|
1082
|
-
AppState
|
|
1083
|
-
>;
|
|
1084
|
-
|
|
1085
|
-
/**
|
|
1086
|
-
* Optional async cleanup function called on app shutdown.
|
|
1087
|
-
* Use this to close connections, flush buffers, etc.
|
|
1088
|
-
*
|
|
1089
|
-
* @param app - Application state from createApp
|
|
1090
|
-
* @param config - Agent config returned from setup function
|
|
1091
|
-
*
|
|
1092
|
-
* @example
|
|
1093
|
-
* ```typescript
|
|
1094
|
-
* shutdown: async (app, config) => {
|
|
1095
|
-
* await config.db.close();
|
|
1096
|
-
* config.cache.clear();
|
|
1097
|
-
* }
|
|
1098
|
-
* ```
|
|
1099
|
-
*/
|
|
1100
|
-
shutdown?: (
|
|
1101
|
-
app: AppState,
|
|
1102
|
-
config: TConfig extends (app: AppState) => infer R ? Awaited<R> : undefined
|
|
1103
|
-
) => Promise<void> | void;
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
|
-
/**
|
|
1107
|
-
* The public interface returned by createAgent().
|
|
1108
|
-
* Provides methods to run the agent, create evaluations, and manage event listeners.
|
|
1109
|
-
*
|
|
1110
|
-
* @template TInput - Input schema type (StandardSchemaV1 or undefined if no input)
|
|
1111
|
-
* @template TOutput - Output schema type (StandardSchemaV1 or undefined if no output)
|
|
1112
|
-
* @template TStream - Whether the agent returns a stream (true/false)
|
|
1113
|
-
*
|
|
1114
|
-
* @example
|
|
1115
|
-
* ```typescript
|
|
1116
|
-
* const agent = createAgent('greeting', {
|
|
1117
|
-
* schema: {
|
|
1118
|
-
* input: z.object({ name: z.string() }),
|
|
1119
|
-
* output: z.string()
|
|
1120
|
-
* },
|
|
1121
|
-
* handler: async (ctx, { name }) => `Hello, ${name}!`
|
|
1122
|
-
* });
|
|
1123
|
-
*
|
|
1124
|
-
* // Run the agent
|
|
1125
|
-
* const result = await agent.run({ name: 'Alice' });
|
|
1126
|
-
*
|
|
1127
|
-
* // Create evaluation
|
|
1128
|
-
* const evalDef = agent.createEval('greeting-accuracy', {
|
|
1129
|
-
* description: 'Checks if greeting includes the user name',
|
|
1130
|
-
* handler: async (ctx, input, output) => {
|
|
1131
|
-
* return { score: output.includes(input.name) ? 1 : 0 };
|
|
1132
|
-
* }
|
|
1133
|
-
* });
|
|
1134
|
-
*
|
|
1135
|
-
* // Listen to events
|
|
1136
|
-
* agent.addEventListener('completed', async (eventName, agent, context) => {
|
|
1137
|
-
* console.log('Agent completed successfully');
|
|
1138
|
-
* });
|
|
1139
|
-
* ```
|
|
1140
|
-
*/
|
|
1141
|
-
export interface AgentRunner<
|
|
1142
|
-
TInput extends StandardSchemaV1 | undefined = any,
|
|
1143
|
-
TOutput extends StandardSchemaV1 | undefined = any,
|
|
1144
|
-
TStream extends boolean = false,
|
|
1145
|
-
> {
|
|
1146
|
-
/** Agent metadata (id, name, description, etc.) */
|
|
1147
|
-
metadata: AgentMetadata;
|
|
1148
|
-
|
|
1149
|
-
/**
|
|
1150
|
-
* Execute the agent with validated input.
|
|
1151
|
-
* If agent has no input schema, call with no arguments.
|
|
1152
|
-
* If agent has input schema, pass validated input object.
|
|
1153
|
-
*
|
|
1154
|
-
* @example
|
|
1155
|
-
* ```typescript
|
|
1156
|
-
* // Agent with input
|
|
1157
|
-
* const result = await agent.run({ name: 'Alice' });
|
|
1158
|
-
*
|
|
1159
|
-
* // Agent without input
|
|
1160
|
-
* const result = await agent.run();
|
|
1161
|
-
* ```
|
|
1162
|
-
*/
|
|
1163
|
-
run: undefined extends TInput
|
|
1164
|
-
? () => Promise<InferStreamOutput<Exclude<TOutput, undefined>, TStream>>
|
|
1165
|
-
: (
|
|
1166
|
-
input: InferSchemaInput<Exclude<TInput, undefined>>
|
|
1167
|
-
) => Promise<InferStreamOutput<Exclude<TOutput, undefined>, TStream>>;
|
|
1168
|
-
|
|
1169
|
-
/**
|
|
1170
|
-
* Create Hono validator middleware for this agent.
|
|
1171
|
-
* Automatically validates request input against the agent's schema.
|
|
1172
|
-
*
|
|
1173
|
-
* @example
|
|
1174
|
-
* ```typescript
|
|
1175
|
-
* import myAgent from './my-agent';
|
|
1176
|
-
* router.post('/', myAgent.validator(), async (c) => {
|
|
1177
|
-
* const data = c.req.valid('json'); // Fully typed!
|
|
1178
|
-
* return c.json(await myAgent.run(data));
|
|
1179
|
-
* });
|
|
1180
|
-
* ```
|
|
1181
|
-
*/
|
|
1182
|
-
validator: AgentValidator<TInput, TOutput>;
|
|
1183
|
-
|
|
1184
|
-
/** Input schema (if defined) */
|
|
1185
|
-
inputSchema?: TInput;
|
|
1186
|
-
|
|
1187
|
-
/** Output schema (if defined) */
|
|
1188
|
-
outputSchema?: TOutput;
|
|
1189
|
-
|
|
1190
|
-
/** Whether agent returns a stream */
|
|
1191
|
-
stream?: TStream;
|
|
1192
|
-
|
|
1193
|
-
/**
|
|
1194
|
-
* Create an evaluation for this agent.
|
|
1195
|
-
* Evaluations run automatically after the agent completes.
|
|
1196
|
-
*
|
|
1197
|
-
* @example
|
|
1198
|
-
* ```typescript
|
|
1199
|
-
* const accuracyEval = agent.createEval('accuracy', {
|
|
1200
|
-
* description: 'Validates output length is non-zero',
|
|
1201
|
-
* handler: async (ctx, input, output) => ({
|
|
1202
|
-
* score: output.length > 0 ? 1 : 0,
|
|
1203
|
-
* metadata: { outputLength: output.length }
|
|
1204
|
-
* })
|
|
1205
|
-
* });
|
|
1206
|
-
* ```
|
|
1207
|
-
*/
|
|
1208
|
-
createEval: CreateEvalMethod<TInput, TOutput>;
|
|
1209
|
-
|
|
1210
|
-
/**
|
|
1211
|
-
* Add event listener for 'started' or 'completed' events.
|
|
1212
|
-
* Listeners fire sequentially in the order they were added.
|
|
1213
|
-
*
|
|
1214
|
-
* @param eventName - 'started' or 'completed'
|
|
1215
|
-
* @param callback - Function to call when event fires
|
|
1216
|
-
*
|
|
1217
|
-
* @example
|
|
1218
|
-
* ```typescript
|
|
1219
|
-
* agent.addEventListener('started', async (eventName, agent, context) => {
|
|
1220
|
-
* context.logger.info('Agent execution started');
|
|
1221
|
-
* });
|
|
1222
|
-
* ```
|
|
1223
|
-
*/
|
|
1224
|
-
addEventListener(
|
|
1225
|
-
eventName: 'started' | 'completed',
|
|
1226
|
-
callback: (
|
|
1227
|
-
eventName: 'started' | 'completed',
|
|
1228
|
-
agent: Agent<TInput, TOutput, TStream, any, any>,
|
|
1229
|
-
context: AgentContext<any, any, any>
|
|
1230
|
-
) => Promise<void> | void
|
|
1231
|
-
): void;
|
|
1232
|
-
|
|
1233
|
-
/**
|
|
1234
|
-
* Add event listener for 'errored' event.
|
|
1235
|
-
* Fires when agent handler throws an error.
|
|
1236
|
-
*
|
|
1237
|
-
* @param eventName - 'errored'
|
|
1238
|
-
* @param callback - Function to call when error occurs
|
|
1239
|
-
*
|
|
1240
|
-
* @example
|
|
1241
|
-
* ```typescript
|
|
1242
|
-
* agent.addEventListener('errored', async (eventName, agent, context, error) => {
|
|
1243
|
-
* context.logger.error('Agent failed', { error: error.message });
|
|
1244
|
-
* });
|
|
1245
|
-
* ```
|
|
1246
|
-
*/
|
|
1247
|
-
addEventListener(
|
|
1248
|
-
eventName: 'errored',
|
|
1249
|
-
callback: (
|
|
1250
|
-
eventName: 'errored',
|
|
1251
|
-
agent: Agent<TInput, TOutput, TStream, any, any>,
|
|
1252
|
-
context: AgentContext<any, any, any>,
|
|
1253
|
-
error: Error
|
|
1254
|
-
) => Promise<void> | void
|
|
1255
|
-
): void;
|
|
1256
|
-
|
|
1257
|
-
/**
|
|
1258
|
-
* Remove event listener for 'started' or 'completed' events.
|
|
1259
|
-
*
|
|
1260
|
-
* @param eventName - 'started' or 'completed'
|
|
1261
|
-
* @param callback - The same callback function that was added
|
|
1262
|
-
*/
|
|
1263
|
-
removeEventListener(
|
|
1264
|
-
eventName: 'started' | 'completed',
|
|
1265
|
-
callback: (
|
|
1266
|
-
eventName: 'started' | 'completed',
|
|
1267
|
-
agent: Agent<TInput, TOutput, TStream, any, any>,
|
|
1268
|
-
context: AgentContext<any, any, any>
|
|
1269
|
-
) => Promise<void> | void
|
|
1270
|
-
): void;
|
|
1271
|
-
|
|
1272
|
-
/**
|
|
1273
|
-
* Remove event listener for 'errored' event.
|
|
1274
|
-
*
|
|
1275
|
-
* @param eventName - 'errored'
|
|
1276
|
-
* @param callback - The same callback function that was added
|
|
1277
|
-
*/
|
|
1278
|
-
removeEventListener(
|
|
1279
|
-
eventName: 'errored',
|
|
1280
|
-
callback: (
|
|
1281
|
-
eventName: 'errored',
|
|
1282
|
-
agent: Agent<TInput, TOutput, TStream, any, any>,
|
|
1283
|
-
context: AgentContext<any, any, any>,
|
|
1284
|
-
error: Error
|
|
1285
|
-
) => Promise<void> | void
|
|
1286
|
-
): void;
|
|
1287
|
-
}
|
|
1288
|
-
|
|
1289
|
-
// Will be populated at runtime with strongly typed agents
|
|
1290
|
-
const agents = new Map<string, Agent<any, any, any, any, any>>();
|
|
1291
|
-
|
|
1292
|
-
// WeakMap to store event listeners for each agent instance (truly private)
|
|
1293
|
-
const agentEventListeners = new WeakMap<
|
|
1294
|
-
Agent<any, any, any, any, any>,
|
|
1295
|
-
Map<AgentEventName, Set<AgentEventCallback<any>>>
|
|
1296
|
-
>();
|
|
1297
|
-
|
|
1298
|
-
// Map to store agent configs returned from setup (keyed by agent name)
|
|
1299
|
-
const agentConfigs = new Map<string, unknown>();
|
|
1300
|
-
|
|
1301
|
-
/**
|
|
1302
|
-
* Get the global runtime state (for production use).
|
|
1303
|
-
* In tests, use TestAgentContext which has isolated runtime state.
|
|
1304
|
-
*/
|
|
1305
|
-
export function getGlobalRuntimeState(): AgentRuntimeState {
|
|
1306
|
-
return {
|
|
1307
|
-
agents,
|
|
1308
|
-
agentConfigs,
|
|
1309
|
-
agentEventListeners,
|
|
1310
|
-
};
|
|
1311
|
-
}
|
|
1312
|
-
|
|
1313
|
-
/**
|
|
1314
|
-
* Get the runtime state from an AgentContext.
|
|
1315
|
-
* @internal
|
|
1316
|
-
*/
|
|
1317
|
-
export function getAgentRuntime(ctx: AgentContext<any, any, any>): AgentRuntimeState {
|
|
1318
|
-
return ctx[AGENT_RUNTIME];
|
|
1319
|
-
}
|
|
1320
|
-
|
|
1321
|
-
// Helper to fire event listeners sequentially, abort on first error
|
|
1322
|
-
async function fireAgentEvent(
|
|
1323
|
-
runtime: AgentRuntimeState,
|
|
1324
|
-
agent: Agent<any, any, any, any, any>,
|
|
1325
|
-
eventName: 'started' | 'completed',
|
|
1326
|
-
context: AgentContext<any, any, any>
|
|
1327
|
-
): Promise<void>;
|
|
1328
|
-
async function fireAgentEvent(
|
|
1329
|
-
runtime: AgentRuntimeState,
|
|
1330
|
-
agent: Agent<any, any, any, any, any>,
|
|
1331
|
-
eventName: 'errored',
|
|
1332
|
-
context: AgentContext<any, any, any>,
|
|
1333
|
-
data: Error
|
|
1334
|
-
): Promise<void>;
|
|
1335
|
-
async function fireAgentEvent(
|
|
1336
|
-
runtime: AgentRuntimeState,
|
|
1337
|
-
agent: Agent<any, any, any, any, any>,
|
|
1338
|
-
eventName: AgentEventName,
|
|
1339
|
-
context: AgentContext<any, any, any>,
|
|
1340
|
-
data?: Error
|
|
1341
|
-
): Promise<void> {
|
|
1342
|
-
// Fire agent-level listeners
|
|
1343
|
-
const listeners = runtime.agentEventListeners.get(agent);
|
|
1344
|
-
if (listeners) {
|
|
1345
|
-
const callbacks = listeners.get(eventName);
|
|
1346
|
-
if (callbacks && callbacks.size > 0) {
|
|
1347
|
-
for (const callback of callbacks) {
|
|
1348
|
-
try {
|
|
1349
|
-
if (eventName === 'errored' && data) {
|
|
1350
|
-
await (callback as any)(eventName, agent, context, data);
|
|
1351
|
-
} else if (eventName === 'started' || eventName === 'completed') {
|
|
1352
|
-
await (callback as any)(eventName, agent, context);
|
|
1353
|
-
}
|
|
1354
|
-
} catch (error) {
|
|
1355
|
-
// Log but don't re-throw - event listener errors should not crash the server
|
|
1356
|
-
internal.error(`Error in agent event listener for '${eventName}':`, error);
|
|
1357
|
-
}
|
|
1358
|
-
}
|
|
1359
|
-
}
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
// Fire global app-level events
|
|
1363
|
-
if (eventName === 'errored' && data) {
|
|
1364
|
-
await fireEvent('agent.errored', agent, context, data);
|
|
1365
|
-
} else if (eventName === 'started') {
|
|
1366
|
-
await fireEvent('agent.started', agent, context);
|
|
1367
|
-
} else if (eventName === 'completed') {
|
|
1368
|
-
await fireEvent('agent.completed', agent, context);
|
|
1369
|
-
}
|
|
1370
|
-
}
|
|
1371
|
-
|
|
1372
|
-
/**
|
|
1373
|
-
* Union type of all registered agent names.
|
|
1374
|
-
* Falls back to `string` when no agents are registered (before augmentation).
|
|
1375
|
-
* After augmentation, this becomes a strict union of agent names for full type safety.
|
|
1376
|
-
*/
|
|
1377
|
-
export type AgentName = keyof AgentRegistry extends never ? string : keyof AgentRegistry;
|
|
1378
|
-
|
|
1379
|
-
/**
|
|
1380
|
-
* Agent registry interface.
|
|
1381
|
-
* This interface is augmented by generated code to provide strongly-typed agent access.
|
|
1382
|
-
*/
|
|
1383
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
1384
|
-
export interface AgentRegistry {}
|
|
1385
|
-
|
|
1386
|
-
export const registerAgent = (name: AgentName, agent: Agent<any, any, any, any, any>): void => {
|
|
1387
|
-
agents.set(name, agent);
|
|
1388
|
-
};
|
|
1389
|
-
|
|
1390
|
-
export const setAgentConfig = (name: AgentName, config: unknown): void => {
|
|
1391
|
-
agentConfigs.set(name, config);
|
|
1392
|
-
};
|
|
1393
|
-
|
|
1394
|
-
export const getAgentConfig = (name: AgentName): unknown => {
|
|
1395
|
-
return agentConfigs.get(name);
|
|
1396
|
-
};
|
|
1397
|
-
|
|
1398
|
-
const ValidationError = StructuredError('ValidationError')<{
|
|
1399
|
-
issues: readonly StandardSchemaV1.Issue[];
|
|
1400
|
-
}>();
|
|
1401
|
-
|
|
1402
|
-
/**
|
|
1403
|
-
* Configuration object for creating an agent with explicit type parameters.
|
|
1404
|
-
*
|
|
1405
|
-
* @template TInput - Input schema type (StandardSchemaV1 or undefined)
|
|
1406
|
-
* @template TOutput - Output schema type (StandardSchemaV1 or undefined)
|
|
1407
|
-
* @template TStream - Whether agent returns a stream (true/false)
|
|
1408
|
-
* @template TConfig - Type returned by setup function
|
|
1409
|
-
* @template TAppState - Custom app state type from createApp
|
|
1410
|
-
*/
|
|
1411
|
-
export interface CreateAgentConfigExplicit<
|
|
1412
|
-
TInput extends StandardSchemaV1 | undefined = undefined,
|
|
1413
|
-
TOutput extends StandardSchemaV1 | undefined = undefined,
|
|
1414
|
-
TStream extends boolean = false,
|
|
1415
|
-
TConfig = unknown,
|
|
1416
|
-
TAppState = AppState,
|
|
1417
|
-
> {
|
|
1418
|
-
/**
|
|
1419
|
-
* Optional schema validation.
|
|
1420
|
-
*
|
|
1421
|
-
* @example
|
|
1422
|
-
* ```typescript
|
|
1423
|
-
* schema: {
|
|
1424
|
-
* input: z.object({ name: z.string() }),
|
|
1425
|
-
* output: z.string(),
|
|
1426
|
-
* stream: false,
|
|
1427
|
-
* }
|
|
1428
|
-
* ```
|
|
1429
|
-
*/
|
|
1430
|
-
schema?: {
|
|
1431
|
-
/** Input validation schema */
|
|
1432
|
-
input?: TInput;
|
|
1433
|
-
/** Output validation schema */
|
|
1434
|
-
output?: TOutput;
|
|
1435
|
-
/** Whether the agent returns a ReadableStream */
|
|
1436
|
-
stream?: TStream;
|
|
1437
|
-
};
|
|
1438
|
-
|
|
1439
|
-
/**
|
|
1440
|
-
* Optional description of what this agent does.
|
|
1441
|
-
*
|
|
1442
|
-
* @example
|
|
1443
|
-
* ```typescript
|
|
1444
|
-
* description: 'Does something useful'
|
|
1445
|
-
* ```
|
|
1446
|
-
*/
|
|
1447
|
-
description?: string;
|
|
1448
|
-
|
|
1449
|
-
/**
|
|
1450
|
-
* Optional metadata object (typically injected by build plugin during compilation).
|
|
1451
|
-
* Contains agent identification and versioning information.
|
|
1452
|
-
*
|
|
1453
|
-
* @internal - Usually populated by build tooling, not manually set
|
|
1454
|
-
*/
|
|
1455
|
-
metadata?: Partial<AgentMetadata>;
|
|
1456
|
-
|
|
1457
|
-
/**
|
|
1458
|
-
* Optional setup function receiving app state, returns agent config.
|
|
1459
|
-
* The returned value is available in the handler via `ctx.config`.
|
|
1460
|
-
*
|
|
1461
|
-
* @param app - Application state from createApp
|
|
1462
|
-
* @returns Agent-specific configuration
|
|
1463
|
-
*
|
|
1464
|
-
* @example
|
|
1465
|
-
* ```typescript
|
|
1466
|
-
* setup: async (app) => ({ cache: new Map() })
|
|
1467
|
-
* ```
|
|
1468
|
-
*/
|
|
1469
|
-
setup?: (app: TAppState) => Promise<TConfig> | TConfig;
|
|
1470
|
-
|
|
1471
|
-
/**
|
|
1472
|
-
* Optional cleanup function called on app shutdown.
|
|
1473
|
-
*
|
|
1474
|
-
* @param app - Application state from createApp
|
|
1475
|
-
* @param config - Agent config returned from setup
|
|
1476
|
-
*
|
|
1477
|
-
* @example
|
|
1478
|
-
* ```typescript
|
|
1479
|
-
* shutdown: async (app, config) => {
|
|
1480
|
-
* config.cache.clear();
|
|
1481
|
-
* }
|
|
1482
|
-
* ```
|
|
1483
|
-
*/
|
|
1484
|
-
shutdown?: (app: TAppState, config: TConfig) => Promise<void> | void;
|
|
1485
|
-
|
|
1486
|
-
/**
|
|
1487
|
-
* Agent handler function.
|
|
1488
|
-
* Type is automatically inferred based on schema definitions.
|
|
1489
|
-
*
|
|
1490
|
-
* @param ctx - Agent context
|
|
1491
|
-
* @param input - Validated input (only present if schema.input is defined)
|
|
1492
|
-
* @returns Output or ReadableStream based on schema
|
|
1493
|
-
*
|
|
1494
|
-
* @example
|
|
1495
|
-
* ```typescript
|
|
1496
|
-
* handler: async (ctx, input) => {
|
|
1497
|
-
* return `Hello, ${input.name}!`;
|
|
1498
|
-
* }
|
|
1499
|
-
* ```
|
|
1500
|
-
*/
|
|
1501
|
-
handler: TInput extends StandardSchemaV1
|
|
1502
|
-
? TStream extends true
|
|
1503
|
-
? TOutput extends StandardSchemaV1
|
|
1504
|
-
? (
|
|
1505
|
-
c: AgentContext<any, TConfig, TAppState>,
|
|
1506
|
-
input: InferOutput<TInput>
|
|
1507
|
-
) =>
|
|
1508
|
-
| Promise<ReadableStream<InferOutput<TOutput>>>
|
|
1509
|
-
| ReadableStream<InferOutput<TOutput>>
|
|
1510
|
-
: (
|
|
1511
|
-
c: AgentContext<any, TConfig, TAppState>,
|
|
1512
|
-
input: InferOutput<TInput>
|
|
1513
|
-
) => Promise<ReadableStream<unknown>> | ReadableStream<unknown>
|
|
1514
|
-
: TOutput extends StandardSchemaV1
|
|
1515
|
-
? (
|
|
1516
|
-
c: AgentContext<any, TConfig, TAppState>,
|
|
1517
|
-
input: InferOutput<TInput>
|
|
1518
|
-
) => Promise<InferOutput<TOutput>> | InferOutput<TOutput>
|
|
1519
|
-
: (
|
|
1520
|
-
c: AgentContext<any, TConfig, TAppState>,
|
|
1521
|
-
input: InferOutput<TInput>
|
|
1522
|
-
) => Promise<void> | void
|
|
1523
|
-
: TStream extends true
|
|
1524
|
-
? TOutput extends StandardSchemaV1
|
|
1525
|
-
? (
|
|
1526
|
-
c: AgentContext<any, TConfig, TAppState>
|
|
1527
|
-
) =>
|
|
1528
|
-
| Promise<ReadableStream<InferOutput<TOutput>>>
|
|
1529
|
-
| ReadableStream<InferOutput<TOutput>>
|
|
1530
|
-
: (
|
|
1531
|
-
c: AgentContext<any, TConfig, TAppState>
|
|
1532
|
-
) => Promise<ReadableStream<unknown>> | ReadableStream<unknown>
|
|
1533
|
-
: TOutput extends StandardSchemaV1
|
|
1534
|
-
? (
|
|
1535
|
-
c: AgentContext<any, TConfig, TAppState>
|
|
1536
|
-
) => Promise<InferOutput<TOutput>> | InferOutput<TOutput>
|
|
1537
|
-
: (c: AgentContext<any, TConfig, TAppState>) => Promise<void> | void;
|
|
1538
|
-
}
|
|
1539
|
-
|
|
1540
|
-
/**
|
|
1541
|
-
* Creates an agent with schema validation and lifecycle hooks.
|
|
1542
|
-
*
|
|
1543
|
-
* This is the recommended way to create agents with automatic type inference from schemas.
|
|
1544
|
-
*
|
|
1545
|
-
* @template TSchema - Schema definition object containing optional input, output, and stream properties
|
|
1546
|
-
* @template TConfig - Function type that returns agent-specific configuration from setup
|
|
1547
|
-
*
|
|
1548
|
-
* @param name - Unique agent name (must be unique within the project)
|
|
1549
|
-
* @param config - Agent configuration object
|
|
1550
|
-
*
|
|
1551
|
-
* @returns AgentRunner with a run method for executing the agent
|
|
1552
|
-
*
|
|
1553
|
-
* @example
|
|
1554
|
-
* ```typescript
|
|
1555
|
-
* const agent = createAgent('greeting-agent', {
|
|
1556
|
-
* description: 'Returns personalized greetings',
|
|
1557
|
-
* schema: {
|
|
1558
|
-
* input: z.object({ name: z.string(), age: z.number() }),
|
|
1559
|
-
* output: z.string()
|
|
1560
|
-
* },
|
|
1561
|
-
* handler: async (ctx, { name, age }) => {
|
|
1562
|
-
* ctx.logger.info(`Processing greeting for ${name}`);
|
|
1563
|
-
* return `Hello, ${name}! You are ${age} years old.`;
|
|
1564
|
-
* }
|
|
1565
|
-
* });
|
|
1566
|
-
*
|
|
1567
|
-
* // Call the agent directly
|
|
1568
|
-
* const result = await agent.run({ name: 'Alice', age: 30 });
|
|
1569
|
-
* ```
|
|
1570
|
-
*/
|
|
1571
|
-
export function createAgent<
|
|
1572
|
-
TSchema extends
|
|
1573
|
-
| {
|
|
1574
|
-
input?: StandardSchemaV1;
|
|
1575
|
-
output?: StandardSchemaV1;
|
|
1576
|
-
stream?: boolean;
|
|
1577
|
-
}
|
|
1578
|
-
| undefined = undefined,
|
|
1579
|
-
TConfig extends (app: AppState) => any = any,
|
|
1580
|
-
>(
|
|
1581
|
-
name: string,
|
|
1582
|
-
config: CreateAgentConfig<TSchema, TConfig>
|
|
1583
|
-
): AgentRunner<SchemaInput<TSchema>, SchemaOutput<TSchema>, SchemaStream<TSchema>>;
|
|
1584
|
-
|
|
1585
|
-
/**
|
|
1586
|
-
* Creates an agent with explicit generic type parameters.
|
|
1587
|
-
*
|
|
1588
|
-
* Use this overload when you need explicit control over types or working with custom app state.
|
|
1589
|
-
*
|
|
1590
|
-
* @template TInput - Input schema type (StandardSchemaV1 or undefined)
|
|
1591
|
-
* @template TOutput - Output schema type (StandardSchemaV1 or undefined)
|
|
1592
|
-
* @template TStream - Whether agent returns a stream (true/false)
|
|
1593
|
-
* @template TConfig - Type returned by setup function
|
|
1594
|
-
* @template TAppState - Custom app state type from createApp
|
|
1595
|
-
*
|
|
1596
|
-
* @param name - Unique agent name (must be unique within the project)
|
|
1597
|
-
* @param config - Agent configuration object
|
|
1598
|
-
*
|
|
1599
|
-
* @returns AgentRunner with explicit types and a run method
|
|
1600
|
-
*
|
|
1601
|
-
* @example
|
|
1602
|
-
* ```typescript
|
|
1603
|
-
* interface MyAppState { db: Database }
|
|
1604
|
-
* interface MyConfig { cache: Map<string, any> }
|
|
1605
|
-
*
|
|
1606
|
-
* const agent = createAgent<
|
|
1607
|
-
* z.ZodObject<any>, // TInput
|
|
1608
|
-
* z.ZodString, // TOutput
|
|
1609
|
-
* false // TStream
|
|
1610
|
-
* >('custom-agent', {
|
|
1611
|
-
* setup: async (app) => ({ cache: new Map() }),
|
|
1612
|
-
* handler: async (ctx, input) => {
|
|
1613
|
-
* const db = ctx.app.db;
|
|
1614
|
-
* const cache = ctx.config.cache;
|
|
1615
|
-
* return 'result';
|
|
1616
|
-
* }
|
|
1617
|
-
* });
|
|
1618
|
-
* ```
|
|
1619
|
-
*/
|
|
1620
|
-
export function createAgent<
|
|
1621
|
-
TInput extends StandardSchemaV1 | undefined = undefined,
|
|
1622
|
-
TOutput extends StandardSchemaV1 | undefined = undefined,
|
|
1623
|
-
TStream extends boolean = false,
|
|
1624
|
-
TConfig = unknown,
|
|
1625
|
-
TAppState = AppState,
|
|
1626
|
-
>(
|
|
1627
|
-
name: string,
|
|
1628
|
-
config: CreateAgentConfigExplicit<TInput, TOutput, TStream, TConfig, TAppState>
|
|
1629
|
-
): AgentRunner<TInput, TOutput, TStream>;
|
|
1630
|
-
|
|
1631
|
-
// Implementation
|
|
1632
|
-
export function createAgent<
|
|
1633
|
-
TInput extends StandardSchemaV1 | undefined = undefined,
|
|
1634
|
-
TOutput extends StandardSchemaV1 | undefined = undefined,
|
|
1635
|
-
TStream extends boolean = false,
|
|
1636
|
-
TConfig = unknown,
|
|
1637
|
-
TAppState = AppState,
|
|
1638
|
-
>(
|
|
1639
|
-
name: string,
|
|
1640
|
-
config: CreateAgentConfigExplicit<TInput, TOutput, TStream, TConfig, TAppState>
|
|
1641
|
-
): AgentRunner<TInput, TOutput, TStream> {
|
|
1642
|
-
const inputSchema = config.schema?.input;
|
|
1643
|
-
const outputSchema = config.schema?.output;
|
|
1644
|
-
|
|
1645
|
-
// Initialize evals array before handler so it can be captured in closure
|
|
1646
|
-
// Evals should only be added via agent.createEval() after agent creation
|
|
1647
|
-
const evalsArray: Eval[] = [];
|
|
1648
|
-
|
|
1649
|
-
const handler = async (input?: any) => {
|
|
1650
|
-
let validatedInput: any = undefined;
|
|
1651
|
-
|
|
1652
|
-
if (inputSchema) {
|
|
1653
|
-
const inputResult = await inputSchema['~standard'].validate(input);
|
|
1654
|
-
if (inputResult.issues) {
|
|
1655
|
-
throw new ValidationError({
|
|
1656
|
-
issues: inputResult.issues,
|
|
1657
|
-
message: `Input validation failed: ${inputResult.issues.map((i: any) => i.message).join(', ')}`,
|
|
1658
|
-
});
|
|
1659
|
-
}
|
|
1660
|
-
validatedInput = inputResult.value;
|
|
1661
|
-
}
|
|
1662
|
-
|
|
1663
|
-
const agentCtx = getAgentContext() as AgentContext<any, TConfig, TAppState>;
|
|
1664
|
-
|
|
1665
|
-
// Store current agent for telemetry (using Symbol to keep it internal)
|
|
1666
|
-
(agentCtx as any)[CURRENT_AGENT] = agent;
|
|
1667
|
-
|
|
1668
|
-
// Expose current agent metadata on the context
|
|
1669
|
-
agentCtx.current = agent.metadata;
|
|
1670
|
-
|
|
1671
|
-
// Update ctx.config with this agent's setup() return value.
|
|
1672
|
-
// This ensures correct config when:
|
|
1673
|
-
// - Agent is called via user router (createAgentMiddleware(''))
|
|
1674
|
-
// - Agent A calls Agent B (agentB.run() inside handler)
|
|
1675
|
-
// - Agent is called via runInAgentContext() in tests
|
|
1676
|
-
const agentConfig = getAgentConfig(agent.metadata.name as AgentName);
|
|
1677
|
-
if (agentConfig !== undefined) {
|
|
1678
|
-
agentCtx.config = agentConfig as TConfig;
|
|
1679
|
-
}
|
|
1680
|
-
|
|
1681
|
-
const attrs = {
|
|
1682
|
-
'@agentuity/agentId': agent.metadata.agentId, // stable ID (agent_*) - consistent across deployments
|
|
1683
|
-
'@agentuity/agentInstanceId': agent.metadata.id, // deployment-specific ID (agentid_*) - changes per deployment
|
|
1684
|
-
'@agentuity/agentDescription': agent.metadata.description,
|
|
1685
|
-
'@agentuity/agentName': agent.metadata.name,
|
|
1686
|
-
'@agentuity/threadId': agentCtx.thread.id,
|
|
1687
|
-
};
|
|
1688
|
-
|
|
1689
|
-
// Set agent attributes on the current active span
|
|
1690
|
-
const activeSpan = trace.getActiveSpan();
|
|
1691
|
-
if (activeSpan) {
|
|
1692
|
-
activeSpan.setAttributes(attrs);
|
|
1693
|
-
}
|
|
1694
|
-
|
|
1695
|
-
if (inHTTPContext()) {
|
|
1696
|
-
const honoCtx = privateContext(getHTTPContext());
|
|
1697
|
-
if (honoCtx.var.agentIds) {
|
|
1698
|
-
if (agent.metadata.id) honoCtx.var.agentIds.add(agent.metadata.id);
|
|
1699
|
-
if (agent.metadata.agentId) honoCtx.var.agentIds.add(agent.metadata.agentId);
|
|
1700
|
-
}
|
|
1701
|
-
} else {
|
|
1702
|
-
// For standalone contexts, check for AGENT_IDS symbol
|
|
1703
|
-
const agentIds = (agentCtx as any)[AGENT_IDS] as Set<string> | undefined;
|
|
1704
|
-
if (agentIds) {
|
|
1705
|
-
if (agent.metadata.id) agentIds.add(agent.metadata.id);
|
|
1706
|
-
if (agent.metadata.agentId) agentIds.add(agent.metadata.agentId);
|
|
1707
|
-
}
|
|
1708
|
-
}
|
|
1709
|
-
|
|
1710
|
-
agentCtx.logger = agentCtx.logger.child(attrs);
|
|
1711
|
-
|
|
1712
|
-
// Get the agent instance from the runtime state to fire events
|
|
1713
|
-
const runtime = getAgentRuntime(agentCtx);
|
|
1714
|
-
|
|
1715
|
-
// Fire 'started' event
|
|
1716
|
-
await fireAgentEvent(runtime, agent as Agent, 'started', agentCtx);
|
|
1717
|
-
|
|
1718
|
-
try {
|
|
1719
|
-
// Execute the handler directly - span creation is handled by the caller (AgentRunner.run)
|
|
1720
|
-
// This avoids duplicate spans when agents call other agents
|
|
1721
|
-
const result = await (async () => {
|
|
1722
|
-
if (agent.metadata.id && !inHTTPContext()) {
|
|
1723
|
-
// For standalone contexts, wrap with agent context to set aid in trace state
|
|
1724
|
-
return runWithAgentContext(agent.metadata.id, () =>
|
|
1725
|
-
inputSchema
|
|
1726
|
-
? (config.handler as any)(agentCtx, validatedInput)
|
|
1727
|
-
: (config.handler as any)(agentCtx)
|
|
1728
|
-
);
|
|
1729
|
-
} else {
|
|
1730
|
-
// HTTP context or no agent ID - invoke handler directly
|
|
1731
|
-
// Span is created by AgentRunner.run or createAgentRunner
|
|
1732
|
-
return inputSchema
|
|
1733
|
-
? (config.handler as any)(agentCtx, validatedInput)
|
|
1734
|
-
: (config.handler as any)(agentCtx);
|
|
1735
|
-
}
|
|
1736
|
-
})();
|
|
1737
|
-
|
|
1738
|
-
let validatedOutput: any = result;
|
|
1739
|
-
// Skip output validation for streaming agents (they return ReadableStream)
|
|
1740
|
-
if (outputSchema && !config.schema?.stream) {
|
|
1741
|
-
const outputResult = await outputSchema['~standard'].validate(result);
|
|
1742
|
-
if (outputResult.issues) {
|
|
1743
|
-
throw new ValidationError({
|
|
1744
|
-
issues: outputResult.issues,
|
|
1745
|
-
message: `Output validation failed: ${outputResult.issues.map((i: any) => i.message).join(', ')}`,
|
|
1746
|
-
});
|
|
1747
|
-
}
|
|
1748
|
-
validatedOutput = outputResult.value;
|
|
1749
|
-
}
|
|
1750
|
-
|
|
1751
|
-
// Store validated input/output in context state for event listeners
|
|
1752
|
-
agentCtx.state.set('_evalInput', validatedInput);
|
|
1753
|
-
agentCtx.state.set('_evalOutput', validatedOutput);
|
|
1754
|
-
|
|
1755
|
-
// Fire 'completed' event - evals will run via event listener
|
|
1756
|
-
await fireAgentEvent(runtime, agent as Agent, 'completed', agentCtx);
|
|
1757
|
-
|
|
1758
|
-
return validatedOutput;
|
|
1759
|
-
} catch (error) {
|
|
1760
|
-
// Fire 'errored' event
|
|
1761
|
-
await fireAgentEvent(runtime, agent as Agent, 'errored', agentCtx, error as Error);
|
|
1762
|
-
throw error;
|
|
1763
|
-
}
|
|
1764
|
-
};
|
|
1765
|
-
|
|
1766
|
-
// Infer input/output types from agent schema
|
|
1767
|
-
type AgentInput = TInput extends StandardSchemaV1 ? InferOutput<TInput> : undefined;
|
|
1768
|
-
type AgentOutput = TOutput extends StandardSchemaV1 ? InferOutput<TOutput> : undefined;
|
|
1769
|
-
|
|
1770
|
-
// Create createEval method that infers types from agent and automatically adds to agent
|
|
1771
|
-
const createEval: CreateEvalMethod<TInput, TOutput> = ((
|
|
1772
|
-
evalNameOrConfig: string | PresetEvalConfig<TInput, TOutput>,
|
|
1773
|
-
evalConfig?: {
|
|
1774
|
-
description?: string;
|
|
1775
|
-
handler: EvalFunction<AgentInput, AgentOutput>;
|
|
1776
|
-
metadata?: {
|
|
1777
|
-
id?: string;
|
|
1778
|
-
identifier?: string;
|
|
1779
|
-
version?: string;
|
|
1780
|
-
filename?: string;
|
|
1781
|
-
};
|
|
1782
|
-
}
|
|
1783
|
-
): Eval<TInput, TOutput> => {
|
|
1784
|
-
// Handle preset eval config (single argument with name property)
|
|
1785
|
-
if (typeof evalNameOrConfig !== 'string' && 'name' in evalNameOrConfig) {
|
|
1786
|
-
const presetConfig = evalNameOrConfig as PresetEvalConfig<TInput, TOutput>;
|
|
1787
|
-
const evalName = presetConfig.name;
|
|
1788
|
-
|
|
1789
|
-
internal.debug(
|
|
1790
|
-
`createEval called for agent "${name || 'unknown'}": registering preset eval "${evalName}"`
|
|
1791
|
-
);
|
|
1792
|
-
|
|
1793
|
-
const evalType: any = {
|
|
1794
|
-
metadata: {
|
|
1795
|
-
identifier: evalName,
|
|
1796
|
-
name: evalName,
|
|
1797
|
-
description: presetConfig.description || '',
|
|
1798
|
-
},
|
|
1799
|
-
handler: presetConfig.handler,
|
|
1800
|
-
};
|
|
1801
|
-
|
|
1802
|
-
if (inputSchema) {
|
|
1803
|
-
evalType.inputSchema = inputSchema;
|
|
1804
|
-
}
|
|
1805
|
-
|
|
1806
|
-
if (outputSchema) {
|
|
1807
|
-
evalType.outputSchema = outputSchema;
|
|
1808
|
-
}
|
|
1809
|
-
|
|
1810
|
-
evalsArray.push(evalType);
|
|
1811
|
-
internal.debug(
|
|
1812
|
-
`Added preset eval "${evalName}" to agent "${name || 'unknown'}". Total evals: ${evalsArray.length}`
|
|
1813
|
-
);
|
|
1814
|
-
|
|
1815
|
-
return evalType as Eval<TInput, TOutput>;
|
|
1816
|
-
}
|
|
1817
|
-
|
|
1818
|
-
// Handle custom eval config (name + config)
|
|
1819
|
-
if (typeof evalNameOrConfig !== 'string' || !evalConfig) {
|
|
1820
|
-
throw new Error(
|
|
1821
|
-
'Invalid arguments: expected (name: string, config) or (config: PresetEvalConfig)'
|
|
1822
|
-
);
|
|
1823
|
-
}
|
|
1824
|
-
|
|
1825
|
-
const evalName = evalNameOrConfig;
|
|
1826
|
-
|
|
1827
|
-
// Trace log to verify evals file is imported
|
|
1828
|
-
internal.debug(
|
|
1829
|
-
`createEval called for agent "${name || 'unknown'}": registering eval "${evalName}"`
|
|
1830
|
-
);
|
|
1831
|
-
|
|
1832
|
-
// Use build-time injected metadata if available (same pattern as agents)
|
|
1833
|
-
const evalMetadata = evalConfig.metadata || {};
|
|
1834
|
-
|
|
1835
|
-
// Build eval metadata - merge injected metadata with defaults
|
|
1836
|
-
const evalType: any = {
|
|
1837
|
-
metadata: {
|
|
1838
|
-
// Use build-time injected metadata if available, otherwise fallback to empty/undefined
|
|
1839
|
-
id: evalMetadata.id || undefined,
|
|
1840
|
-
identifier: evalMetadata.identifier || undefined,
|
|
1841
|
-
version: evalMetadata.version || undefined,
|
|
1842
|
-
filename: evalMetadata.filename || '',
|
|
1843
|
-
name: evalName,
|
|
1844
|
-
description: evalConfig.description || '',
|
|
1845
|
-
},
|
|
1846
|
-
handler: evalConfig.handler,
|
|
1847
|
-
};
|
|
1848
|
-
|
|
1849
|
-
if (inputSchema) {
|
|
1850
|
-
evalType.inputSchema = inputSchema;
|
|
1851
|
-
}
|
|
1852
|
-
|
|
1853
|
-
if (outputSchema) {
|
|
1854
|
-
evalType.outputSchema = outputSchema;
|
|
1855
|
-
}
|
|
1856
|
-
|
|
1857
|
-
// Automatically add eval to agent's evals array
|
|
1858
|
-
evalsArray.push(evalType);
|
|
1859
|
-
internal.debug(
|
|
1860
|
-
`Added eval "${evalName}" to agent "${name || 'unknown'}". Total evals: ${evalsArray.length}`
|
|
1861
|
-
);
|
|
1862
|
-
|
|
1863
|
-
return evalType as Eval<TInput, TOutput>;
|
|
1864
|
-
}) as CreateEvalMethod<TInput, TOutput>;
|
|
1865
|
-
|
|
1866
|
-
// Build metadata - merge user-provided metadata with defaults
|
|
1867
|
-
// The build plugin injects metadata via config.metadata during AST transformation
|
|
1868
|
-
let metadata: Partial<AgentMetadata> = {
|
|
1869
|
-
// Defaults (used when running without build, e.g., dev mode)
|
|
1870
|
-
name,
|
|
1871
|
-
description: config.description,
|
|
1872
|
-
id: '',
|
|
1873
|
-
agentId: '',
|
|
1874
|
-
filename: '',
|
|
1875
|
-
version: '',
|
|
1876
|
-
inputSchemaCode: '',
|
|
1877
|
-
outputSchemaCode: '',
|
|
1878
|
-
// Merge in build-time injected metadata (overrides defaults)
|
|
1879
|
-
...config.metadata,
|
|
1880
|
-
};
|
|
1881
|
-
|
|
1882
|
-
// If id/agentId are empty, try to load from agentuity.metadata.json
|
|
1883
|
-
if (!metadata.id || !metadata.agentId) {
|
|
1884
|
-
const fileMetadata = getAgentMetadataByName(name);
|
|
1885
|
-
if (fileMetadata) {
|
|
1886
|
-
internal.info(
|
|
1887
|
-
'[agent] loaded metadata for "%s" from file: id=%s, agentId=%s',
|
|
1888
|
-
name,
|
|
1889
|
-
fileMetadata.id,
|
|
1890
|
-
fileMetadata.agentId
|
|
1891
|
-
);
|
|
1892
|
-
metadata = {
|
|
1893
|
-
...metadata,
|
|
1894
|
-
id: fileMetadata.id || metadata.id,
|
|
1895
|
-
agentId: fileMetadata.agentId || metadata.agentId,
|
|
1896
|
-
filename: fileMetadata.filename || metadata.filename,
|
|
1897
|
-
version: fileMetadata.version || metadata.version,
|
|
1898
|
-
};
|
|
1899
|
-
}
|
|
1900
|
-
}
|
|
1901
|
-
|
|
1902
|
-
// Error if agent has no metadata IDs in production - this causes agent_ids to be empty in sessions
|
|
1903
|
-
// which affects analytics, billing attribution, and session filtering
|
|
1904
|
-
// Only enforce in production (when AGENTUITY_CLOUD_PROJECT_ID is set) to allow dev/test without metadata
|
|
1905
|
-
if (!metadata.id && !metadata.agentId && runtimeConfig.getProjectId()) {
|
|
1906
|
-
throw new Error(
|
|
1907
|
-
`Agent "${name}" has no metadata IDs (id and agentId are empty). ` +
|
|
1908
|
-
`This will result in empty agent_ids in session events. ` +
|
|
1909
|
-
`Ensure agentuity.metadata.json exists in the runtime directory ` +
|
|
1910
|
-
`(checked: ${process.cwd()}/agentuity.metadata.json and ${process.cwd()}/.agentuity/agentuity.metadata.json). ` +
|
|
1911
|
-
`Run 'agentuity build' to generate the metadata file.`
|
|
1912
|
-
);
|
|
1913
|
-
}
|
|
1914
|
-
|
|
1915
|
-
const agent: any = {
|
|
1916
|
-
handler,
|
|
1917
|
-
metadata,
|
|
1918
|
-
evals: evalsArray,
|
|
1919
|
-
createEval,
|
|
1920
|
-
setup: config.setup,
|
|
1921
|
-
shutdown: config.shutdown,
|
|
1922
|
-
};
|
|
1923
|
-
|
|
1924
|
-
// Add event listener methods
|
|
1925
|
-
agent.addEventListener = (eventName: AgentEventName, callback: any): void => {
|
|
1926
|
-
const agentForListeners = agent as any as Agent<any, any, any>;
|
|
1927
|
-
const callbackForListeners = callback as any as AgentEventCallback<any>;
|
|
1928
|
-
let listeners = agentEventListeners.get(agentForListeners);
|
|
1929
|
-
if (!listeners) {
|
|
1930
|
-
listeners = new Map();
|
|
1931
|
-
agentEventListeners.set(agentForListeners, listeners);
|
|
1932
|
-
}
|
|
1933
|
-
let callbacks = listeners.get(eventName);
|
|
1934
|
-
if (!callbacks) {
|
|
1935
|
-
callbacks = new Set();
|
|
1936
|
-
listeners.set(eventName, callbacks);
|
|
1937
|
-
}
|
|
1938
|
-
callbacks.add(callbackForListeners);
|
|
1939
|
-
};
|
|
1940
|
-
|
|
1941
|
-
// Automatically add event listener for 'completed' event to run evals
|
|
1942
|
-
(agent as Agent).addEventListener('completed', async (_event, _agent, ctx) => {
|
|
1943
|
-
// Use the agent instance passed to event listener to access its evals array
|
|
1944
|
-
// This ensures we get evals that were added via agent.createEval() after agent creation
|
|
1945
|
-
const agentEvals = _agent?.evals || evalsArray;
|
|
1946
|
-
|
|
1947
|
-
internal.debug(
|
|
1948
|
-
`Checking evals: agent=${_agent.metadata?.name}, evalsArray.length=${evalsArray?.length || 0}, agent.evals.length=${_agent?.evals?.length || 0}`
|
|
1949
|
-
);
|
|
1950
|
-
|
|
1951
|
-
if (agentEvals && agentEvals.length > 0) {
|
|
1952
|
-
internal.info(`Executing ${agentEvals.length} evaluations after agent run`);
|
|
1953
|
-
|
|
1954
|
-
// Get validated input/output from context state
|
|
1955
|
-
const validatedInput = ctx.state.get('_evalInput');
|
|
1956
|
-
const validatedOutput = ctx.state.get('_evalOutput');
|
|
1957
|
-
|
|
1958
|
-
// Capture agentRunSpanId synchronously before waitUntil (which may run outside AsyncLocalStorage)
|
|
1959
|
-
let agentRunSpanId: string | undefined;
|
|
1960
|
-
try {
|
|
1961
|
-
const httpCtx = getHTTPContext();
|
|
1962
|
-
const _httpCtx = privateContext(httpCtx);
|
|
1963
|
-
agentRunSpanId = _httpCtx.var.agentRunSpanId;
|
|
1964
|
-
} catch {
|
|
1965
|
-
// HTTP context may not be available, spanId will be undefined
|
|
1966
|
-
}
|
|
1967
|
-
|
|
1968
|
-
// Capture the agent span context so eval spans are parented to the agent
|
|
1969
|
-
const agentSpanContext = context.active();
|
|
1970
|
-
|
|
1971
|
-
// Execute each eval using waitUntil to avoid blocking the response
|
|
1972
|
-
for (const evalItem of agentEvals) {
|
|
1973
|
-
const evalName = evalItem.metadata.name || 'unnamed';
|
|
1974
|
-
const agentName = _agent?.metadata?.name || name;
|
|
1975
|
-
const evalRunId = generateId('evalrun');
|
|
1976
|
-
|
|
1977
|
-
// Look up eval metadata synchronously before async execution
|
|
1978
|
-
const evalMeta = getEvalMetadata(agentName, evalName);
|
|
1979
|
-
const evalId = evalMeta?.id || '';
|
|
1980
|
-
const evalIdentifier = evalMeta?.identifier || '';
|
|
1981
|
-
|
|
1982
|
-
// Create eval span FIRST, parented to agent, then call waitUntil inside it
|
|
1983
|
-
// This makes waitUntil a child of the eval span
|
|
1984
|
-
const tracer = ctx.tracer;
|
|
1985
|
-
if (tracer) {
|
|
1986
|
-
const evalSpan = tracer.startSpan(evalName, {}, agentSpanContext);
|
|
1987
|
-
evalSpan.setAttributes({
|
|
1988
|
-
'@agentuity/evalId': evalId,
|
|
1989
|
-
'@agentuity/evalIdentifier': evalIdentifier,
|
|
1990
|
-
'@agentuity/evalName': evalName,
|
|
1991
|
-
'@agentuity/evalRunId': evalRunId,
|
|
1992
|
-
'@agentuity/agentName': agentName,
|
|
1993
|
-
'@agentuity/evalDescription':
|
|
1994
|
-
evalMeta?.description || evalItem.metadata.description || '',
|
|
1995
|
-
'@agentuity/evalFilename': evalMeta?.filename || evalItem.metadata.filename || '',
|
|
1996
|
-
});
|
|
1997
|
-
|
|
1998
|
-
const evalSpanContext = trace.setSpan(agentSpanContext, evalSpan);
|
|
1999
|
-
|
|
2000
|
-
// Run waitUntil INSIDE the eval span context - this makes waitUntil a child of eval
|
|
2001
|
-
// Pass a function (not an already-executing promise) so waitUntil executes it
|
|
2002
|
-
// AFTER setting up its span context, making operations children of waitUntil
|
|
2003
|
-
context.with(evalSpanContext, () => {
|
|
2004
|
-
ctx.waitUntil(async () => {
|
|
2005
|
-
const orgId = runtimeConfig.getOrganizationId();
|
|
2006
|
-
const projectId = runtimeConfig.getProjectId();
|
|
2007
|
-
const devMode = runtimeConfig.isDevMode() ?? false;
|
|
2008
|
-
const evalRunEventProvider = getEvalRunEventProvider();
|
|
2009
|
-
|
|
2010
|
-
const shouldSendEvalRunEvents =
|
|
2011
|
-
orgId && projectId && evalId !== '' && evalIdentifier !== '';
|
|
2012
|
-
|
|
2013
|
-
try {
|
|
2014
|
-
internal.info(`[EVALRUN] Starting eval run tracking for '${evalName}'`);
|
|
2015
|
-
|
|
2016
|
-
// Send eval run start event
|
|
2017
|
-
if (shouldSendEvalRunEvents && evalRunEventProvider) {
|
|
2018
|
-
try {
|
|
2019
|
-
const deploymentId = runtimeConfig.getDeploymentId();
|
|
2020
|
-
await evalRunEventProvider.start({
|
|
2021
|
-
id: evalRunId,
|
|
2022
|
-
sessionId: ctx.sessionId,
|
|
2023
|
-
evalId,
|
|
2024
|
-
evalIdentifier,
|
|
2025
|
-
orgId: orgId!,
|
|
2026
|
-
projectId: projectId!,
|
|
2027
|
-
devmode: Boolean(devMode),
|
|
2028
|
-
deploymentId: deploymentId || undefined,
|
|
2029
|
-
spanId: agentRunSpanId,
|
|
2030
|
-
});
|
|
2031
|
-
} catch (error) {
|
|
2032
|
-
internal.error(
|
|
2033
|
-
`[EVALRUN] Error sending start event for '${evalName}'`,
|
|
2034
|
-
{ error }
|
|
2035
|
-
);
|
|
2036
|
-
}
|
|
2037
|
-
}
|
|
2038
|
-
|
|
2039
|
-
// Validate eval input/output if schemas exist
|
|
2040
|
-
let evalValidatedInput: any = validatedInput;
|
|
2041
|
-
let evalValidatedOutput: any = validatedOutput;
|
|
2042
|
-
|
|
2043
|
-
if (evalItem.inputSchema) {
|
|
2044
|
-
const result =
|
|
2045
|
-
await evalItem.inputSchema['~standard'].validate(validatedInput);
|
|
2046
|
-
if (result.issues) {
|
|
2047
|
-
throw new ValidationError({
|
|
2048
|
-
issues: result.issues,
|
|
2049
|
-
message: `Eval input validation failed`,
|
|
2050
|
-
});
|
|
2051
|
-
}
|
|
2052
|
-
evalValidatedInput = result.value;
|
|
2053
|
-
}
|
|
2054
|
-
|
|
2055
|
-
if (evalItem.outputSchema) {
|
|
2056
|
-
const result =
|
|
2057
|
-
await evalItem.outputSchema['~standard'].validate(validatedOutput);
|
|
2058
|
-
if (result.issues) {
|
|
2059
|
-
throw new ValidationError({
|
|
2060
|
-
issues: result.issues,
|
|
2061
|
-
message: `Eval output validation failed`,
|
|
2062
|
-
});
|
|
2063
|
-
}
|
|
2064
|
-
evalValidatedOutput = result.value;
|
|
2065
|
-
}
|
|
2066
|
-
|
|
2067
|
-
// Execute the eval handler
|
|
2068
|
-
let handlerResult: EvalHandlerResult;
|
|
2069
|
-
if (inputSchema && outputSchema) {
|
|
2070
|
-
handlerResult = await (evalItem.handler as any)(
|
|
2071
|
-
ctx,
|
|
2072
|
-
evalValidatedInput,
|
|
2073
|
-
evalValidatedOutput
|
|
2074
|
-
);
|
|
2075
|
-
} else if (inputSchema) {
|
|
2076
|
-
handlerResult = await (evalItem.handler as any)(ctx, evalValidatedInput);
|
|
2077
|
-
} else if (outputSchema) {
|
|
2078
|
-
handlerResult = await (evalItem.handler as any)(
|
|
2079
|
-
ctx,
|
|
2080
|
-
evalValidatedOutput
|
|
2081
|
-
);
|
|
2082
|
-
} else {
|
|
2083
|
-
handlerResult = await (evalItem.handler as any)(ctx);
|
|
2084
|
-
}
|
|
2085
|
-
|
|
2086
|
-
const result: EvalRunResult = { success: true, ...handlerResult };
|
|
2087
|
-
|
|
2088
|
-
// Send eval run complete event
|
|
2089
|
-
if (shouldSendEvalRunEvents && evalRunEventProvider) {
|
|
2090
|
-
try {
|
|
2091
|
-
await evalRunEventProvider.complete({ id: evalRunId, result });
|
|
2092
|
-
} catch (error) {
|
|
2093
|
-
internal.error(
|
|
2094
|
-
`[EVALRUN] Error sending complete event for '${evalName}'`,
|
|
2095
|
-
{ error }
|
|
2096
|
-
);
|
|
2097
|
-
}
|
|
2098
|
-
}
|
|
2099
|
-
|
|
2100
|
-
internal.debug(`Eval '${evalName}' completed successfully`);
|
|
2101
|
-
} catch (error) {
|
|
2102
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2103
|
-
evalSpan.recordException(error as Error);
|
|
2104
|
-
evalSpan.setStatus({
|
|
2105
|
-
code: SpanStatusCode.ERROR,
|
|
2106
|
-
message: errorMessage,
|
|
2107
|
-
});
|
|
2108
|
-
internal.error(`Error executing eval '${evalName}'`, { error });
|
|
2109
|
-
|
|
2110
|
-
// Send error event
|
|
2111
|
-
if (shouldSendEvalRunEvents && evalRunEventProvider) {
|
|
2112
|
-
try {
|
|
2113
|
-
await evalRunEventProvider.complete({
|
|
2114
|
-
id: evalRunId,
|
|
2115
|
-
error: errorMessage,
|
|
2116
|
-
result: {
|
|
2117
|
-
success: false,
|
|
2118
|
-
passed: false,
|
|
2119
|
-
error: errorMessage,
|
|
2120
|
-
metadata: {},
|
|
2121
|
-
},
|
|
2122
|
-
});
|
|
2123
|
-
} catch (e) {
|
|
2124
|
-
internal.debug('Failed to send eval run complete event', {
|
|
2125
|
-
evalRunId,
|
|
2126
|
-
errorMessage,
|
|
2127
|
-
error: e instanceof Error ? e.message : String(e),
|
|
2128
|
-
});
|
|
2129
|
-
}
|
|
2130
|
-
}
|
|
2131
|
-
} finally {
|
|
2132
|
-
evalSpan.end();
|
|
2133
|
-
}
|
|
2134
|
-
});
|
|
2135
|
-
});
|
|
2136
|
-
} else {
|
|
2137
|
-
// No tracer - execute without span
|
|
2138
|
-
ctx.waitUntil(async () => {
|
|
2139
|
-
const orgId = runtimeConfig.getOrganizationId();
|
|
2140
|
-
const projectId = runtimeConfig.getProjectId();
|
|
2141
|
-
const devMode = runtimeConfig.isDevMode() ?? false;
|
|
2142
|
-
const evalRunEventProvider = getEvalRunEventProvider();
|
|
2143
|
-
const shouldSendEvalRunEvents =
|
|
2144
|
-
orgId && projectId && evalId !== '' && evalIdentifier !== '';
|
|
2145
|
-
|
|
2146
|
-
try {
|
|
2147
|
-
if (shouldSendEvalRunEvents && evalRunEventProvider) {
|
|
2148
|
-
try {
|
|
2149
|
-
await evalRunEventProvider.start({
|
|
2150
|
-
id: evalRunId,
|
|
2151
|
-
sessionId: ctx.sessionId,
|
|
2152
|
-
evalId,
|
|
2153
|
-
evalIdentifier,
|
|
2154
|
-
orgId: orgId!,
|
|
2155
|
-
projectId: projectId!,
|
|
2156
|
-
devmode: Boolean(devMode),
|
|
2157
|
-
deploymentId: runtimeConfig.getDeploymentId() || undefined,
|
|
2158
|
-
spanId: agentRunSpanId,
|
|
2159
|
-
});
|
|
2160
|
-
} catch (e) {
|
|
2161
|
-
internal.debug('Failed to send eval run start event', {
|
|
2162
|
-
evalRunId,
|
|
2163
|
-
evalId,
|
|
2164
|
-
evalIdentifier,
|
|
2165
|
-
sessionId: ctx.sessionId,
|
|
2166
|
-
error: e instanceof Error ? e.message : String(e),
|
|
2167
|
-
});
|
|
2168
|
-
}
|
|
2169
|
-
}
|
|
2170
|
-
|
|
2171
|
-
let evalValidatedInput: any = validatedInput;
|
|
2172
|
-
let evalValidatedOutput: any = validatedOutput;
|
|
2173
|
-
|
|
2174
|
-
if (evalItem.inputSchema) {
|
|
2175
|
-
const result =
|
|
2176
|
-
await evalItem.inputSchema['~standard'].validate(validatedInput);
|
|
2177
|
-
if (result.issues) {
|
|
2178
|
-
throw new ValidationError({
|
|
2179
|
-
issues: result.issues,
|
|
2180
|
-
message: `Eval input validation failed`,
|
|
2181
|
-
});
|
|
2182
|
-
}
|
|
2183
|
-
evalValidatedInput = result.value;
|
|
2184
|
-
}
|
|
2185
|
-
if (evalItem.outputSchema) {
|
|
2186
|
-
const result =
|
|
2187
|
-
await evalItem.outputSchema['~standard'].validate(validatedOutput);
|
|
2188
|
-
if (result.issues) {
|
|
2189
|
-
throw new ValidationError({
|
|
2190
|
-
issues: result.issues,
|
|
2191
|
-
message: `Eval output validation failed`,
|
|
2192
|
-
});
|
|
2193
|
-
}
|
|
2194
|
-
evalValidatedOutput = result.value;
|
|
2195
|
-
}
|
|
2196
|
-
|
|
2197
|
-
let handlerResult: EvalHandlerResult;
|
|
2198
|
-
if (inputSchema && outputSchema) {
|
|
2199
|
-
handlerResult = await (evalItem.handler as any)(
|
|
2200
|
-
ctx,
|
|
2201
|
-
evalValidatedInput,
|
|
2202
|
-
evalValidatedOutput
|
|
2203
|
-
);
|
|
2204
|
-
} else if (inputSchema) {
|
|
2205
|
-
handlerResult = await (evalItem.handler as any)(ctx, evalValidatedInput);
|
|
2206
|
-
} else if (outputSchema) {
|
|
2207
|
-
handlerResult = await (evalItem.handler as any)(ctx, evalValidatedOutput);
|
|
2208
|
-
} else {
|
|
2209
|
-
handlerResult = await (evalItem.handler as any)(ctx);
|
|
2210
|
-
}
|
|
2211
|
-
|
|
2212
|
-
if (shouldSendEvalRunEvents && evalRunEventProvider) {
|
|
2213
|
-
try {
|
|
2214
|
-
await evalRunEventProvider.complete({
|
|
2215
|
-
id: evalRunId,
|
|
2216
|
-
result: { success: true, ...handlerResult },
|
|
2217
|
-
});
|
|
2218
|
-
} catch (e) {
|
|
2219
|
-
internal.debug('Failed to send eval run complete event', {
|
|
2220
|
-
evalRunId,
|
|
2221
|
-
error: e instanceof Error ? e.message : String(e),
|
|
2222
|
-
});
|
|
2223
|
-
}
|
|
2224
|
-
}
|
|
2225
|
-
} catch (error) {
|
|
2226
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2227
|
-
internal.error(`Error executing eval '${evalName}'`, { error });
|
|
2228
|
-
|
|
2229
|
-
// Send error event to match traced branch behavior
|
|
2230
|
-
if (shouldSendEvalRunEvents && evalRunEventProvider) {
|
|
2231
|
-
try {
|
|
2232
|
-
await evalRunEventProvider.complete({
|
|
2233
|
-
id: evalRunId,
|
|
2234
|
-
error: errorMessage,
|
|
2235
|
-
result: {
|
|
2236
|
-
success: false,
|
|
2237
|
-
passed: false,
|
|
2238
|
-
error: errorMessage,
|
|
2239
|
-
metadata: {},
|
|
2240
|
-
},
|
|
2241
|
-
});
|
|
2242
|
-
} catch (e) {
|
|
2243
|
-
internal.debug('Failed to send eval run complete event', {
|
|
2244
|
-
evalRunId,
|
|
2245
|
-
errorMessage,
|
|
2246
|
-
error: e instanceof Error ? e.message : String(e),
|
|
2247
|
-
});
|
|
2248
|
-
}
|
|
2249
|
-
}
|
|
2250
|
-
}
|
|
2251
|
-
});
|
|
2252
|
-
}
|
|
2253
|
-
}
|
|
2254
|
-
}
|
|
2255
|
-
});
|
|
2256
|
-
|
|
2257
|
-
agent.removeEventListener = (eventName: AgentEventName, callback: any): void => {
|
|
2258
|
-
const agentForListeners = agent as any as Agent<any, any, any>;
|
|
2259
|
-
const callbackForListeners = callback as any as AgentEventCallback<any>;
|
|
2260
|
-
const listeners = agentEventListeners.get(agentForListeners);
|
|
2261
|
-
if (!listeners) return;
|
|
2262
|
-
const callbacks = listeners.get(eventName);
|
|
2263
|
-
if (!callbacks) return;
|
|
2264
|
-
callbacks.delete(callbackForListeners);
|
|
2265
|
-
};
|
|
2266
|
-
|
|
2267
|
-
if (inputSchema) {
|
|
2268
|
-
agent.inputSchema = inputSchema;
|
|
2269
|
-
}
|
|
2270
|
-
|
|
2271
|
-
if (outputSchema) {
|
|
2272
|
-
agent.outputSchema = outputSchema;
|
|
2273
|
-
}
|
|
2274
|
-
|
|
2275
|
-
if (config.schema?.stream) {
|
|
2276
|
-
agent.stream = config.schema.stream;
|
|
2277
|
-
}
|
|
2278
|
-
|
|
2279
|
-
// Add validator method with overloads
|
|
2280
|
-
agent.validator = ((override?: any) => {
|
|
2281
|
-
const effectiveInputSchema = override?.input ?? inputSchema;
|
|
2282
|
-
// Only use agent's output schema if no override was provided at all.
|
|
2283
|
-
// If override is provided (even with just input), don't auto-apply agent's output schema
|
|
2284
|
-
// unless the override explicitly includes output.
|
|
2285
|
-
const effectiveOutputSchema = override ? override.output : outputSchema;
|
|
2286
|
-
|
|
2287
|
-
// Helper to build the standard Hono input validator so types flow
|
|
2288
|
-
const buildInputValidator = (schema?: StandardSchemaV1) =>
|
|
2289
|
-
validator('json', async (value, c) => {
|
|
2290
|
-
if (schema) {
|
|
2291
|
-
const result = await validateSchema(schema, value);
|
|
2292
|
-
if (!result.success) {
|
|
2293
|
-
return c.json(
|
|
2294
|
-
{
|
|
2295
|
-
error: 'Validation failed',
|
|
2296
|
-
message: formatValidationIssues(result.issues),
|
|
2297
|
-
issues: result.issues,
|
|
2298
|
-
},
|
|
2299
|
-
400
|
|
2300
|
-
);
|
|
2301
|
-
}
|
|
2302
|
-
return result.data;
|
|
2303
|
-
}
|
|
2304
|
-
return value;
|
|
2305
|
-
});
|
|
2306
|
-
|
|
2307
|
-
// If no output schema, preserve existing behavior: pure input validation
|
|
2308
|
-
if (!effectiveOutputSchema) {
|
|
2309
|
-
return buildInputValidator(effectiveInputSchema);
|
|
2310
|
-
}
|
|
2311
|
-
|
|
2312
|
-
// Output validation middleware (runs after handler)
|
|
2313
|
-
const outputValidator: MiddlewareHandler = async (c, next) => {
|
|
2314
|
-
await next();
|
|
2315
|
-
|
|
2316
|
-
const res = c.res;
|
|
2317
|
-
if (!res) return;
|
|
2318
|
-
|
|
2319
|
-
// Skip output validation for streaming agents
|
|
2320
|
-
if (config.schema?.stream) {
|
|
2321
|
-
return;
|
|
2322
|
-
}
|
|
2323
|
-
|
|
2324
|
-
// Only validate JSON responses
|
|
2325
|
-
const contentType = res.headers.get('Content-Type') ?? '';
|
|
2326
|
-
if (!contentType.toLowerCase().includes('application/json')) {
|
|
2327
|
-
return;
|
|
2328
|
-
}
|
|
2329
|
-
|
|
2330
|
-
// Clone so we don't consume the body that will be sent
|
|
2331
|
-
let responseBody: unknown;
|
|
2332
|
-
try {
|
|
2333
|
-
const cloned = res.clone();
|
|
2334
|
-
responseBody = await cloned.json();
|
|
2335
|
-
} catch {
|
|
2336
|
-
const OutputValidationError = StructuredError('OutputValidationError')<{
|
|
2337
|
-
issues: any[];
|
|
2338
|
-
}>();
|
|
2339
|
-
throw new OutputValidationError({
|
|
2340
|
-
message: 'Output validation failed: response is not valid JSON',
|
|
2341
|
-
issues: [],
|
|
2342
|
-
});
|
|
2343
|
-
}
|
|
2344
|
-
|
|
2345
|
-
const result = await validateSchema(effectiveOutputSchema, responseBody);
|
|
2346
|
-
if (!result.success) {
|
|
2347
|
-
const OutputValidationError = StructuredError('OutputValidationError')<{
|
|
2348
|
-
issues: any[];
|
|
2349
|
-
}>();
|
|
2350
|
-
throw new OutputValidationError({
|
|
2351
|
-
message: `Output validation failed: ${formatValidationIssues(result.issues)}`,
|
|
2352
|
-
issues: result.issues,
|
|
2353
|
-
});
|
|
2354
|
-
}
|
|
2355
|
-
|
|
2356
|
-
// Replace response with validated/sanitized JSON
|
|
2357
|
-
c.res = new Response(JSON.stringify(result.data), {
|
|
2358
|
-
status: res.status,
|
|
2359
|
-
headers: res.headers,
|
|
2360
|
-
});
|
|
2361
|
-
};
|
|
2362
|
-
|
|
2363
|
-
// If we have no input schema, we only do output validation
|
|
2364
|
-
if (!effectiveInputSchema) {
|
|
2365
|
-
return outputValidator as unknown as Handler;
|
|
2366
|
-
}
|
|
2367
|
-
|
|
2368
|
-
// Compose: input validator → output validator
|
|
2369
|
-
const inputMiddleware = buildInputValidator(effectiveInputSchema);
|
|
2370
|
-
|
|
2371
|
-
const composed: MiddlewareHandler = async (c, next) => {
|
|
2372
|
-
// Run the validator first; its next() runs the output validator,
|
|
2373
|
-
// whose next() runs the actual handler(s)
|
|
2374
|
-
const result = await inputMiddleware(c, async () => {
|
|
2375
|
-
await outputValidator(c, next);
|
|
2376
|
-
});
|
|
2377
|
-
// If inputMiddleware returned early (validation failed), return that response
|
|
2378
|
-
return result;
|
|
2379
|
-
};
|
|
2380
|
-
|
|
2381
|
-
return composed as unknown as Handler;
|
|
2382
|
-
}) as AgentValidator<TInput, TOutput>;
|
|
2383
|
-
|
|
2384
|
-
// Register the agent for runtime use
|
|
2385
|
-
// @ts-expect-error - metadata might be incomplete until build plugin injects InternalAgentMetadata
|
|
2386
|
-
agents.set(name, agent as Agent<TInput, TOutput, TStream, TConfig, TAppState>);
|
|
2387
|
-
|
|
2388
|
-
// Create and return AgentRunner
|
|
2389
|
-
const runner: any = {
|
|
2390
|
-
metadata: metadata as AgentMetadata,
|
|
2391
|
-
validator: agent.validator,
|
|
2392
|
-
inputSchema: inputSchema as TInput,
|
|
2393
|
-
outputSchema: outputSchema as TOutput,
|
|
2394
|
-
stream: (config.schema?.stream as TStream) || (false as TStream),
|
|
2395
|
-
evals: agent.evals,
|
|
2396
|
-
createEval,
|
|
2397
|
-
addEventListener: agent.addEventListener,
|
|
2398
|
-
removeEventListener: agent.removeEventListener,
|
|
2399
|
-
run: inputSchema
|
|
2400
|
-
? async (input: InferSchemaInput<Exclude<TInput, undefined>>) => {
|
|
2401
|
-
// Wrap with span if in HTTP context with tracer
|
|
2402
|
-
if (inHTTPContext()) {
|
|
2403
|
-
const honoCtx = getHTTPContext();
|
|
2404
|
-
const tracer = honoCtx.var.tracer;
|
|
2405
|
-
if (tracer) {
|
|
2406
|
-
return runWithSpan(
|
|
2407
|
-
tracer,
|
|
2408
|
-
agent as Agent<TInput, TOutput, TStream>,
|
|
2409
|
-
honoCtx,
|
|
2410
|
-
async () => await agent.handler(input)
|
|
2411
|
-
);
|
|
2412
|
-
}
|
|
2413
|
-
}
|
|
2414
|
-
return await agent.handler(input);
|
|
2415
|
-
}
|
|
2416
|
-
: async () => {
|
|
2417
|
-
// Wrap with span if in HTTP context with tracer
|
|
2418
|
-
if (inHTTPContext()) {
|
|
2419
|
-
const honoCtx = getHTTPContext();
|
|
2420
|
-
const tracer = honoCtx.var.tracer;
|
|
2421
|
-
if (tracer) {
|
|
2422
|
-
return runWithSpan(
|
|
2423
|
-
tracer,
|
|
2424
|
-
agent as Agent<TInput, TOutput, TStream>,
|
|
2425
|
-
honoCtx,
|
|
2426
|
-
async () => await agent.handler()
|
|
2427
|
-
);
|
|
2428
|
-
}
|
|
2429
|
-
}
|
|
2430
|
-
return await agent.handler();
|
|
2431
|
-
},
|
|
2432
|
-
[INTERNAL_AGENT]: agent, // Store reference to internal agent for testing
|
|
2433
|
-
};
|
|
2434
|
-
|
|
2435
|
-
return runner as AgentRunner<TInput, TOutput, TStream>;
|
|
2436
|
-
}
|
|
2437
|
-
|
|
2438
|
-
/**
|
|
2439
|
-
* Run a handler with the agent identifier set in trace state.
|
|
2440
|
-
* Used for non-HTTP contexts (standalone) where we still want to propagate
|
|
2441
|
-
* the agent ID to downstream API calls.
|
|
2442
|
-
*/
|
|
2443
|
-
const runWithAgentContext = async <T>(agentId: string, handler: () => Promise<T>): Promise<T> => {
|
|
2444
|
-
const currentContext = context.active();
|
|
2445
|
-
const activeSpan = trace.getSpan(currentContext);
|
|
2446
|
-
|
|
2447
|
-
if (!activeSpan) {
|
|
2448
|
-
// No active span, just run the handler
|
|
2449
|
-
return handler();
|
|
2450
|
-
}
|
|
2451
|
-
|
|
2452
|
-
// Enrich the context's traceState with the agent ID so it propagates
|
|
2453
|
-
// to downstream calls. Note: this does NOT affect the active recording
|
|
2454
|
-
// span's exported traceState (that was set at span creation). This only
|
|
2455
|
-
// affects propagation context for outbound requests.
|
|
2456
|
-
const contextWithAgentId = enrichContextWithTraceState(currentContext, {
|
|
2457
|
-
aid: agentId,
|
|
2458
|
-
});
|
|
2459
|
-
|
|
2460
|
-
return context.with(contextWithAgentId, handler);
|
|
2461
|
-
};
|
|
2462
|
-
|
|
2463
|
-
const runWithSpan = async <
|
|
2464
|
-
T,
|
|
2465
|
-
TInput extends StandardSchemaV1 | undefined = any,
|
|
2466
|
-
TOutput extends StandardSchemaV1 | undefined = any,
|
|
2467
|
-
TStream extends boolean = false,
|
|
2468
|
-
>(
|
|
2469
|
-
tracer: Tracer,
|
|
2470
|
-
agent: Agent<TInput, TOutput, TStream>,
|
|
2471
|
-
ctx: Context,
|
|
2472
|
-
handler: () => Promise<T>
|
|
2473
|
-
): Promise<T> => {
|
|
2474
|
-
const currentContext = context.active();
|
|
2475
|
-
|
|
2476
|
-
// Build enriched traceState BEFORE span creation so the recording span
|
|
2477
|
-
// inherits it and it gets exported to OTLP. This ensures the agent ID
|
|
2478
|
-
// and other metadata appear in ClickHouse TraceState column.
|
|
2479
|
-
const deploymentId = runtimeConfig.getDeploymentId();
|
|
2480
|
-
const projectId = runtimeConfig.getProjectId();
|
|
2481
|
-
const orgId = runtimeConfig.getOrganizationId();
|
|
2482
|
-
const isDevMode = runtimeConfig.isDevMode();
|
|
2483
|
-
|
|
2484
|
-
const enrichedContext = enrichContextWithTraceState(currentContext, {
|
|
2485
|
-
aid: agent.metadata.id,
|
|
2486
|
-
did: deploymentId,
|
|
2487
|
-
pid: projectId,
|
|
2488
|
-
oid: orgId,
|
|
2489
|
-
d: isDevMode ? '1' : undefined,
|
|
2490
|
-
});
|
|
2491
|
-
|
|
2492
|
-
const span = tracer.startSpan('agent.run', {}, enrichedContext);
|
|
2493
|
-
|
|
2494
|
-
// Set agent attributes on the span immediately after creation
|
|
2495
|
-
span.setAttributes({
|
|
2496
|
-
'@agentuity/agentId': agent.metadata.agentId, // stable ID (agent_*) - consistent across deployments
|
|
2497
|
-
'@agentuity/agentInstanceId': agent.metadata.id, // deployment-specific ID (agentid_*) - changes per deployment
|
|
2498
|
-
'@agentuity/agentDescription': agent.metadata.description,
|
|
2499
|
-
'@agentuity/agentName': agent.metadata.name,
|
|
2500
|
-
'@agentuity/threadId': ctx.var.thread.id,
|
|
2501
|
-
});
|
|
2502
|
-
|
|
2503
|
-
const spanId = span.spanContext().spanId;
|
|
2504
|
-
|
|
2505
|
-
// Store span ID in PrivateVariables
|
|
2506
|
-
const _ctx = privateContext(ctx);
|
|
2507
|
-
_ctx.set('agentRunSpanId', spanId);
|
|
2508
|
-
|
|
2509
|
-
try {
|
|
2510
|
-
// Create a new context with the span active.
|
|
2511
|
-
// The span already carries the enriched traceState (inherited from
|
|
2512
|
-
// enrichedContext), so downstream API calls via propagation.inject()
|
|
2513
|
-
// will see aid/pid/oid/did/d.
|
|
2514
|
-
const spanContext = trace.setSpan(currentContext, span);
|
|
2515
|
-
return await context.with(spanContext, handler);
|
|
2516
|
-
} catch (error) {
|
|
2517
|
-
span.recordException(error as Error);
|
|
2518
|
-
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
2519
|
-
throw error;
|
|
2520
|
-
} finally {
|
|
2521
|
-
span.end();
|
|
2522
|
-
}
|
|
2523
|
-
};
|
|
2524
|
-
|
|
2525
|
-
const createAgentRunner = <
|
|
2526
|
-
TInput extends StandardSchemaV1 | undefined = any,
|
|
2527
|
-
TOutput extends StandardSchemaV1 | undefined = any,
|
|
2528
|
-
TStream extends boolean = false,
|
|
2529
|
-
>(
|
|
2530
|
-
agent: Agent<TInput, TOutput, TStream>,
|
|
2531
|
-
ctx: Context
|
|
2532
|
-
): AgentRunner<TInput, TOutput, TStream> => {
|
|
2533
|
-
const tracer = ctx.var.tracer;
|
|
2534
|
-
|
|
2535
|
-
if (agent.inputSchema) {
|
|
2536
|
-
return {
|
|
2537
|
-
metadata: agent.metadata,
|
|
2538
|
-
run: async (input: InferSchemaInput<Exclude<TInput, undefined>>) => {
|
|
2539
|
-
return runWithSpan<any, TInput, TOutput, TStream>(
|
|
2540
|
-
tracer,
|
|
2541
|
-
agent,
|
|
2542
|
-
ctx,
|
|
2543
|
-
async () => await agent.handler(input)
|
|
2544
|
-
);
|
|
2545
|
-
},
|
|
2546
|
-
} as AgentRunner<TInput, TOutput, TStream>;
|
|
2547
|
-
} else {
|
|
2548
|
-
return {
|
|
2549
|
-
metadata: agent.metadata,
|
|
2550
|
-
run: async () => {
|
|
2551
|
-
return runWithSpan<any, TInput, TOutput, TStream>(
|
|
2552
|
-
tracer,
|
|
2553
|
-
agent,
|
|
2554
|
-
ctx,
|
|
2555
|
-
async () => await agent.handler()
|
|
2556
|
-
);
|
|
2557
|
-
},
|
|
2558
|
-
} as AgentRunner<TInput, TOutput, TStream>;
|
|
2559
|
-
}
|
|
2560
|
-
};
|
|
2561
|
-
|
|
2562
|
-
/**
|
|
2563
|
-
* Populate the agents object with all registered agents
|
|
2564
|
-
* Keys are converted to camelCase to match the generated TypeScript types
|
|
2565
|
-
*/
|
|
2566
|
-
export const populateAgentsRegistry = (ctx: Context): any => {
|
|
2567
|
-
const agentsObj: any = {};
|
|
2568
|
-
// Track ownership of camelCase keys to detect collisions between different raw names
|
|
2569
|
-
const ownershipMap = new Map<string, string>();
|
|
2570
|
-
|
|
2571
|
-
// Build flat registry of agents
|
|
2572
|
-
for (const [name, agentFn] of agents) {
|
|
2573
|
-
const runner = createAgentRunner(agentFn, ctx);
|
|
2574
|
-
const key = toCamelCase(name);
|
|
2575
|
-
|
|
2576
|
-
// Validate key is non-empty
|
|
2577
|
-
if (!key) {
|
|
2578
|
-
internal.warn(`Agent name "${name}" converts to empty camelCase key. Skipping.`);
|
|
2579
|
-
continue;
|
|
2580
|
-
}
|
|
2581
|
-
|
|
2582
|
-
// Detect collision on key - check ownership
|
|
2583
|
-
const existingOwner = ownershipMap.get(key);
|
|
2584
|
-
if (existingOwner && existingOwner !== name) {
|
|
2585
|
-
internal.error(
|
|
2586
|
-
`Agent registry collision: "${name}" conflicts with "${existingOwner}" (both map to camelCase key "${key}")`
|
|
2587
|
-
);
|
|
2588
|
-
throw new Error(`Agent registry collision detected for key "${key}"`);
|
|
2589
|
-
}
|
|
2590
|
-
|
|
2591
|
-
agentsObj[key] = runner;
|
|
2592
|
-
// Record ownership
|
|
2593
|
-
ownershipMap.set(key, name);
|
|
2594
|
-
}
|
|
2595
|
-
|
|
2596
|
-
return agentsObj;
|
|
2597
|
-
};
|
|
2598
|
-
|
|
2599
|
-
export const createAgentMiddleware = (agentName: AgentName | ''): MiddlewareHandler => {
|
|
2600
|
-
return async (ctx, next) => {
|
|
2601
|
-
// Populate agents object with strongly-typed keys
|
|
2602
|
-
const agentsObj = populateAgentsRegistry(ctx);
|
|
2603
|
-
|
|
2604
|
-
// Track agent ID for session telemetry
|
|
2605
|
-
if (agentName) {
|
|
2606
|
-
const agentKey = toCamelCase(agentName);
|
|
2607
|
-
const agent = agentsObj[agentKey];
|
|
2608
|
-
const _ctx = privateContext(ctx);
|
|
2609
|
-
// we add both so that you can query by either
|
|
2610
|
-
if (agent?.metadata?.id) {
|
|
2611
|
-
_ctx.var.agentIds.add(agent.metadata.id);
|
|
2612
|
-
}
|
|
2613
|
-
if (agent?.metadata?.agentId) {
|
|
2614
|
-
_ctx.var.agentIds.add(agent.metadata.agentId);
|
|
2615
|
-
}
|
|
2616
|
-
}
|
|
2617
|
-
|
|
2618
|
-
const sessionId = ctx.var.sessionId;
|
|
2619
|
-
const thread = ctx.var.thread;
|
|
2620
|
-
const session = ctx.var.session;
|
|
2621
|
-
const config = agentName ? getAgentConfig(agentName as AgentName) : undefined;
|
|
2622
|
-
const app = ctx.var.app;
|
|
2623
|
-
|
|
2624
|
-
const args: RequestAgentContextArgs<AgentRegistry, unknown, unknown> = {
|
|
2625
|
-
agent: agentsObj,
|
|
2626
|
-
logger: ctx.var.logger,
|
|
2627
|
-
tracer: ctx.var.tracer,
|
|
2628
|
-
sessionId,
|
|
2629
|
-
session,
|
|
2630
|
-
thread,
|
|
2631
|
-
handler: ctx.var.waitUntilHandler,
|
|
2632
|
-
config: config || {},
|
|
2633
|
-
app: app || {},
|
|
2634
|
-
runtime: getGlobalRuntimeState(),
|
|
2635
|
-
auth: ctx.var.auth ?? null,
|
|
2636
|
-
};
|
|
2637
|
-
|
|
2638
|
-
return setupRequestAgentContext(ctx as unknown as Record<string, unknown>, args, next);
|
|
2639
|
-
};
|
|
2640
|
-
};
|
|
2641
|
-
|
|
2642
|
-
export const getAgents = () => agents;
|
|
2643
|
-
|
|
2644
|
-
export const runAgentSetups = async (appState: AppState): Promise<void> => {
|
|
2645
|
-
for (const [name, agent] of agents.entries()) {
|
|
2646
|
-
if (agent.setup) {
|
|
2647
|
-
const config = await agent.setup(appState);
|
|
2648
|
-
setAgentConfig(name as AgentName, config);
|
|
2649
|
-
}
|
|
2650
|
-
}
|
|
2651
|
-
// Note: Server readiness is managed by Vite (dev) or Bun.serve (prod)
|
|
2652
|
-
};
|
|
2653
|
-
|
|
2654
|
-
export const runAgentShutdowns = async (appState: AppState): Promise<void> => {
|
|
2655
|
-
const runtime = getGlobalRuntimeState();
|
|
2656
|
-
for (const [name, agent] of runtime.agents.entries()) {
|
|
2657
|
-
if (agent.shutdown) {
|
|
2658
|
-
const config = runtime.agentConfigs.get(name) as any;
|
|
2659
|
-
await agent.shutdown(appState, config);
|
|
2660
|
-
}
|
|
2661
|
-
}
|
|
2662
|
-
};
|
|
2663
|
-
|
|
2664
|
-
/**
|
|
2665
|
-
* Run an agent within a specific AgentContext.
|
|
2666
|
-
* Sets up AsyncLocalStorage with the provided context and executes the agent.
|
|
2667
|
-
*
|
|
2668
|
-
* This is the recommended way to test agents in unit tests. It automatically:
|
|
2669
|
-
* - Registers the agent in the runtime state so event listeners fire
|
|
2670
|
-
* - Sets up AsyncLocalStorage so getAgentContext() works inside the agent
|
|
2671
|
-
* - Handles both agents with input and agents without input
|
|
2672
|
-
*
|
|
2673
|
-
* **Use cases:**
|
|
2674
|
-
* - Unit testing agents with TestAgentContext
|
|
2675
|
-
* - Running agents outside HTTP request flow
|
|
2676
|
-
* - Custom agent execution environments
|
|
2677
|
-
* - Testing event listeners and evaluations
|
|
2678
|
-
*
|
|
2679
|
-
* @template TInput - Type of the input parameter
|
|
2680
|
-
* @template TOutput - Type of the return value
|
|
2681
|
-
*
|
|
2682
|
-
* @param ctx - The AgentContext to use (typically TestAgentContext in tests)
|
|
2683
|
-
* @param agent - The AgentRunner to execute (returned from createAgent)
|
|
2684
|
-
* @param input - Input data (required if agent has input schema, omit otherwise)
|
|
2685
|
-
*
|
|
2686
|
-
* @returns Promise resolving to the agent's output
|
|
2687
|
-
*
|
|
2688
|
-
* @example
|
|
2689
|
-
* ```typescript
|
|
2690
|
-
* import { runInAgentContext, TestAgentContext } from '@agentuity/runtime/test';
|
|
2691
|
-
*
|
|
2692
|
-
* test('greeting agent', async () => {
|
|
2693
|
-
* const ctx = new TestAgentContext();
|
|
2694
|
-
* const result = await runInAgentContext(ctx, greetingAgent, {
|
|
2695
|
-
* name: 'Alice',
|
|
2696
|
-
* age: 30
|
|
2697
|
-
* });
|
|
2698
|
-
* expect(result).toBe('Hello, Alice! You are 30 years old.');
|
|
2699
|
-
* });
|
|
2700
|
-
*
|
|
2701
|
-
* test('no-input agent', async () => {
|
|
2702
|
-
* const ctx = new TestAgentContext();
|
|
2703
|
-
* const result = await runInAgentContext(ctx, statusAgent);
|
|
2704
|
-
* expect(result).toEqual({ status: 'ok' });
|
|
2705
|
-
* });
|
|
2706
|
-
* ```
|
|
2707
|
-
*/
|
|
2708
|
-
export async function runInAgentContext<TInput, TOutput>(
|
|
2709
|
-
ctx: AgentContext<any, any, any>,
|
|
2710
|
-
agent: AgentRunner<any, any, any>,
|
|
2711
|
-
input?: TInput
|
|
2712
|
-
): Promise<TOutput> {
|
|
2713
|
-
const storage = getAgentAsyncLocalStorage();
|
|
2714
|
-
|
|
2715
|
-
// Register agent in runtime state so events fire (lookup by metadata.name)
|
|
2716
|
-
const agentName = agent.metadata.name;
|
|
2717
|
-
const runtime = getAgentRuntime(ctx);
|
|
2718
|
-
|
|
2719
|
-
// Get internal agent from runner (stored via symbol) or global registry
|
|
2720
|
-
const internalAgent = (agent as any)[INTERNAL_AGENT] || agents.get(agentName);
|
|
2721
|
-
|
|
2722
|
-
if (internalAgent && agentName) {
|
|
2723
|
-
runtime.agents.set(agentName, internalAgent);
|
|
2724
|
-
|
|
2725
|
-
// Copy event listeners from global to context runtime
|
|
2726
|
-
const globalListeners = agentEventListeners.get(internalAgent);
|
|
2727
|
-
if (globalListeners) {
|
|
2728
|
-
runtime.agentEventListeners.set(internalAgent, globalListeners);
|
|
2729
|
-
}
|
|
2730
|
-
}
|
|
2731
|
-
|
|
2732
|
-
return storage.run(ctx, async () => {
|
|
2733
|
-
if (input !== undefined) {
|
|
2734
|
-
return await (agent.run as any)(input);
|
|
2735
|
-
} else {
|
|
2736
|
-
return await (agent.run as any)();
|
|
2737
|
-
}
|
|
2738
|
-
});
|
|
2739
|
-
}
|