@agentuity/runtime 0.0.103 → 0.0.105

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 (67) hide show
  1. package/README.md +61 -35
  2. package/dist/_config.d.ts.map +1 -1
  3. package/dist/_config.js +9 -2
  4. package/dist/_config.js.map +1 -1
  5. package/dist/agent.d.ts.map +1 -1
  6. package/dist/agent.js +6 -0
  7. package/dist/agent.js.map +1 -1
  8. package/dist/app.d.ts +5 -2
  9. package/dist/app.d.ts.map +1 -1
  10. package/dist/app.js.map +1 -1
  11. package/dist/eval.d.ts +2 -0
  12. package/dist/eval.d.ts.map +1 -1
  13. package/dist/handlers/cron.d.ts +47 -0
  14. package/dist/handlers/cron.d.ts.map +1 -0
  15. package/dist/handlers/cron.js +49 -0
  16. package/dist/handlers/cron.js.map +1 -0
  17. package/dist/handlers/index.d.ts +5 -0
  18. package/dist/handlers/index.d.ts.map +1 -0
  19. package/dist/handlers/index.js +5 -0
  20. package/dist/handlers/index.js.map +1 -0
  21. package/dist/handlers/sse.d.ts +74 -0
  22. package/dist/handlers/sse.d.ts.map +1 -0
  23. package/dist/handlers/sse.js +70 -0
  24. package/dist/handlers/sse.js.map +1 -0
  25. package/dist/handlers/stream.d.ts +52 -0
  26. package/dist/handlers/stream.d.ts.map +1 -0
  27. package/dist/handlers/stream.js +75 -0
  28. package/dist/handlers/stream.js.map +1 -0
  29. package/dist/handlers/websocket.d.ts +49 -0
  30. package/dist/handlers/websocket.d.ts.map +1 -0
  31. package/dist/handlers/websocket.js +130 -0
  32. package/dist/handlers/websocket.js.map +1 -0
  33. package/dist/index.d.ts +2 -2
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +4 -2
  36. package/dist/index.js.map +1 -1
  37. package/dist/otel/logger.d.ts +1 -4
  38. package/dist/otel/logger.d.ts.map +1 -1
  39. package/dist/otel/logger.js +11 -2
  40. package/dist/otel/logger.js.map +1 -1
  41. package/dist/router.d.ts +46 -236
  42. package/dist/router.d.ts.map +1 -1
  43. package/dist/router.js +82 -349
  44. package/dist/router.js.map +1 -1
  45. package/dist/workbench.d.ts +6 -2
  46. package/dist/workbench.d.ts.map +1 -1
  47. package/dist/workbench.js +29 -26
  48. package/dist/workbench.js.map +1 -1
  49. package/package.json +5 -7
  50. package/src/_config.ts +9 -2
  51. package/src/agent.ts +6 -0
  52. package/src/app.ts +7 -2
  53. package/src/eval.ts +2 -0
  54. package/src/handlers/cron.ts +70 -0
  55. package/src/handlers/index.ts +4 -0
  56. package/src/handlers/sse.ts +118 -0
  57. package/src/handlers/stream.ts +86 -0
  58. package/src/handlers/websocket.ts +153 -0
  59. package/src/index.ts +16 -3
  60. package/src/otel/logger.ts +13 -2
  61. package/src/router.ts +110 -597
  62. package/src/workbench.ts +30 -27
  63. package/dist/io/email.d.ts +0 -77
  64. package/dist/io/email.d.ts.map +0 -1
  65. package/dist/io/email.js +0 -162
  66. package/dist/io/email.js.map +0 -1
  67. package/src/io/email.ts +0 -191
package/src/router.ts CHANGED
@@ -1,300 +1,77 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- /* eslint-disable @typescript-eslint/no-empty-object-type */
3
- import {
4
- type Context,
5
- Hono,
6
- type Input,
7
- type MiddlewareHandler,
8
- type Schema,
9
- type Env as HonoEnv,
10
- } from 'hono';
11
- import { stream as honoStream, streamSSE as honoStreamSSE } from 'hono/streaming';
12
- import { upgradeWebSocket } from 'hono/bun';
13
- import { hash, returnResponse } from './_util';
2
+ import { type Context, Hono, type Schema, type Env as HonoEnv } from 'hono';
3
+ import { returnResponse } from './_util';
14
4
  import type { Env } from './app';
