@agentuity/runtime 0.0.71 → 0.0.73

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/_process-protection.d.ts +25 -0
  2. package/dist/_process-protection.d.ts.map +1 -0
  3. package/dist/_process-protection.js +51 -0
  4. package/dist/_process-protection.js.map +1 -0
  5. package/dist/_server.d.ts.map +1 -1
  6. package/dist/_server.js +9 -17
  7. package/dist/_server.js.map +1 -1
  8. package/dist/_waituntil.d.ts.map +1 -1
  9. package/dist/_waituntil.js +9 -2
  10. package/dist/_waituntil.js.map +1 -1
  11. package/dist/agent.d.ts.map +1 -1
  12. package/dist/agent.js +30 -5
  13. package/dist/agent.js.map +1 -1
  14. package/dist/app.d.ts.map +1 -1
  15. package/dist/app.js +8 -1
  16. package/dist/app.js.map +1 -1
  17. package/dist/otel/console.d.ts +2 -0
  18. package/dist/otel/console.d.ts.map +1 -1
  19. package/dist/otel/console.js +34 -18
  20. package/dist/otel/console.js.map +1 -1
  21. package/dist/otel/otel.d.ts.map +1 -1
  22. package/dist/otel/otel.js +6 -3
  23. package/dist/otel/otel.js.map +1 -1
  24. package/dist/services/evalrun/http.d.ts.map +1 -1
  25. package/dist/services/evalrun/http.js +2 -2
  26. package/dist/services/evalrun/http.js.map +1 -1
  27. package/dist/services/local/keyvalue.d.ts.map +1 -1
  28. package/dist/services/local/keyvalue.js +5 -1
  29. package/dist/services/local/keyvalue.js.map +1 -1
  30. package/dist/services/session/http.d.ts.map +1 -1
  31. package/dist/services/session/http.js +2 -2
  32. package/dist/services/session/http.js.map +1 -1
  33. package/dist/session.d.ts.map +1 -1
  34. package/dist/session.js +19 -4
  35. package/dist/session.js.map +1 -1
  36. package/dist/workbench.d.ts.map +1 -1
  37. package/dist/workbench.js +1 -21
  38. package/dist/workbench.js.map +1 -1
  39. package/package.json +5 -5
  40. package/src/_process-protection.ts +66 -0
  41. package/src/_server.ts +10 -17
  42. package/src/_waituntil.ts +11 -2
  43. package/src/agent.ts +35 -5
  44. package/src/app.ts +7 -1
  45. package/src/otel/console.ts +34 -18
  46. package/src/otel/otel.ts +6 -3
  47. package/src/services/evalrun/http.ts +4 -6
  48. package/src/services/local/keyvalue.ts +7 -1
  49. package/src/services/session/http.ts +4 -6
  50. package/src/session.ts +19 -4
  51. package/src/workbench.ts +1 -21
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Process protection utilities
3
+ *
4
+ * Prevents user code from calling process.exit() which would crash the server.
5
+ * The runtime can still exit gracefully using the internal exit function.
6
+ */
7
+
8
+ import { StructuredError } from '@agentuity/core';
9
+
10
+ // Store the original process.exit
11
+ const originalExit = process.exit.bind(process);
12
+
13
+ // Flag to track if protection is enabled
14
+ let protectionEnabled = false;
15
+
16
+ const ProcessExitAttemptError = StructuredError(
17
+ 'ProcessExitAttemptError',
18
+ 'Calling process.exit() is not allowed in agent code. The server must remain running to handle requests.'
19
+ )<{
20
+ code?: number | string | null | undefined;
21
+ }>();
22
+
23
+ /**
24
+ * Enable protection against process.exit calls.
25
+ * After calling this, user code calling process.exit() will throw an error.
26
+ */
27
+ export function enableProcessExitProtection(): void {
28
+ if (protectionEnabled) {
29
+ return;
30
+ }
31
+
32
+ protectionEnabled = true;
33
+
34
+ // Replace process.exit with a function that throws
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
+ (process as any).exit = function (code?: number | string | null | undefined): never {
37
+ throw new ProcessExitAttemptError({ code });
38
+ };
39
+ }
40
+
41
+ /**
42
+ * Disable protection (mainly for testing)
43
+ */
44
+ export function disableProcessExitProtection(): void {
45
+ if (!protectionEnabled) {
46
+ return;
47
+ }
48
+
49
+ protectionEnabled = false;
50
+ process.exit = originalExit;
51
+ }
52
+
53
+ /**
54
+ * Internal function for the runtime to call when it needs to exit.
55
+ * This bypasses the protection and calls the original process.exit.
56
+ */
57
+ export function internalExit(code?: number): never {
58
+ return originalExit(code);
59
+ }
60
+
61
+ /**
62
+ * Check if protection is currently enabled
63
+ */
64
+ export function isProtectionEnabled(): boolean {
65
+ return protectionEnabled;
66
+ }
package/src/_server.ts CHANGED
@@ -26,8 +26,9 @@ import { register } from './otel/config';
26
26
  import type { Logger } from './logger';
