@agentuity/cli 0.0.69 → 0.0.70

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 (153) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +1 -1
  3. package/dist/cmd/ai/capabilities/show.d.ts.map +1 -1
  4. package/dist/cmd/ai/capabilities/show.js +0 -13
  5. package/dist/cmd/ai/capabilities/show.js.map +1 -1
  6. package/dist/cmd/ai/prompt/agent.js +1 -1
  7. package/dist/cmd/ai/prompt/api.js +1 -1
  8. package/dist/cmd/build/ast.d.ts +1 -2
  9. package/dist/cmd/build/ast.d.ts.map +1 -1
  10. package/dist/cmd/build/ast.js +261 -260
  11. package/dist/cmd/build/ast.js.map +1 -1
  12. package/dist/cmd/build/bundler.d.ts +2 -1
  13. package/dist/cmd/build/bundler.d.ts.map +1 -1
  14. package/dist/cmd/build/bundler.js +11 -2
  15. package/dist/cmd/build/bundler.js.map +1 -1
  16. package/dist/cmd/build/index.js +1 -1
  17. package/dist/cmd/build/index.js.map +1 -1
  18. package/dist/cmd/build/plugin.d.ts.map +1 -1
  19. package/dist/cmd/build/plugin.js +152 -414
  20. package/dist/cmd/build/plugin.js.map +1 -1
  21. package/dist/cmd/build/route-registry.d.ts +36 -0
  22. package/dist/cmd/build/route-registry.d.ts.map +1 -0
  23. package/dist/cmd/build/route-registry.js +151 -0
  24. package/dist/cmd/build/route-registry.js.map +1 -0
  25. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  26. package/dist/cmd/cloud/deploy.js +1 -0
  27. package/dist/cmd/cloud/deploy.js.map +1 -1
  28. package/dist/cmd/cloud/index.d.ts.map +1 -1
  29. package/dist/cmd/cloud/index.js +0 -2
  30. package/dist/cmd/cloud/index.js.map +1 -1
  31. package/dist/cmd/dev/index.js +3 -3
  32. package/dist/cmd/dev/index.js.map +1 -1
  33. package/dist/cmd/dev/sync.d.ts.map +1 -1
  34. package/dist/cmd/dev/sync.js +0 -15
  35. package/dist/cmd/dev/sync.js.map +1 -1
  36. package/dist/cmd/dev/templates.d.ts.map +1 -1
  37. package/dist/cmd/dev/templates.js +11 -35
  38. package/dist/cmd/dev/templates.js.map +1 -1
  39. package/dist/cmd/profile/create.d.ts.map +1 -1
  40. package/dist/cmd/profile/create.js +0 -1
  41. package/dist/cmd/profile/create.js.map +1 -1
  42. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  43. package/dist/cmd/project/template-flow.js +64 -53
  44. package/dist/cmd/project/template-flow.js.map +1 -1
  45. package/dist/config.d.ts.map +1 -1
  46. package/dist/config.js +0 -3
  47. package/dist/config.js.map +1 -1
  48. package/dist/tui/box.d.ts +19 -0
  49. package/dist/tui/box.d.ts.map +1 -0
  50. package/dist/tui/box.js +160 -0
  51. package/dist/tui/box.js.map +1 -0
  52. package/dist/tui/colors.d.ts +20 -0
  53. package/dist/tui/colors.d.ts.map +1 -0
  54. package/dist/tui/colors.js +52 -0
  55. package/dist/tui/colors.js.map +1 -0
  56. package/dist/tui/group.d.ts +25 -0
  57. package/dist/tui/group.d.ts.map +1 -0
  58. package/dist/tui/group.js +32 -0
  59. package/dist/tui/group.js.map +1 -0
  60. package/dist/tui/prompt.d.ts +65 -0
  61. package/dist/tui/prompt.d.ts.map +1 -0
  62. package/dist/tui/prompt.js +377 -0
  63. package/dist/tui/prompt.js.map +1 -0
  64. package/dist/tui/symbols.d.ts +32 -0
  65. package/dist/tui/symbols.d.ts.map +1 -0
  66. package/dist/tui/symbols.js +52 -0
  67. package/dist/tui/symbols.js.map +1 -0
  68. package/dist/tui.d.ts +6 -0
  69. package/dist/tui.d.ts.map +1 -1
  70. package/dist/tui.js +6 -0
  71. package/dist/tui.js.map +1 -1
  72. package/dist/types.d.ts +0 -24
  73. package/dist/types.d.ts.map +1 -1
  74. package/dist/types.js +0 -1
  75. package/dist/types.js.map +1 -1
  76. package/package.json +3 -3
  77. package/src/cmd/ai/capabilities/show.ts +0 -13
  78. package/src/cmd/ai/prompt/agent.ts +1 -1
  79. package/src/cmd/ai/prompt/api.ts +1 -1
  80. package/src/cmd/build/ast.ts +364 -334
  81. package/src/cmd/build/bundler.ts +16 -1
  82. package/src/cmd/build/index.ts +1 -1
  83. package/src/cmd/build/plugin.ts +171 -493
  84. package/src/cmd/build/route-registry.ts +198 -0
  85. package/src/cmd/cloud/deploy.ts +1 -0
  86. package/src/cmd/cloud/index.ts +0 -2
  87. package/src/cmd/dev/index.ts +3 -3
  88. package/src/cmd/dev/sync.ts +0 -28
  89. package/src/cmd/dev/templates.ts +11 -35
  90. package/src/cmd/profile/create.ts +0 -1
  91. package/src/cmd/project/template-flow.ts +77 -57
  92. package/src/config.ts +0 -3
  93. package/src/tui/box.ts +202 -0
  94. package/src/tui/colors.ts +55 -0
  95. package/src/tui/group.ts +56 -0
  96. package/src/tui/prompt.ts +506 -0
  97. package/src/tui/symbols.ts +64 -0
  98. package/src/tui.ts +14 -0
  99. package/src/types.ts +0 -1
  100. package/dist/cmd/cloud/objectstore/delete-bucket.d.ts +0 -3
  101. package/dist/cmd/cloud/objectstore/delete-bucket.d.ts.map +0 -1
  102. package/dist/cmd/cloud/objectstore/delete-bucket.js +0 -72
  103. package/dist/cmd/cloud/objectstore/delete-bucket.js.map +0 -1
  104. package/dist/cmd/cloud/objectstore/delete.d.ts +0 -3
  105. package/dist/cmd/cloud/objectstore/delete.d.ts.map +0 -1
  106. package/dist/cmd/cloud/objectstore/delete.js +0 -65
  107. package/dist/cmd/cloud/objectstore/delete.js.map +0 -1
  108. package/dist/cmd/cloud/objectstore/get.d.ts +0 -3
  109. package/dist/cmd/cloud/objectstore/get.d.ts.map +0 -1
  110. package/dist/cmd/cloud/objectstore/get.js +0 -75
  111. package/dist/cmd/cloud/objectstore/get.js.map +0 -1
  112. package/dist/cmd/cloud/objectstore/index.d.ts +0 -3
  113. package/dist/cmd/cloud/objectstore/index.d.ts.map +0 -1
  114. package/dist/cmd/cloud/objectstore/index.js +0 -36
  115. package/dist/cmd/cloud/objectstore/index.js.map +0 -1
  116. package/dist/cmd/cloud/objectstore/list-buckets.d.ts +0 -3
  117. package/dist/cmd/cloud/objectstore/list-buckets.d.ts.map +0 -1
  118. package/dist/cmd/cloud/objectstore/list-buckets.js +0 -46
  119. package/dist/cmd/cloud/objectstore/list-buckets.js.map +0 -1
  120. package/dist/cmd/cloud/objectstore/list-keys.d.ts +0 -3
  121. package/dist/cmd/cloud/objectstore/list-keys.d.ts.map +0 -1
  122. package/dist/cmd/cloud/objectstore/list-keys.js +0 -58
  123. package/dist/cmd/cloud/objectstore/list-keys.js.map +0 -1
  124. package/dist/cmd/cloud/objectstore/put.d.ts +0 -3
  125. package/dist/cmd/cloud/objectstore/put.d.ts.map +0 -1
  126. package/dist/cmd/cloud/objectstore/put.js +0 -66
  127. package/dist/cmd/cloud/objectstore/put.js.map +0 -1
  128. package/dist/cmd/cloud/objectstore/repl.d.ts +0 -3
  129. package/dist/cmd/cloud/objectstore/repl.d.ts.map +0 -1
  130. package/dist/cmd/cloud/objectstore/repl.js +0 -224
  131. package/dist/cmd/cloud/objectstore/repl.js.map +0 -1
  132. package/dist/cmd/cloud/objectstore/url.d.ts +0 -3
  133. package/dist/cmd/cloud/objectstore/url.d.ts.map +0 -1
  134. package/dist/cmd/cloud/objectstore/url.js +0 -64
  135. package/dist/cmd/cloud/objectstore/url.js.map +0 -1
  136. package/dist/cmd/cloud/objectstore/util.d.ts +0 -11
  137. package/dist/cmd/cloud/objectstore/util.d.ts.map +0 -1
  138. package/dist/cmd/cloud/objectstore/util.js +0 -18
  139. package/dist/cmd/cloud/objectstore/util.js.map +0 -1
  140. package/src/cmd/build/ast.test.ts +0 -418
  141. package/src/cmd/build/fix-duplicate-exports.test.ts +0 -387
  142. package/src/cmd/cloud/objectstore/delete-bucket.ts +0 -77
  143. package/src/cmd/cloud/objectstore/delete.ts +0 -67
  144. package/src/cmd/cloud/objectstore/get.ts +0 -77
  145. package/src/cmd/cloud/objectstore/index.ts +0 -36
  146. package/src/cmd/cloud/objectstore/list-buckets.ts +0 -51
  147. package/src/cmd/cloud/objectstore/list-keys.ts +0 -63
  148. package/src/cmd/cloud/objectstore/put.ts +0 -74
  149. package/src/cmd/cloud/objectstore/repl.ts +0 -239
  150. package/src/cmd/cloud/objectstore/url.ts +0 -67
  151. package/src/cmd/cloud/objectstore/util.ts +0 -29
  152. package/src/crypto/box.test.ts +0 -431
  153. package/src/env-util.test.ts +0 -194
