@agentuity/runtime 0.0.59 → 0.0.61

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 (61) hide show
  1. package/dist/_context.d.ts +11 -7
  2. package/dist/_context.d.ts.map +1 -1
  3. package/dist/_context.js +9 -2
  4. package/dist/_context.js.map +1 -1
  5. package/dist/_server.d.ts +4 -2
  6. package/dist/_server.d.ts.map +1 -1
  7. package/dist/_server.js +71 -31
  8. package/dist/_server.js.map +1 -1
  9. package/dist/_services.d.ts +1 -1
  10. package/dist/_services.d.ts.map +1 -1
  11. package/dist/_services.js +4 -2
  12. package/dist/_services.js.map +1 -1
  13. package/dist/_waituntil.d.ts.map +1 -1
  14. package/dist/_waituntil.js +5 -2
  15. package/dist/_waituntil.js.map +1 -1
  16. package/dist/agent.d.ts +647 -19
  17. package/dist/agent.d.ts.map +1 -1
  18. package/dist/agent.js +55 -6
  19. package/dist/agent.js.map +1 -1
  20. package/dist/app.d.ts +205 -28
  21. package/dist/app.d.ts.map +1 -1
  22. package/dist/app.js +181 -13
  23. package/dist/app.js.map +1 -1
  24. package/dist/index.d.ts +41 -2
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +2 -2
  27. package/dist/index.js.map +1 -1
  28. package/dist/io/email.d.ts.map +1 -1
  29. package/dist/io/email.js +11 -3
  30. package/dist/io/email.js.map +1 -1
  31. package/dist/router.d.ts +282 -32
  32. package/dist/router.d.ts.map +1 -1
  33. package/dist/router.js +110 -35
  34. package/dist/router.js.map +1 -1
  35. package/dist/services/evalrun/http.d.ts.map +1 -1
  36. package/dist/services/evalrun/http.js +7 -5
  37. package/dist/services/evalrun/http.js.map +1 -1
  38. package/dist/services/local/_util.d.ts.map +1 -1
  39. package/dist/services/local/_util.js +3 -1
  40. package/dist/services/local/_util.js.map +1 -1
  41. package/dist/services/session/http.d.ts.map +1 -1
  42. package/dist/services/session/http.js +4 -3
  43. package/dist/services/session/http.js.map +1 -1
  44. package/dist/session.d.ts +284 -4
  45. package/dist/session.d.ts.map +1 -1
  46. package/dist/session.js +2 -2
  47. package/dist/session.js.map +1 -1
  48. package/package.json +5 -4
  49. package/src/_context.ts +37 -9
  50. package/src/_server.ts +88 -36
  51. package/src/_services.ts +9 -2
  52. package/src/_waituntil.ts +13 -2
  53. package/src/agent.ts +856 -68
  54. package/src/app.ts +238 -38
  55. package/src/index.ts +42 -2
  56. package/src/io/email.ts +23 -5
  57. package/src/router.ts +359 -83
  58. package/src/services/evalrun/http.ts +15 -4
  59. package/src/services/local/_util.ts +7 -1
  60. package/src/services/session/http.ts +5 -2
  61. package/src/session.ts +297 -4
package/src/session.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  import type { Context } from 'hono';
4
4
  import { getCookie, setCookie } from 'hono/cookie';
5
5
  import { type Env, fireEvent } from './app';
6
+ import type { AppState } from './index';
6
7
 
7
8
  export type ThreadEventName = 'destroyed';
8
9
  export type SessionEventName = 'completed';
@@ -17,44 +18,336 @@ type SessionEventCallback<T extends Session> = (
17
18
  session: T
18
19
  ) => Promise<void> | void;
19
20
 