27
27
  import { isIdle } from './_idle';
28
28
  import * as runtimeConfig from './_config';
29
- import { inAgentContext, runInHTTPContext } from './_context';
29
+ import { runInHTTPContext } from './_context';
30
30
  import { runAgentShutdowns, createAgentMiddleware } from './agent';
31
+ import { enableProcessExitProtection, internalExit } from './_process-protection';
31
32
  import {
32
33
  createServices,
33
34
  getThreadProvider,
@@ -107,17 +108,6 @@ function registerAgentuitySpanProcessor() {
107
108
  '@agentuity/devmode': devmode,
108
109
  '@agentuity/environment': environment,
109
110
  };
110
- if (inAgentContext()) {
111
- // eslint-disable-next-line @typescript-eslint/no-require-imports
112
- const { getCurrentAgentMetadata } = require('./_context');
113
- const current = getCurrentAgentMetadata();
114
- if (current) {
115
- attrs['@agentuity/agentId'] = current.id;
116
- attrs['@agentuity/agentInstanceId'] = current.agentId;
117
- attrs['@agentuity/agentDescription'] = current.description;
118
- attrs['@agentuity/agentName'] = current.name;
119
- }
120
- }
121
111
  span.setAttributes(attrs);
122
112
  }
123
113
 
@@ -168,6 +158,9 @@ export const createServer = async <TAppState>(
168
158
  const hostname = '127.0.0.1';
169
159
  const serverUrl = `http://${hostname}:${port}`;
170
160
 
161
+ // Enable process.exit protection before any user code can run
162
+ enableProcessExitProtection();
163
+
171
164
  // this must come before registering any otel stuff
172
165
  registerAgentuitySpanProcessor();
173
166
  registerTokenProcessor();
@@ -377,7 +370,7 @@ export const createServer = async <TAppState>(
377
370
  // Force exit after timeout if cleanup hangs
378
371
  const forceExitTimer = setTimeout(() => {
379
372
  otel.logger.warn('shutdown timed out after 5s, forcing exit');
380
- process.exit(1);
373
+ internalExit(1);
381
374
  }, 5_000);
382
375
  try {
383
376
  // stop accepting new connections
@@ -424,21 +417,21 @@ export const createServer = async <TAppState>(
424
417
 
425
418
  process.once('SIGINT', async () => {
426
419
  await shutdown();
427
- process.exit(0);
420
+ internalExit(0);
428
421
  });
429
422
  process.once('SIGTERM', async () => {
430
423
  await shutdown();
431
- process.exit(0);
424
+ internalExit(0);
432
425
  });
433
426
  process.once('uncaughtException', async (err) => {
434
427
  otel.logger.error('An uncaught exception was received: %s', err);
435
428
  await shutdown();
436
- process.exit(1);
429
+ internalExit(1);
437
430
  });
438
431
  process.once('unhandledRejection', async (reason) => {
439
432
  otel.logger.error('An unhandled promise rejection was received: %s', reason);
440
433
  await shutdown();
441
- process.exit(1);
434
+ internalExit(1);
442
435
  });
443
436
 
444
437
  const server = Bun.serve({
package/src/_waituntil.ts CHANGED
@@ -61,7 +61,8 @@ export default class WaitUntilHandler {
61
61
  } catch (ex: unknown) {
62
62
  span.recordException(ex as Error);
63
63
  span.setStatus({ code: SpanStatusCode.ERROR });
64
- throw ex;
64
+ // Log the error but don't re-throw - background tasks should never crash the server
65
+ internal.error('Background task error: %s', ex);
65
66
  } finally {
66
67
  span.end();
67
68
  }
@@ -93,8 +94,16 @@ export default class WaitUntilHandler {
93
94
  internal.debug(`⏳ Waiting for ${this.promises.length} promises to complete...`);
94
95
  try {
95
96
  // Promises are already executing, just wait for them to complete
96
- await Promise.all(this.promises);
97
+ // Use allSettled so one failing promise doesn't stop others
98
+ const results = await Promise.allSettled(this.promises);
97
99
  const duration = Date.now() - (this.started as number);
100
+
101
+ // Log any failures
102
+ const failures = results.filter((r) => r.status === 'rejected');
103
+ if (failures.length > 0) {
104
+ logger.error('%d background task(s) failed during execution', failures.length);
105
+ }
106
+
98
107
  internal.debug(
99
108
  '✅ All promises completed, marking session completed (duration %dms)',
100
109
  duration
package/src/agent.ts CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  type VectorStorage,
8
8
  type InferOutput,
9
9
  toCamelCase,
10
+ EvalRunStartEvent,
10
11
  } from '@agentuity/core';
11
12
  import { context, SpanStatusCode, type Tracer, trace } from '@opentelemetry/api';
12
13
  import type { Context, MiddlewareHandler } from 'hono';
@@ -15,6 +16,8 @@ import { validator } from 'hono/validator';
15
16
  import { AGENT_RUNTIME, INTERNAL_AGENT, CURRENT_AGENT } from './_config';
16
17
  import {
17
18
  getAgentContext,
19
+ inHTTPContext,
20
+ getHTTPContext,
18
21
  setupRequestAgentContext,
19
22
  type RequestAgentContextArgs,
20
23
  } from './_context';
@@ -27,7 +30,6 @@ import { privateContext, notifyReady } from './_server';
27
30
  import { generateId } from './session';
28
31
  import { getEvalRunEventProvider } from './_services';
29
32
  import * as runtimeConfig from './_config';
30
- import type { EvalRunStartEvent } from '@agentuity/core';
31
33
  import type { AppState } from './index';
32
34
  import { validateSchema, formatValidationIssues } from './_validation';
33
35
 
@@ -1198,10 +1200,15 @@ async function fireAgentEvent(
1198
1200
  const callbacks = listeners.get(eventName);
1199
1201
  if (callbacks && callbacks.size > 0) {
1200
1202
  for (const callback of callbacks) {
1201
- if (eventName === 'errored' && data) {
1202
- await (callback as any)(eventName, agent, context, data);
1203
- } else if (eventName === 'started' || eventName === 'completed') {
1204
- await (callback as any)(eventName, agent, context);
1203
+ try {
1204
+ if (eventName === 'errored' && data) {
1205
+ await (callback as any)(eventName, agent, context, data);
1206
+ } else if (eventName === 'started' || eventName === 'completed') {
1207
+ await (callback as any)(eventName, agent, context);
1208
+ }
1209
+ } catch (error) {
1210
+ // Log but don't re-throw - event listener errors should not crash the server
1211
+ internal.error(`Error in agent event listener for '${eventName}':`, error);
1205
1212
  }
1206
1213
  }
1207
1214
  }
@@ -1516,6 +1523,29 @@ export function createAgent<
1516
1523
  // Store current agent for telemetry (using Symbol to keep it internal)
1517
1524
  (agentCtx as any)[CURRENT_AGENT] = agent;
1518
1525
 
1526
+ const attrs = {
1527
+ '@agentuity/agentId': agent.metadata.id,
1528
+ '@agentuity/agentInstanceId': agent.metadata.agentId,
1529
+ '@agentuity/agentDescription': agent.metadata.description,
1530
+ '@agentuity/agentName': agent.metadata.name,
1531
+ };
1532
+
1533
+ // Set agent attributes on the current active span
1534
+ const activeSpan = trace.getActiveSpan();
1535
+ if (activeSpan) {
1536
+ activeSpan.setAttributes(attrs);
1537
+ }
1538
+
1539
+ if (inHTTPContext()) {
1540
+ const honoCtx = privateContext(getHTTPContext());
1541
+ if (honoCtx.var.agentIds) {
1542
+ honoCtx.var.agentIds.add(agent.metadata.id);
1543
+ honoCtx.var.agentIds.add(agent.metadata.agentId);
1544
+ }
1545
+ }
1546
+
1547
+ agentCtx.logger = agentCtx.logger.child(attrs);
1548
+
1519
1549
  // Get the agent instance from the runtime state to fire events
1520
1550
  const runtime = getAgentRuntime(agentCtx);
1521
1551
 
package/src/app.ts CHANGED
@@ -6,6 +6,7 @@ import type { BunWebSocketData } from 'hono/bun';
6
6
  import type { Logger } from './logger';
7
7
  import { createServer, getLogger } from './_server';
8
8
  import type { Meter, Tracer } from '@opentelemetry/api';
9
+ import { internal } from './logger/internal';
9
10
  import type {
10
11
  KeyValueStorage,
11
12
  SessionEventProvider,
@@ -279,7 +280,12 @@ export class App<TAppState = Record<string, never>> {
279
280
  if (!callbacks || callbacks.size === 0) return;
280
281
 
281
282
  for (const callback of callbacks) {
282
- await callback(eventName, ...args);
283
+ try {
284
+ await callback(eventName, ...args);
285
+ } catch (error) {
286
+ // Log but don't re-throw - event listener errors should not crash the server
287
+ internal.error(`Error in app event listener for '${eventName}':`, error);
288
+ }
283
289
  }
284
290
  }
285
291
  }
@@ -9,6 +9,11 @@ import { __originalConsole } from './logger';
9
9
  * Uses __originalConsole to avoid infinite loop when console is patched
10
10
  */
11
11
  export class ConsoleLogRecordExporter implements LogRecordExporter {
12
+ private dumpRecords = false;
13
+
14
+ constructor(dumpRecords: boolean) {
15
+ this.dumpRecords = dumpRecords;
16
+ }
12
17
  /**
13
18
  * Exports log records to the console
14
19
  *
@@ -17,24 +22,35 @@ export class ConsoleLogRecordExporter implements LogRecordExporter {
17
22
  */
18
23
  export(logs: ReadableLogRecord[], resultCallback: (result: ExportResult) => void): void {
19
24
  for (const log of logs) {
20
- const severity = log.severityNumber ? SeverityNumber[log.severityNumber] : 'INFO';
21
- const msg = `[${severity}] ${log.body}`;
22
- switch (log.severityNumber) {
23
- case SeverityNumber.DEBUG:
24
- __originalConsole.debug(msg);
25
- break;
26
- case SeverityNumber.INFO:
27
- __originalConsole.info(msg);
28
- break;
29
- case SeverityNumber.WARN:
30
- __originalConsole.warn(msg);
31
- break;
32
- case SeverityNumber.ERROR:
33
- __originalConsole.error(msg);
34
- break;
35
- default:
36
- __originalConsole.log(msg);
37
- break;
25
+ if (this.dumpRecords) {
26
+ __originalConsole.log('[LOG]', {
27
+ body: log.body,
28
+ severityNumber: log.severityNumber,
29
+ severityText: log.severityText,
30
+ timestamp: log.hrTime,
31
+ attributes: log.attributes,
32
+ resource: log.resource.attributes,
33
+ });
34
+ } else {
35
+ const severity = log.severityNumber ? SeverityNumber[log.severityNumber] : 'INFO';
36
+ const msg = `[${severity}] ${log.body}`;
37
+ switch (log.severityNumber) {
38
+ case SeverityNumber.DEBUG:
39
+ __originalConsole.debug(msg);
40
+ break;
41
+ case SeverityNumber.INFO:
42
+ __originalConsole.info(msg);
43
+ break;
44
+ case SeverityNumber.WARN:
45
+ __originalConsole.warn(msg);
46
+ break;
47
+ case SeverityNumber.ERROR:
48
+ __originalConsole.error(msg);
49
+ break;
50
+ default:
51
+ __originalConsole.log(msg);
52
+ break;
53
+ }
38
54
  }
39
55
  }
40
56
  resultCallback({ code: ExportResultCode.SUCCESS });
package/src/otel/otel.ts CHANGED
@@ -102,7 +102,7 @@ export const createAgentuityLoggerProvider = ({
102
102
  let exporter: OTLPLogExporter | JSONLLogExporter | undefined;
103
103
 
104
104
  if (useConsoleExporters) {
105
- processor = new SimpleLogRecordProcessor(new ConsoleLogRecordExporter());
105
+ processor = new SimpleLogRecordProcessor(new ConsoleLogRecordExporter(true));
106
106
  } else if (jsonlBasePath) {
107
107
  exporter = new JSONLLogExporter(jsonlBasePath);
108
108
  processor = new BatchLogRecordProcessor(exporter);
@@ -117,7 +117,7 @@ export const createAgentuityLoggerProvider = ({
117
117
  exporter = otlpExporter;
118
118
  processor = new BatchLogRecordProcessor(otlpExporter);
119
119
  } else {
120
- processor = new SimpleLogRecordProcessor(new ConsoleLogRecordExporter());
120
+ processor = new SimpleLogRecordProcessor(new ConsoleLogRecordExporter(false));
121
121
  }
122
122
  const provider = new LoggerProvider({
123
123
  resource,
@@ -210,7 +210,10 @@ export function registerOtel(config: OtelConfig): OtelResponse {
210
210
  const logger = createLogger(!!url, attrs, logLevel);
211
211
 
212
212
  // must do this after we have created the logger
213
- patchConsole(!!url, attrs, logLevel);
213
+ // don't patch console if we're using console exporters (to avoid double logging)
214
+ if (!useConsoleExporters) {
215
+ patchConsole(!!url, attrs, logLevel);
216
+ }
214
217
 
215
218
  // Build trace exporter (OTLP or JSONL)
216
219
  const traceExporter = jsonlBasePath
@@ -46,11 +46,10 @@ export class HTTPEvalRunEventProvider implements EvalRunEventProvider {
46
46
  this.logger.debug('[EVALRUN HTTP] Start event payload: %s', JSON.stringify(payload, null, 2));
47
47
 
48
48
  try {
49
- const resp = await this.apiClient.request(
50
- 'POST',
49
+ const resp = await this.apiClient.post(
51
50
  endpoint,
52
- APIResponseSchemaNoData(),
53
51
  payload,
52
+ APIResponseSchemaNoData(),
54
53
  EvalRunStartEventDelayedSchema
55
54
  );
56
55
  if (resp.success) {
@@ -93,11 +92,10 @@ export class HTTPEvalRunEventProvider implements EvalRunEventProvider {
93
92
  this.logger.debug('[EVALRUN HTTP] Base URL: %s', this.baseUrl);
94
93
 
95
94
  try {
96
- const resp = await this.apiClient.request(
97
- 'PUT',
95
+ const resp = await this.apiClient.put(
98
96
  endpoint,
99
- APIResponseSchemaNoData(),
100
97
  { ...event, timestamp: Date.now() },
98
+ APIResponseSchemaNoData(),
101
99
  EvalRunCompleteEventDelayedSchema
102
100
  );
103
101
  if (resp.success) {
@@ -83,10 +83,16 @@ export class LocalKeyValueStorage implements KeyValueStorage {
83
83
  buffer = Buffer.from(value);
84
84
  } else if (value instanceof ArrayBuffer) {
85
85
  buffer = Buffer.from(new Uint8Array(value));
86
- } else if (typeof value === 'object') {
86
+ } else if (
87
+ typeof value === 'number' ||
88
+ typeof value === 'boolean' ||
89
+ typeof value === 'object'
90
+ ) {
91
+ // Use JSON for numbers, booleans, and objects to preserve type on round-trip
87
92
  buffer = Buffer.from(JSON.stringify(value), 'utf-8');
88
93
  contentType = 'application/json';
89
94
  } else {
95
+ // Fallback for other types
90
96
  buffer = Buffer.from(String(value), 'utf-8');
91
97
  }
92
98
 
@@ -30,11 +30,10 @@ export class HTTPSessionEventProvider implements SessionEventProvider {
30
30
  */
31
31
  async start(event: SessionStartEvent): Promise<void> {
32
32
  this.logger.debug('Sending session start event: %s', event.id);
33
- const resp = await this.apiClient.request(
34
- 'POST',
33
+ const resp = await this.apiClient.post(
35
34
  '/session/2025-03-17',
36
- APIResponseSchemaNoData(),
37
35
  { ...event, timestamp: Date.now() },
36
+ APIResponseSchemaNoData(),
38
37
  SessionStartEventDelayedSchema
39
38
  );
40
39
  if (resp.success) {
@@ -51,11 +50,10 @@ export class HTTPSessionEventProvider implements SessionEventProvider {
51
50
  */
52
51
  async complete(event: SessionCompleteEvent): Promise<void> {
53
52
  this.logger.debug('Sending session complete event: %s', event.id);
54
- const resp = await this.apiClient.request(
55
- 'PUT',
53
+ const resp = await this.apiClient.put(
56
54
  '/session/2025-03-17',
57
- APIResponseSchemaNoData(),
58
55
  { ...event, timestamp: Date.now() },
56
+ APIResponseSchemaNoData(),
59
57
  SessionCompleteEventDelayedSchema
60
58
  );
61
59
  if (resp.success) {
package/src/session.ts CHANGED
@@ -6,6 +6,7 @@ import { type Env, fireEvent } from './app';
6
6
  import type { AppState } from './index';
7
7
  import { getServiceUrls } from '@agentuity/server';
8
8
  import { WebSocket } from 'ws';
9
+ import { internal } from './logger/internal';
9
10
 
10
11
  export type ThreadEventName = 'destroyed';
11
12
  export type SessionEventName = 'completed';
@@ -418,7 +419,12 @@ async function fireThreadEvent(thread: Thread, eventName: ThreadEventName): Prom
418
419
  if (!callbacks || callbacks.size === 0) return;
419
420
 
420
421
  for (const callback of callbacks) {
421
- await (callback as any)(eventName, thread);
422
+ try {
423
+ await (callback as any)(eventName, thread);
424
+ } catch (error) {
425
+ // Log but don't re-throw - event listener errors should not crash the server
426
+ internal.error(`Error in thread event listener for '${eventName}':`, error);
427
+ }
422
428
  }
423
429
  }
424
430
 
@@ -431,7 +437,12 @@ async function fireSessionEvent(session: Session, eventName: SessionEventName):
431
437
  if (!callbacks || callbacks.size === 0) return;
432
438
 
433
439
  for (const callback of callbacks) {
434
- await (callback as any)(eventName, session);
440
+ try {
441
+ await (callback as any)(eventName, session);
442
+ } catch (error) {
443
+ // Log but don't re-throw - event listener errors should not crash the server
444
+ internal.error(`Error in session event listener for '${eventName}':`, error);
445
+ }
435
446
  }
436
447
  }
437
448
 
@@ -977,8 +988,12 @@ export class DefaultThreadProvider implements ThreadProvider {
977
988
  if (this.wsClient) {
978
989
  try {
979
990
  await this.wsClient.delete(thread.id);
980
- } catch (err) {
981
- console.error(`Failed to delete thread ${thread.id} from remote storage:`, err);
991
+ } catch {
992
+ // Thread might not exist in remote storage if it was never persisted
993
+ // This is normal for ephemeral threads, so just log at debug level
994
+ internal.debug(
995
+ `Thread ${thread.id} not found in remote storage (already deleted or never persisted)`
996
+ );
982
997
  // Continue with local cleanup even if remote delete fails
983
998
  }
984
999
  }
package/src/workbench.ts CHANGED
@@ -1,7 +1,6 @@
1
- import { z } from 'zod';
2
1
  import type { Context, Handler } from 'hono';
3
- import { s } from '@agentuity/schema';
4
2
  import { timingSafeEqual } from 'node:crypto';
3
+ import { toJSONSchema } from '@agentuity/server';
5
4
  import { getAgents, createAgentMiddleware } from './agent';
6
5
  import { createRouter } from './router';
7
6
  import type { WebSocketConnection } from './router';
@@ -135,25 +134,6 @@ export const createWorkbenchRouter = () => {
135
134
  return router;
136
135
  };
137
136
 
138
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
139
- const toJSONSchema = (schema: any) => {
140
- // Check if it's an Agentuity schema via StandardSchemaV1 vendor
141
- if (schema?.['~standard']?.vendor === 'agentuity') {
142
- return s.toJSONSchema(schema);
143
- }
144
- // Check if it's a Zod schema
145
- if (schema?._def?.typeName) {
146
- try {
147
- return z.toJSONSchema(schema);
148
- } catch {
149
- return {};
150
- }
151
- }
152
- // TODO: this is going to only work for zod schema for now. need a way to handle others
153
- // Unknown schema type
154
- return {};
155
- };
156
-
157
137
  export const createWorkbenchMetadataRoute = (): Handler => {
158
138
  const authHeader = process.env.AGENTUITY_WORKBENCH_APIKEY
159
139
  ? `Bearer ${process.env.AGENTUITY_WORKBENCH_APIKEY}`