@agentuity/runtime 0.1.13 → 0.1.14
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/package.json +8 -8
- package/src/_config.ts +13 -0
- package/src/_standalone.ts +167 -19
- package/src/index.ts +1 -0
- package/dist/_config.d.ts +0 -90
- package/dist/_config.d.ts.map +0 -1
- package/dist/_config.js +0 -135
- package/dist/_config.js.map +0 -1
- package/dist/_context.d.ts +0 -76
- package/dist/_context.d.ts.map +0 -1
- package/dist/_context.js +0 -147
- 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/_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 -246
- package/dist/_metadata.js.map +0 -1
- package/dist/_process-protection.d.ts +0 -25
- package/dist/_process-protection.d.ts.map +0 -1
- package/dist/_process-protection.js +0 -65
- package/dist/_process-protection.js.map +0 -1
- package/dist/_server.d.ts +0 -46
- package/dist/_server.d.ts.map +0 -1
- package/dist/_server.js +0 -85
- package/dist/_server.js.map +0 -1
- package/dist/_services.d.ts +0 -21
- package/dist/_services.d.ts.map +0 -1
- package/dist/_services.js +0 -248
- package/dist/_services.js.map +0 -1
- package/dist/_standalone.d.ts +0 -168
- package/dist/_standalone.d.ts.map +0 -1
- package/dist/_standalone.js +0 -441
- 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 -96
- 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 -18
- package/dist/_waituntil.d.ts.map +0 -1
- package/dist/_waituntil.js +0 -97
- package/dist/_waituntil.js.map +0 -1
- package/dist/agent.d.ts +0 -1210
- package/dist/agent.d.ts.map +0 -1
- package/dist/agent.js +0 -903
- package/dist/agent.js.map +0 -1
- package/dist/app.d.ts +0 -322
- package/dist/app.d.ts.map +0 -1
- package/dist/app.js +0 -160
- package/dist/app.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 -139
- 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/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/cron.d.ts +0 -47
- package/dist/handlers/cron.d.ts.map +0 -1
- package/dist/handlers/cron.js +0 -49
- package/dist/handlers/cron.js.map +0 -1
- package/dist/handlers/index.d.ts +0 -5
- package/dist/handlers/index.d.ts.map +0 -1
- package/dist/handlers/index.js +0 -5
- package/dist/handlers/index.js.map +0 -1
- package/dist/handlers/sse.d.ts +0 -91
- package/dist/handlers/sse.d.ts.map +0 -1
- package/dist/handlers/sse.js +0 -213
- 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 -116
- package/dist/handlers/stream.js.map +0 -1
- package/dist/handlers/websocket.d.ts +0 -49
- package/dist/handlers/websocket.d.ts.map +0 -1
- package/dist/handlers/websocket.js +0 -143
- package/dist/handlers/websocket.js.map +0 -1
- package/dist/index.d.ts +0 -71
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -58
- package/dist/index.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 -274
- 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 -112
- package/dist/middleware.d.ts.map +0 -1
- package/dist/middleware.js +0 -507
- 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 -268
- package/dist/otel/logger.js.map +0 -1
- package/dist/otel/otel.d.ts +0 -65
- package/dist/otel/otel.d.ts.map +0 -1
- package/dist/otel/otel.js +0 -261
- package/dist/otel/otel.js.map +0 -1
- package/dist/router.d.ts +0 -100
- package/dist/router.d.ts.map +0 -1
- package/dist/router.js +0 -163
- 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 -86
- 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 -123
- 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/index.d.ts +0 -7
- package/dist/services/local/index.d.ts.map +0 -1
- package/dist/services/local/index.js +0 -7
- 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 -125
- package/dist/services/local/keyvalue.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 -262
- package/dist/services/local/stream.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 -13
- package/dist/services/sandbox/http.d.ts.map +0 -1
- package/dist/services/sandbox/http.js +0 -130
- 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 -80
- 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 -1139
- package/dist/session.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/web.d.ts +0 -8
- package/dist/web.d.ts.map +0 -1
- package/dist/web.js +0 -66
- package/dist/web.js.map +0 -1
- package/dist/workbench.d.ts +0 -17
- package/dist/workbench.d.ts.map +0 -1
- package/dist/workbench.js +0 -507
- package/dist/workbench.js.map +0 -1
package/dist/agent.js
DELETED
|
@@ -1,903 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { StructuredError, toCamelCase, } from '@agentuity/core';
|
|
3
|
-
import { context, SpanStatusCode, trace } from '@opentelemetry/api';
|
|
4
|
-
import { TraceState } from '@opentelemetry/core';
|
|
5
|
-
import { validator } from 'hono/validator';
|
|
6
|
-
import { AGENT_RUNTIME, INTERNAL_AGENT, CURRENT_AGENT, AGENT_IDS } from './_config';
|
|
7
|
-
import { getAgentContext, inHTTPContext, getHTTPContext, setupRequestAgentContext, getAgentAsyncLocalStorage, } from './_context';
|
|
8
|
-
import { internal } from './logger/internal';
|
|
9
|
-
import { fireEvent } from './_events';
|
|
10
|
-
import { privateContext } from './_server';
|
|
11
|
-
import { generateId } from './session';
|
|
12
|
-
import { getEvalRunEventProvider } from './_services';
|
|
13
|
-
import * as runtimeConfig from './_config';
|
|
14
|
-
import { validateSchema, formatValidationIssues } from './_validation';
|
|
15
|
-
import { getAgentMetadataByName, getEvalMetadata } from './_metadata';
|
|
16
|
-
// Will be populated at runtime with strongly typed agents
|
|
17
|
-
const agents = new Map();
|
|
18
|
-
// WeakMap to store event listeners for each agent instance (truly private)
|
|
19
|
-
const agentEventListeners = new WeakMap();
|
|
20
|
-
// Map to store agent configs returned from setup (keyed by agent name)
|
|
21
|
-
const agentConfigs = new Map();
|
|
22
|
-
/**
|
|
23
|
-
* Get the global runtime state (for production use).
|
|
24
|
-
* In tests, use TestAgentContext which has isolated runtime state.
|
|
25
|
-
*/
|
|
26
|
-
export function getGlobalRuntimeState() {
|
|
27
|
-
return {
|
|
28
|
-
agents,
|
|
29
|
-
agentConfigs,
|
|
30
|
-
agentEventListeners,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Get the runtime state from an AgentContext.
|
|
35
|
-
* @internal
|
|
36
|
-
*/
|
|
37
|
-
export function getAgentRuntime(ctx) {
|
|
38
|
-
return ctx[AGENT_RUNTIME];
|
|
39
|
-
}
|
|
40
|
-
async function fireAgentEvent(runtime, agent, eventName, context, data) {
|
|
41
|
-
// Fire agent-level listeners
|
|
42
|
-
const listeners = runtime.agentEventListeners.get(agent);
|
|
43
|
-
if (listeners) {
|
|
44
|
-
const callbacks = listeners.get(eventName);
|
|
45
|
-
if (callbacks && callbacks.size > 0) {
|
|
46
|
-
for (const callback of callbacks) {
|
|
47
|
-
try {
|
|
48
|
-
if (eventName === 'errored' && data) {
|
|
49
|
-
await callback(eventName, agent, context, data);
|
|
50
|
-
}
|
|
51
|
-
else if (eventName === 'started' || eventName === 'completed') {
|
|
52
|
-
await callback(eventName, agent, context);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
catch (error) {
|
|
56
|
-
// Log but don't re-throw - event listener errors should not crash the server
|
|
57
|
-
internal.error(`Error in agent event listener for '${eventName}':`, error);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
// Fire global app-level events
|
|
63
|
-
if (eventName === 'errored' && data) {
|
|
64
|
-
await fireEvent('agent.errored', agent, context, data);
|
|
65
|
-
}
|
|
66
|
-
else if (eventName === 'started') {
|
|
67
|
-
await fireEvent('agent.started', agent, context);
|
|
68
|
-
}
|
|
69
|
-
else if (eventName === 'completed') {
|
|
70
|
-
await fireEvent('agent.completed', agent, context);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
export const registerAgent = (name, agent) => {
|
|
74
|
-
agents.set(name, agent);
|
|
75
|
-
};
|
|
76
|
-
export const setAgentConfig = (name, config) => {
|
|
77
|
-
agentConfigs.set(name, config);
|
|
78
|
-
};
|
|
79
|
-
export const getAgentConfig = (name) => {
|
|
80
|
-
return agentConfigs.get(name);
|
|
81
|
-
};
|
|
82
|
-
const ValidationError = StructuredError('ValidationError')();
|
|
83
|
-
// Implementation
|
|
84
|
-
export function createAgent(name, config) {
|
|
85
|
-
const inputSchema = config.schema?.input;
|
|
86
|
-
const outputSchema = config.schema?.output;
|
|
87
|
-
// Initialize evals array before handler so it can be captured in closure
|
|
88
|
-
// Evals should only be added via agent.createEval() after agent creation
|
|
89
|
-
const evalsArray = [];
|
|
90
|
-
const handler = async (input) => {
|
|
91
|
-
let validatedInput = undefined;
|
|
92
|
-
if (inputSchema) {
|
|
93
|
-
const inputResult = await inputSchema['~standard'].validate(input);
|
|
94
|
-
if (inputResult.issues) {
|
|
95
|
-
throw new ValidationError({
|
|
96
|
-
issues: inputResult.issues,
|
|
97
|
-
message: `Input validation failed: ${inputResult.issues.map((i) => i.message).join(', ')}`,
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
validatedInput = inputResult.value;
|
|
101
|
-
}
|
|
102
|
-
const agentCtx = getAgentContext();
|
|
103
|
-
// Store current agent for telemetry (using Symbol to keep it internal)
|
|
104
|
-
agentCtx[CURRENT_AGENT] = agent;
|
|
105
|
-
// Expose current agent metadata on the context
|
|
106
|
-
agentCtx.current = agent.metadata;
|
|
107
|
-
const attrs = {
|
|
108
|
-
'@agentuity/agentId': agent.metadata.id,
|
|
109
|
-
'@agentuity/agentInstanceId': agent.metadata.agentId,
|
|
110
|
-
'@agentuity/agentDescription': agent.metadata.description,
|
|
111
|
-
'@agentuity/agentName': agent.metadata.name,
|
|
112
|
-
'@agentuity/threadId': agentCtx.thread.id,
|
|
113
|
-
};
|
|
114
|
-
// Set agent attributes on the current active span
|
|
115
|
-
const activeSpan = trace.getActiveSpan();
|
|
116
|
-
if (activeSpan) {
|
|
117
|
-
activeSpan.setAttributes(attrs);
|
|
118
|
-
}
|
|
119
|
-
if (inHTTPContext()) {
|
|
120
|
-
const honoCtx = privateContext(getHTTPContext());
|
|
121
|
-
if (honoCtx.var.agentIds) {
|
|
122
|
-
if (agent.metadata.id)
|
|
123
|
-
honoCtx.var.agentIds.add(agent.metadata.id);
|
|
124
|
-
if (agent.metadata.agentId)
|
|
125
|
-
honoCtx.var.agentIds.add(agent.metadata.agentId);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
// For standalone contexts, check for AGENT_IDS symbol
|
|
130
|
-
const agentIds = agentCtx[AGENT_IDS];
|
|
131
|
-
if (agentIds) {
|
|
132
|
-
if (agent.metadata.id)
|
|
133
|
-
agentIds.add(agent.metadata.id);
|
|
134
|
-
if (agent.metadata.agentId)
|
|
135
|
-
agentIds.add(agent.metadata.agentId);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
agentCtx.logger = agentCtx.logger.child(attrs);
|
|
139
|
-
// Get the agent instance from the runtime state to fire events
|
|
140
|
-
const runtime = getAgentRuntime(agentCtx);
|
|
141
|
-
// Fire 'started' event
|
|
142
|
-
await fireAgentEvent(runtime, agent, 'started', agentCtx);
|
|
143
|
-
try {
|
|
144
|
-
// Wrap agent execution with span tracking if tracer is available
|
|
145
|
-
const result = await (async () => {
|
|
146
|
-
if (agentCtx.tracer && inHTTPContext()) {
|
|
147
|
-
const honoCtx = getHTTPContext();
|
|
148
|
-
return runWithSpan(agentCtx.tracer, agent, honoCtx, async () => inputSchema
|
|
149
|
-
? await config.handler(agentCtx, validatedInput)
|
|
150
|
-
: await config.handler(agentCtx));
|
|
151
|
-
}
|
|
152
|
-
else if (agent.metadata.id) {
|
|
153
|
-
// For standalone contexts, wrap with agent context to set aid in trace state
|
|
154
|
-
return runWithAgentContext(agent.metadata.id, () => inputSchema
|
|
155
|
-
? config.handler(agentCtx, validatedInput)
|
|
156
|
-
: config.handler(agentCtx));
|
|
157
|
-
}
|
|
158
|
-
else {
|
|
159
|
-
// No agent ID, invoke handler directly
|
|
160
|
-
return inputSchema
|
|
161
|
-
? config.handler(agentCtx, validatedInput)
|
|
162
|
-
: config.handler(agentCtx);
|
|
163
|
-
}
|
|
164
|
-
})();
|
|
165
|
-
let validatedOutput = result;
|
|
166
|
-
// Skip output validation for streaming agents (they return ReadableStream)
|
|
167
|
-
if (outputSchema && !config.schema?.stream) {
|
|
168
|
-
const outputResult = await outputSchema['~standard'].validate(result);
|
|
169
|
-
if (outputResult.issues) {
|
|
170
|
-
throw new ValidationError({
|
|
171
|
-
issues: outputResult.issues,
|
|
172
|
-
message: `Output validation failed: ${outputResult.issues.map((i) => i.message).join(', ')}`,
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
validatedOutput = outputResult.value;
|
|
176
|
-
}
|
|
177
|
-
// Store validated input/output in context state for event listeners
|
|
178
|
-
agentCtx.state.set('_evalInput', validatedInput);
|
|
179
|
-
agentCtx.state.set('_evalOutput', validatedOutput);
|
|
180
|
-
// Fire 'completed' event - evals will run via event listener
|
|
181
|
-
await fireAgentEvent(runtime, agent, 'completed', agentCtx);
|
|
182
|
-
return validatedOutput;
|
|
183
|
-
}
|
|
184
|
-
catch (error) {
|
|
185
|
-
// Fire 'errored' event
|
|
186
|
-
await fireAgentEvent(runtime, agent, 'errored', agentCtx, error);
|
|
187
|
-
throw error;
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
|
-
// Create createEval method that infers types from agent and automatically adds to agent
|
|
191
|
-
const createEval = ((evalNameOrConfig, evalConfig) => {
|
|
192
|
-
// Handle preset eval config (single argument with name property)
|
|
193
|
-
if (typeof evalNameOrConfig !== 'string' && 'name' in evalNameOrConfig) {
|
|
194
|
-
const presetConfig = evalNameOrConfig;
|
|
195
|
-
const evalName = presetConfig.name;
|
|
196
|
-
internal.debug(`createEval called for agent "${name || 'unknown'}": registering preset eval "${evalName}"`);
|
|
197
|
-
const evalType = {
|
|
198
|
-
metadata: {
|
|
199
|
-
identifier: evalName,
|
|
200
|
-
name: evalName,
|
|
201
|
-
description: presetConfig.description || '',
|
|
202
|
-
},
|
|
203
|
-
handler: presetConfig.handler,
|
|
204
|
-
};
|
|
205
|
-
if (inputSchema) {
|
|
206
|
-
evalType.inputSchema = inputSchema;
|
|
207
|
-
}
|
|
208
|
-
if (outputSchema) {
|
|
209
|
-
evalType.outputSchema = outputSchema;
|
|
210
|
-
}
|
|
211
|
-
evalsArray.push(evalType);
|
|
212
|
-
internal.debug(`Added preset eval "${evalName}" to agent "${name || 'unknown'}". Total evals: ${evalsArray.length}`);
|
|
213
|
-
return evalType;
|
|
214
|
-
}
|
|
215
|
-
// Handle custom eval config (name + config)
|
|
216
|
-
if (typeof evalNameOrConfig !== 'string' || !evalConfig) {
|
|
217
|
-
throw new Error('Invalid arguments: expected (name: string, config) or (config: PresetEvalConfig)');
|
|
218
|
-
}
|
|
219
|
-
const evalName = evalNameOrConfig;
|
|
220
|
-
// Trace log to verify evals file is imported
|
|
221
|
-
internal.debug(`createEval called for agent "${name || 'unknown'}": registering eval "${evalName}"`);
|
|
222
|
-
// Use build-time injected metadata if available (same pattern as agents)
|
|
223
|
-
const evalMetadata = evalConfig.metadata || {};
|
|
224
|
-
// Build eval metadata - merge injected metadata with defaults
|
|
225
|
-
const evalType = {
|
|
226
|
-
metadata: {
|
|
227
|
-
// Use build-time injected metadata if available, otherwise fallback to empty/undefined
|
|
228
|
-
id: evalMetadata.id || undefined,
|
|
229
|
-
identifier: evalMetadata.identifier || undefined,
|
|
230
|
-
version: evalMetadata.version || undefined,
|
|
231
|
-
filename: evalMetadata.filename || '',
|
|
232
|
-
name: evalName,
|
|
233
|
-
description: evalConfig.description || '',
|
|
234
|
-
},
|
|
235
|
-
handler: evalConfig.handler,
|
|
236
|
-
};
|
|
237
|
-
if (inputSchema) {
|
|
238
|
-
evalType.inputSchema = inputSchema;
|
|
239
|
-
}
|
|
240
|
-
if (outputSchema) {
|
|
241
|
-
evalType.outputSchema = outputSchema;
|
|
242
|
-
}
|
|
243
|
-
// Automatically add eval to agent's evals array
|
|
244
|
-
evalsArray.push(evalType);
|
|
245
|
-
internal.debug(`Added eval "${evalName}" to agent "${name || 'unknown'}". Total evals: ${evalsArray.length}`);
|
|
246
|
-
return evalType;
|
|
247
|
-
});
|
|
248
|
-
// Build metadata - merge user-provided metadata with defaults
|
|
249
|
-
// The build plugin injects metadata via config.metadata during AST transformation
|
|
250
|
-
let metadata = {
|
|
251
|
-
// Defaults (used when running without build, e.g., dev mode)
|
|
252
|
-
name,
|
|
253
|
-
description: config.description,
|
|
254
|
-
id: '',
|
|
255
|
-
agentId: '',
|
|
256
|
-
filename: '',
|
|
257
|
-
version: '',
|
|
258
|
-
inputSchemaCode: '',
|
|
259
|
-
outputSchemaCode: '',
|
|
260
|
-
// Merge in build-time injected metadata (overrides defaults)
|
|
261
|
-
...config.metadata,
|
|
262
|
-
};
|
|
263
|
-
// If id/agentId are empty, try to load from agentuity.metadata.json
|
|
264
|
-
if (!metadata.id || !metadata.agentId) {
|
|
265
|
-
const fileMetadata = getAgentMetadataByName(name);
|
|
266
|
-
if (fileMetadata) {
|
|
267
|
-
internal.info('[agent] loaded metadata for "%s" from file: id=%s, agentId=%s', name, fileMetadata.id, fileMetadata.agentId);
|
|
268
|
-
metadata = {
|
|
269
|
-
...metadata,
|
|
270
|
-
id: fileMetadata.id || metadata.id,
|
|
271
|
-
agentId: fileMetadata.agentId || metadata.agentId,
|
|
272
|
-
filename: fileMetadata.filename || metadata.filename,
|
|
273
|
-
version: fileMetadata.version || metadata.version,
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
// Error if agent has no metadata IDs in production - this causes agent_ids to be empty in sessions
|
|
278
|
-
// which affects analytics, billing attribution, and session filtering
|
|
279
|
-
// Only enforce in production (when AGENTUITY_CLOUD_PROJECT_ID is set) to allow dev/test without metadata
|
|
280
|
-
if (!metadata.id && !metadata.agentId && runtimeConfig.getProjectId()) {
|
|
281
|
-
throw new Error(`Agent "${name}" has no metadata IDs (id and agentId are empty). ` +
|
|
282
|
-
`This will result in empty agent_ids in session events. ` +
|
|
283
|
-
`Ensure agentuity.metadata.json exists in the runtime directory ` +
|
|
284
|
-
`(checked: ${process.cwd()}/agentuity.metadata.json and ${process.cwd()}/.agentuity/agentuity.metadata.json). ` +
|
|
285
|
-
`Run 'agentuity build' to generate the metadata file.`);
|
|
286
|
-
}
|
|
287
|
-
const agent = {
|
|
288
|
-
handler,
|
|
289
|
-
metadata,
|
|
290
|
-
evals: evalsArray,
|
|
291
|
-
createEval,
|
|
292
|
-
setup: config.setup,
|
|
293
|
-
shutdown: config.shutdown,
|
|
294
|
-
};
|
|
295
|
-
// Add event listener methods
|
|
296
|
-
agent.addEventListener = (eventName, callback) => {
|
|
297
|
-
const agentForListeners = agent;
|
|
298
|
-
const callbackForListeners = callback;
|
|
299
|
-
let listeners = agentEventListeners.get(agentForListeners);
|
|
300
|
-
if (!listeners) {
|
|
301
|
-
listeners = new Map();
|
|
302
|
-
agentEventListeners.set(agentForListeners, listeners);
|
|
303
|
-
}
|
|
304
|
-
let callbacks = listeners.get(eventName);
|
|
305
|
-
if (!callbacks) {
|
|
306
|
-
callbacks = new Set();
|
|
307
|
-
listeners.set(eventName, callbacks);
|
|
308
|
-
}
|
|
309
|
-
callbacks.add(callbackForListeners);
|
|
310
|
-
};
|
|
311
|
-
// Automatically add event listener for 'completed' event to run evals
|
|
312
|
-
agent.addEventListener('completed', async (_event, _agent, ctx) => {
|
|
313
|
-
// Use the agent instance passed to event listener to access its evals array
|
|
314
|
-
// This ensures we get evals that were added via agent.createEval() after agent creation
|
|
315
|
-
const agentEvals = _agent?.evals || evalsArray;
|
|
316
|
-
internal.debug(`Checking evals: agent=${_agent.metadata?.name}, evalsArray.length=${evalsArray?.length || 0}, agent.evals.length=${_agent?.evals?.length || 0}`);
|
|
317
|
-
if (agentEvals && agentEvals.length > 0) {
|
|
318
|
-
internal.info(`Executing ${agentEvals.length} eval(s) after agent run`);
|
|
319
|
-
// Get validated input/output from context state
|
|
320
|
-
const validatedInput = ctx.state.get('_evalInput');
|
|
321
|
-
const validatedOutput = ctx.state.get('_evalOutput');
|
|
322
|
-
// Capture agentRunSpanId synchronously before waitUntil (which may run outside AsyncLocalStorage)
|
|
323
|
-
let agentRunSpanId;
|
|
324
|
-
try {
|
|
325
|
-
const httpCtx = getHTTPContext();
|
|
326
|
-
const _httpCtx = privateContext(httpCtx);
|
|
327
|
-
agentRunSpanId = _httpCtx.var.agentRunSpanId;
|
|
328
|
-
}
|
|
329
|
-
catch {
|
|
330
|
-
// HTTP context may not be available, spanId will be undefined
|
|
331
|
-
}
|
|
332
|
-
// Execute each eval using waitUntil to avoid blocking the response
|
|
333
|
-
for (const evalItem of agentEvals) {
|
|
334
|
-
const evalName = evalItem.metadata.name || 'unnamed';
|
|
335
|
-
const agentName = _agent?.metadata?.name || name;
|
|
336
|
-
ctx.waitUntil((async () => {
|
|
337
|
-
internal.info(`[EVALRUN] Starting eval run tracking for '${evalName}'`);
|
|
338
|
-
const evalRunId = generateId('evalrun');
|
|
339
|
-
// Look up eval metadata from agentuity.metadata.json by agent name and eval name
|
|
340
|
-
internal.info(`[EVALRUN] Looking up eval metadata: agentName='${agentName}', evalName='${evalName}'`);
|
|
341
|
-
const evalMeta = getEvalMetadata(agentName, evalName);
|
|
342
|
-
internal.info(`[EVALRUN] Eval metadata lookup result:`, {
|
|
343
|
-
found: !!evalMeta,
|
|
344
|
-
identifier: evalMeta?.identifier,
|
|
345
|
-
id: evalMeta?.id,
|
|
346
|
-
filename: evalMeta?.filename,
|
|
347
|
-
});
|
|
348
|
-
// evalId = deployment-specific ID (evalid_...), evalIdentifier = stable (eval_...)
|
|
349
|
-
const evalId = evalMeta?.id || '';
|
|
350
|
-
const evalIdentifier = evalMeta?.identifier || '';
|
|
351
|
-
internal.info(`[EVALRUN] Resolved evalId='${evalId}', evalIdentifier='${evalIdentifier}'`);
|
|
352
|
-
// Log eval metadata using structured logging and tracing
|
|
353
|
-
ctx.logger.debug('Starting eval run with metadata', {
|
|
354
|
-
evalName,
|
|
355
|
-
agentName,
|
|
356
|
-
evalRunId,
|
|
357
|
-
evalId,
|
|
358
|
-
evalMetaFromFile: !!evalMeta,
|
|
359
|
-
evalMetadata: evalItem.metadata,
|
|
360
|
-
});
|
|
361
|
-
// Add eval metadata to the active span for observability
|
|
362
|
-
const activeSpan = ctx.tracer ? trace.getActiveSpan() : undefined;
|
|
363
|
-
if (activeSpan) {
|
|
364
|
-
activeSpan.setAttributes({
|
|
365
|
-
'eval.name': evalName,
|
|
366
|
-
'eval.id': evalId,
|
|
367
|
-
'eval.runId': evalRunId,
|
|
368
|
-
'eval.description': evalMeta?.description || evalItem.metadata.description || '',
|
|
369
|
-
'eval.filename': evalMeta?.filename || evalItem.metadata.filename || '',
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
const orgId = runtimeConfig.getOrganizationId();
|
|
373
|
-
const projectId = runtimeConfig.getProjectId();
|
|
374
|
-
const devMode = runtimeConfig.isDevMode() ?? false;
|
|
375
|
-
const evalRunEventProvider = getEvalRunEventProvider();
|
|
376
|
-
// Only send events if we have required context (devmode flag will be set based on devMode)
|
|
377
|
-
const shouldSendEvalRunEvents = orgId && projectId && evalId !== '' && evalIdentifier !== '';
|
|
378
|
-
internal.info(`[EVALRUN] Checking conditions for eval '${evalName}':`, {
|
|
379
|
-
orgId: orgId,
|
|
380
|
-
projectId: projectId,
|
|
381
|
-
evalId: evalId,
|
|
382
|
-
evalIdentifier: evalIdentifier,
|
|
383
|
-
devMode,
|
|
384
|
-
hasEvalRunEventProvider: !!evalRunEventProvider,
|
|
385
|
-
shouldSendEvalRunEvents,
|
|
386
|
-
});
|
|
387
|
-
if (!shouldSendEvalRunEvents) {
|
|
388
|
-
const reasons = [];
|
|
389
|
-
if (!orgId)
|
|
390
|
-
reasons.push('missing orgId');
|
|
391
|
-
if (!projectId)
|
|
392
|
-
reasons.push('missing projectId');
|
|
393
|
-
if (!evalId || evalId === '')
|
|
394
|
-
reasons.push('empty evalId');
|
|
395
|
-
if (!evalIdentifier || evalIdentifier === '')
|
|
396
|
-
reasons.push('empty evalIdentifier');
|
|
397
|
-
internal.info(`[EVALRUN] Skipping eval run events for '${evalName}': ${reasons.join(', ')}`);
|
|
398
|
-
}
|
|
399
|
-
try {
|
|
400
|
-
internal.debug(`Executing eval: ${evalName}`);
|
|
401
|
-
// Send eval run start event
|
|
402
|
-
if (shouldSendEvalRunEvents && evalRunEventProvider) {
|
|
403
|
-
internal.info(`[EVALRUN] Sending start event for eval '${evalName}' (id: ${evalRunId}, evalId: ${evalId})`);
|
|
404
|
-
try {
|
|
405
|
-
const deploymentId = runtimeConfig.getDeploymentId();
|
|
406
|
-
// Use captured agentRunSpanId (may be undefined if HTTP context unavailable)
|
|
407
|
-
if (!agentRunSpanId) {
|
|
408
|
-
internal.warn(`[EVALRUN] agentRunSpanId not available for eval '${evalName}' (id: ${evalRunId}). This may occur if waitUntil runs outside AsyncLocalStorage context.`);
|
|
409
|
-
}
|
|
410
|
-
const startEvent = {
|
|
411
|
-
id: evalRunId,
|
|
412
|
-
sessionId: ctx.sessionId,
|
|
413
|
-
evalId: evalId, // deployment-specific ID (evalid_...)
|
|
414
|
-
evalIdentifier: evalIdentifier, // stable identifier (eval_...)
|
|
415
|
-
orgId: orgId,
|
|
416
|
-
projectId: projectId,
|
|
417
|
-
devmode: Boolean(devMode),
|
|
418
|
-
deploymentId: deploymentId || undefined,
|
|
419
|
-
spanId: agentRunSpanId,
|
|
420
|
-
};
|
|
421
|
-
internal.debug('[EVALRUN] Start event payload: %s', JSON.stringify(startEvent, null, 2));
|
|
422
|
-
await evalRunEventProvider.start(startEvent);
|
|
423
|
-
internal.info(`[EVALRUN] Start event sent successfully for eval '${evalName}' (id: ${evalRunId})`);
|
|
424
|
-
}
|
|
425
|
-
catch (error) {
|
|
426
|
-
internal.error(`[EVALRUN] Error sending eval run start event for '${evalName}' (id: ${evalRunId})`, {
|
|
427
|
-
error,
|
|
428
|
-
});
|
|
429
|
-
// Don't throw - continue with eval execution even if start event fails
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
else if (shouldSendEvalRunEvents && !evalRunEventProvider) {
|
|
433
|
-
internal.warn(`[EVALRUN] Conditions met but no evalRunEventProvider available for '${evalName}'`);
|
|
434
|
-
}
|
|
435
|
-
else {
|
|
436
|
-
internal.debug(`[EVALRUN] Not sending start event for '${evalName}': shouldSendEvalRunEvents=${shouldSendEvalRunEvents}, hasProvider=${!!evalRunEventProvider}`);
|
|
437
|
-
}
|
|
438
|
-
// Validate eval input if schema exists
|
|
439
|
-
let evalValidatedInput = validatedInput;
|
|
440
|
-
if (evalItem.inputSchema) {
|
|
441
|
-
const evalInputResult = await evalItem.inputSchema['~standard'].validate(validatedInput);
|
|
442
|
-
if (evalInputResult.issues) {
|
|
443
|
-
throw new ValidationError({
|
|
444
|
-
issues: evalInputResult.issues,
|
|
445
|
-
message: `Eval input validation failed: ${evalInputResult.issues.map((i) => i.message).join(', ')}`,
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
evalValidatedInput = evalInputResult.value;
|
|
449
|
-
}
|
|
450
|
-
// Validate eval output if schema exists
|
|
451
|
-
let evalValidatedOutput = validatedOutput;
|
|
452
|
-
if (evalItem.outputSchema) {
|
|
453
|
-
const evalOutputResult = await evalItem.outputSchema['~standard'].validate(validatedOutput);
|
|
454
|
-
if (evalOutputResult.issues) {
|
|
455
|
-
throw new ValidationError({
|
|
456
|
-
issues: evalOutputResult.issues,
|
|
457
|
-
message: `Eval output validation failed: ${evalOutputResult.issues.map((i) => i.message).join(', ')}`,
|
|
458
|
-
});
|
|
459
|
-
}
|
|
460
|
-
evalValidatedOutput = evalOutputResult.value;
|
|
461
|
-
}
|
|
462
|
-
// Create EvalContext (just an alias for AgentContext)
|
|
463
|
-
const evalContext = ctx;
|
|
464
|
-
// Execute the eval handler conditionally based on agent schema
|
|
465
|
-
let handlerResult;
|
|
466
|
-
if (inputSchema && outputSchema) {
|
|
467
|
-
// Both input and output defined
|
|
468
|
-
handlerResult = await evalItem.handler(evalContext, evalValidatedInput, evalValidatedOutput);
|
|
469
|
-
}
|
|
470
|
-
else if (inputSchema) {
|
|
471
|
-
// Only input defined
|
|
472
|
-
handlerResult = await evalItem.handler(evalContext, evalValidatedInput);
|
|
473
|
-
}
|
|
474
|
-
else if (outputSchema) {
|
|
475
|
-
// Only output defined
|
|
476
|
-
handlerResult = await evalItem.handler(evalContext, evalValidatedOutput);
|
|
477
|
-
}
|
|
478
|
-
else {
|
|
479
|
-
// Neither defined
|
|
480
|
-
handlerResult = await evalItem.handler(evalContext);
|
|
481
|
-
}
|
|
482
|
-
// Wrap handler result with success for catalyst
|
|
483
|
-
const result = {
|
|
484
|
-
success: true,
|
|
485
|
-
...handlerResult,
|
|
486
|
-
};
|
|
487
|
-
// Log the result
|
|
488
|
-
if (result.score !== undefined) {
|
|
489
|
-
internal.info(`Eval '${evalName}' pass: ${result.passed}, score: ${result.score}`, result.metadata);
|
|
490
|
-
}
|
|
491
|
-
else {
|
|
492
|
-
internal.info(`Eval '${evalName}' pass: ${result.passed}`, result.metadata);
|
|
493
|
-
}
|
|
494
|
-
// Send eval run complete event
|
|
495
|
-
if (shouldSendEvalRunEvents && evalRunEventProvider) {
|
|
496
|
-
internal.info(`[EVALRUN] Sending complete event for eval '${evalName}' (id: ${evalRunId})`);
|
|
497
|
-
try {
|
|
498
|
-
await evalRunEventProvider.complete({
|
|
499
|
-
id: evalRunId,
|
|
500
|
-
result,
|
|
501
|
-
});
|
|
502
|
-
internal.info(`[EVALRUN] Complete event sent successfully for eval '${evalName}' (id: ${evalRunId})`);
|
|
503
|
-
}
|
|
504
|
-
catch (error) {
|
|
505
|
-
internal.error(`[EVALRUN] Error sending eval run complete event for '${evalName}' (id: ${evalRunId})`, {
|
|
506
|
-
error,
|
|
507
|
-
});
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
internal.debug(`Eval '${evalName}' completed successfully`);
|
|
511
|
-
}
|
|
512
|
-
catch (error) {
|
|
513
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
514
|
-
internal.error(`Error executing eval '${evalName}'`, { error });
|
|
515
|
-
// Send eval run complete event with error
|
|
516
|
-
if (shouldSendEvalRunEvents && evalRunEventProvider) {
|
|
517
|
-
internal.info(`[EVALRUN] Sending complete event (error) for eval '${evalName}' (id: ${evalRunId})`);
|
|
518
|
-
try {
|
|
519
|
-
await evalRunEventProvider.complete({
|
|
520
|
-
id: evalRunId,
|
|
521
|
-
error: errorMessage,
|
|
522
|
-
result: {
|
|
523
|
-
success: false,
|
|
524
|
-
passed: false,
|
|
525
|
-
error: errorMessage,
|
|
526
|
-
metadata: {},
|
|
527
|
-
},
|
|
528
|
-
});
|
|
529
|
-
internal.info(`[EVALRUN] Complete event (error) sent successfully for eval '${evalName}' (id: ${evalRunId})`);
|
|
530
|
-
}
|
|
531
|
-
catch (eventError) {
|
|
532
|
-
internal.error(`[EVALRUN] Error sending eval run complete event (error) for '${evalName}' (id: ${evalRunId})`, { error: eventError });
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
})());
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
});
|
|
540
|
-
agent.removeEventListener = (eventName, callback) => {
|
|
541
|
-
const agentForListeners = agent;
|
|
542
|
-
const callbackForListeners = callback;
|
|
543
|
-
const listeners = agentEventListeners.get(agentForListeners);
|
|
544
|
-
if (!listeners)
|
|
545
|
-
return;
|
|
546
|
-
const callbacks = listeners.get(eventName);
|
|
547
|
-
if (!callbacks)
|
|
548
|
-
return;
|
|
549
|
-
callbacks.delete(callbackForListeners);
|
|
550
|
-
};
|
|
551
|
-
if (inputSchema) {
|
|
552
|
-
agent.inputSchema = inputSchema;
|
|
553
|
-
}
|
|
554
|
-
if (outputSchema) {
|
|
555
|
-
agent.outputSchema = outputSchema;
|
|
556
|
-
}
|
|
557
|
-
if (config.schema?.stream) {
|
|
558
|
-
agent.stream = config.schema.stream;
|
|
559
|
-
}
|
|
560
|
-
// Add validator method with overloads
|
|
561
|
-
agent.validator = ((override) => {
|
|
562
|
-
const effectiveInputSchema = override?.input ?? inputSchema;
|
|
563
|
-
// Only use agent's output schema if no override was provided at all.
|
|
564
|
-
// If override is provided (even with just input), don't auto-apply agent's output schema
|
|
565
|
-
// unless the override explicitly includes output.
|
|
566
|
-
const effectiveOutputSchema = override ? override.output : outputSchema;
|
|
567
|
-
// Helper to build the standard Hono input validator so types flow
|
|
568
|
-
const buildInputValidator = (schema) => validator('json', async (value, c) => {
|
|
569
|
-
if (schema) {
|
|
570
|
-
const result = await validateSchema(schema, value);
|
|
571
|
-
if (!result.success) {
|
|
572
|
-
return c.json({
|
|
573
|
-
error: 'Validation failed',
|
|
574
|
-
message: formatValidationIssues(result.issues),
|
|
575
|
-
issues: result.issues,
|
|
576
|
-
}, 400);
|
|
577
|
-
}
|
|
578
|
-
return result.data;
|
|
579
|
-
}
|
|
580
|
-
return value;
|
|
581
|
-
});
|
|
582
|
-
// If no output schema, preserve existing behavior: pure input validation
|
|
583
|
-
if (!effectiveOutputSchema) {
|
|
584
|
-
return buildInputValidator(effectiveInputSchema);
|
|
585
|
-
}
|
|
586
|
-
// Output validation middleware (runs after handler)
|
|
587
|
-
const outputValidator = async (c, next) => {
|
|
588
|
-
await next();
|
|
589
|
-
const res = c.res;
|
|
590
|
-
if (!res)
|
|
591
|
-
return;
|
|
592
|
-
// Skip output validation for streaming agents
|
|
593
|
-
if (config.schema?.stream) {
|
|
594
|
-
return;
|
|
595
|
-
}
|
|
596
|
-
// Only validate JSON responses
|
|
597
|
-
const contentType = res.headers.get('Content-Type') ?? '';
|
|
598
|
-
if (!contentType.toLowerCase().includes('application/json')) {
|
|
599
|
-
return;
|
|
600
|
-
}
|
|
601
|
-
// Clone so we don't consume the body that will be sent
|
|
602
|
-
let responseBody;
|
|
603
|
-
try {
|
|
604
|
-
const cloned = res.clone();
|
|
605
|
-
responseBody = await cloned.json();
|
|
606
|
-
}
|
|
607
|
-
catch {
|
|
608
|
-
const OutputValidationError = StructuredError('OutputValidationError')();
|
|
609
|
-
throw new OutputValidationError({
|
|
610
|
-
message: 'Output validation failed: response is not valid JSON',
|
|
611
|
-
issues: [],
|
|
612
|
-
});
|
|
613
|
-
}
|
|
614
|
-
const result = await validateSchema(effectiveOutputSchema, responseBody);
|
|
615
|
-
if (!result.success) {
|
|
616
|
-
const OutputValidationError = StructuredError('OutputValidationError')();
|
|
617
|
-
throw new OutputValidationError({
|
|
618
|
-
message: `Output validation failed: ${formatValidationIssues(result.issues)}`,
|
|
619
|
-
issues: result.issues,
|
|
620
|
-
});
|
|
621
|
-
}
|
|
622
|
-
// Replace response with validated/sanitized JSON
|
|
623
|
-
c.res = new Response(JSON.stringify(result.data), {
|
|
624
|
-
status: res.status,
|
|
625
|
-
headers: res.headers,
|
|
626
|
-
});
|
|
627
|
-
};
|
|
628
|
-
// If we have no input schema, we only do output validation
|
|
629
|
-
if (!effectiveInputSchema) {
|
|
630
|
-
return outputValidator;
|
|
631
|
-
}
|
|
632
|
-
// Compose: input validator → output validator
|
|
633
|
-
const inputMiddleware = buildInputValidator(effectiveInputSchema);
|
|
634
|
-
const composed = async (c, next) => {
|
|
635
|
-
// Run the validator first; its next() runs the output validator,
|
|
636
|
-
// whose next() runs the actual handler(s)
|
|
637
|
-
const result = await inputMiddleware(c, async () => {
|
|
638
|
-
await outputValidator(c, next);
|
|
639
|
-
});
|
|
640
|
-
// If inputMiddleware returned early (validation failed), return that response
|
|
641
|
-
return result;
|
|
642
|
-
};
|
|
643
|
-
return composed;
|
|
644
|
-
});
|
|
645
|
-
// Register the agent for runtime use
|
|
646
|
-
// @ts-expect-error - metadata might be incomplete until build plugin injects InternalAgentMetadata
|
|
647
|
-
agents.set(name, agent);
|
|
648
|
-
// Create and return AgentRunner
|
|
649
|
-
const runner = {
|
|
650
|
-
metadata: metadata,
|
|
651
|
-
validator: agent.validator,
|
|
652
|
-
inputSchema: inputSchema,
|
|
653
|
-
outputSchema: outputSchema,
|
|
654
|
-
stream: config.schema?.stream || false,
|
|
655
|
-
createEval,
|
|
656
|
-
addEventListener: agent.addEventListener,
|
|
657
|
-
removeEventListener: agent.removeEventListener,
|
|
658
|
-
run: inputSchema
|
|
659
|
-
? async (input) => {
|
|
660
|
-
return await agent.handler(input);
|
|
661
|
-
}
|
|
662
|
-
: async () => {
|
|
663
|
-
return await agent.handler();
|
|
664
|
-
},
|
|
665
|
-
[INTERNAL_AGENT]: agent, // Store reference to internal agent for testing
|
|
666
|
-
};
|
|
667
|
-
return runner;
|
|
668
|
-
}
|
|
669
|
-
/**
|
|
670
|
-
* Run a handler with the agent identifier set in trace state.
|
|
671
|
-
* Used for non-HTTP contexts (standalone) where we still want to propagate
|
|
672
|
-
* the agent ID to downstream API calls.
|
|
673
|
-
*/
|
|
674
|
-
const runWithAgentContext = async (agentId, handler) => {
|
|
675
|
-
const currentContext = context.active();
|
|
676
|
-
const activeSpan = trace.getSpan(currentContext);
|
|
677
|
-
if (!activeSpan) {
|
|
678
|
-
// No active span, just run the handler
|
|
679
|
-
return handler();
|
|
680
|
-
}
|
|
681
|
-
const currentSpanContext = activeSpan.spanContext();
|
|
682
|
-
const existingTraceState = currentSpanContext.traceState ?? new TraceState();
|
|
683
|
-
const updatedTraceState = existingTraceState.set('aid', agentId);
|
|
684
|
-
const contextWithAgentId = trace.setSpanContext(currentContext, {
|
|
685
|
-
...currentSpanContext,
|
|
686
|
-
traceState: updatedTraceState,
|
|
687
|
-
});
|
|
688
|
-
return context.with(contextWithAgentId, handler);
|
|
689
|
-
};
|
|
690
|
-
const runWithSpan = async (tracer, agent, ctx, handler) => {
|
|
691
|
-
const currentContext = context.active();
|
|
692
|
-
const span = tracer.startSpan('agent.run', {}, currentContext);
|
|
693
|
-
// Set agent attributes on the span immediately after creation
|
|
694
|
-
span.setAttributes({
|
|
695
|
-
'@agentuity/agentId': agent.metadata.id,
|
|
696
|
-
'@agentuity/agentInstanceId': agent.metadata.agentId,
|
|
697
|
-
'@agentuity/agentDescription': agent.metadata.description,
|
|
698
|
-
'@agentuity/agentName': agent.metadata.name,
|
|
699
|
-
'@agentuity/threadId': ctx.var.thread.id,
|
|
700
|
-
});
|
|
701
|
-
const spanId = span.spanContext().spanId;
|
|
702
|
-
// Store span ID in PrivateVariables
|
|
703
|
-
const _ctx = privateContext(ctx);
|
|
704
|
-
_ctx.set('agentRunSpanId', spanId);
|
|
705
|
-
try {
|
|
706
|
-
// Create a new context with the span and updated trace state including agent id
|
|
707
|
-
const spanContext = trace.setSpan(currentContext, span);
|
|
708
|
-
// Update trace state with agent identifier (aid) so downstream API calls (e.g., sandbox)
|
|
709
|
-
// can associate operations with this agent. The trace state is scoped to this execution,
|
|
710
|
-
// so when the agent finishes, the parent context's trace state is automatically restored.
|
|
711
|
-
const currentSpanContext = span.spanContext();
|
|
712
|
-
const existingTraceState = currentSpanContext.traceState ?? new TraceState();
|
|
713
|
-
const updatedTraceState = existingTraceState.set('aid', agent.metadata.id);
|
|
714
|
-
// Create context with both the span and the updated trace state
|
|
715
|
-
const contextWithAgentId = trace.setSpanContext(spanContext, {
|
|
716
|
-
...currentSpanContext,
|
|
717
|
-
traceState: updatedTraceState,
|
|
718
|
-
});
|
|
719
|
-
return await context.with(contextWithAgentId, handler);
|
|
720
|
-
}
|
|
721
|
-
catch (error) {
|
|
722
|
-
span.recordException(error);
|
|
723
|
-
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
724
|
-
throw error;
|
|
725
|
-
}
|
|
726
|
-
finally {
|
|
727
|
-
span.end();
|
|
728
|
-
}
|
|
729
|
-
};
|
|
730
|
-
const createAgentRunner = (agent, ctx) => {
|
|
731
|
-
const tracer = ctx.var.tracer;
|
|
732
|
-
if (agent.inputSchema) {
|
|
733
|
-
return {
|
|
734
|
-
metadata: agent.metadata,
|
|
735
|
-
run: async (input) => {
|
|
736
|
-
return runWithSpan(tracer, agent, ctx, async () => await agent.handler(input));
|
|
737
|
-
},
|
|
738
|
-
};
|
|
739
|
-
}
|
|
740
|
-
else {
|
|
741
|
-
return {
|
|
742
|
-
metadata: agent.metadata,
|
|
743
|
-
run: async () => {
|
|
744
|
-
return runWithSpan(tracer, agent, ctx, async () => await agent.handler());
|
|
745
|
-
},
|
|
746
|
-
};
|
|
747
|
-
}
|
|
748
|
-
};
|
|
749
|
-
/**
|
|
750
|
-
* Populate the agents object with all registered agents
|
|
751
|
-
* Keys are converted to camelCase to match the generated TypeScript types
|
|
752
|
-
*/
|
|
753
|
-
export const populateAgentsRegistry = (ctx) => {
|
|
754
|
-
const agentsObj = {};
|
|
755
|
-
// Track ownership of camelCase keys to detect collisions between different raw names
|
|
756
|
-
const ownershipMap = new Map();
|
|
757
|
-
// Build flat registry of agents
|
|
758
|
-
for (const [name, agentFn] of agents) {
|
|
759
|
-
const runner = createAgentRunner(agentFn, ctx);
|
|
760
|
-
const key = toCamelCase(name);
|
|
761
|
-
// Validate key is non-empty
|
|
762
|
-
if (!key) {
|
|
763
|
-
internal.warn(`Agent name "${name}" converts to empty camelCase key. Skipping.`);
|
|
764
|
-
continue;
|
|
765
|
-
}
|
|
766
|
-
// Detect collision on key - check ownership
|
|
767
|
-
const existingOwner = ownershipMap.get(key);
|
|
768
|
-
if (existingOwner && existingOwner !== name) {
|
|
769
|
-
internal.error(`Agent registry collision: "${name}" conflicts with "${existingOwner}" (both map to camelCase key "${key}")`);
|
|
770
|
-
throw new Error(`Agent registry collision detected for key "${key}"`);
|
|
771
|
-
}
|
|
772
|
-
agentsObj[key] = runner;
|
|
773
|
-
// Record ownership
|
|
774
|
-
ownershipMap.set(key, name);
|
|
775
|
-
}
|
|
776
|
-
return agentsObj;
|
|
777
|
-
};
|
|
778
|
-
export const createAgentMiddleware = (agentName) => {
|
|
779
|
-
return async (ctx, next) => {
|
|
780
|
-
// Populate agents object with strongly-typed keys
|
|
781
|
-
const agentsObj = populateAgentsRegistry(ctx);
|
|
782
|
-
// Track agent ID for session telemetry
|
|
783
|
-
if (agentName) {
|
|
784
|
-
const agentKey = toCamelCase(agentName);
|
|
785
|
-
const agent = agentsObj[agentKey];
|
|
786
|
-
const _ctx = privateContext(ctx);
|
|
787
|
-
// we add both so that you can query by either
|
|
788
|
-
if (agent?.metadata?.id) {
|
|
789
|
-
_ctx.var.agentIds.add(agent.metadata.id);
|
|
790
|
-
}
|
|
791
|
-
if (agent?.metadata?.agentId) {
|
|
792
|
-
_ctx.var.agentIds.add(agent.metadata.agentId);
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
const sessionId = ctx.var.sessionId;
|
|
796
|
-
const thread = ctx.var.thread;
|
|
797
|
-
const session = ctx.var.session;
|
|
798
|
-
const config = agentName ? getAgentConfig(agentName) : undefined;
|
|
799
|
-
const app = ctx.var.app;
|
|
800
|
-
const args = {
|
|
801
|
-
agent: agentsObj,
|
|
802
|
-
logger: ctx.var.logger,
|
|
803
|
-
tracer: ctx.var.tracer,
|
|
804
|
-
sessionId,
|
|
805
|
-
session,
|
|
806
|
-
thread,
|
|
807
|
-
handler: ctx.var.waitUntilHandler,
|
|
808
|
-
config: config || {},
|
|
809
|
-
app: app || {},
|
|
810
|
-
runtime: getGlobalRuntimeState(),
|
|
811
|
-
auth: ctx.var.auth ?? null,
|
|
812
|
-
};
|
|
813
|
-
return setupRequestAgentContext(ctx, args, next);
|
|
814
|
-
};
|
|
815
|
-
};
|
|
816
|
-
export const getAgents = () => agents;
|
|
817
|
-
export const runAgentSetups = async (appState) => {
|
|
818
|
-
for (const [name, agent] of agents.entries()) {
|
|
819
|
-
if (agent.setup) {
|
|
820
|
-
const config = await agent.setup(appState);
|
|
821
|
-
setAgentConfig(name, config);
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
// Note: Server readiness is managed by Vite (dev) or Bun.serve (prod)
|
|
825
|
-
};
|
|
826
|
-
export const runAgentShutdowns = async (appState) => {
|
|
827
|
-
const runtime = getGlobalRuntimeState();
|
|
828
|
-
for (const [name, agent] of runtime.agents.entries()) {
|
|
829
|
-
if (agent.shutdown) {
|
|
830
|
-
const config = runtime.agentConfigs.get(name);
|
|
831
|
-
await agent.shutdown(appState, config);
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
};
|
|
835
|
-
/**
|
|
836
|
-
* Run an agent within a specific AgentContext.
|
|
837
|
-
* Sets up AsyncLocalStorage with the provided context and executes the agent.
|
|
838
|
-
*
|
|
839
|
-
* This is the recommended way to test agents in unit tests. It automatically:
|
|
840
|
-
* - Registers the agent in the runtime state so event listeners fire
|
|
841
|
-
* - Sets up AsyncLocalStorage so getAgentContext() works inside the agent
|
|
842
|
-
* - Handles both agents with input and agents without input
|
|
843
|
-
*
|
|
844
|
-
* **Use cases:**
|
|
845
|
-
* - Unit testing agents with TestAgentContext
|
|
846
|
-
* - Running agents outside HTTP request flow
|
|
847
|
-
* - Custom agent execution environments
|
|
848
|
-
* - Testing event listeners and evaluations
|
|
849
|
-
*
|
|
850
|
-
* @template TInput - Type of the input parameter
|
|
851
|
-
* @template TOutput - Type of the return value
|
|
852
|
-
*
|
|
853
|
-
* @param ctx - The AgentContext to use (typically TestAgentContext in tests)
|
|
854
|
-
* @param agent - The AgentRunner to execute (returned from createAgent)
|
|
855
|
-
* @param input - Input data (required if agent has input schema, omit otherwise)
|
|
856
|
-
*
|
|
857
|
-
* @returns Promise resolving to the agent's output
|
|
858
|
-
*
|
|
859
|
-
* @example
|
|
860
|
-
* ```typescript
|
|
861
|
-
* import { runInAgentContext, TestAgentContext } from '@agentuity/runtime/test';
|
|
862
|
-
*
|
|
863
|
-
* test('greeting agent', async () => {
|
|
864
|
-
* const ctx = new TestAgentContext();
|
|
865
|
-
* const result = await runInAgentContext(ctx, greetingAgent, {
|
|
866
|
-
* name: 'Alice',
|
|
867
|
-
* age: 30
|
|
868
|
-
* });
|
|
869
|
-
* expect(result).toBe('Hello, Alice! You are 30 years old.');
|
|
870
|
-
* });
|
|
871
|
-
*
|
|
872
|
-
* test('no-input agent', async () => {
|
|
873
|
-
* const ctx = new TestAgentContext();
|
|
874
|
-
* const result = await runInAgentContext(ctx, statusAgent);
|
|
875
|
-
* expect(result).toEqual({ status: 'ok' });
|
|
876
|
-
* });
|
|
877
|
-
* ```
|
|
878
|
-
*/
|
|
879
|
-
export async function runInAgentContext(ctx, agent, input) {
|
|
880
|
-
const storage = getAgentAsyncLocalStorage();
|
|
881
|
-
// Register agent in runtime state so events fire (lookup by metadata.name)
|
|
882
|
-
const agentName = agent.metadata.name;
|
|
883
|
-
const runtime = getAgentRuntime(ctx);
|
|
884
|
-
// Get internal agent from runner (stored via symbol) or global registry
|
|
885
|
-
const internalAgent = agent[INTERNAL_AGENT] || agents.get(agentName);
|
|
886
|
-
if (internalAgent && agentName) {
|
|
887
|
-
runtime.agents.set(agentName, internalAgent);
|
|
888
|
-
// Copy event listeners from global to context runtime
|
|
889
|
-
const globalListeners = agentEventListeners.get(internalAgent);
|
|
890
|
-
if (globalListeners) {
|
|
891
|
-
runtime.agentEventListeners.set(internalAgent, globalListeners);
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
return storage.run(ctx, async () => {
|
|
895
|
-
if (input !== undefined) {
|
|
896
|
-
return await agent.run(input);
|
|
897
|
-
}
|
|
898
|
-
else {
|
|
899
|
-
return await agent.run();
|
|
900
|
-
}
|
|
901
|
-
});
|
|
902
|
-
}
|
|
903
|
-
//# sourceMappingURL=agent.js.map
|