21
+ /**
22
+ * Represents a conversation thread that persists across multiple sessions.
23
+ * Threads maintain state and can contain multiple request-response sessions.
24
+ *
25
+ * Threads are automatically managed by the runtime and stored in cookies.
26
+ * They expire after 1 hour of inactivity by default.
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * // Access thread in agent handler
31
+ * const agent = createAgent({
32
+ * handler: async (ctx, input) => {
33
+ * // Get thread ID
34
+ * console.log('Thread:', ctx.thread.id);
35
+ *
36
+ * // Store data in thread state (persists across sessions)
37
+ * ctx.thread.state.set('conversationCount',
38
+ * (ctx.thread.state.get('conversationCount') as number || 0) + 1
39
+ * );
40
+ *
41
+ * // Listen for thread destruction
42
+ * ctx.thread.addEventListener('destroyed', (eventName, thread) => {
43
+ * console.log('Thread destroyed:', thread.id);
44
+ * });
45
+ *
46
+ * return 'Response';
47
+ * }
48
+ * });
49
+ * ```
50
+ */
20
51
  export interface Thread {
52
+ /**
53
+ * Unique thread identifier (e.g., "thrd_a1b2c3d4...").
54
+ * Stored in cookie and persists across requests.
55
+ */
21
56
  id: string;
57
+
58
+ /**
59
+ * Thread-scoped state storage that persists across multiple sessions.
60
+ * Use this to maintain conversation history or user preferences.
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * // Store conversation count
65
+ * ctx.thread.state.set('messageCount',
66
+ * (ctx.thread.state.get('messageCount') as number || 0) + 1
67
+ * );
68
+ * ```
69
+ */
22
70
  state: Map<string, unknown>;
71
+
72
+ /**
73
+ * Register an event listener for when the thread is destroyed.
74
+ * Thread is destroyed when it expires or is manually destroyed.
75
+ *
76
+ * @param eventName - Must be 'destroyed'
77
+ * @param callback - Function called when thread is destroyed
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * ctx.thread.addEventListener('destroyed', (eventName, thread) => {
82
+ * console.log('Cleaning up thread:', thread.id);
83
+ * });
84
+ * ```
85
+ */
23
86
  addEventListener(
24
87
  eventName: 'destroyed',
25
88
  callback: (eventName: 'destroyed', thread: Thread) => Promise<void> | void
26
89
  ): void;
90
+
91
+ /**
92
+ * Remove a previously registered 'destroyed' event listener.
93
+ *
94
+ * @param eventName - Must be 'destroyed'
95
+ * @param callback - The callback function to remove
96
+ */
27
97
  removeEventListener(
28
98
  eventName: 'destroyed',
29
99
  callback: (eventName: 'destroyed', thread: Thread) => Promise<void> | void
30
100
  ): void;
101
+
102
+ /**
103
+ * Manually destroy the thread and clean up resources.
104
+ * Fires the 'destroyed' event and removes thread from storage.
105
+ *
106
+ * @example
107
+ * ```typescript
108
+ * // End conversation
109
+ * await ctx.thread.destroy();
110
+ * ```
111
+ */
31
112
  destroy(): Promise<void>;
32
113
  }
33
114
 
