@agentuity/runtime 0.0.94 → 0.0.96

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 (72) hide show
  1. package/AGENTS.md +3 -1
  2. package/dist/_events.d.ts +64 -0
  3. package/dist/_events.d.ts.map +1 -0
  4. package/dist/_events.js +92 -0
  5. package/dist/_events.js.map +1 -0
  6. package/dist/_idle.d.ts +1 -1
  7. package/dist/_idle.d.ts.map +1 -1
  8. package/dist/_idle.js +2 -16
  9. package/dist/_idle.js.map +1 -1
  10. package/dist/_server.d.ts +30 -13
  11. package/dist/_server.d.ts.map +1 -1
  12. package/dist/_server.js +39 -572
  13. package/dist/_server.js.map +1 -1
  14. package/dist/_services.d.ts.map +1 -1
  15. package/dist/_services.js +4 -2
  16. package/dist/_services.js.map +1 -1
  17. package/dist/_standalone.d.ts.map +1 -1
  18. package/dist/_standalone.js +2 -1
  19. package/dist/_standalone.js.map +1 -1
  20. package/dist/agent.d.ts.map +1 -1
  21. package/dist/agent.js +13 -17
  22. package/dist/agent.js.map +1 -1
  23. package/dist/app.d.ts +58 -171
  24. package/dist/app.d.ts.map +1 -1
  25. package/dist/app.js +119 -218
  26. package/dist/app.js.map +1 -1
  27. package/dist/index.d.ts +11 -2
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +18 -3
  30. package/dist/index.js.map +1 -1
  31. package/dist/middleware.d.ts +29 -0
  32. package/dist/middleware.d.ts.map +1 -0
  33. package/dist/middleware.js +200 -0
  34. package/dist/middleware.js.map +1 -0
  35. package/dist/router.d.ts.map +1 -1
  36. package/dist/router.js +5 -2
  37. package/dist/router.js.map +1 -1
  38. package/dist/services/local/vector.d.ts.map +1 -1
  39. package/dist/services/local/vector.js +3 -2
  40. package/dist/services/local/vector.js.map +1 -1
  41. package/dist/services/thread/local.d.ts +20 -0
  42. package/dist/services/thread/local.d.ts.map +1 -0
  43. package/dist/services/thread/local.js +76 -0
  44. package/dist/services/thread/local.js.map +1 -0
  45. package/dist/session.d.ts +60 -8
  46. package/dist/session.d.ts.map +1 -1
  47. package/dist/session.js +186 -54
  48. package/dist/session.js.map +1 -1
  49. package/dist/web.d.ts +8 -0
  50. package/dist/web.d.ts.map +1 -0
  51. package/dist/web.js +66 -0
  52. package/dist/web.js.map +1 -0
  53. package/dist/workbench.d.ts +3 -0
  54. package/dist/workbench.d.ts.map +1 -1
  55. package/dist/workbench.js +300 -31
  56. package/dist/workbench.js.map +1 -1
  57. package/package.json +10 -10
  58. package/src/_events.ts +142 -0
  59. package/src/_idle.ts +2 -18
  60. package/src/_server.ts +48 -681
  61. package/src/_services.ts +4 -2
  62. package/src/_standalone.ts +2 -1
  63. package/src/agent.ts +11 -14
  64. package/src/app.ts +164 -246
  65. package/src/index.ts +42 -4
  66. package/src/middleware.ts +252 -0
  67. package/src/router.ts +6 -2
  68. package/src/services/local/vector.ts +3 -2
  69. package/src/services/thread/local.ts +106 -0
  70. package/src/session.ts +238 -59
  71. package/src/web.ts +75 -0
  72. package/src/workbench.ts +367 -30
package/src/workbench.ts CHANGED
@@ -5,6 +5,9 @@ import { getAgents, createAgentMiddleware } from './agent';
5
5
  import { createRouter } from './router';
6
6
  import type { WebSocketConnection } from './router';
7
7
  import { privateContext } from './_server';
8
+ import { getThreadProvider } from './_services';
9
+ import { readFileSync, existsSync } from 'node:fs';
10
+ import { join } from 'node:path';
8
11
 
9
12
  export const createWorkbenchExecutionRoute = (): Handler => {
10
13
  const authHeader = process.env.AGENTUITY_WORKBENCH_APIKEY
@@ -28,6 +31,12 @@ export const createWorkbenchExecutionRoute = (): Handler => {
28
31
  }
29
32
  }
30
33
 