15
- import { getAgentAsyncLocalStorage } from './_context';
16
- import { parseEmail, type Email } from './io/email';
17
5
 
18
6
  // Re-export both Env types
19
7
  export type { Env };
20
8
  export type { HonoEnv };
21
9
 
22
- type AgentHandler<E extends Env = Env, P extends string = string, I extends Input = {}> = (
23
- c: Context<E, P, I>
24
- ) => any | Promise<any>;
10
+ // Re-export WebSocketConnection from handlers
11
+ export type { WebSocketConnection } from './handlers/websocket';
25
12
 
26
- type EmailHandler<E extends Env = Env, P extends string = string, I extends Input = {}> = (
27
- email: Email,
28
- c: Context<E, P, I>
29
- ) => any | Promise<any>;
30
-
31
- type StreamHandler<E extends Env = Env, P extends string = string, I extends Input = {}> = (
32
- c: Context<E, P, I>
33
- ) => ReadableStream<any> | Promise<ReadableStream<any>>;
34
-
35
- export interface WebSocketConnection {
36
- onOpen: (handler: (event: any) => void | Promise<void>) => void;
37
- onMessage: (handler: (event: any) => void | Promise<void>) => void;
38
- onClose: (handler: (event: any) => void | Promise<void>) => void;
39
- send: (data: string | ArrayBuffer | Uint8Array) => void;
40
- }
41
-
42
- type WebSocketHandler<E extends Env = Env, P extends string = string, I extends Input = {}> = (
43
- c: Context<E, P, I>
44
- ) => (ws: WebSocketConnection) => void | Promise<void>;
45
-
46
- type SSEHandler<E extends Env = Env, P extends string = string, I extends Input = {}> = (
47
- c: Context<E, P, I>
48
- ) => (stream: any) => void | Promise<void>;
49
-
50
- // Module augmentation to add custom methods to Hono
51
- // This avoids wrapper types and type instantiation depth issues
52
- // Use simplified signatures to avoid type instantiation depth issues
13
+ // Module augmentation to add deprecated methods to Hono
14
+ // These stubs throw errors with migration instructions
53
15
  declare module 'hono' {
54
16
  interface Hono {
55
17
  /**
56
- * Register a route to handle incoming emails at a specific address.
57
- *
58
- * @param address - The email address to handle (e.g., 'support@example.com')
59
- * @param handler - Handler function receiving parsed email and context
60
- *
61
- * @example
62
- * ```typescript
63
- * router.email('support@example.com', (email, c) => {
64
- * console.log('From:', email.fromEmail());
65
- * console.log('Subject:', email.subject());
66
- * console.log('Body:', email.text());
67
- * return c.text('Email received');
68
- * });
69
- * ```
70
- */
71
- email(address: string, handler: (email: Email, c: Context) => any): this;
72
-
73
- /**
74
- * Register a route to handle incoming emails with middleware.
75
- *
76
- * @param address - The email address to handle
77
- * @param middleware - Middleware to run before the handler
78
- * @param handler - Handler function receiving parsed email and context
79
- *
80
- * @example
81
- * ```typescript
82
- * router.email('support@example.com', authMiddleware, (email, c) => {
83
- * return c.json({ received: email.subject() });
84
- * });
85
- * ```
86
- */
87
- email(
88
- address: string,
89
- middleware: MiddlewareHandler,
90
- handler: (email: Email, c: Context) => any
91
- ): this;
92
-
93
- /**
94
- * Register a route to handle incoming SMS messages to a phone number.
95
- *
96
- * @param params - Configuration object with phone number
97
- * @param params.number - Phone number to handle (e.g., '+1234567890')
98
- * @param handler - Handler function receiving context
99
- *
100
- * @example
101
- * ```typescript
102
- * router.sms({ number: '+1234567890' }, (c) => {
103
- * const message = c.req.query('message');
104
- * console.log('SMS received:', message);
105
- * return c.text('SMS received');
106
- * });
107
- * ```
108
- */
109
- sms(params: { number: string }, handler: (c: Context) => any): this;
110
-
111
- /**
112
- * Schedule a handler to run at specific intervals using cron syntax.
113
- *
114
- * @param schedule - Cron expression (e.g., '0 0 * * *' for daily at midnight)
115
- * @param handler - Handler function to run on schedule
116
- *
117
- * @example
118
- * ```typescript
119
- * // Run daily at midnight
120
- * router.cron('0 0 * * *', (c) => {
121
- * console.log('Daily cleanup running');
122
- * return c.text('Cleanup complete');
123
- * });
124
- *
125
- * // Run every hour
126
- * router.cron('0 * * * *', (c) => {
127
- * console.log('Hourly health check');
128
- * return c.text('OK');
129
- * });
130
- * ```
131
- */
132
- cron(schedule: string, handler: (c: Context) => any): this;
133
-
134
- /**
135
- * Create a streaming route that returns a ReadableStream.
136
- *
137
- * @param path - The route path
138
- * @param handler - Handler returning a ReadableStream
139
- *
140
- * @example
18
+ * @deprecated Use the `websocket` middleware instead:
141
19
  * ```typescript
142
- * router.stream('/events', (c) => {
143
- * return new ReadableStream({
144
- * start(controller) {
145
- * controller.enqueue('event 1\n');
146
- * controller.enqueue('event 2\n');
147
- * controller.close();
148
- * }
149
- * });
150
- * });
20
+ * import { websocket } from '@agentuity/runtime';
21
+ * router.get('/ws', websocket((c, ws) => { ... }));
151
22
  * ```
152
23
  */
153
- stream(
154
- path: string,
155
- handler: (c: Context) => ReadableStream<any> | Promise<ReadableStream<any>>
156
- ): this;
24
+ websocket(path: string, ...args: any[]): this;
157
25
 
158
26
  /**
159
- * Create a streaming route with middleware.
160
- *
161
- * @param path - The route path
162
- * @param middleware - Middleware to run before streaming
163
- * @param handler - Handler returning a ReadableStream
164
- *
165
- * @example
27
+ * @deprecated Use the `sse` middleware instead:
166
28
  * ```typescript
167
- * router.stream('/protected-stream', authMiddleware, (c) => {
168
- * return new ReadableStream({
169
- * start(controller) {
170
- * controller.enqueue('secure data\n');
171
- * controller.close();
172
- * }
173
- * });
174
- * });
29
+ * import { sse } from '@agentuity/runtime';
30
+ * router.get('/events', sse((c, stream) => { ... }));
175
31
  * ```
176
32
  */
177
- stream(
178
- path: string,
179
- middleware: MiddlewareHandler,
180
- handler: (c: Context) => ReadableStream<any> | Promise<ReadableStream<any>>
181
- ): this;
33
+ sse(path: string, ...args: any[]): this;
182
34
 
183
35
  /**
184
- * Create a WebSocket route for real-time bidirectional communication.
185
- *
186
- * @param path - The route path
187
- * @param handler - Setup function that registers WebSocket event handlers
188
- *
189
- * @example
36
+ * @deprecated Use the `stream` middleware instead:
190
37
  * ```typescript
191
- * router.websocket('/ws', (c) => (ws) => {
192
- * ws.onOpen((event) => {
193
- * console.log('WebSocket opened');
194
- * ws.send('Welcome!');
195
- * });
196
- *
197
- * ws.onMessage((event) => {
198
- * console.log('Received:', event.data);
199
- * ws.send('Echo: ' + event.data);
200
- * });
201
- *
202
- * ws.onClose((event) => {
203
- * console.log('WebSocket closed');
204
- * });
205
- * });
38
+ * import { stream } from '@agentuity/runtime';
39
+ * router.post('/data', stream((c) => new ReadableStream({ ... })));
206
40
  * ```
207
41
  */
208
- websocket(path: string, handler: (c: Context) => (ws: WebSocketConnection) => void): this;
42
+ stream(path: string, ...args: any[]): this;
209
43
 
210
44
  /**
211
- * Create a WebSocket route with middleware.
212
- *
213
- * @param path - The route path
214
- * @param middleware - Middleware to run before WebSocket upgrade
215
- * @param handler - Setup function that registers WebSocket event handlers
216
- *
217
- * @example
45
+ * @deprecated Use the `cron` middleware instead:
218
46
  * ```typescript
219
- * router.websocket('/ws', authMiddleware, (c) => (ws) => {
220
- * ws.onMessage((event) => {
221
- * ws.send('Authenticated echo: ' + event.data);
222
- * });
223
- * });
47
+ * import { cron } from '@agentuity/runtime';
48
+ * router.post('/job', cron('0 0 * * *', (c) => { ... }));
224
49
  * ```
225
50
  */
226
- websocket(
227
- path: string,
228
- middleware: MiddlewareHandler,
229
- handler: (c: Context) => (ws: WebSocketConnection) => void
230
- ): this;
231
-
232
- /**
233
- * Create a Server-Sent Events (SSE) route for streaming updates to clients.
234
- *
235
- * @param path - The route path
236
- * @param handler - Handler receiving SSE stream writer
237
- *
238
- * @example
239
- * ```typescript
240
- * router.sse('/notifications', (c) => async (stream) => {
241
- * let count = 0;
242
- * const interval = setInterval(() => {
243
- * stream.writeSSE({
244
- * data: `Notification ${++count}`,
245
- * event: 'notification'
246
- * });
247
- * if (count >= 10) {
248
- * clearInterval(interval);
249
- * stream.close();
250
- * }
251
- * }, 1000);
252
- * });
253
- * ```
254
- */
255
- sse(path: string, handler: (c: Context) => (stream: any) => void): this;
256
-
257
- /**
258
- * Create an SSE route with middleware.
259
- *
260
- * @param path - The route path
261
- * @param middleware - Middleware to run before SSE streaming
262
- * @param handler - Handler receiving SSE stream writer
263
- *
264
- * @example
265
- * ```typescript
266
- * router.sse('/protected-events', authMiddleware, (c) => async (stream) => {
267
- * stream.writeSSE({ data: 'Secure event', event: 'update' });
268
- * stream.close();
269
- * });
270
- * ```
271
- */
272
- sse(
273
- path: string,
274
- middleware: MiddlewareHandler,
275
- handler: (c: Context) => (stream: any) => void
276
- ): this;
51
+ cron(schedule: string, ...args: any[]): this;
277
52
  }
278
53
  }