115
+ /**
116
+ * Represents a single request-response session within a thread.
117
+ * Sessions are scoped to a single agent execution and its sub-agent calls.
118
+ *
119
+ * Each HTTP request creates a new session with a unique ID, but shares the same thread.
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * const agent = createAgent({
124
+ * handler: async (ctx, input) => {
125
+ * // Get session ID (unique per request)
126
+ * console.log('Session:', ctx.session.id);
127
+ *
128
+ * // Store data in session state (only for this request)
129
+ * ctx.session.state.set('startTime', Date.now());
130
+ *
131
+ * // Access parent thread
132
+ * console.log('Thread:', ctx.session.thread.id);
133
+ *
134
+ * // Listen for session completion
135
+ * ctx.session.addEventListener('completed', (eventName, session) => {
136
+ * const duration = Date.now() - (session.state.get('startTime') as number);
137
+ * console.log(`Session completed in ${duration}ms`);
138
+ * });
139
+ *
140
+ * return 'Response';
141
+ * }
142
+ * });
143
+ * ```
144
+ */
34
145
  export interface Session {
146
+ /**
147
+ * Unique session identifier for this request.
148
+ * Changes with each HTTP request, even within the same thread.
149
+ */
35
150
  id: string;
151
+
152
+ /**
153
+ * The parent thread this session belongs to.
154
+ * Multiple sessions can share the same thread.
155
+ */
36
156
  thread: Thread;
157
+
158
+ /**
159
+ * Session-scoped state storage that only exists for this request.
160
+ * Use this for temporary data that shouldn't persist across requests.
161
+ *
162
+ * @example
163
+ * ```typescript
164
+ * ctx.session.state.set('requestStartTime', Date.now());
165
+ * ```
166
+ */
37
167
  state: Map<string, unknown>;
168
+
169
+ /**
170
+ * Register an event listener for when the session completes.
171
+ * Fired after the agent handler returns and response is sent.
172
+ *
173
+ * @param eventName - Must be 'completed'
174
+ * @param callback - Function called when session completes
175
+ *
176
+ * @example
177
+ * ```typescript
178
+ * ctx.session.addEventListener('completed', (eventName, session) => {
179
+ * console.log('Session finished:', session.id);
180
+ * });
181
+ * ```
182
+ */
38
183
  addEventListener(
39
184
  eventName: 'completed',
40
185
  callback: (eventName: 'completed', session: Session) => Promise<void> | void
41
186
  ): void;
187
+
188
+ /**
189
+ * Remove a previously registered 'completed' event listener.
190
+ *
191
+ * @param eventName - Must be 'completed'
192
+ * @param callback - The callback function to remove
193
+ */
42
194
  removeEventListener(
43
195
  eventName: 'completed',
44
196
  callback: (eventName: 'completed', session: Session) => Promise<void> | void
45
197
  ): void;
46
198
  }
47
199
 
200
+ /**
201
+ * Provider interface for managing thread lifecycle and persistence.
202
+ * Implement this to customize how threads are stored and retrieved.
203
+ *
204
+ * The default implementation (DefaultThreadProvider) stores threads in-memory
205
+ * with cookie-based identification and 1-hour expiration.
206
+ *
207
+ * @example
208
+ * ```typescript
209
+ * class RedisThreadProvider implements ThreadProvider {
210
+ * private redis: Redis;
211
+ *
212
+ * async initialize(appState: AppState): Promise<void> {
213
+ * this.redis = await connectRedis();
214
+ * }
215
+ *
216
+ * async restore(ctx: Context<Env>): Promise<Thread> {
217
+ * const threadId = getCookie(ctx, 'atid') || generateId('thrd');
218
+ * const data = await this.redis.get(`thread:${threadId}`);
219
+ * const thread = new DefaultThread(this, threadId);
220
+ * if (data) {
221
+ * thread.state = new Map(JSON.parse(data));
222
+ * }
223
+ * return thread;
224
+ * }
225
+ *
226
+ * async save(thread: Thread): Promise<void> {
227
+ * await this.redis.setex(
228
+ * `thread:${thread.id}`,
229
+ * 3600,
230
+ * JSON.stringify([...thread.state])
231
+ * );
232
+ * }
233
+ *
234
+ * async destroy(thread: Thread): Promise<void> {
235
+ * await this.redis.del(`thread:${thread.id}`);
236
+ * }
237
+ * }
238
+ *
239
+ * // Use custom provider
240
+ * const app = await createApp({
241
+ * services: {
242
+ * thread: new RedisThreadProvider()
243
+ * }
244
+ * });
245
+ * ```
246
+ */
48
247
  export interface ThreadProvider {
49
- initialize(): Promise<void>;
248
+ /**
249
+ * Initialize the provider when the app starts.
250
+ * Use this to set up connections, start cleanup intervals, etc.
251
+ *
252
+ * @param appState - The app state from createApp setup function
253
+ */
254
+ initialize(appState: AppState): Promise<void>;
255
+
256
+ /**
257
+ * Restore or create a thread from the HTTP request context.
258
+ * Should check cookies for existing thread ID or create a new one.
259
+ *
260
+ * @param ctx - Hono request context
261
+ * @returns The restored or newly created thread
262
+ */
50
263
  restore(ctx: Context<Env>): Promise<Thread>;
264
+
265
+ /**
266
+ * Persist thread state to storage.
267
+ * Called periodically to save thread data.
268
+ *
269
+ * @param thread - The thread to save
270
+ */
51
271
  save(thread: Thread): Promise<void>;
272
+
273
+ /**
274
+ * Destroy a thread and clean up resources.
275
+ * Should fire the 'destroyed' event and remove from storage.
276
+ *
277
+ * @param thread - The thread to destroy
278
+ */
52
279
  destroy(thread: Thread): Promise<void>;
53
280
  }