34
+ // Content-type validation
35
+ const contentType = ctx.req.header('Content-Type');
36
+ if (!contentType || !contentType.includes('application/json')) {
37
+ return ctx.json({ error: 'Content-Type must be application/json' }, { status: 400 });
38
+ }
39
+
31
40
  try {
32
41
  let agentId: string;
33
42
  let input: unknown;
@@ -39,20 +48,25 @@ export const createWorkbenchExecutionRoute = (): Handler => {
39
48
  return ctx.json({ error: 'Invalid JSON in request body' }, { status: 400 });
40
49
  }
41
50
 
42
- // Get agents registry and find the agent
43
- const allAgents = getAgents();
51
+ // Read metadata to find agent name by agentId
52
+ const metadataPath = join(process.cwd(), '.agentuity', 'agentuity.metadata.json');
53
+ if (!existsSync(metadataPath)) {
54
+ return ctx.json({ error: 'Metadata file not found' }, { status: 500 });
55
+ }
44
56
 
45
- let agentObj;
46
- let agentName;
57
+ const fileContent = readFileSync(metadataPath, 'utf-8');
58
+ const metadata = JSON.parse(fileContent);
59
+ const agentMeta = metadata.agents?.find((a: { agentId: string }) => a.agentId === agentId);
47
60
 
48
- for (const [name, agent] of allAgents) {
49
- if (agent.metadata.agentId === agentId) {
50
- agentObj = agent;
51
- agentName = name;
52
- break;
53
- }
61
+ if (!agentMeta) {
62
+ return ctx.text('Agent not found', { status: 404 });
54
63
  }
55
64
 
65
+ // Get runtime agent by name
66
+ const allAgents = getAgents();
67
+ const agentName = agentMeta.name;
68
+ const agentObj = allAgents.get(agentName);
69
+
56
70
  if (!agentObj || !agentName) {
57
71
  return ctx.text('Agent not found', { status: 404 });
58
72
  }
@@ -76,6 +90,32 @@ export const createWorkbenchExecutionRoute = (): Handler => {
76
90
  result = await (agentObj as any).handler();
77
91
  }
78
92
 
93
+ // Store input and output in thread state, keyed by agentId
94
+ // This allows multiple agents to have separate message histories in the same thread
95
+ if (ctx.var.thread) {
96
+ const agentMessagesKey = `messages_${agentId}`;
97
+ const existingMessages = ctx.var.thread.state.get(agentMessagesKey);
98
+ const messages = (existingMessages as unknown[] | undefined) || [];
99
+
100
+ messages.push({ type: 'input', data: input });
101
+
102
+ if (result !== undefined && result !== null) {
103
+ messages.push({ type: 'output', data: result });
104
+ }
105
+
106
+ ctx.var.thread.state.set(agentMessagesKey, messages);
107
+
108
+ // Manually save the thread to ensure state persists
109
+ try {
110
+ const threadProvider = getThreadProvider();
111
+ await threadProvider.save(ctx.var.thread);
112
+ } catch {
113
+ ctx.var.logger?.warn('Failed to save thread state');
114
+ }
115
+ } else {
116
+ ctx.var.logger?.warn('Thread not available in workbench execution route');
117
+ }
118
+
79
119
  // Handle cases where result might be undefined/null
80
120
  if (result === undefined || result === null) {
81
121
  return ctx.json({ success: true, result: null });
@@ -94,6 +134,97 @@ export const createWorkbenchExecutionRoute = (): Handler => {
94
134
  };
95
135
  };
96
136
 
137
+ export const createWorkbenchClearStateRoute = (): Handler => {
138
+ const authHeader = process.env.AGENTUITY_WORKBENCH_APIKEY
139
+ ? `Bearer ${process.env.AGENTUITY_WORKBENCH_APIKEY}`
140
+ : undefined;
141
+ return async (ctx: Context) => {
142
+ // Authentication check
143
+ if (authHeader) {
144
+ try {
145
+ const authValue = ctx.req.header('Authorization');
146
+ if (
147
+ !authValue ||
148
+ !timingSafeEqual(Buffer.from(authValue, 'utf-8'), Buffer.from(authHeader, 'utf-8'))
149
+ ) {
150
+ return ctx.text('Unauthorized', { status: 401 });
151
+ }
152
+ } catch {
153
+ // timing safe equals will throw if the input/output lengths are mismatched
154
+ // so we treat all exceptions as invalid
155
+ return ctx.text('Unauthorized', { status: 401 });
156
+ }
157
+ }
158
+
159
+ const agentId = ctx.req.query('agentId');
160
+
161
+ if (!agentId) {
162
+ return ctx.json({ error: 'agentId query parameter is required' }, { status: 400 });
163
+ }
164
+
165
+ if (!ctx.var.thread) {
166
+ return ctx.json({ error: 'Thread not available' }, { status: 404 });
167
+ }
168
+
169
+ const agentMessagesKey = `messages_${agentId}`;
170
+
171
+ // Remove the messages for this agent
172
+ ctx.var.thread.state.delete(agentMessagesKey);
173
+
174
+ // Save the thread to persist the cleared state
175
+ try {
176
+ const threadProvider = getThreadProvider();
177
+ await threadProvider.save(ctx.var.thread);
178
+ } catch {
179
+ return ctx.json({ error: 'Failed to save thread state' }, { status: 500 });
180
+ }
181
+
182
+ return ctx.json({ success: true, message: `State cleared for agent ${agentId}` });
183
+ };
184
+ };
185
+
186
+ export const createWorkbenchStateRoute = (): Handler => {
187
+ const authHeader = process.env.AGENTUITY_WORKBENCH_APIKEY
188
+ ? `Bearer ${process.env.AGENTUITY_WORKBENCH_APIKEY}`
189
+ : undefined;
190
+ return async (ctx: Context) => {
191
+ // Authentication check
192
+ if (authHeader) {
193
+ try {
194
+ const authValue = ctx.req.header('Authorization');
195
+ if (
196
+ !authValue ||
197
+ !timingSafeEqual(Buffer.from(authValue, 'utf-8'), Buffer.from(authHeader, 'utf-8'))
198
+ ) {
199
+ return ctx.text('Unauthorized', { status: 401 });
200
+ }
201
+ } catch {
202
+ // timing safe equals will throw if the input/output lengths are mismatched
203
+ // so we treat all exceptions as invalid
204
+ return ctx.text('Unauthorized', { status: 401 });
205
+ }
206
+ }
207
+
208
+ const agentId = ctx.req.query('agentId');
209
+ if (!agentId) {
210
+ return ctx.json({ error: 'agentId query parameter is required' }, { status: 400 });
211
+ }
212
+
213
+ if (!ctx.var.thread) {
214
+ return ctx.json({ error: 'Thread not available' }, { status: 404 });
215
+ }
216
+
217
+ const agentMessagesKey = `messages_${agentId}`;
218
+ const messages = ctx.var.thread.state.get(agentMessagesKey);
219
+
220
+ return ctx.json({
221
+ threadId: ctx.var.thread.id,
222
+ agentId,
223
+ messages: Array.isArray(messages) ? messages : [],
224
+ });
225
+ };
226
+ };
227
+
97
228
  /**
98
229
  * Creates a workbench router with proper agent middleware for execution routes
99
230
  */
@@ -130,15 +261,173 @@ export const createWorkbenchRouter = () => {
130
261
  // Add workbench routes
131
262
  router.websocket('/_agentuity/workbench/ws', createWorkbenchWebsocketRoute());
132
263
  router.get('/_agentuity/workbench/metadata.json', createWorkbenchMetadataRoute());
264
+ router.get('/_agentuity/workbench/sample', createWorkbenchSampleRoute());
265
+ router.get('/_agentuity/workbench/state', createWorkbenchStateRoute());
266
+ router.delete('/_agentuity/workbench/state', createWorkbenchClearStateRoute());
133
267
  router.post('/_agentuity/workbench/execute', createWorkbenchExecutionRoute());
134
268
  return router;
135
269
  };
136
270
 
271
+ export const createWorkbenchSampleRoute = (): Handler => {
272
+ const authHeader = process.env.AGENTUITY_WORKBENCH_APIKEY
273
+ ? `Bearer ${process.env.AGENTUITY_WORKBENCH_APIKEY}`
274
+ : undefined;
275
+ return async (ctx: Context) => {
276
+ // Authentication check
277
+ if (authHeader) {
278
+ try {
279
+ const authValue = ctx.req.header('Authorization');
280
+ if (
281
+ !authValue ||
282
+ !timingSafeEqual(Buffer.from(authValue, 'utf-8'), Buffer.from(authHeader, 'utf-8'))
283
+ ) {
284
+ return ctx.text('Unauthorized', { status: 401 });
285
+ }
286
+ } catch {
287
+ return ctx.text('Unauthorized', { status: 401 });
288
+ }
289
+ }
290
+
291
+ try {
292
+ const agentId = ctx.req.query('agentId');
293
+ if (!agentId) {
294
+ return ctx.json({ error: 'Missing agentId query parameter' }, { status: 400 });
295
+ }
296
+
297
+ // Read metadata to find agent name by agentId
298
+ const metadataPath = join(process.cwd(), '.agentuity', 'agentuity.metadata.json');
299
+ if (!existsSync(metadataPath)) {
300
+ return ctx.json({ error: 'Metadata file not found' }, { status: 500 });
301
+ }
302
+
303
+ const fileContent = readFileSync(metadataPath, 'utf-8');
304
+ const metadata = JSON.parse(fileContent);
305
+ const agentMeta = metadata.agents?.find((a: { agentId: string }) => a.agentId === agentId);
306
+
307
+ if (!agentMeta) {
308
+ return ctx.text('Agent not found', { status: 404 });
309
+ }
310
+
311
+ // Get runtime agent by name
312
+ const allAgents = getAgents();
313
+ const agentObj = allAgents.get(agentMeta.name);
314
+
315
+ if (!agentObj) {
316
+ return ctx.text('Agent not found', { status: 404 });
317
+ }
318
+
319
+ // Check if agent has input schema
320
+ if (!agentObj.inputSchema) {
321
+ return ctx.json({ error: 'Agent has no input schema' }, { status: 400 });
322
+ }
323
+
324
+ // Convert schema to JSON Schema
325
+ const jsonSchema = toJSONSchema(agentObj.inputSchema);
326
+
327
+ // Get Agentuity SDK key and gateway URL
328
+ const sdkKey = process.env.AGENTUITY_SDK_KEY;
329
+ const gatewayUrl =
330
+ process.env.AGENTUITY_AIGATEWAY_URL ||
331
+ process.env.AGENTUITY_TRANSPORT_URL ||
332
+ (sdkKey ? 'https://agentuity.ai' : '');
333
+
334
+ if (!sdkKey || !gatewayUrl) {
335
+ return ctx.json(
336
+ {
337
+ error: 'AGENTUITY_SDK_KEY and gateway URL must be configured',
338
+ message:
339
+ 'Set AGENTUITY_SDK_KEY and either AGENTUITY_AIGATEWAY_URL, AGENTUITY_TRANSPORT_URL, or use https://agentuity.ai',
340
+ },
341
+ { status: 500 }
342
+ );
343
+ }
344
+
345
+ // Generate sample using Groq via Agentuity Gateway
346
+ const prompt = `Generate a realistic sample data object that matches this JSON schema. Return only valid JSON, no markdown code blocks or explanations.
347
+
348
+ JSON Schema:
349
+ ${JSON.stringify(jsonSchema, null, 2)}
350
+
351
+ Return a JSON object that matches this schema with realistic values.`;
352
+
353
+ const gatewayEndpoint = `${gatewayUrl}/gateway/groq/openai/v1/chat/completions`;
354
+ const groqResponse = await fetch(gatewayEndpoint, {
355
+ method: 'POST',
356
+ headers: {
357
+ Authorization: `Bearer ${sdkKey}`,
358
+ 'Content-Type': 'application/json',
359
+ },
360
+ body: JSON.stringify({
361
+ model: 'llama-3.3-70b-versatile',
362
+ messages: [
363
+ {
364
+ role: 'user',
365
+ content: prompt,
366
+ },
367
+ ],
368
+ }),
369
+ });
370
+
371
+ if (!groqResponse.ok) {
372
+ const errorText = await groqResponse.text();
373
+ return ctx.json(
374
+ {
375
+ error: 'Groq API request failed',
376
+ message: `Status ${groqResponse.status}: ${errorText}`,
377
+ },
378
+ { status: 500 }
379
+ );
380
+ }
381
+
382
+ const groqData = (await groqResponse.json()) as {
383
+ choices?: Array<{ message?: { content?: string } }>;
384
+ };
385
+ const text = groqData.choices?.[0]?.message?.content;
386
+ if (!text) {
387
+ return ctx.json(
388
+ { error: 'Invalid response from Groq API', response: groqData },
389
+ { status: 500 }
390
+ );
391
+ }
392
+
393
+ // Parse the JSON response
394
+ let sample: unknown;
395
+ try {
396
+ // Remove markdown code blocks if present
397
+ const cleanedText = text
398
+ .trim()
399
+ .replace(/^```json\s*|\s*```$/g, '')
400
+ .replace(/^```\s*|\s*```$/g, '');
401
+ sample = JSON.parse(cleanedText);
402
+ } catch (parseError) {
403
+ return ctx.json(
404
+ {
405
+ error: 'Failed to parse generated JSON',
406
+ message: parseError instanceof Error ? parseError.message : String(parseError),
407
+ generatedText: text,
408
+ },
409
+ { status: 500 }
410
+ );
411
+ }
412
+
413
+ return ctx.json(sample);
414
+ } catch (error) {
415
+ return ctx.json(
416
+ {
417
+ error: 'Internal server error',
418
+ message: error instanceof Error ? error.message : String(error),
419
+ },
420
+ { status: 500 }
421
+ );
422
+ }
423
+ };
424
+ };
425
+
137
426
  export const createWorkbenchMetadataRoute = (): Handler => {
138
427
  const authHeader = process.env.AGENTUITY_WORKBENCH_APIKEY
139
428
  ? `Bearer ${process.env.AGENTUITY_WORKBENCH_APIKEY}`
140
429
  : undefined;
141
- const agents = getAgents();
430
+
142
431
  return async (ctx) => {
143
432
  if (authHeader) {
144
433
  try {
@@ -155,27 +444,75 @@ export const createWorkbenchMetadataRoute = (): Handler => {
155
444
  return ctx.text('Unauthorized', { status: 401 });
156
445
  }
157
446
  }
158
- const schemas: { agents: Record<string, unknown> } = { agents: {} };
159
- for (const [, agent] of agents) {
160
- schemas.agents[agent.metadata.id] = {
161
- schema: {
162
- input: agent.inputSchema
163
- ? {
164
- code: agent.metadata.inputSchemaCode || undefined,
165
- json: toJSONSchema(agent.inputSchema),
166
- }
167
- : undefined,
168
- output: agent.outputSchema
169
- ? {
170
- code: agent.metadata.outputSchemaCode || undefined,
171
- json: toJSONSchema(agent.outputSchema),
172
- }
173
- : undefined,
447
+
448
+ // Read metadata from agentuity.metadata.json file
449
+ const metadataPath = join(process.cwd(), '.agentuity', 'agentuity.metadata.json');
450
+
451
+ if (!existsSync(metadataPath)) {
452
+ return ctx.json(
453
+ { error: 'Metadata file not found. Run build to generate metadata.' },
454
+ { status: 500 }
455
+ );
456
+ }
457
+
458
+ try {
459
+ const fileContent = readFileSync(metadataPath, 'utf-8');
460
+ const metadata = JSON.parse(fileContent);
461
+
462
+ // Get runtime agents for JSON schema generation
463
+ const agents = getAgents();
464
+ const agentsByName = new Map();
465
+ for (const [name, agent] of agents) {
466
+ agentsByName.set(name, agent);
467
+ }
468
+
469
+ // Transform metadata structure to workbench format
470
+ const schemas: { agents: Record<string, unknown> } = { agents: {} };
471
+
472
+ for (const agent of metadata.agents || []) {
473
+ // Try to find runtime agent by name to get JSON schemas
474
+ const runtimeAgent = agentsByName.get(agent.name);
475
+
476
+ schemas.agents[agent.id] = {
477
+ schema: {
478
+ input: agent.schema?.input
479
+ ? {
480
+ code: agent.schema.input,
481
+ json: runtimeAgent?.inputSchema
482
+ ? toJSONSchema(runtimeAgent.inputSchema)
483
+ : undefined,
484
+ }
485
+ : undefined,
486
+ output: agent.schema?.output
487
+ ? {
488
+ code: agent.schema.output,
489
+ json: runtimeAgent?.outputSchema
490
+ ? toJSONSchema(runtimeAgent.outputSchema)
491
+ : undefined,
492
+ }
493
+ : undefined,
494
+ },
495
+ metadata: {
496
+ id: agent.id,
497
+ agentId: agent.agentId,
498
+ name: agent.name,
499
+ description: agent.description,
500
+ filename: agent.filename,
501
+ version: agent.version,
502
+ },
503
+ };
504
+ }
505
+
506
+ return ctx.json(schemas);
507
+ } catch (error) {
508
+ return ctx.json(
509
+ {
510
+ error: 'Failed to read metadata file',
511
+ message: error instanceof Error ? error.message : String(error),
174
512
  },
175
- metadata: agent.metadata,
176
- };
513
+ { status: 500 }
514
+ );
177
515
  }
178
- return ctx.json(schemas);
179
516
  };
180
517
  };
181
518