279
54
 
280
55
  /**
281
56
  * Creates a Hono router with extended methods for Agentuity-specific routing patterns.
282
57
  *
283
- * In addition to standard HTTP methods (get, post, put, delete, patch), the router includes:
284
- * - **stream()** - Stream responses with ReadableStream
285
- * - **websocket()** - WebSocket connections
286
- * - **sse()** - Server-Sent Events
287
- * - **email()** - Email handler routing
288
- * - **sms()** - SMS handler routing
289
- * - **cron()** - Scheduled task routing
58
+ * Standard HTTP methods (get, post, put, delete, patch) are available, plus middleware
59
+ * functions for specialized protocols:
60
+ *
61
+ * - **websocket()** - WebSocket connections (import { websocket } from '@agentuity/runtime')
62
+ * - **sse()** - Server-Sent Events (import { sse } from '@agentuity/runtime')
63
+ * - **stream()** - Streaming responses (import { stream } from '@agentuity/runtime')
64
+ * - **cron()** - Scheduled tasks (import { cron } from '@agentuity/runtime')
290
65
  *
291
66
  * @template E - Environment type (Hono Env)
292
67
  * @template S - Schema type for route definitions
293
68
  *
294
- * @returns Extended Hono router with custom methods
69
+ * @returns Extended Hono router
295
70
  *
296
71
  * @example
297
72
  * ```typescript
73
+ * import { createRouter, websocket, sse, stream, cron } from '@agentuity/runtime';
74
+ *
298
75
  * const router = createRouter();
299
76
  *
300
77
  * // Standard HTTP routes
@@ -304,54 +81,32 @@ declare module 'hono' {
304
81
  * return c.json({ received: body });
305
82
  * });
306
83
  *
307
- * // Streaming response
308
- * router.stream('/events', (c) => {
309
- * return new ReadableStream({
310
- * start(controller) {
311
- * controller.enqueue('event 1\n');
312
- * controller.enqueue('event 2\n');
313
- * controller.close();
314
- * }
315
- * });
316
- * });
317
- *
318
84
  * // WebSocket connection
319
- * router.websocket('/ws', (c) => (ws) => {
85
+ * router.get('/ws', websocket((c, ws) => {
320
86
  * ws.onMessage((event) => {
321
- * console.log('Received:', event.data);
322
87
  * ws.send('Echo: ' + event.data);
323
88
  * });
324
- * });
89
+ * }));
325
90
  *
326
91
  * // Server-Sent Events
327
- * router.sse('/notifications', (c) => async (stream) => {
328
- * let count = 0;
329
- * const interval = setInterval(() => {
330
- * stream.writeSSE({ data: `Message ${++count}` });
331
- * if (count >= 10) {
332
- * clearInterval(interval);
333
- * stream.close();
334
- * }
335
- * }, 1000);
336
- * });
92
+ * router.get('/events', sse((c, stream) => {
93
+ * stream.writeSSE({ data: 'Hello', event: 'message' });
94
+ * }));
337
95
  *
338
- * // Email routing
339
- * router.email('support@example.com', (email, c) => {
340
- * console.log('From:', email.fromEmail());
341
- * console.log('Subject:', email.subject());
342
- * return c.text('Email received');
343
- * });
344
- *
345
- * // SMS routing
346
- * router.sms({ number: '+1234567890' }, (c) => {
347
- * return c.text('SMS received');
348
- * });
96
+ * // Streaming response
97
+ * router.post('/stream', stream((c) => {
98
+ * return new ReadableStream({
99
+ * start(controller) {
100
+ * controller.enqueue('data\n');
101
+ * controller.close();
102
+ * }
103
+ * });
104
+ * }));
349
105
  *
350
- * // Scheduled cron
351
- * router.cron('0 0 * * *', (c) => {
352
- * console.log('Daily task running');
353
- * return c.text('OK');
354
- * });
106
+ * // Cron job
107
+ * router.post('/daily', cron('0 0 * * *', (c) => {
108
+ * return { status: 'complete' };
109
+ * }));
355
110
  * ```
356
111
  */