54
281
 
282
+ /**
283
+ * Provider interface for managing session lifecycle and persistence.
284
+ * Implement this to customize how sessions are stored and retrieved.
285
+ *
286
+ * The default implementation (DefaultSessionProvider) stores sessions in-memory
287
+ * and automatically cleans them up after completion.
288
+ *
289
+ * @example
290
+ * ```typescript
291
+ * class PostgresSessionProvider implements SessionProvider {
292
+ * private db: Database;
293
+ *
294
+ * async initialize(appState: AppState): Promise<void> {
295
+ * this.db = appState.db;
296
+ * }
297
+ *
298
+ * async restore(thread: Thread, sessionId: string): Promise<Session> {
299
+ * const row = await this.db.query(
300
+ * 'SELECT state FROM sessions WHERE id = $1',
301
+ * [sessionId]
302
+ * );
303
+ * const session = new DefaultSession(thread, sessionId);
304
+ * if (row) {
305
+ * session.state = new Map(JSON.parse(row.state));
306
+ * }
307
+ * return session;
308
+ * }
309
+ *
310
+ * async save(session: Session): Promise<void> {
311
+ * await this.db.query(
312
+ * 'INSERT INTO sessions (id, thread_id, state) VALUES ($1, $2, $3)',
313
+ * [session.id, session.thread.id, JSON.stringify([...session.state])]
314
+ * );
315
+ * }
316
+ * }
317
+ *
318
+ * // Use custom provider
319
+ * const app = await createApp({
320
+ * services: {
321
+ * session: new PostgresSessionProvider()
322
+ * }
323
+ * });
324
+ * ```
325
+ */
55
326
  export interface SessionProvider {
56
- initialize(): Promise<void>;
327
+ /**
328
+ * Initialize the provider when the app starts.
329
+ * Use this to set up database connections or other resources.
330
+ *
331
+ * @param appState - The app state from createApp setup function
332
+ */
333
+ initialize(appState: AppState): Promise<void>;
334
+
335
+ /**
336
+ * Restore or create a session for the given thread and session ID.
337
+ * Should load existing session data or create a new session.
338
+ *
339
+ * @param thread - The parent thread for this session
340
+ * @param sessionId - The unique session identifier
341
+ * @returns The restored or newly created session
342
+ */
57
343
  restore(thread: Thread, sessionId: string): Promise<Session>;
344
+
345
+ /**
346
+ * Persist session state and fire completion events.
347
+ * Called after the agent handler completes.
348
+ *
349
+ * @param session - The session to save
350
+ */
58
351
  save(session: Session): Promise<void>;
59
352
  }
60
353
 
@@ -194,7 +487,7 @@ export class DefaultSession implements Session {
194
487
  export class DefaultThreadProvider implements ThreadProvider {
195
488
  private threads = new Map<string, DefaultThread>();
196
489
 
197
- async initialize(): Promise<void> {
490
+ async initialize(_appState: AppState): Promise<void> {
198
491
  setInterval(() => {
199
492
  for (const [, thread] of this.threads) {
200
493
  if (thread.expired()) {
@@ -256,7 +549,7 @@ export class DefaultThreadProvider implements ThreadProvider {
256
549
  export class DefaultSessionProvider implements SessionProvider {
257
550
  private sessions = new Map<string, DefaultSession>();
258
551
 
259
- async initialize(): Promise<void> {
552
+ async initialize(_appState: AppState): Promise<void> {
260
553
  // No initialization needed for in-memory provider
261
554
  }
262
555