@@ -174,6 +174,14 @@ function generateStableEvalId(projectId: string, agentId: string, name: string):
174
174
  return `evalid_${hashSHA1(projectId, agentId, name)}`.substring(0, 64);
175
175
  }
176
176
 
177
+ /**
178
+ * Type guard to check if an AST node is an ObjectExpression
179
+ */
180
+ function isObjectExpression(node: unknown): node is ASTObjectExpression {
181
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
182
+ return typeof node === 'object' && node !== null && (node as any).type === 'ObjectExpression';
183
+ }
184
+
177
185
  /**
178
186
  * Extract schema code from createAgent call arguments
179
187
  * Returns input and output schema code as strings
@@ -227,7 +235,6 @@ const MetadataError = StructuredError('MetatadataNameMissingError')<{
227
235
  function augmentAgentMetadataNode(
228
236
  projectId: string,
229
237
  id: string,
230
- identifier: string,
231
238
  rel: string,
232
239
  version: string,
233
240
  ast: AcornParseResultType,
@@ -246,19 +253,10 @@ function augmentAgentMetadataNode(
246
253
  });
247
254
  }
248
255
  const name = metadata.get('name')!;
249
- if (metadata.has('identifier') && identifier !== metadata.get('identifier')) {
250
- const location = ast.loc?.start?.line ? ` on line ${ast.loc.start.line}` : '';
251
- throw new MetadataError({
252
- filename,
253
- line: ast.loc?.start?.line,
254
- message: `metadata.identifier (${metadata.get('identifier')}) in ${filename}${location} is mismatched (${name}). This is an internal error.`,
255
- });
256
- }
257
256
  const descriptionNode = propvalue.properties.find((x) => x.key.name === 'description')?.value;
258
257
  const description = descriptionNode ? (descriptionNode as ASTLiteral).value : '';
259
258
  const agentId = generateStableAgentId(projectId, name);
260
259
  metadata.set('version', version);
261
- metadata.set('identifier', identifier);
262
260
  metadata.set('filename', rel);
263
261
  metadata.set('id', id);
264
262
  metadata.set('agentId', agentId);
@@ -273,7 +271,6 @@ function augmentAgentMetadataNode(
273
271
  createObjectPropertyNode('id', id),
274
272
  createObjectPropertyNode('agentId', agentId),
275
273
  createObjectPropertyNode('version', version),
276
- createObjectPropertyNode('identifier', name),
277
274
  createObjectPropertyNode('filename', rel),
278
275
  createObjectPropertyNode('description', description)
279
276
  );
@@ -297,15 +294,25 @@ function createAgentMetadataNode(
297
294
  version: string,
298
295
  ast: AcornParseResultType,
299
296
  callargexp: ASTObjectExpression,
300
- _filename: string
297
+ _filename: string,
298
+ projectId: string,
299
+ inputSchemaCode?: string,
300
+ outputSchemaCode?: string
301
301
  ): [string, Map<string, string>] {
302
302
  const newmetadata = createNewMetadataNode();
303
+ const agentId = generateStableAgentId(projectId, name);
303
304
  const md = new Map<string, string>();
304
305
  md.set('id', id);
306
+ md.set('agentId', agentId);
305
307
  md.set('version', version);
306
308
  md.set('name', name);
307
- md.set('identifier', name);
308
309
  md.set('filename', rel);
310
+ if (inputSchemaCode) {
311
+ md.set('inputSchemaCode', inputSchemaCode);
312
+ }
313
+ if (outputSchemaCode) {
314
+ md.set('outputSchemaCode', outputSchemaCode);
315
+ }
309
316
  for (const [key, value] of md) {
310
317
  newmetadata.value.properties.push(createObjectPropertyNode(key, value));
311
318
  }
@@ -317,132 +324,6 @@ function createAgentMetadataNode(
317
324
  return [newsource, md];
318
325
  }
319
326
 
320
- function camelToKebab(str: string): string {
321
- return str
322
- .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
323
- .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
324
- .toLowerCase();
325
- }
326
-
327
- function setLiteralValue(literal: ASTLiteral, value: string) {
328
- literal.value = value;
329
- if (literal.raw !== undefined) {
330
- literal.raw = JSON.stringify(value);
331
- }
332
- }
333
-
334
- function augmentEvalMetadataNode(
335
- projectId: string,
336
- agentId: string,
337
- id: string,
338
- name: string,
339
- rel: string,
340
- version: string,
341
- _ast: AcornParseResultType,
342
- metadataObj: ASTObjectExpression,
343
- _filename: string
344
- ): void {
345
- const metadata = parseObjectExpressionToMap(metadataObj);
346
- // Name can come from metadata.name or variable name (already resolved in caller)
347
- // If metadata doesn't have name, we'll add it from the resolved name
348
- if (!metadata.has('name')) {
349
- metadataObj.properties.push(createObjectPropertyNode('name', name));
350
- }
351
- const descriptionNode = metadataObj.properties.find((x) => x.key.name === 'description')?.value;
352
- const description = descriptionNode ? (descriptionNode as ASTLiteral).value : '';
353
- const effectiveAgentId = agentId || '';
354
- const _evalId = getEvalId(projectId, effectiveAgentId, rel, name, version); // Deployment-specific ID (not used, kept for potential future use)
355
- const stableEvalId = generateStableEvalId(projectId, effectiveAgentId, name);
356
-
357
- // Check if id, version, identifier, filename, evalId already exist
358
- const existingKeys = new Set<string>();
359
- for (const prop of metadataObj.properties) {
360
- if (prop.key.type === 'Identifier') {
361
- existingKeys.add(prop.key.name);
362
- }
363
- }
364
-
365
- // Add or update metadata properties
366
- if (!existingKeys.has('id')) {
367
- metadataObj.properties.push(createObjectPropertyNode('id', id));
368
- } else {
369
- // Update existing id
370
- for (const prop of metadataObj.properties) {
371
- if (prop.key.type === 'Identifier' && prop.key.name === 'id') {
372
- if (prop.value.type === 'Literal') {
373
- setLiteralValue(prop.value as ASTLiteral, id);
374
- }
375
- break;
376
- }
377
- }
378
- }
379
-
380
- if (!existingKeys.has('version')) {
381
- metadataObj.properties.push(createObjectPropertyNode('version', version));
382
- } else {
383
- for (const prop of metadataObj.properties) {
384
- if (prop.key.type === 'Identifier' && prop.key.name === 'version') {
385
- if (prop.value.type === 'Literal') {
386
- setLiteralValue(prop.value as ASTLiteral, version);
387
- }
388
- break;
389
- }
390
- }
391
- }
392
-
393
- if (!existingKeys.has('identifier')) {
394
- metadataObj.properties.push(createObjectPropertyNode('identifier', name));
395
- } else {
396
- for (const prop of metadataObj.properties) {
397
- if (prop.key.type === 'Identifier' && prop.key.name === 'identifier') {
398
- if (prop.value.type === 'Literal') {
399
- setLiteralValue(prop.value as ASTLiteral, name);
400
- }
401
- break;
402
- }
403
- }
404
- }
405
-
406
- if (!existingKeys.has('filename')) {
407
- metadataObj.properties.push(createObjectPropertyNode('filename', rel));
408
- } else {
409
- for (const prop of metadataObj.properties) {
410
- if (prop.key.type === 'Identifier' && prop.key.name === 'filename') {
411
- if (prop.value.type === 'Literal') {
412
- setLiteralValue(prop.value as ASTLiteral, rel);
413
- }
414
- break;
415
- }
416
- }
417
- }
418
-
419
- if (!existingKeys.has('evalId')) {
420
- metadataObj.properties.push(createObjectPropertyNode('evalId', stableEvalId));
421
- } else {
422
- for (const prop of metadataObj.properties) {
423
- if (prop.key.type === 'Identifier' && prop.key.name === 'evalId') {
424
- if (prop.value.type === 'Literal') {
425
- setLiteralValue(prop.value as ASTLiteral, stableEvalId);
426
- }
427
- break;
428
- }
429
- }
430
- }
431
-
432
- if (!existingKeys.has('description')) {
433
- metadataObj.properties.push(createObjectPropertyNode('description', description));
434
- } else {
435
- for (const prop of metadataObj.properties) {
436
- if (prop.key.type === 'Identifier' && prop.key.name === 'description') {
437
- if (prop.value.type === 'Literal') {
438
- setLiteralValue(prop.value as ASTLiteral, description);
439
- }
440
- break;
441
- }
442
- }
443
- }
444
- }
445
-
446
327
  const DuplicateNameError = StructuredError('DuplicateNameError')<{ filename: string }>();
447
328
 
448
329
  export function parseEvalMetadata(
@@ -458,7 +339,6 @@ export function parseEvalMetadata(
458
339
  filename: string;
459
340
  id: string;
460
341
  version: string;
461
- identifier: string;
462
342
  name: string;
463
343
  evalId: string;
464
344
  description?: string;
@@ -483,7 +363,6 @@ export function parseEvalMetadata(
483
363
  filename: string;
484
364
  id: string;
485
365
  version: string;
486
- identifier: string;
487
366
  name: string;
488
367
  evalId: string;
489
368
  description?: string;
@@ -520,110 +399,73 @@ export function parseEvalMetadata(
520
399
  property.name === 'createEval'
521
400
  ) {
522
401
  // Found agent.createEval() call
523
- if (call.arguments.length > 0) {
402
+ // New signature: agent.createEval(name, { description?, handler })
403
+ if (call.arguments.length >= 2) {
524
404
  const firstArg = call.arguments[0] as ASTNode;
525
- if (firstArg.type === 'ObjectExpression') {
526
- const evalConfig = firstArg as ASTObjectExpression;
527
- let evalName: string | undefined;
528
- let evalDescription: string | undefined;
529
- let variableName: string | undefined;
530
- let metadataObj: ASTObjectExpression | undefined;
531
-
532
- // Capture variable name if available
533
- if (vardecl.id.type === 'Identifier') {
534
- variableName = (vardecl.id as ASTNodeIdentifier).name;
535
- }
405
+ const secondArg = call.arguments[1] as ASTNode;
406
+
407
+ let evalName: string | undefined;
408
+ let evalDescription: string | undefined;
409
+ let configObj: ASTObjectExpression | undefined;
410
+
411
+ // First argument should be a string literal (the name)
412
+ if (
413
+ firstArg.type === 'Literal' &&
414
+ typeof (firstArg as ASTLiteral).value === 'string'
415
+ ) {
416
+ evalName = (firstArg as ASTLiteral).value;
417
+ } else {
418
+ throw new MetadataError({
419
+ filename,
420
+ line: body.loc?.start?.line,
421
+ message:
422
+ 'agent.createEval() first argument must be a string literal name.',
423
+ });
424
+ }
536
425
 
537
- // Extract metadata from the eval config
538
- for (const prop of evalConfig.properties) {
539
- if (prop.key.type === 'Identifier' && prop.key.name === 'metadata') {
540
- if (prop.value.type === 'ObjectExpression') {
541
- metadataObj = prop.value as ASTObjectExpression;
542
- for (const metaProp of metadataObj.properties) {
543
- if (metaProp.key.type === 'Identifier') {
544
- if (
545
- metaProp.key.name === 'name' &&
546
- metaProp.value.type === 'Literal'
547
- ) {
548
- evalName = (metaProp.value as ASTLiteral).value;
549
- } else if (
550
- metaProp.key.name === 'description' &&
551
- metaProp.value.type === 'Literal'
552
- ) {
553
- evalDescription = (metaProp.value as ASTLiteral).value;
554
- }
555
- }
556
- }
426
+ // Second argument should be the config object
427
+ if (secondArg.type === 'ObjectExpression') {
428
+ configObj = secondArg as ASTObjectExpression;
429
+
430
+ // Extract description from config object
431
+ for (const prop of configObj.properties) {
432
+ if (
433
+ prop.key.type === 'Identifier' &&
434
+ prop.key.name === 'description'
435
+ ) {
436
+ if (prop.value.type === 'Literal') {
437
+ evalDescription = (prop.value as ASTLiteral).value;
557
438
  }
558
439
  }
559
440
  }
441
+ }
560
442
 
561
- // Use metadata.name if provided, otherwise use variable name
562
- // Throw error if neither is available (should never happen)
563
- let finalName: string;
564
- if (evalName) {
565
- finalName = evalName;
566
- } else if (variableName) {
567
- finalName = camelToKebab(variableName);
568
- } else {
569
- throw new MetadataError({
570
- filename,
571
- line: body.loc?.start?.line,
572
- message:
573
- 'Eval is missing a name. Please provide metadata.name or use a named export.',
574
- });
575
- }
443
+ const finalName = evalName;
576
444
 
577
- logger.trace(
578
- `Found eval: ${finalName}${evalDescription ? ` - ${evalDescription}` : ''}`
579
- );
580
- const evalId = getEvalId(
581
- projectId,
582
- deploymentId,
583
- rel,
584
- finalName,
585
- version
586
- );
445
+ logger.trace(
446
+ `Found eval: ${finalName}${evalDescription ? ` - ${evalDescription}` : ''}`
447
+ );
448
+ const evalId = getEvalId(projectId, deploymentId, rel, finalName, version);
587
449
 
588
- // Inject metadata into AST if metadata object exists
589
- let stableEvalId: string;
590
- const effectiveAgentId = agentId || '';
591
- if (metadataObj) {
592
- augmentEvalMetadataNode(
593
- projectId,
594
- effectiveAgentId,
595
- evalId,
596
- finalName,
597
- rel,
598
- version,
599
- ast,
600
- metadataObj,
601
- filename
602
- );
603
- // Extract evalId from metadata after augmentation
604
- const metadata = parseObjectExpressionToMap(metadataObj);
605
- stableEvalId =
606
- metadata.get('evalId') ||
607
- generateStableEvalId(projectId, effectiveAgentId, finalName);
608
- } else {
609
- // If no metadata object, generate stable evalId
610
- stableEvalId = generateStableEvalId(
611
- projectId,
612
- effectiveAgentId,
613
- finalName
614
- );
615
- }
450
+ // Generate stable evalId
451
+ const effectiveAgentId = agentId || '';
452
+ const stableEvalId = generateStableEvalId(
453
+ projectId,
454
+ effectiveAgentId,
455
+ finalName
456
+ );
616
457
 
617
- evals.push({
618
- filename: rel,
619
- id: evalId,
620
- version,
621
- identifier: finalName,
622
- name: finalName,
623
- evalId: stableEvalId,
624
- description: evalDescription,
625
- });
626
- }
458
+ // Note: We no longer inject metadata into the AST since there's no metadata object
459
+ // The runtime will generate IDs from the name parameter
460
+
461
+ evals.push({
462
+ filename: rel,
463
+ id: evalId,
464
+ version,
465
+ name: finalName,
466
+ evalId: stableEvalId,
467
+ description: evalDescription,
468
+ });
627
469
  }
628
470
  }
629
471
  }
@@ -662,7 +504,6 @@ export function parseEvalMetadata(
662
504
  }
663
505
 
664
506
  const InvalidExportError = StructuredError('InvalidExportError')<{ filename: string }>();
665
- const InvalidCreateAgentError = StructuredError('InvalidCreateAgentError')<{ filename: string }>();
666
507
 
667
508
  export async function parseAgentMetadata(
668
509
  rootDir: string,
@@ -670,7 +511,12 @@ export async function parseAgentMetadata(
670
511
  contents: string,
671
512
  projectId: string,
672
513
  deploymentId: string
673
- ): Promise<[string, Map<string, string>]> {
514
+ ): Promise<[string, Map<string, string>] | undefined> {
515
+ // Quick string search optimization - skip AST parsing if no createAgent call
516
+ if (!contents.includes('createAgent')) {
517
+ return undefined;
518
+ }
519
+
674
520
  const ast = acornLoose.parse(contents, {
675
521
  locations: true,
676
522
  ecmaVersion: 'latest',
@@ -690,63 +536,87 @@ export async function parseAgentMetadata(
690
536
  if (body.declaration?.type === 'CallExpression') {
691
537
  const call = body.declaration as ASTCallExpression;
692
538
  if (call.callee.name === 'createAgent') {
693
- for (const callarg of call.arguments) {
694
- const callargexp = callarg as ASTObjectExpression;
695
-
696
- // Extract schema code before processing metadata
697
- let inputSchemaCode: string | undefined;
698
- let outputSchemaCode: string | undefined;
699
- if (!schemaCodeExtracted) {
700
- const schemaCode = extractSchemaCode(callargexp);
701
- inputSchemaCode = schemaCode.inputSchemaCode;
702
- outputSchemaCode = schemaCode.outputSchemaCode;
703
- schemaCodeExtracted = true;
704
- }
539
+ // Enforce new API: createAgent('name', {config})
540
+ if (call.arguments.length < 2) {
541
+ throw new Error(
542
+ `createAgent requires 2 arguments: createAgent('name', config) in ${filename}`
543
+ );
544
+ }
705
545
 
706
- for (const prop of callargexp.properties) {
707
- if (prop.key.type === 'Identifier' && prop.key.name === 'metadata') {
708
- result = augmentAgentMetadataNode(
709
- projectId,
710
- id,
711
- name,
712
- rel,
713
- version,
714
- ast,
715
- prop.value as ASTObjectExpression,
716
- filename,
717
- inputSchemaCode,
718
- outputSchemaCode
719
- );
720
- break;
721
- }
722
- }
723
- if (!result) {
724
- result = createAgentMetadataNode(
546
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
547
+ const nameArg = call.arguments[0] as any;
548
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
549
+ const configArg = call.arguments[1] as any;
550
+
551
+ if (!nameArg || nameArg.type !== 'Literal' || typeof nameArg.value !== 'string') {
552
+ throw new Error(
553
+ `createAgent first argument must be a string literal in ${filename}`
554
+ );
555
+ }
556
+
557
+ if (!isObjectExpression(configArg)) {
558
+ throw new Error(
559
+ `createAgent second argument must be a config object in ${filename}`
560
+ );
561
+ }
562
+
563
+ const callargexp = configArg;
564
+
565
+ // Extract schema code before processing metadata
566
+ let inputSchemaCode: string | undefined;
567
+ let outputSchemaCode: string | undefined;
568
+ if (!schemaCodeExtracted) {
569
+ const schemaCode = extractSchemaCode(callargexp);
570
+ inputSchemaCode = schemaCode.inputSchemaCode;
571
+ outputSchemaCode = schemaCode.outputSchemaCode;
572
+ schemaCodeExtracted = true;
573
+ }
574
+
575
+ for (const prop of callargexp.properties) {
576
+ if (prop.key.type === 'Identifier' && prop.key.name === 'metadata') {
577
+ result = augmentAgentMetadataNode(
578
+ projectId,
725
579
  id,
726
- name,
727
580
  rel,
728
581
  version,
729
582
  ast,
730
- callargexp,
731
- filename
583
+ prop.value as ASTObjectExpression,
584
+ filename,
585
+ inputSchemaCode,
586
+ outputSchemaCode
732
587
  );
588
+ break;
733
589
  }
734
- break;
735
590
  }
591
+ if (!result) {
592
+ result = createAgentMetadataNode(
593
+ id,
594
+ name,
595
+ rel,
596
+ version,
597
+ ast,
598
+ callargexp,
599
+ filename,
600
+ projectId,
601
+ inputSchemaCode,
602
+ outputSchemaCode
603
+ );
604
+ }
605
+ break;
736
606
  }
737
607
  }
738
- if (!result) {
739
- const identifier = body.declaration as ASTNodeIdentifier;
740
- exportName = identifier.name;
741
- break;
742
- }
608
+ }
609
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
610
+ if (!result && (body as any).declaration?.type === 'Identifier') {
611
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
612
+ const identifier = (body as any).declaration as ASTNodeIdentifier;
613
+ exportName = identifier.name;
614
+ break;
743
615
  }
744
616
  }
617
+ // If no default export or createAgent found, skip this file (it's not an agent)
745
618
  if (!result && !exportName) {
746
- throw new InvalidExportError({
747
- filename,
748
- message: `could not find default export for ${filename} using ${rootDir}`,
749
- });
619
+ return undefined;
750
620
  }
751
621
  if (!result) {
752
622
  for (const body of ast.body) {
@@ -758,52 +628,77 @@ export async function parseAgentMetadata(
758
628
  if (vardecl.init?.type === 'CallExpression') {
759
629
  const call = vardecl.init as ASTCallExpression;
760
630
  if (call.callee.name === 'createAgent') {
761
- for (const callarg of call.arguments) {
762
- const callargexp = callarg as ASTObjectExpression;
763
-
764
- // Extract schema code before processing metadata
765
- let inputSchemaCode: string | undefined;
766
- let outputSchemaCode: string | undefined;
767
- if (!schemaCodeExtracted) {
768
- const schemaCode = extractSchemaCode(callargexp);
769
- inputSchemaCode = schemaCode.inputSchemaCode;
770
- outputSchemaCode = schemaCode.outputSchemaCode;
771
- schemaCodeExtracted = true;
772
- }
631
+ // Enforce new API: createAgent('name', {config})
632
+ if (call.arguments.length < 2) {
633
+ throw new Error(
634
+ `createAgent requires 2 arguments: createAgent('name', config) in ${filename}`
635
+ );
636
+ }
773
637
 
774
- for (const prop of callargexp.properties) {
775
- if (
776
- prop.key.type === 'Identifier' &&
777
- prop.key.name === 'metadata'
778
- ) {
779
- result = augmentAgentMetadataNode(
780
- projectId,
781
- id,
782
- name,
783
- rel,
784
- version,
785
- ast,
786
- prop.value as ASTObjectExpression,
787
- filename,
788
- inputSchemaCode,
789
- outputSchemaCode
790
- );
791
- break;
792
- }
793
- }
794
- if (!result) {
795
- result = createAgentMetadataNode(
638
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
639
+ const nameArg = call.arguments[0] as any;
640
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
641
+ const configArg = call.arguments[1] as any;
642
+
643
+ if (
644
+ !nameArg ||
645
+ nameArg.type !== 'Literal' ||
646
+ typeof nameArg.value !== 'string'
647
+ ) {
648
+ throw new Error(
649
+ `createAgent first argument must be a string literal in ${filename}`
650
+ );
651
+ }
652
+
653
+ if (!isObjectExpression(configArg)) {
654
+ throw new Error(
655
+ `createAgent second argument must be a config object in ${filename}`
656
+ );
657
+ }
658
+
659
+ const callargexp = configArg;
660
+
661
+ // Extract schema code before processing metadata
662
+ let inputSchemaCode: string | undefined;
663
+ let outputSchemaCode: string | undefined;
664
+ if (!schemaCodeExtracted) {
665
+ const schemaCode = extractSchemaCode(callargexp);
666
+ inputSchemaCode = schemaCode.inputSchemaCode;
667
+ outputSchemaCode = schemaCode.outputSchemaCode;
668
+ schemaCodeExtracted = true;
669
+ }
670
+
671
+ for (const prop of callargexp.properties) {
672
+ if (prop.key.type === 'Identifier' && prop.key.name === 'metadata') {
673
+ result = augmentAgentMetadataNode(
674
+ projectId,
796
675
  id,
797
- name,
798
676
  rel,
799
677
  version,
800
678
  ast,
801
- callargexp,
802
- filename
679
+ prop.value as ASTObjectExpression,
680
+ filename,
681
+ inputSchemaCode,
682
+ outputSchemaCode
803
683
  );
684
+ break;
804
685
  }
805
- break;
806
686
  }
687
+ if (!result) {
688
+ result = createAgentMetadataNode(
689
+ id,
690
+ name,
691
+ rel,
692
+ version,
693
+ ast,
694
+ callargexp,
695
+ filename,
696
+ projectId,
697
+ inputSchemaCode,
698
+ outputSchemaCode
699
+ );
700
+ }
701
+ break;
807
702
  }
808
703
  }
809
704
  }
@@ -812,11 +707,9 @@ export async function parseAgentMetadata(
812
707
  }
813
708
  }
814
709
  }
710
+ // If no createAgent found after checking all declarations, skip this file
815
711
  if (!result) {
816
- throw new InvalidCreateAgentError({
817
- filename,
818
- message: `error parsing: ${filename}. could not find an proper createAgent defined in this file`,
819
- });
712
+ return undefined;
820
713
  }
821
714
 
822
715
  // Parse evals from eval.ts file in the same directory
@@ -869,6 +762,89 @@ const InvalidRouterConfigError = StructuredError('InvalidRouterConfigError')<{
869
762
  line?: number;
870
763
  }>();
871
764
 
765
+ /**
766
+ * Check if an AST node contains a validator() call
767
+ */
768
+ interface ValidatorInfo {
769
+ hasValidator: boolean;
770
+ agentVariable?: string;
771
+ inputSchemaVariable?: string;
772
+ outputSchemaVariable?: string;
773
+ }
774
+
775
+ function hasValidatorCall(args: unknown[]): ValidatorInfo {
776
+ if (!args || args.length === 0) return { hasValidator: false };
777
+
778
+ for (const arg of args) {
779
+ if (!arg || typeof arg !== 'object') continue;
780
+ const node = arg as ASTNode;
781
+
782
+ // Check if this is a CallExpression with callee named 'validator'
783
+ if (node.type === 'CallExpression') {
784
+ const callExpr = node as ASTCallExpression;
785
+ if (callExpr.callee.type === 'Identifier') {
786
+ const identifier = callExpr.callee as ASTNodeIdentifier;
787
+ if (identifier.name === 'validator') {
788
+ // Try to extract schema variables from validator({ input, output })
789
+ const schemas = extractValidatorSchemas(callExpr);
790
+ return { hasValidator: true, ...schemas };
791
+ }
792
+ }
793
+ // Check for agent.validator()
794
+ if (callExpr.callee.type === 'MemberExpression') {
795
+ const member = callExpr.callee as ASTMemberExpression;
796
+ if (member.property && (member.property as ASTNodeIdentifier).name === 'validator') {
797
+ // Extract agent variable name (the object before .validator())
798
+ const agentVariable =
799
+ member.object.type === 'Identifier'
800
+ ? (member.object as ASTNodeIdentifier).name
801
+ : undefined;
802
+ return { hasValidator: true, agentVariable };
803
+ }
804
+ }
805
+ }
806
+ }
807
+
808
+ return { hasValidator: false };
809
+ }
810
+
811
+ /**
812
+ * Extract schema variable names from validator() call arguments
813
+ * Example: validator({ input: myInputSchema, output: myOutputSchema })
814
+ */
815
+ function extractValidatorSchemas(callExpr: ASTCallExpression): {
816
+ inputSchemaVariable?: string;
817
+ outputSchemaVariable?: string;
818
+ } {
819
+ const result: { inputSchemaVariable?: string; outputSchemaVariable?: string } = {};
820
+
821
+ // Check if validator has arguments
822
+ if (!callExpr.arguments || callExpr.arguments.length === 0) {
823
+ return result;
824
+ }
825
+
826
+ // First argument should be an object expression
827
+ const firstArg = callExpr.arguments[0] as ASTNode;
828
+ if (!firstArg || firstArg.type !== 'ObjectExpression') {
829
+ return result;
830
+ }
831
+
832
+ const objExpr = firstArg as ASTObjectExpression;
833
+ for (const prop of objExpr.properties) {
834
+ const keyName = prop.key.name;
835
+ if ((keyName === 'input' || keyName === 'output') && prop.value.type === 'Identifier') {
836
+ const valueName = (prop.value as ASTNodeIdentifier).name;
837
+ if (keyName === 'input') {
838
+ result.inputSchemaVariable = valueName;
839
+ } else {
840
+ result.outputSchemaVariable = valueName;
841
+ }
842
+ }
843
+ }
844
+
845
+ return result;
846
+ }
847
+
872
848
  export async function parseRoute(
873
849
  rootDir: string,
874
850
  filename: string,
@@ -884,6 +860,32 @@ export async function parseRoute(
884
860
  });
