@agentuity/runtime 0.0.91 → 0.0.92
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/_config.d.ts +6 -0
- package/dist/_config.d.ts.map +1 -1
- package/dist/_config.js +6 -0
- package/dist/_config.js.map +1 -1
- package/dist/_standalone.d.ts +165 -0
- package/dist/_standalone.d.ts.map +1 -0
- package/dist/_standalone.js +391 -0
- package/dist/_standalone.js.map +1 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +9 -1
- package/dist/agent.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/_config.ts +7 -0
- package/src/_standalone.ts +484 -0
- package/src/agent.ts +8 -1
- package/src/index.ts +8 -0
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
import { context, SpanKind, SpanStatusCode, type Context, trace } from '@opentelemetry/api';
|
|
2
|
+
import { TraceState } from '@opentelemetry/core';
|
|
3
|
+
import type {
|
|
4
|
+
KeyValueStorage,
|
|
5
|
+
StreamStorage,
|
|
6
|
+
VectorStorage,
|
|
7
|
+
} from '@agentuity/core';
|
|
8
|
+
import type { AgentContext, AgentRegistry, AgentRuntimeState } from './agent';
|
|
9
|
+
import { AGENT_RUNTIME, AGENT_IDS } from './_config';
|
|
10
|
+
import type { Logger } from './logger';
|
|
11
|
+
import type { Thread, Session } from './session';
|
|
12
|
+
import { generateId } from './session';
|
|
13
|
+
import WaitUntilHandler from './_waituntil';
|
|
14
|
+
import { registerServices } from './_services';
|
|
15
|
+
import { getAgentAsyncLocalStorage } from './_context';
|
|
16
|
+
import {
|
|
17
|
+
getLogger,
|
|
18
|
+
getTracer,
|
|
19
|
+
getAppState,
|
|
20
|
+
} from './_server';
|
|
21
|
+
import {
|
|
22
|
+
getThreadProvider,
|
|
23
|
+
getSessionProvider,
|
|
24
|
+
getSessionEventProvider,
|
|
25
|
+
} from './_services';
|
|
26
|
+
import * as runtimeConfig from './_config';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Options for creating a standalone agent context.
|
|
30
|
+
*
|
|
31
|
+
* Use this when executing agents outside of HTTP requests (Discord bots, cron jobs, etc.)
|
|
32
|
+
*/
|
|
33
|
+
export interface StandaloneContextOptions {
|
|
34
|
+
/**
|
|
35
|
+
* Session ID for this execution. If not provided, will be auto-generated from trace context.
|
|
36
|
+
*/
|
|
37
|
+
sessionId?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Thread for multi-turn conversations. If not provided, will be restored/created from thread provider.
|
|
40
|
+
*/
|
|
41
|
+
thread?: Thread;
|
|
42
|
+
/**
|
|
43
|
+
* Session for this execution. If not provided, will be created.
|
|
44
|
+
*/
|
|
45
|
+
session?: Session;
|
|
46
|
+
/**
|
|
47
|
+
* Parent OpenTelemetry context for distributed tracing.
|
|
48
|
+
*/
|
|
49
|
+
parentContext?: Context;
|
|
50
|
+
/**
|
|
51
|
+
* Trigger type for this execution (used in telemetry and session events).
|
|
52
|
+
*/
|
|
53
|
+
trigger?: import('@agentuity/core').SessionStartEvent['trigger'];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Options for invoke() method.
|
|
58
|
+
*/
|
|
59
|
+
export interface InvokeOptions {
|
|
60
|
+
/**
|
|
61
|
+
* Span name for OpenTelemetry trace (default: 'agent-invocation')
|
|
62
|
+
*/
|
|
63
|
+
spanName?: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Standalone agent context for executing agents outside of HTTP requests.
|
|
68
|
+
*
|
|
69
|
+
* This context provides the same infrastructure as HTTP request contexts:
|
|
70
|
+
* - OpenTelemetry tracing with proper span hierarchy
|
|
71
|
+
* - Session and thread management (save/restore)
|
|
72
|
+
* - Background task handling (waitUntil)
|
|
73
|
+
* - Session event tracking (start/complete)
|
|
74
|
+
* - Access to all services (kv, stream, vector)
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* import { createAgentContext } from '@agentuity/runtime';
|
|
79
|
+
* import myAgent from './agents/my-agent';
|
|
80
|
+
*
|
|
81
|
+
* // Simple usage:
|
|
82
|
+
* const ctx = createAgentContext();
|
|
83
|
+
* const result = await ctx.invoke(() => myAgent.run(input));
|
|
84
|
+
*
|
|
85
|
+
* // With custom session tracking:
|
|
86
|
+
* const ctx = createAgentContext({
|
|
87
|
+
* sessionId: discordMessage.id,
|
|
88
|
+
* trigger: 'discord'
|
|
89
|
+
* });
|
|
90
|
+
* const result = await ctx.invoke(() => myAgent.run(input));
|
|
91
|
+
*
|
|
92
|
+
* // Reuse context for multiple agents:
|
|
93
|
+
* const ctx = createAgentContext();
|
|
94
|
+
* const result1 = await ctx.invoke(() => agent1.run(input1));
|
|
95
|
+
* const result2 = await ctx.invoke(() => agent2.run(result1));
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
export class StandaloneAgentContext<
|
|
99
|
+
TAgentMap extends AgentRegistry = AgentRegistry,
|
|
100
|
+
TConfig = unknown,
|
|
101
|
+
TAppState = Record<string, never>,
|
|
102
|
+
> implements AgentContext<TAgentMap, TConfig, TAppState>
|
|
103
|
+
{
|
|
104
|
+
// Immutable context properties (safe for concurrent access)
|
|
105
|
+
agent: TAgentMap = {} as TAgentMap;
|
|
106
|
+
logger: Logger;
|
|
107
|
+
tracer: import('@opentelemetry/api').Tracer;
|
|
108
|
+
kv!: KeyValueStorage;
|
|
109
|
+
stream!: StreamStorage;
|
|
110
|
+
vector!: VectorStorage;
|
|
111
|
+
config: TConfig;
|
|
112
|
+
app: TAppState;
|
|
113
|
+
[AGENT_RUNTIME]: AgentRuntimeState;
|
|
114
|
+
|
|
115
|
+
// Note: The following are mutable and will be set per-invocation via AsyncLocalStorage
|
|
116
|
+
// They exist on the interface for compatibility but are overwritten during invoke()
|
|
117
|
+
sessionId: string;
|
|
118
|
+
state: Map<string, unknown>;
|
|
119
|
+
session: Session;
|
|
120
|
+
thread: Thread;
|
|
121
|
+
[AGENT_IDS]?: Set<string>;
|
|
122
|
+
|
|
123
|
+
// Immutable options stored from constructor
|
|
124
|
+
private readonly parentContext: Context;
|
|
125
|
+
private readonly trigger: import('@agentuity/core').SessionStartEvent['trigger'];
|
|
126
|
+
private readonly initialSessionId?: string;
|
|
127
|
+
|
|
128
|
+
constructor(options?: StandaloneContextOptions) {
|
|
129
|
+
const logger = getLogger();
|
|
130
|
+
const tracer = getTracer();
|
|
131
|
+
const app = getAppState();
|
|
132
|
+
|
|
133
|
+
if (!logger || !tracer || !app) {
|
|
134
|
+
throw new Error(
|
|
135
|
+
'Global state not initialized. Make sure createServer() has been called before createAgentContext().'
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
this.logger = logger;
|
|
140
|
+
this.tracer = tracer;
|
|
141
|
+
this.app = app as TAppState;
|
|
142
|
+
this.config = {} as TConfig;
|
|
143
|
+
this.state = new Map();
|
|
144
|
+
this.parentContext = options?.parentContext ?? context.active();
|
|
145
|
+
this.trigger = (options?.trigger as typeof this.trigger) ?? 'manual';
|
|
146
|
+
this.initialSessionId = options?.sessionId;
|
|
147
|
+
|
|
148
|
+
// Session ID will be set properly in invoke() after span is created
|
|
149
|
+
this.sessionId = options?.sessionId ?? 'pending';
|
|
150
|
+
|
|
151
|
+
// Thread and session will be restored in invoke()
|
|
152
|
+
this.thread = options?.thread ?? ({
|
|
153
|
+
id: 'pending',
|
|
154
|
+
state: new Map(),
|
|
155
|
+
addEventListener: () => {},
|
|
156
|
+
removeEventListener: () => {},
|
|
157
|
+
destroy: async () => {},
|
|
158
|
+
empty: () => true,
|
|
159
|
+
} as Thread);
|
|
160
|
+
|
|
161
|
+
this.session = options?.session ?? ({
|
|
162
|
+
id: 'pending',
|
|
163
|
+
thread: this.thread,
|
|
164
|
+
state: new Map(),
|
|
165
|
+
addEventListener: () => {},
|
|
166
|
+
removeEventListener: () => {},
|
|
167
|
+
serializeUserData: () => undefined,
|
|
168
|
+
} as Session);
|
|
169
|
+
|
|
170
|
+
// Create isolated runtime state
|
|
171
|
+
this[AGENT_RUNTIME] = {
|
|
172
|
+
agents: new Map(),
|
|
173
|
+
agentConfigs: new Map(),
|
|
174
|
+
agentEventListeners: new WeakMap(),
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// Register services (kv, stream, vector)
|
|
178
|
+
registerServices(this, true); // true = populate agents registry
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
waitUntil(_callback: Promise<void> | (() => void | Promise<void>)): void {
|
|
182
|
+
// This will be called from within invoke() where waitUntilHandler is in scope
|
|
183
|
+
// We need to access the per-call waitUntilHandler from the current invocation
|
|
184
|
+
// This is handled by updating the context during invoke() via AsyncLocalStorage
|
|
185
|
+
throw new Error('waitUntil must be called from within invoke() execution context');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Execute a function within this agent context.
|
|
190
|
+
*
|
|
191
|
+
* This method:
|
|
192
|
+
* 1. Creates an OpenTelemetry span for the invocation
|
|
193
|
+
* 2. Restores/creates session and thread
|
|
194
|
+
* 3. Sends session start event
|
|
195
|
+
* 4. Executes the function within AsyncLocalStorage context
|
|
196
|
+
* 5. Waits for background tasks (waitUntil)
|
|
197
|
+
* 6. Saves session and thread
|
|
198
|
+
* 7. Sends session complete event
|
|
199
|
+
*
|
|
200
|
+
* @param fn - Function to execute (typically () => agent.run(input))
|
|
201
|
+
* @param options - Optional configuration for the invocation
|
|
202
|
+
* @returns Promise that resolves to the function's return value
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```typescript
|
|
206
|
+
* const result = await ctx.invoke(() => myAgent.run({ userId: '123' }));
|
|
207
|
+
* ```
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```typescript
|
|
211
|
+
* // Multiple agents in sequence:
|
|
212
|
+
* const result = await ctx.invoke(async () => {
|
|
213
|
+
* const step1 = await agent1.run(input);
|
|
214
|
+
* return agent2.run(step1);
|
|
215
|
+
* });
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
async invoke<T>(fn: () => Promise<T>, options?: InvokeOptions): Promise<T> {
|
|
219
|
+
const threadProvider = getThreadProvider();
|
|
220
|
+
const sessionProvider = getSessionProvider();
|
|
221
|
+
const sessionEventProvider = getSessionEventProvider();
|
|
222
|
+
const storage = getAgentAsyncLocalStorage();
|
|
223
|
+
|
|
224
|
+
// Create per-invocation state (prevents race conditions on concurrent calls)
|
|
225
|
+
const waitUntilHandler = new WaitUntilHandler(this.tracer);
|
|
226
|
+
const agentIds = new Set<string>();
|
|
227
|
+
let invocationSessionId = this.initialSessionId ?? 'pending';
|
|
228
|
+
let invocationThread: Thread;
|
|
229
|
+
let invocationSession: Session;
|
|
230
|
+
const invocationState = new Map<string, unknown>();
|
|
231
|
+
|
|
232
|
+
// Create a per-call context that inherits from this but has isolated mutable state
|
|
233
|
+
const callContext = Object.create(this) as StandaloneAgentContext<TAgentMap, TConfig, TAppState>;
|
|
234
|
+
callContext.sessionId = invocationSessionId;
|
|
235
|
+
callContext.state = invocationState;
|
|
236
|
+
callContext[AGENT_IDS] = agentIds;
|
|
237
|
+
callContext.waitUntil = (callback: Promise<void> | (() => void | Promise<void>)) => {
|
|
238
|
+
waitUntilHandler.waitUntil(callback);
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
// Execute within parent context (for distributed tracing)
|
|
242
|
+
return await context.with(this.parentContext, async () => {
|
|
243
|
+
// Create a span for this invocation (similar to otelMiddleware's HTTP span)
|
|
244
|
+
return await trace.getTracer('standalone-agent').startActiveSpan(
|
|
245
|
+
options?.spanName ?? 'agent-invocation',
|
|
246
|
+
{
|
|
247
|
+
kind: SpanKind.INTERNAL, // Not HTTP, but internal invocation
|
|
248
|
+
attributes: {
|
|
249
|
+
'trigger': this.trigger,
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
async (span) => {
|
|
253
|
+
const sctx = span.spanContext();
|
|
254
|
+
|
|
255
|
+
// Generate sessionId from traceId if not provided
|
|
256
|
+
invocationSessionId = this.initialSessionId ?? (sctx?.traceId ? `sess_${sctx.traceId}` : generateId('sess'));
|
|
257
|
+
callContext.sessionId = invocationSessionId;
|
|
258
|
+
|
|
259
|
+
// Add to tracestate (like otelMiddleware does)
|
|
260
|
+
// Note: SpanContext.traceState is readonly, so we update it by setting the span with a new context
|
|
261
|
+
let traceState = sctx.traceState ?? new TraceState();
|
|
262
|
+
const projectId = runtimeConfig.getProjectId();
|
|
263
|
+
const orgId = runtimeConfig.getOrganizationId();
|
|
264
|
+
const deploymentId = runtimeConfig.getDeploymentId();
|
|
265
|
+
const isDevMode = runtimeConfig.isDevMode();
|
|
266
|
+
if (projectId) {
|
|
267
|
+
traceState = traceState.set('pid', projectId);
|
|
268
|
+
}
|
|
269
|
+
if (orgId) {
|
|
270
|
+
traceState = traceState.set('oid', orgId);
|
|
271
|
+
}
|
|
272
|
+
if (isDevMode) {
|
|
273
|
+
traceState = traceState.set('d', '1');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Update the active context with the new trace state
|
|
277
|
+
// We do this by setting the span in the context with updated trace state
|
|
278
|
+
// Note: This creates a new context but we don't need to use it directly
|
|
279
|
+
// as the span already has the trace state we need for propagation
|
|
280
|
+
trace.setSpan(
|
|
281
|
+
context.active(),
|
|
282
|
+
trace.wrapSpanContext({
|
|
283
|
+
...sctx,
|
|
284
|
+
traceState
|
|
285
|
+
})
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
// Restore thread and session (like otelMiddleware does)
|
|
289
|
+
// For standalone contexts, we create a simple thread/session if not provided
|
|
290
|
+
// The threadProvider.restore expects a Hono context with cookie/header access
|
|
291
|
+
// For standalone contexts without HTTP, we just create a new thread
|
|
292
|
+
const { DefaultThread, generateId: genId } = await import('./session');
|
|
293
|
+
const threadId = genId('thrd');
|
|
294
|
+
invocationThread = new DefaultThread(threadProvider, threadId);
|
|
295
|
+
callContext.thread = invocationThread;
|
|
296
|
+
|
|
297
|
+
invocationSession = await sessionProvider.restore(invocationThread, invocationSessionId);
|
|
298
|
+
callContext.session = invocationSession;
|
|
299
|
+
|
|
300
|
+
// Send session start event (if configured)
|
|
301
|
+
const shouldSendSession = !!(orgId && projectId);
|
|
302
|
+
let canSendSessionEvents = true;
|
|
303
|
+
|
|
304
|
+
if (shouldSendSession) {
|
|
305
|
+
await sessionEventProvider
|
|
306
|
+
.start({
|
|
307
|
+
id: invocationSessionId,
|
|
308
|
+
orgId,
|
|
309
|
+
projectId,
|
|
310
|
+
threadId: invocationThread.id,
|
|
311
|
+
routeId: 'standalone', // No route for standalone contexts
|
|
312
|
+
deploymentId,
|
|
313
|
+
devmode: isDevMode,
|
|
314
|
+
environment: runtimeConfig.getEnvironment(),
|
|
315
|
+
method: 'STANDALONE',
|
|
316
|
+
url: '',
|
|
317
|
+
trigger: this.trigger,
|
|
318
|
+
})
|
|
319
|
+
.catch((ex) => {
|
|
320
|
+
canSendSessionEvents = false;
|
|
321
|
+
this.logger.error('error sending session start event: %s', ex);
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
let hasPendingWaits = false;
|
|
326
|
+
|
|
327
|
+
try {
|
|
328
|
+
// Execute function within AsyncLocalStorage context with per-call context
|
|
329
|
+
const result = await storage.run(callContext, fn);
|
|
330
|
+
|
|
331
|
+
// Wait for background tasks (like otelMiddleware does)
|
|
332
|
+
if (waitUntilHandler.hasPending()) {
|
|
333
|
+
hasPendingWaits = true;
|
|
334
|
+
waitUntilHandler
|
|
335
|
+
.waitUntilAll(this.logger, invocationSessionId)
|
|
336
|
+
.then(async () => {
|
|
337
|
+
this.logger.debug('wait until finished for session %s', invocationSessionId);
|
|
338
|
+
await sessionProvider.save(invocationSession);
|
|
339
|
+
await threadProvider.save(invocationThread);
|
|
340
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
341
|
+
if (shouldSendSession && canSendSessionEvents) {
|
|
342
|
+
const userData = invocationSession.serializeUserData();
|
|
343
|
+
sessionEventProvider
|
|
344
|
+
.complete({
|
|
345
|
+
id: invocationSessionId,
|
|
346
|
+
threadId: invocationThread.empty() ? null : invocationThread.id,
|
|
347
|
+
statusCode: 200, // Success
|
|
348
|
+
agentIds: Array.from(agentIds),
|
|
349
|
+
userData,
|
|
350
|
+
})
|
|
351
|
+
.then(() => {})
|
|
352
|
+
.catch((ex) => this.logger.error(ex));
|
|
353
|
+
}
|
|
354
|
+
})
|
|
355
|
+
.catch((ex) => {
|
|
356
|
+
this.logger.error('wait until errored for session %s. %s', invocationSessionId, ex);
|
|
357
|
+
if (ex instanceof Error) {
|
|
358
|
+
span.recordException(ex);
|
|
359
|
+
}
|
|
360
|
+
const message = (ex as Error).message ?? String(ex);
|
|
361
|
+
span.setStatus({
|
|
362
|
+
code: SpanStatusCode.ERROR,
|
|
363
|
+
message,
|
|
364
|
+
});
|
|
365
|
+
this.logger.error(message);
|
|
366
|
+
if (shouldSendSession && canSendSessionEvents) {
|
|
367
|
+
const userData = invocationSession.serializeUserData();
|
|
368
|
+
sessionEventProvider
|
|
369
|
+
.complete({
|
|
370
|
+
id: invocationSessionId,
|
|
371
|
+
threadId: invocationThread.empty() ? null : invocationThread.id,
|
|
372
|
+
statusCode: 500, // Error
|
|
373
|
+
error: message,
|
|
374
|
+
agentIds: Array.from(agentIds),
|
|
375
|
+
userData,
|
|
376
|
+
})
|
|
377
|
+
.then(() => {})
|
|
378
|
+
.catch((ex) => this.logger.error(ex));
|
|
379
|
+
}
|
|
380
|
+
})
|
|
381
|
+
.finally(() => {
|
|
382
|
+
span.end();
|
|
383
|
+
});
|
|
384
|
+
} else {
|
|
385
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
386
|
+
if (shouldSendSession && canSendSessionEvents) {
|
|
387
|
+
const userData = invocationSession.serializeUserData();
|
|
388
|
+
sessionEventProvider
|
|
389
|
+
.complete({
|
|
390
|
+
id: invocationSessionId,
|
|
391
|
+
threadId: invocationThread.empty() ? null : invocationThread.id,
|
|
392
|
+
statusCode: 200,
|
|
393
|
+
agentIds: Array.from(agentIds),
|
|
394
|
+
userData,
|
|
395
|
+
})
|
|
396
|
+
.then(() => {})
|
|
397
|
+
.catch((ex) => this.logger.error(ex));
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return result;
|
|
402
|
+
} catch (ex) {
|
|
403
|
+
if (ex instanceof Error) {
|
|
404
|
+
span.recordException(ex);
|
|
405
|
+
}
|
|
406
|
+
const message = (ex as Error).message ?? String(ex);
|
|
407
|
+
span.setStatus({
|
|
408
|
+
code: SpanStatusCode.ERROR,
|
|
409
|
+
message,
|
|
410
|
+
});
|
|
411
|
+
this.logger.error(message);
|
|
412
|
+
if (shouldSendSession && canSendSessionEvents) {
|
|
413
|
+
const userData = invocationSession.serializeUserData();
|
|
414
|
+
sessionEventProvider
|
|
415
|
+
.complete({
|
|
416
|
+
id: invocationSessionId,
|
|
417
|
+
threadId: invocationThread.empty() ? null : invocationThread.id,
|
|
418
|
+
statusCode: 500,
|
|
419
|
+
error: message,
|
|
420
|
+
agentIds: Array.from(agentIds),
|
|
421
|
+
userData,
|
|
422
|
+
})
|
|
423
|
+
.then(() => {})
|
|
424
|
+
.catch((ex) => this.logger.error(ex));
|
|
425
|
+
}
|
|
426
|
+
throw ex;
|
|
427
|
+
} finally {
|
|
428
|
+
if (!hasPendingWaits) {
|
|
429
|
+
try {
|
|
430
|
+
await sessionProvider.save(invocationSession);
|
|
431
|
+
await threadProvider.save(invocationThread);
|
|
432
|
+
} finally {
|
|
433
|
+
span.end();
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
);
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Create a standalone agent context for executing agents outside of HTTP requests.
|
|
445
|
+
*
|
|
446
|
+
* This is useful for Discord bots, cron jobs, WebSocket callbacks, or any scenario
|
|
447
|
+
* where you need to run agents but don't have an HTTP request context.
|
|
448
|
+
*
|
|
449
|
+
* @param options - Optional configuration for the context
|
|
450
|
+
* @returns A StandaloneAgentContext instance
|
|
451
|
+
*
|
|
452
|
+
* @example
|
|
453
|
+
* ```typescript
|
|
454
|
+
* import { createAgentContext } from '@agentuity/runtime';
|
|
455
|
+
* import myAgent from './agents/my-agent';
|
|
456
|
+
*
|
|
457
|
+
* // Simple usage:
|
|
458
|
+
* const ctx = createAgentContext();
|
|
459
|
+
* const result = await ctx.invoke(() => myAgent.run(input));
|
|
460
|
+
*
|
|
461
|
+
* // Discord bot example:
|
|
462
|
+
* client.on('messageCreate', async (message) => {
|
|
463
|
+
* const ctx = createAgentContext({
|
|
464
|
+
* sessionId: message.id,
|
|
465
|
+
* trigger: 'discord'
|
|
466
|
+
* });
|
|
467
|
+
* const response = await ctx.invoke(() =>
|
|
468
|
+
* chatAgent.run({ message: message.content })
|
|
469
|
+
* );
|
|
470
|
+
* await message.reply(response.text);
|
|
471
|
+
* });
|
|
472
|
+
*
|
|
473
|
+
* // Cron job example:
|
|
474
|
+
* cron.schedule('0 * * * *', async () => {
|
|
475
|
+
* const ctx = createAgentContext({ trigger: 'cron' });
|
|
476
|
+
* await ctx.invoke(() => cleanupAgent.run());
|
|
477
|
+
* });
|
|
478
|
+
* ```
|
|
479
|
+
*/
|
|
480
|
+
export function createAgentContext<TAppState = Record<string, never>>(
|
|
481
|
+
options?: StandaloneContextOptions
|
|
482
|
+
): StandaloneAgentContext<AgentRegistry, unknown, TAppState> {
|
|
483
|
+
return new StandaloneAgentContext<AgentRegistry, unknown, TAppState>(options);
|
|
484
|
+
}
|
package/src/agent.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { context, SpanStatusCode, type Tracer, trace } from '@opentelemetry/api'
|
|
|
13
13
|
import type { Context, MiddlewareHandler } from 'hono';
|
|
14
14
|
import type { Handler } from 'hono/types';
|
|
15
15
|
import { validator } from 'hono/validator';
|
|
16
|
-
import { AGENT_RUNTIME, INTERNAL_AGENT, CURRENT_AGENT } from './_config';
|
|
16
|
+
import { AGENT_RUNTIME, INTERNAL_AGENT, CURRENT_AGENT, AGENT_IDS } from './_config';
|
|
17
17
|
import {
|
|
18
18
|
getAgentContext,
|
|
19
19
|
inHTTPContext,
|
|
@@ -1542,6 +1542,13 @@ export function createAgent<
|
|
|
1542
1542
|
honoCtx.var.agentIds.add(agent.metadata.id);
|
|
1543
1543
|
honoCtx.var.agentIds.add(agent.metadata.agentId);
|
|
1544
1544
|
}
|
|
1545
|
+
} else {
|
|
1546
|
+
// For standalone contexts, check for AGENT_IDS symbol
|
|
1547
|
+
const agentIds = (agentCtx as any)[AGENT_IDS] as Set<string> | undefined;
|
|
1548
|
+
if (agentIds) {
|
|
1549
|
+
agentIds.add(agent.metadata.id);
|
|
1550
|
+
agentIds.add(agent.metadata.agentId);
|
|
1551
|
+
}
|
|
1545
1552
|
}
|
|
1546
1553
|
|
|
1547
1554
|
agentCtx.logger = agentCtx.logger.child(attrs);
|
package/src/index.ts
CHANGED
|
@@ -91,6 +91,14 @@ export type { Logger } from './logger';
|
|
|
91
91
|
// _server.ts exports
|
|
92
92
|
export { getRouter, getAppState, AGENT_CONTEXT_PROPERTIES } from './_server';
|
|
93
93
|
|
|
94
|
+
// _standalone.ts exports
|
|
95
|
+
export {
|
|
96
|
+
createAgentContext,
|
|
97
|
+
StandaloneAgentContext,
|
|
98
|
+
type StandaloneContextOptions,
|
|
99
|
+
type InvokeOptions,
|
|
100
|
+
} from './_standalone';
|
|
101
|
+
|
|
94
102
|
// io/email exports
|
|
95
103
|
export { Email, parseEmail } from './io/email';
|
|
96
104
|
|