357
112
  export const createRouter = <E extends Env = Env, S extends Schema = Schema>(): Hono<E, S> => {
@@ -405,314 +160,72 @@ export const createRouter = <E extends Env = Env, S extends Schema = Schema>():
405
160
  };
406
161
  }
407
162
 
408
- // shim in special routes
409
- _router.email = (address: string, ...args: any[]) => {
410
- let middleware: MiddlewareHandler | undefined;
411
- let handler: EmailHandler;
412
-
413
- if (args.length === 1) {
414
- handler = args[0];
415
- } else {
416
- middleware = args[0];
417
- handler = args[1];
418
- }
419
-
420
- const id = hash(address);
421
- const path = `/${id}`;
422
- // registerEmailHandler(address)
423
- const wrapper = async (c: Context): Promise<Response> => {
424
- const contentType = (c.req.header('content-type') || '').trim().toLowerCase();
425
- if (!contentType.includes('message/rfc822')) {
426
- return c.text('Bad Request: Content-Type must be message/rfc822', 400);
427
- }
428
-
429
- const arrayBuffer = await c.req.arrayBuffer();
430
- const buffer = Buffer.from(arrayBuffer);
431
-
432
- const email = await parseEmail(buffer);
433
-
434
- let result = handler(email, c);
435
- if (result instanceof Promise) result = await result;
436
-
437
- if (result === undefined) {
438
- return c.text('OK', 200);
439
- }
440
- return returnResponse(c, result);
441
- };
442
-
443
- if (middleware) {
444
- return router.post(path, middleware, wrapper);
445
- } else {
446
- return router.post(path, wrapper);
447
- }
163
+ // Deprecated stubs that throw errors with migration instructions
164
+ _router.websocket = (path: string, ..._args: any[]) => {
165
+ throw new Error(
166
+ `router.websocket() is deprecated and has been removed.\n\n` +
167
+ `Migration: Use the websocket middleware instead:\n\n` +
168
+ ` import { createRouter, websocket } from '@agentuity/runtime';\n\n` +
169
+ ` const router = createRouter();\n\n` +
170
+ ` // Before (deprecated):\n` +
171
+ ` // router.websocket('${path}', (c) => (ws) => { ... });\n\n` +
172
+ ` // After:\n` +
173
+ ` router.get('${path}', websocket((c, ws) => {\n` +
174
+ ` ws.onMessage((event) => {\n` +
175
+ ` ws.send('Echo: ' + event.data);\n` +
176
+ ` });\n` +
177
+ ` }));`
178
+ );
448
179
  };
449
180
 
450
- _router.sms = ({ number }: { number: string }, handler: AgentHandler) => {
451
- const id = hash(number);
452
- const path = `/${id}`;
453
- // registerSMSHandler(number)
454
- const wrapper = async (c: Context): Promise<Response> => {
455
- let result = handler(c);
456
- if (result instanceof Promise) result = await result;
457
- return returnResponse(c, result);
458
- };
459
- return router.post(path, wrapper);
181
+ _router.sse = (path: string, ..._args: any[]) => {
182
+ throw new Error(
183
+ `router.sse() is deprecated and has been removed.\n\n` +
184
+ `Migration: Use the sse middleware instead:\n\n` +
185
+ ` import { createRouter, sse } from '@agentuity/runtime';\n\n` +
186
+ ` const router = createRouter();\n\n` +
187
+ ` // Before (deprecated):\n` +
188
+ ` // router.sse('${path}', (c) => async (stream) => { ... });\n\n` +
189
+ ` // After:\n` +
190
+ ` router.get('${path}', sse((c, stream) => {\n` +
191
+ ` stream.writeSSE({ data: 'Hello', event: 'message' });\n` +
192
+ ` }));`
193
+ );
460
194
  };
461
195
 
462
- _router.cron = (schedule: string, handler: AgentHandler) => {
463
- const id = hash(schedule);
464
- const path = `/${id}`;
465
- // registerCronHandler(schedule)
466
- const wrapper = async (c: Context): Promise<Response> => {
467
- let result = handler(c);
468
- if (result instanceof Promise) result = await result;
469
- return returnResponse(c, result);
470
- };
471
- return router.post(path, wrapper);
196
+ _router.stream = (path: string, ..._args: any[]) => {
197
+ throw new Error(
198
+ `router.stream() is deprecated and has been removed.\n\n` +
199
+ `Migration: Use the stream middleware instead:\n\n` +
200
+ ` import { createRouter, stream } from '@agentuity/runtime';\n\n` +
201
+ ` const router = createRouter();\n\n` +
202
+ ` // Before (deprecated):\n` +
203
+ ` // router.stream('${path}', (c) => new ReadableStream({ ... }));\n\n` +
204
+ ` // After:\n` +
205
+ ` router.post('${path}', stream((c) => {\n` +
206
+ ` return new ReadableStream({\n` +
207
+ ` start(controller) {\n` +
208
+ ` controller.enqueue('data\\n');\n` +
209
+ ` controller.close();\n` +
210
+ ` }\n` +
211
+ ` });\n` +
212
+ ` }));`
213
+ );
472
214
  };
473
215
 
474
- _router.stream = (path: string, ...args: any[]) => {
475
- let middleware: MiddlewareHandler | undefined;
476
- let handler: StreamHandler;
477
-
478
- if (args.length === 1) {
479
- handler = args[0];
480
- } else {
481
- middleware = args[0];
482
- handler = args[1];
483
- }
484
-
485
- const wrapper = (c: Context) => {
486
- // Capture the AgentContext from the request
487
- const asyncLocalStorage = getAgentAsyncLocalStorage();
488
- const capturedContext = asyncLocalStorage.getStore();
489
-
490
- // Set Content-Type header for streaming response detection by clients
491
- c.header('Content-Type', 'application/octet-stream');
492
-
493
- return honoStream(c, async (s: any) => {
494
- const runInContext = async () => {
495
- try {
496
- let streamResult = handler(c);
497
- if (streamResult instanceof Promise) streamResult = await streamResult;
498
- await s.pipe(streamResult);
499
- } catch (err) {
500
- c.var.logger.error('Stream error:', err);
501
- throw err;
502
- }
503
- };
504
-
505
- if (capturedContext) {
506
- await asyncLocalStorage.run(capturedContext, runInContext);
507
- } else {
508
- await runInContext();
509
- }
510
- });
511
- };
512
-
513
- // Use POST for stream routes - allows accepting request body
514
- // (validators will handle input validation if present)
515
- if (middleware) {
516
- return router.post(path, middleware, wrapper);
517
- } else {
518
- return router.post(path, wrapper);
519
- }
520
- };
521
-
522
- _router.websocket = (path: string, ...args: any[]) => {
523
- let middleware: MiddlewareHandler | undefined;
524
- let handler: WebSocketHandler;
525
-
526
- if (args.length === 1) {
527
- handler = args[0];
528
- } else {
529
- middleware = args[0];
530
- handler = args[1];
531
- }
532
-
533
- // Use upgradeWebSocket directly from hono/bun
534
- const wrapper = upgradeWebSocket((c: Context) => {
535
- let openHandler: ((event: any) => void | Promise<void>) | undefined;
536
- let messageHandler: ((event: any) => void | Promise<void>) | undefined;
537
- let closeHandler: ((event: any) => void | Promise<void>) | undefined;
538
- let initialized = false;
539
-
540
- // Capture the AgentContext from the upgrade request
541
- const asyncLocalStorage = getAgentAsyncLocalStorage();
542
- const capturedContext = asyncLocalStorage.getStore();
543
-
544
- const wsConnection: WebSocketConnection = {
545
- onOpen: (h) => {
546
- openHandler = h;
547
- },
548
- onMessage: (h) => {
549
- messageHandler = h;
550
- },
551
- onClose: (h) => {
552
- closeHandler = h;
553
- },
554
- send: (_data: string | ArrayBuffer | Uint8Array) => {
555
- // This will be bound to the actual ws in the handlers
556
- },
557
- };
558
-
559
- const setupResult = handler(c);
560
- const setupFn = typeof setupResult === 'function' ? setupResult : null;
561
-
562
- // Call setup IMMEDIATELY during upgrade, not in onOpen
563
- // This allows the user's code to register handlers before events fire
564
- if (setupFn) {
565
- if (capturedContext) {
566
- asyncLocalStorage.run(capturedContext, () => setupFn(wsConnection));
567
- } else {
568
- setupFn(wsConnection);
569
- }
570
- initialized = true;
571
- }
572
-
573
- return {
574
- onOpen: async (event: any, ws: any) => {
575
- try {
576
- // Bind the real ws.send now that we have the actual websocket
577
- wsConnection.send = (data) => ws.send(data);
578
-
579
- if (openHandler) {
580
- // Run handler in captured context
581
- const handler = openHandler;
582
- if (capturedContext) {
583
- await asyncLocalStorage.run(capturedContext, () => handler(event));
584
- } else {
585
- await handler(event);
586
- }
587
- }
588
- } catch (err) {
589
- c.var.logger?.error('WebSocket onOpen error:', err);
590
- throw err;
591
- }
592
- },
593
- onMessage: async (event: any, ws: any) => {
594
- try {
595
- // Lazy initialization fallback (shouldn't normally happen)
596
- if (!initialized && setupFn) {
597
- wsConnection.send = (data) => ws.send(data);
598
- if (capturedContext) {
599
- await asyncLocalStorage.run(capturedContext, async () => {
600
- const result = setupFn(wsConnection);
601
- if (result instanceof Promise) await result;
602
- });
603
- } else {
604
- const result = setupFn(wsConnection);
605
- if (result instanceof Promise) await result;
606
- }
607
- initialized = true;
608
- }
609
- if (messageHandler) {
610
- // Run handler in captured context
611
- const handler = messageHandler;
612
- if (capturedContext) {
613
- await asyncLocalStorage.run(capturedContext, () => handler(event));
614
- } else {
615
- await handler(event);
616
- }
617
- }
618
- } catch (err) {
619
- c.var.logger?.error('WebSocket onMessage error:', err);
620
- throw err;
621
- }
622
- },
623
- onClose: async (event: any, _ws: any) => {
624
- try {
625
- if (closeHandler) {
626
- // Run handler in captured context
627
- const handler = closeHandler;
628
- if (capturedContext) {
629
- await asyncLocalStorage.run(capturedContext, () => handler(event));
630
- } else {
631
- await handler(event);
632
- }
633
- }
634
- } catch (err) {
635
- c.var.logger?.error('WebSocket onClose error:', err);
636
- }
637
- },
638
- };
639
- });
640
-
641
- // wrapper is what upgradeWebSocket(...) returned. Force arity=2 so our get shim
642
- // recognizes it as middleware and does not wrap/convert undefined -> 200.
643
- const wsMiddleware: MiddlewareHandler = (c, next) =>
644
- (wrapper as unknown as MiddlewareHandler)(c, next);
645
-
646
- if (middleware) {
647
- // Compose into a single middleware to avoid the 3-arg route which treats the
648
- // second function as a handler and wraps it.
649
- const composed: MiddlewareHandler = async (c, next) => {
650
- return middleware(c, async () => {
651
- await wsMiddleware(c, next);
652
- });
653
- };
654
- return router.get(path, composed);
655
- } else {
656
- return router.get(path, wsMiddleware);
657
- }
658
- };
659
-
660
- _router.sse = (path: string, ...args: any[]) => {
661
- let middleware: MiddlewareHandler | undefined;
662
- let handler: SSEHandler;
663
-
664
- if (args.length === 1) {
665
- handler = args[0];
666
- } else {
667
- middleware = args[0];
668
- handler = args[1];
669
- }
670
-
671
- const wrapper = (c: Context) => {
672
- // Capture the AgentContext from the request
673
- const asyncLocalStorage = getAgentAsyncLocalStorage();
674
- const capturedContext = asyncLocalStorage.getStore();
675
-
676
- return honoStreamSSE(c, async (stream: any) => {
677
- // Wrap the stream to intercept write() calls
678
- const wrappedStream = {
679
- ...stream,
680
- write: async (data: any) => {
681
- // Convert simple write to writeSSE format
682
- if (
683
- typeof data === 'string' ||
684
- typeof data === 'number' ||
685
- typeof data === 'boolean'
686
- ) {
687
- return stream.writeSSE({ data: String(data) });
688
- } else if (typeof data === 'object' && data !== null) {
689
- // If it's already an SSE message object, pass it through
690
- return stream.writeSSE(data);
691
- }
692
- return stream.writeSSE({ data: String(data) });
693
- },
694
- writeSSE: stream.writeSSE.bind(stream),
695
- onAbort: stream.onAbort.bind(stream),
696
- close: stream.close?.bind(stream),
697
- };
698
-
699
- const runInContext = async () => {
700
- await handler(c)(wrappedStream);
701
- };
702
-
703
- if (capturedContext) {
704
- await asyncLocalStorage.run(capturedContext, runInContext);
705
- } else {
706
- await runInContext();
707
- }
708
- });
709
- };
710
-
711
- if (middleware) {
712
- return router.get(path, middleware, wrapper);
713
- } else {
714
- return router.get(path, wrapper);
715
- }
216
+ _router.cron = (schedule: string, ..._args: any[]) => {
217
+ throw new Error(
218
+ `router.cron() is deprecated and has been removed.\n\n` +
219
+ `Migration: Use the cron middleware instead:\n\n` +
220
+ ` import { createRouter, cron } from '@agentuity/runtime';\n\n` +
221
+ ` const router = createRouter();\n\n` +
222
+ ` // Before (deprecated):\n` +
223
+ ` // router.cron('${schedule}', (c) => { ... });\n\n` +
224
+ ` // After:\n` +
225
+ ` router.post('/your-cron-path', cron('${schedule}', (c) => {\n` +
226
+ ` return { status: 'complete' };\n` +
227
+ ` }));`
228
+ );
716
229
  };
717
230
 
718
231
  return router;