885
861
  let exportName: string | undefined;
886
862
  let variableName: string | undefined;
863
+
864
+ // Extract import statements to map variable names to their import sources
865
+ const importMap = new Map<string, string>(); // Maps variable name to import path
866
+ for (const body of ast.body) {
867
+ if (body.type === 'ImportDeclaration') {
868
+ const importDecl = body as {
869
+ source?: { value?: string };
870
+ specifiers?: Array<{
871
+ type: string;
872
+ local?: { name?: string };
873
+ }>;
874
+ };
875
+ const importPath = importDecl.source?.value;
876
+ if (importPath && importDecl.specifiers) {
877
+ for (const spec of importDecl.specifiers) {
878
+ if (spec.type === 'ImportDefaultSpecifier' && spec.local?.name) {
879
+ // import hello from '@agent/hello'
880
+ importMap.set(spec.local.name, importPath);
881
+ } else if (spec.type === 'ImportSpecifier' && spec.local?.name) {
882
+ // import { hello } from './shared'
883
+ importMap.set(spec.local.name, importPath);
884
+ }
885
+ }
886
+ }
887
+ }
888
+ }
887
889
  for (const body of ast.body) {
888
890
  if (body.type === 'ExportDefaultDeclaration') {
889
891
  const identifier = body.declaration as ASTNodeIdentifier;
@@ -905,10 +907,18 @@ export async function parseRoute(
905
907
  if (identifier.name === exportName) {
906
908
  if (vardecl.init?.type === 'CallExpression') {
907
909
  const call = vardecl.init as ASTCallExpression;
910
+ // Support both createRouter() and new Hono()
908
911
  if (call.callee.name === 'createRouter') {
909
912
  variableName = identifier.name;
910
913
  break;
911
914
  }
915
+ } else if (vardecl.init?.type === 'NewExpression') {
916
+ const newExpr = vardecl.init as ASTCallExpression;
917
+ // Support new Hono() pattern
918
+ if (newExpr.callee.name === 'Hono') {
919
+ variableName = identifier.name;
920
+ break;
921
+ }
912
922
  }
913
923
  }
914
924
  }
@@ -918,7 +928,7 @@ export async function parseRoute(
918
928
  if (!variableName) {
919
929
  throw new InvalidCreateRouterError({
920
930
  filename,
921
- message: `error parsing: ${filename}. could not find an proper createRouter defined in this file`,
931
+ message: `error parsing: ${filename}. could not find an proper createRouter or new Hono() defined in this file`,
922
932
  });
923
933
  }
924
934
 
@@ -926,16 +936,12 @@ export async function parseRoute(
926
936
  const dir = dirname(filename);
927
937
  const name = basename(dir);
928
938
 
929
- // Detect if this is a subagent route and build proper path
930
- const relativePath = relative(rootDir, dir)
931
- .replace(/^src\/agent\//, '')
932
- .replace(/^src\/web\//, '');
933
- const pathParts = relativePath.split('/').filter(Boolean);
934
- const isSubagent = pathParts.length === 2 && filename.includes('src/agent');
935
- const routeName = isSubagent ? pathParts.join('/') : name;
939
+ // For src/api/index.ts, we don't want to add the folder name since it's the root API router
940
+ const isRootApi = filename.includes('src/api/index.ts');
941
+ const routeName = isRootApi ? '' : name;
936
942
 
937
943
  const routes: RouteDefinition = [];
938
- const routePrefix = filename.includes('src/agent') ? '/agent' : '/api';
944
+ const routePrefix = '/api';
939
945
 
940
946
  try {
941
947
  for (const body of ast.body) {
@@ -1067,6 +1073,30 @@ export async function parseRoute(
1067
1073
  thepath,
1068
1074
  version
1069
1075
  );
1076
+
1077
+ // Check if this route uses validator middleware
1078
+ const validatorInfo = hasValidatorCall(statement.expression.arguments);
1079
+
1080
+ // Store validator info in config if present
1081
+ const routeConfig = config ? { ...config } : {};
1082
+ if (validatorInfo.hasValidator) {
1083
+ routeConfig.hasValidator = true;
1084
+ if (validatorInfo.agentVariable) {
1085
+ routeConfig.agentVariable = validatorInfo.agentVariable;
1086
+ // Look up where this agent variable is imported from
1087
+ const agentImportPath = importMap.get(validatorInfo.agentVariable);
1088
+ if (agentImportPath) {
1089
+ routeConfig.agentImportPath = agentImportPath;
1090
+ }
1091
+ }
1092
+ if (validatorInfo.inputSchemaVariable) {
1093
+ routeConfig.inputSchemaVariable = validatorInfo.inputSchemaVariable;
1094
+ }
1095
+ if (validatorInfo.outputSchemaVariable) {
1096
+ routeConfig.outputSchemaVariable = validatorInfo.outputSchemaVariable;
1097
+ }
1098
+ }
1099
+
1070
1100
  routes.push({
1071
1101
  id,
1072
1102
  method: method as 'get' | 'post' | 'put' | 'delete' | 'patch',
@@ -1074,7 +1104,7 @@ export async function parseRoute(
1074
1104
  filename: rel,
1075
1105
  path: thepath,
1076
1106
  version,
1077
- config,
1107
+ config: Object.keys(routeConfig).length > 0 ? routeConfig : undefined,
1078
1108
  });
1079
1109
  }
1080
1110
  }