@agentuity/runtime 0.0.95 → 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 +2 -0
  54. package/dist/workbench.d.ts.map +1 -1
  55. package/dist/workbench.js +192 -39
  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 +226 -38
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
  */
@@ -131,6 +262,8 @@ export const createWorkbenchRouter = () => {
131
262
  router.websocket('/_agentuity/workbench/ws', createWorkbenchWebsocketRoute());
132
263
  router.get('/_agentuity/workbench/metadata.json', createWorkbenchMetadataRoute());
133
264
  router.get('/_agentuity/workbench/sample', createWorkbenchSampleRoute());
265
+ router.get('/_agentuity/workbench/state', createWorkbenchStateRoute());
266
+ router.delete('/_agentuity/workbench/state', createWorkbenchClearStateRoute());
134
267
  router.post('/_agentuity/workbench/execute', createWorkbenchExecutionRoute());
135
268
  return router;
136
269
  };
@@ -161,17 +294,24 @@ export const createWorkbenchSampleRoute = (): Handler => {
161
294
  return ctx.json({ error: 'Missing agentId query parameter' }, { status: 400 });
162
295
  }
163
296
 
164
- // Get agents registry and find the agent
165
- const allAgents = getAgents();
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
+ }
166
302
 
167
- let agentObj;
168
- for (const [, agent] of allAgents) {
169
- if (agent.metadata.agentId === agentId) {
170
- agentObj = agent;
171
- break;
172
- }
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 });
173
309
  }
174
310
 
311
+ // Get runtime agent by name
312
+ const allAgents = getAgents();
313
+ const agentObj = allAgents.get(agentMeta.name);
314
+
175
315
  if (!agentObj) {
176
316
  return ctx.text('Agent not found', { status: 404 });
177
317
  }
@@ -287,7 +427,7 @@ export const createWorkbenchMetadataRoute = (): Handler => {
287
427
  const authHeader = process.env.AGENTUITY_WORKBENCH_APIKEY
288
428
  ? `Bearer ${process.env.AGENTUITY_WORKBENCH_APIKEY}`
289
429
  : undefined;
290
- const agents = getAgents();
430
+
291
431
  return async (ctx) => {
292
432
  if (authHeader) {
293
433
  try {
@@ -304,27 +444,75 @@ export const createWorkbenchMetadataRoute = (): Handler => {
304
444
  return ctx.text('Unauthorized', { status: 401 });
305
445
  }
306
446
  }
307
- const schemas: { agents: Record<string, unknown> } = { agents: {} };
308
- for (const [, agent] of agents) {
309
- schemas.agents[agent.metadata.id] = {
310
- schema: {
311
- input: agent.inputSchema
312
- ? {
313
- code: agent.metadata.inputSchemaCode || undefined,
314
- json: toJSONSchema(agent.inputSchema),
315
- }
316
- : undefined,
317
- output: agent.outputSchema
318
- ? {
319
- code: agent.metadata.outputSchemaCode || undefined,
320
- json: toJSONSchema(agent.outputSchema),
321
- }
322
- : 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),
323
512
  },
324
- metadata: agent.metadata,
325
- };
513
+ { status: 500 }
514
+ );
326
515
  }
327
- return ctx.json(schemas);
328
516
  };
329
517
  };
330
518