@agentuity/cli 1.0.59 → 2.0.0-beta.1

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 (189) hide show
  1. package/bin/cli.ts +2 -3
  2. package/dist/cmd/build/app-config-extractor.d.ts +27 -0
  3. package/dist/cmd/build/app-config-extractor.d.ts.map +1 -0
  4. package/dist/cmd/build/app-config-extractor.js +152 -0
  5. package/dist/cmd/build/app-config-extractor.js.map +1 -0
  6. package/dist/cmd/build/app-router-detector.d.ts +2 -5
  7. package/dist/cmd/build/app-router-detector.d.ts.map +1 -1
  8. package/dist/cmd/build/app-router-detector.js +130 -154
  9. package/dist/cmd/build/app-router-detector.js.map +1 -1
  10. package/dist/cmd/build/ci.d.ts.map +1 -1
  11. package/dist/cmd/build/ci.js +5 -21
  12. package/dist/cmd/build/ci.js.map +1 -1
  13. package/dist/cmd/build/ids.d.ts +11 -0
  14. package/dist/cmd/build/ids.d.ts.map +1 -0
  15. package/dist/cmd/build/ids.js +18 -0
  16. package/dist/cmd/build/ids.js.map +1 -0
  17. package/dist/cmd/build/index.d.ts.map +1 -1
  18. package/dist/cmd/build/index.js +8 -0
  19. package/dist/cmd/build/index.js.map +1 -1
  20. package/dist/cmd/build/vite/agent-discovery.d.ts +8 -4
  21. package/dist/cmd/build/vite/agent-discovery.d.ts.map +1 -1
  22. package/dist/cmd/build/vite/agent-discovery.js +166 -487
  23. package/dist/cmd/build/vite/agent-discovery.js.map +1 -1
  24. package/dist/cmd/build/vite/bun-dev-server.d.ts +43 -14
  25. package/dist/cmd/build/vite/bun-dev-server.d.ts.map +1 -1
  26. package/dist/cmd/build/vite/bun-dev-server.js +290 -129
  27. package/dist/cmd/build/vite/bun-dev-server.js.map +1 -1
  28. package/dist/cmd/build/vite/config-loader.d.ts +15 -20
  29. package/dist/cmd/build/vite/config-loader.d.ts.map +1 -1
  30. package/dist/cmd/build/vite/config-loader.js +41 -74
  31. package/dist/cmd/build/vite/config-loader.js.map +1 -1
  32. package/dist/cmd/build/vite/docs-generator.d.ts.map +1 -1
  33. package/dist/cmd/build/vite/docs-generator.js +0 -2
  34. package/dist/cmd/build/vite/docs-generator.js.map +1 -1
  35. package/dist/cmd/build/vite/index.d.ts.map +1 -1
  36. package/dist/cmd/build/vite/index.js +0 -36
  37. package/dist/cmd/build/vite/index.js.map +1 -1
  38. package/dist/cmd/build/vite/lifecycle-generator.d.ts +10 -2
  39. package/dist/cmd/build/vite/lifecycle-generator.d.ts.map +1 -1
  40. package/dist/cmd/build/vite/lifecycle-generator.js +302 -23
  41. package/dist/cmd/build/vite/lifecycle-generator.js.map +1 -1
  42. package/dist/cmd/build/vite/route-discovery.d.ts +11 -38
  43. package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -1
  44. package/dist/cmd/build/vite/route-discovery.js +97 -177
  45. package/dist/cmd/build/vite/route-discovery.js.map +1 -1
  46. package/dist/cmd/build/vite/server-bundler.js +1 -1
  47. package/dist/cmd/build/vite/server-bundler.js.map +1 -1
  48. package/dist/cmd/build/vite/static-renderer.d.ts +0 -2
  49. package/dist/cmd/build/vite/static-renderer.d.ts.map +1 -1
  50. package/dist/cmd/build/vite/static-renderer.js +19 -13
  51. package/dist/cmd/build/vite/static-renderer.js.map +1 -1
  52. package/dist/cmd/build/vite/vite-asset-server-config.d.ts +6 -3
  53. package/dist/cmd/build/vite/vite-asset-server-config.d.ts.map +1 -1
  54. package/dist/cmd/build/vite/vite-asset-server-config.js +175 -69
  55. package/dist/cmd/build/vite/vite-asset-server-config.js.map +1 -1
  56. package/dist/cmd/build/vite/vite-asset-server.d.ts +8 -3
  57. package/dist/cmd/build/vite/vite-asset-server.d.ts.map +1 -1
  58. package/dist/cmd/build/vite/vite-asset-server.js +14 -13
  59. package/dist/cmd/build/vite/vite-asset-server.js.map +1 -1
  60. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
  61. package/dist/cmd/build/vite/vite-builder.js +42 -190
  62. package/dist/cmd/build/vite/vite-builder.js.map +1 -1
  63. package/dist/cmd/build/vite/ws-proxy.d.ts +53 -0
  64. package/dist/cmd/build/vite/ws-proxy.d.ts.map +1 -0
  65. package/dist/cmd/build/vite/ws-proxy.js +95 -0
  66. package/dist/cmd/build/vite/ws-proxy.js.map +1 -0
  67. package/dist/cmd/build/vite-bundler.d.ts.map +1 -1
  68. package/dist/cmd/build/vite-bundler.js +0 -3
  69. package/dist/cmd/build/vite-bundler.js.map +1 -1
  70. package/dist/cmd/cloud/deploy-fork.d.ts.map +1 -1
  71. package/dist/cmd/cloud/deploy-fork.js +15 -36
  72. package/dist/cmd/cloud/deploy-fork.js.map +1 -1
  73. package/dist/cmd/cloud/sandbox/exec.d.ts.map +1 -1
  74. package/dist/cmd/cloud/sandbox/exec.js +28 -86
  75. package/dist/cmd/cloud/sandbox/exec.js.map +1 -1
  76. package/dist/cmd/cloud/sandbox/run.d.ts.map +1 -1
  77. package/dist/cmd/cloud/sandbox/run.js +2 -9
  78. package/dist/cmd/cloud/sandbox/run.js.map +1 -1
  79. package/dist/cmd/cloud/sandbox/snapshot/build.js +2 -2
  80. package/dist/cmd/cloud/sandbox/snapshot/build.js.map +1 -1
  81. package/dist/cmd/coder/hub-url.d.ts.map +1 -1
  82. package/dist/cmd/coder/hub-url.js +1 -3
  83. package/dist/cmd/coder/hub-url.js.map +1 -1
  84. package/dist/cmd/coder/start.js +6 -6
  85. package/dist/cmd/coder/start.js.map +1 -1
  86. package/dist/cmd/coder/tui-init.d.ts +2 -2
  87. package/dist/cmd/coder/tui-init.js +2 -2
  88. package/dist/cmd/coder/tui-init.js.map +1 -1
  89. package/dist/cmd/dev/file-watcher.d.ts.map +1 -1
  90. package/dist/cmd/dev/file-watcher.js +2 -8
  91. package/dist/cmd/dev/file-watcher.js.map +1 -1
  92. package/dist/cmd/dev/index.d.ts.map +1 -1
  93. package/dist/cmd/dev/index.js +432 -752
  94. package/dist/cmd/dev/index.js.map +1 -1
  95. package/dist/cmd/dev/process-manager.d.ts +104 -0
  96. package/dist/cmd/dev/process-manager.d.ts.map +1 -0
  97. package/dist/cmd/dev/process-manager.js +204 -0
  98. package/dist/cmd/dev/process-manager.js.map +1 -0
  99. package/dist/errors.d.ts +10 -24
  100. package/dist/errors.d.ts.map +1 -1
  101. package/dist/errors.js +12 -42
  102. package/dist/errors.js.map +1 -1
  103. package/dist/schema-generator.d.ts.map +1 -1
  104. package/dist/schema-generator.js +12 -2
  105. package/dist/schema-generator.js.map +1 -1
  106. package/dist/tui.d.ts.map +1 -1
  107. package/dist/tui.js +5 -19
  108. package/dist/tui.js.map +1 -1
  109. package/dist/utils/version-mismatch.d.ts +39 -0
  110. package/dist/utils/version-mismatch.d.ts.map +1 -0
  111. package/dist/utils/version-mismatch.js +161 -0
  112. package/dist/utils/version-mismatch.js.map +1 -0
  113. package/package.json +6 -6
  114. package/src/cmd/ai/prompt/agent.md +0 -1
  115. package/src/cmd/ai/prompt/api.md +0 -7
  116. package/src/cmd/ai/prompt/web.md +51 -213
  117. package/src/cmd/build/app-config-extractor.ts +186 -0
  118. package/src/cmd/build/app-router-detector.ts +152 -182
  119. package/src/cmd/build/ci.ts +5 -21
  120. package/src/cmd/build/ids.ts +19 -0
  121. package/src/cmd/build/index.ts +10 -0
  122. package/src/cmd/build/vite/agent-discovery.ts +208 -679
  123. package/src/cmd/build/vite/bun-dev-server.ts +383 -146
  124. package/src/cmd/build/vite/config-loader.ts +45 -77
  125. package/src/cmd/build/vite/docs-generator.ts +0 -2
  126. package/src/cmd/build/vite/index.ts +1 -42
  127. package/src/cmd/build/vite/lifecycle-generator.ts +345 -21
  128. package/src/cmd/build/vite/route-discovery.ts +116 -274
  129. package/src/cmd/build/vite/server-bundler.ts +1 -1
  130. package/src/cmd/build/vite/static-renderer.ts +23 -15
  131. package/src/cmd/build/vite/vite-asset-server-config.ts +200 -70
  132. package/src/cmd/build/vite/vite-asset-server.ts +25 -15
  133. package/src/cmd/build/vite/vite-builder.ts +49 -220
  134. package/src/cmd/build/vite/ws-proxy.ts +126 -0
  135. package/src/cmd/build/vite-bundler.ts +0 -4
  136. package/src/cmd/cloud/deploy-fork.ts +16 -39
  137. package/src/cmd/cloud/sandbox/exec.ts +23 -130
  138. package/src/cmd/cloud/sandbox/run.ts +2 -9
  139. package/src/cmd/cloud/sandbox/snapshot/build.ts +2 -2
  140. package/src/cmd/coder/hub-url.ts +1 -3
  141. package/src/cmd/coder/start.ts +6 -6
  142. package/src/cmd/coder/tui-init.ts +4 -4
  143. package/src/cmd/dev/file-watcher.ts +2 -9
  144. package/src/cmd/dev/index.ts +476 -859
  145. package/src/cmd/dev/process-manager.ts +261 -0
  146. package/src/errors.ts +12 -44
  147. package/src/schema-generator.ts +12 -2
  148. package/src/tui.ts +5 -18
  149. package/src/utils/version-mismatch.ts +204 -0
  150. package/dist/cmd/build/ast.d.ts +0 -78
  151. package/dist/cmd/build/ast.d.ts.map +0 -1
  152. package/dist/cmd/build/ast.js +0 -2703
  153. package/dist/cmd/build/ast.js.map +0 -1
  154. package/dist/cmd/build/entry-generator.d.ts +0 -25
  155. package/dist/cmd/build/entry-generator.d.ts.map +0 -1
  156. package/dist/cmd/build/entry-generator.js +0 -695
  157. package/dist/cmd/build/entry-generator.js.map +0 -1
  158. package/dist/cmd/build/vite/api-mount-path.d.ts +0 -61
  159. package/dist/cmd/build/vite/api-mount-path.d.ts.map +0 -1
  160. package/dist/cmd/build/vite/api-mount-path.js +0 -83
  161. package/dist/cmd/build/vite/api-mount-path.js.map +0 -1
  162. package/dist/cmd/build/vite/registry-generator.d.ts +0 -19
  163. package/dist/cmd/build/vite/registry-generator.d.ts.map +0 -1
  164. package/dist/cmd/build/vite/registry-generator.js +0 -1108
  165. package/dist/cmd/build/vite/registry-generator.js.map +0 -1
  166. package/dist/cmd/build/webanalytics-generator.d.ts +0 -16
  167. package/dist/cmd/build/webanalytics-generator.d.ts.map +0 -1
  168. package/dist/cmd/build/webanalytics-generator.js +0 -178
  169. package/dist/cmd/build/webanalytics-generator.js.map +0 -1
  170. package/dist/cmd/build/workbench.d.ts +0 -7
  171. package/dist/cmd/build/workbench.d.ts.map +0 -1
  172. package/dist/cmd/build/workbench.js +0 -55
  173. package/dist/cmd/build/workbench.js.map +0 -1
  174. package/dist/utils/route-migration.d.ts +0 -62
  175. package/dist/utils/route-migration.d.ts.map +0 -1
  176. package/dist/utils/route-migration.js +0 -630
  177. package/dist/utils/route-migration.js.map +0 -1
  178. package/dist/utils/stream-capture.d.ts +0 -9
  179. package/dist/utils/stream-capture.d.ts.map +0 -1
  180. package/dist/utils/stream-capture.js +0 -34
  181. package/dist/utils/stream-capture.js.map +0 -1
  182. package/src/cmd/build/ast.ts +0 -3529
  183. package/src/cmd/build/entry-generator.ts +0 -760
  184. package/src/cmd/build/vite/api-mount-path.ts +0 -87
  185. package/src/cmd/build/vite/registry-generator.ts +0 -1267
  186. package/src/cmd/build/webanalytics-generator.ts +0 -197
  187. package/src/cmd/build/workbench.ts +0 -58
  188. package/src/utils/route-migration.ts +0 -757
  189. package/src/utils/stream-capture.ts +0 -39
@@ -1,125 +1,35 @@
1
1
  /**
2
- * Agent Discovery - READ-ONLY AST analysis
2
+ * Agent Discovery import-based
3
3
  *
4
- * Discovers agents by scanning src/agent/**\/*.ts files
5
- * Extracts metadata WITHOUT mutating source files
4
+ * Discovers agents by scanning src/agent/**\/*.ts files and importing them
5
+ * at build time. The agent instance already knows its own metadata, schemas,
6
+ * and evals — no AST parsing needed.
6
7
  */
7
- import * as acornLoose from 'acorn-loose';
8
- import { generate } from 'astring';
8
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
9
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
10
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
11
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
12
+ });
13
+ }
14
+ return path;
15
+ };
9
16
  import { dirname, join, relative } from 'node:path';
10
- import { existsSync } from 'node:fs';
11
- import { formatSchemaCode } from '../format-schema';
12
17
  import { StructuredError } from '@agentuity/core';
13
18
  import { toForwardSlash } from '../../../utils/normalize-path';
14
19
  const DuplicateEvalNameError = StructuredError('DuplicateEvalNameError')();
15
- /**
16
- * Build a file-local identifier resolver that maps top-level variable names
17
- * to their initializer AST nodes. This allows resolving schema variable references.
18
- */
19
- function buildIdentifierResolver(program) {
20
- const initMap = new Map();
21
- for (const node of program.body) {
22
- // const x = ... or let x = ... or var x = ...
23
- if (node.type === 'VariableDeclaration') {
24
- const decl = node;
25
- for (const d of decl.declarations) {
26
- if (d.id.type === 'Identifier' && d.init) {
27
- const id = d.id;
28
- initMap.set(id.name, d.init);
29
- }
30
- }
31
- }
32
- // export const x = ...
33
- if (node.type === 'ExportNamedDeclaration') {
34
- const exp = node;
35
- if (exp.declaration && exp.declaration.type === 'VariableDeclaration') {
36
- const decl = exp.declaration;
37
- for (const d of decl.declarations) {
38
- if (d.id.type === 'Identifier' && d.init) {
39
- const id = d.id;
40
- initMap.set(id.name, d.init);
41
- }
42
- }
43
- }
44
- }
45
- }
46
- return (name) => initMap.get(name);
47
- }
48
- /**
49
- * Get the property name from an AST node (Identifier or Literal).
50
- */
51
- function getPropertyName(node) {
52
- if (node.type === 'Identifier') {
53
- return node.name;
54
- }
55
- if (node.type === 'Literal') {
56
- const lit = node;
57
- return typeof lit.value === 'string' ? lit.value : undefined;
58
- }
59
- return undefined;
60
- }
61
- /**
62
- * Resolve an expression by following identifier references and member access chains.
63
- * Applies a recursion limit to prevent infinite loops from cyclic references.
64
- *
65
- * Supported patterns:
66
- * - Identifiers: `AgentInput` -> resolves to variable definition
67
- * - Member access: `configs.agent1.schema` -> traverses object literals
68
- */
69
- function resolveExpression(node, resolveIdentifier, depth = 0) {
70
- if (!node)
71
- return node;
72
- if (depth > 8)
73
- return node; // Prevent cycles / deep alias chains
74
- // Follow identifiers to their definitions
75
- if (node.type === 'Identifier') {
76
- const id = node;
77
- const resolved = resolveIdentifier(id.name);
78
- if (resolved) {
79
- return resolveExpression(resolved, resolveIdentifier, depth + 1);
80
- }
81
- }
82
- // Follow member expressions: configs.agent1.schema, baseSchemas.shared
83
- if (node.type === 'MemberExpression') {
84
- const memberExpr = node;
85
- // Skip computed properties like configs[agentName]
86
- if (memberExpr.computed)
87
- return node;
88
- const propName = getPropertyName(memberExpr.property);
89
- if (!propName)
90
- return node;
91
- // First resolve the object side (e.g., configs -> { agent1: {...} })
92
- const resolvedObj = resolveExpression(memberExpr.object, resolveIdentifier, depth + 1);
93
- // If we got an object literal, look up the property
94
- if (resolvedObj.type === 'ObjectExpression') {
95
- const obj = resolvedObj;
96
- for (const prop of obj.properties) {
97
- // Skip spread elements
98
- if (!prop || !('key' in prop) || !prop.key)
99
- continue;
100
- const keyName = getPropertyName(prop.key);
101
- if (keyName === propName && prop.value) {
102
- // Recurse into the property value
103
- return resolveExpression(prop.value, resolveIdentifier, depth + 1);
104
- }
105
- }
106
- }
107
- // Couldn't resolve - return original node
108
- return node;
109
- }
110
- return node;
111
- }
112
20
  /**
113
21
  * Hash function for generating stable IDs
114
22
  */
115
23
  function hash(...val) {
116
24
  const hasher = new Bun.CryptoHasher('sha256');
117
- val.forEach((v) => hasher.update(v));
25
+ for (const v of val)
26
+ hasher.update(v);
118
27
  return hasher.digest().toHex();
119
28
  }
120
29
  function hashSHA1(...val) {
121
30
  const hasher = new Bun.CryptoHasher('sha1');
122
- val.forEach((v) => hasher.update(v));
31
+ for (const v of val)
32
+ hasher.update(v);
123
33
  return hasher.digest().toHex();
124
34
  }
125
35
  function getAgentId(projectId, deploymentId, filename, version) {
@@ -135,423 +45,192 @@ function generateStableEvalId(projectId, agentId, name) {
135
45
  return `eval_${hashSHA1(projectId, agentId, name)}`.substring(0, 64);
136
46
  }
137
47
  /**
138
- * Check if a property key matches a given name.
139
- * Handles both Identifier keys (schema) and Literal keys ('schema').
140
- */
141
- function isKeyNamed(prop, name) {
142
- if (!prop || !prop.key)
143
- return false;
144
- if (prop.key.type === 'Identifier') {
145
- return prop.key.name === name;
146
- }
147
- if (prop.key.type === 'Literal') {
148
- const lit = prop.key;
149
- return typeof lit.value === 'string' && lit.value === name;
150
- }
151
- return false;
152
- }
153
- /**
154
- * Extract schema code from createAgent call arguments.
155
- * Resolves variable references to their actual definitions when possible.
156
- *
157
- * Supported patterns:
158
- * - Inline schema: `schema: { input: s.object({...}), output: s.object({...}) }`
159
- * - Variable reference: `schema: { input: AgentInput, output: AgentOutput }`
160
- * - Schema object variable: `schema: schemaVar` where schemaVar is a top-level const
161
- * - Shorthand: `schema: { input, output }` where input/output are top-level consts
162
- *
163
- * Unsupported patterns (returns empty or partial result):
164
- * - Config alias: `createAgent('x', configVar)` - config must be inline object
165
- * - Schema from member access: `schema: configs.agent1.schema`
166
- * - Schema from function call: `schema: getSchema()`
167
- * - Destructured variables: `const { schema } = config`
168
- * - Cross-file imports (falls back to identifier name)
169
- */
170
- function extractSchemaCode(callargexp, resolveIdentifier) {
171
- let schemaObj;
172
- // Find the schema property
173
- for (const prop of callargexp.properties) {
174
- // Skip spread elements or any non-Property nodes
175
- if (!prop || !('key' in prop) || !prop.key)
176
- continue;
177
- if (isKeyNamed(prop, 'schema')) {
178
- // Resolve the schema value if it's an identifier (e.g., schema: schemaVar)
179
- let valueNode = prop.value;
180
- valueNode = resolveExpression(valueNode, resolveIdentifier);
181
- if (valueNode.type === 'ObjectExpression') {
182
- schemaObj = valueNode;
183
- break;
184
- }
185
- }
186
- }
187
- if (!schemaObj) {
188
- return {};
189
- }
190
- let inputSchemaCode;
191
- let outputSchemaCode;
192
- // Extract input and output schema code
193
- for (const prop of schemaObj.properties) {
194
- // Skip spread elements or any non-Property nodes
195
- if (!prop || !('key' in prop) || !prop.key)
196
- continue;
197
- if (isKeyNamed(prop, 'input') && prop.value) {
198
- // Resolve variable reference if the value is an identifier
199
- const resolvedValue = resolveExpression(prop.value, resolveIdentifier);
200
- inputSchemaCode = formatSchemaCode(generate(resolvedValue));
201
- }
202
- else if (isKeyNamed(prop, 'output') && prop.value) {
203
- // Resolve variable reference if the value is an identifier
204
- const resolvedValue = resolveExpression(prop.value, resolveIdentifier);
205
- outputSchemaCode = formatSchemaCode(generate(resolvedValue));
206
- }
207
- }
208
- return { inputSchemaCode, outputSchemaCode };
209
- }
210
- /**
211
- * Parse object expression to extract metadata
212
- */
213
- function parseObjectExpressionToMap(expr) {
214
- const result = new Map();
215
- for (const prop of expr.properties) {
216
- if (prop.value.type === 'Literal') {
217
- const value = prop.value;
218
- result.set(prop.key.name, String(value.value));
219
- }
220
- }
221
- return result;
222
- }
223
- /**
224
- * Extract metadata from createAgent call (READ-ONLY)
48
+ * Convert a StandardSchemaV1-compatible schema to a JSON Schema string.
49
+ * Dynamically imports toJSONSchema from @agentuity/schema (available in user's project).
225
50
  */
226
- function extractAgentMetadata(code, filename, projectId, deploymentId) {
227
- const ast = acornLoose.parse(code, {
228
- ecmaVersion: 'latest',
229
- sourceType: 'module',
230
- });
231
- // Build identifier resolver for resolving schema variable references
232
- const resolveIdentifier = buildIdentifierResolver(ast);
233
- // Calculate file version (hash of contents)
234
- const version = hash(code);
235
- // Find createAgent calls
236
- for (const node of ast.body) {
237
- if (node.type === 'ExportDefaultDeclaration') {
238
- const declaration = node.declaration;
239
- if (declaration.type === 'CallExpression') {
240
- const callExpr = declaration;
241
- if (callExpr.callee.type === 'Identifier' &&
242
- callExpr.callee.name === 'createAgent' &&
243
- callExpr.arguments.length >= 2) {
244
- // First arg is agent name
245
- const nameArg = callExpr.arguments[0];
246
- const name = String(nameArg.value);
247
- // Second arg is config object
248
- const callargexp = callExpr.arguments[1];
249
- // Extract schemas (with variable resolution)
250
- const { inputSchemaCode, outputSchemaCode } = extractSchemaCode(callargexp, resolveIdentifier);
251
- // Extract description from either direct property or metadata object
252
- let description;
253
- for (const prop of callargexp.properties) {
254
- // Check for direct description property
255
- if (prop.key.name === 'description' && prop.value.type === 'Literal') {
256
- description = String(prop.value.value);
257
- break; // Direct description takes precedence
258
- }
259
- // Also check metadata.description for backwards compat
260
- if (prop.key.name === 'metadata' && prop.value.type === 'ObjectExpression') {
261
- const metadataMap = parseObjectExpressionToMap(prop.value);
262
- if (!description) {
263
- description = metadataMap.get('description');
264
- }
265
- break;
266
- }
267
- }
268
- // Generate IDs
269
- const id = getAgentId(projectId, deploymentId, filename, version);
270
- const agentId = generateStableAgentId(projectId, name);
271
- return {
272
- filename,
273
- name,
274
- id,
275
- agentId,
276
- version,
277
- description,
278
- inputSchemaCode,
279
- outputSchemaCode,
280
- };
281
- }
282
- }
283
- }
284
- // Also check variable declarations (e.g., const agent = createAgent(...))
285
- if (node.type === 'VariableDeclaration') {
286
- const declarations = node
287
- .declarations;
288
- for (const decl of declarations) {
289
- if (decl.init && decl.init.type === 'CallExpression') {
290
- const callExpr = decl.init;
291
- if (callExpr.callee.type === 'Identifier' &&
292
- callExpr.callee.name === 'createAgent' &&
293
- callExpr.arguments.length >= 2) {
294
- const nameArg = callExpr.arguments[0];
295
- const name = String(nameArg.value);
296
- const callargexp = callExpr.arguments[1];
297
- const { inputSchemaCode, outputSchemaCode } = extractSchemaCode(callargexp, resolveIdentifier);
298
- let description;
299
- for (const prop of callargexp.properties) {
300
- // Check for direct description property
301
- if (prop.key.name === 'description' && prop.value.type === 'Literal') {
302
- description = String(prop.value.value);
303
- break; // Direct description takes precedence
304
- }
305
- // Also check metadata.description for backwards compat
306
- if (prop.key.name === 'metadata' && prop.value.type === 'ObjectExpression') {
307
- const metadataMap = parseObjectExpressionToMap(prop.value);
308
- if (!description) {
309
- description = metadataMap.get('description');
310
- }
311
- break;
312
- }
313
- }
314
- const id = getAgentId(projectId, deploymentId, filename, version);
315
- const agentId = generateStableAgentId(projectId, name);
316
- return {
317
- filename,
318
- name,
319
- id,
320
- agentId,
321
- version,
322
- description,
323
- inputSchemaCode,
324
- outputSchemaCode,
325
- };
326
- }
327
- }
328
- }
329
- }
330
- }
331
- return null;
332
- }
333
- /**
334
- * Extract evals from a file (READ-ONLY)
335
- * Finds createEval calls regardless of whether they're exported or not
336
- */
337
- async function extractEvalMetadata(evalsPath, relativeEvalsPath, agentId, projectId, deploymentId, logger) {
338
- const evalsFile = Bun.file(evalsPath);
339
- if (!(await evalsFile.exists())) {
340
- return [];
341
- }
51
+ async function schemaToJsonString(schema, rootDir, logger) {
52
+ if (!schema)
53
+ return undefined;
342
54
  try {
343
- const evalsSource = await evalsFile.text();
344
- return extractEvalsFromSource(evalsSource, relativeEvalsPath, agentId, projectId, deploymentId, logger);
55
+ // Resolve @agentuity/schema from the user's project and import its public entry point.
56
+ // The CLI doesn't declare @agentuity/schema as its own dependency — it lives in the
57
+ // user's node_modules, so we resolve the path dynamically.
58
+ const schemaPackageDir = join(rootDir, 'node_modules', '@agentuity', 'schema');
59
+ if (!(await Bun.file(join(schemaPackageDir, 'package.json')).exists())) {
60
+ logger.debug('[agent-discovery] @agentuity/schema not found in user project');
61
+ return undefined;
62
+ }
63
+ const { toJSONSchema } = await import(__rewriteRelativeImportExtension(schemaPackageDir));
64
+ const jsonSchema = toJSONSchema(schema);
65
+ return JSON.stringify(jsonSchema);
345
66
  }
346
67
  catch (error) {
347
- logger.warn(`Failed to parse evals from ${evalsPath}: ${error}`);
348
- return [];
68
+ logger.debug('[agent-discovery] Failed to convert schema to JSON Schema: %s', error instanceof Error ? error.message : String(error));
69
+ return undefined;
349
70
  }
350
71
  }
351
72
  /**
352
- * Extract evals from source code (READ-ONLY)
353
- * Finds all createEval calls in the source, exported or not
73
+ * Import an agent file and extract metadata from the agent instance.
354
74
  */
355
- function extractEvalsFromSource(source, filename, agentId, projectId, deploymentId, logger) {
356
- // Quick check - skip if no createEval in source
357
- if (!source.includes('createEval')) {
358
- return [];
359
- }
75
+ async function importAgentMetadata(filePath, relativeFilename, rootDir, projectId, deploymentId, logger) {
360
76
  try {
361
- const transpiler = new Bun.Transpiler({ loader: 'ts', target: 'bun' });
362
- const contents = transpiler.transformSync(source);
363
- const version = hash(contents);
364
- const ast = acornLoose.parse(contents, { ecmaVersion: 'latest', sourceType: 'module' });
77
+ const source = await Bun.file(filePath).text();
78
+ const version = hash(source);
79
+ // Import the agent file — Bun handles TS natively
80
+ // No source-level gate: files may re-export agents created elsewhere
81
+ const mod = await import(__rewriteRelativeImportExtension(filePath));
82
+ const agent = mod.default;
83
+ if (!agent?.metadata?.name) {
84
+ logger.debug('[agent-discovery] No valid agent found in %s', relativeFilename);
85
+ return null;
86
+ }
87
+ const name = agent.metadata.name;
88
+ const description = agent.metadata.description;
89
+ const id = getAgentId(projectId, deploymentId, relativeFilename, version);
90
+ const agentId = generateStableAgentId(projectId, name);
91
+ // Extract schemas as JSON Schema strings
92
+ const inputSchemaCode = await schemaToJsonString(agent.inputSchema, rootDir, logger);
93
+ const outputSchemaCode = await schemaToJsonString(agent.outputSchema, rootDir, logger);
94
+ // Extract evals from agent.evals array (self-registered by createEval())
365
95
  const evals = [];
366
- // Recursively find all createEval calls in the AST
367
- function findCreateEvalCalls(node) {
368
- if (!node || typeof node !== 'object')
369
- return;
370
- const n = node;
371
- // Check if this is a createEval call (either direct or method call)
372
- // Direct: createEval('name', {...})
373
- // Method: agent.createEval('name', {...})
374
- let isCreateEvalCall = false;
375
- if (n.type === 'CallExpression' && n.callee && typeof n.callee === 'object') {
376
- const callee = n.callee;
377
- // Direct function call: createEval(...)
378
- if (callee.type === 'Identifier' &&
379
- callee.name === 'createEval') {
380
- isCreateEvalCall = true;
381
- }
382
- // Method call: someAgent.createEval(...)
383
- if (callee.type === 'MemberExpression' &&
384
- callee.property &&
385
- callee.property.type === 'Identifier' &&
386
- callee.property.name === 'createEval') {
387
- isCreateEvalCall = true;
388
- }
96
+ if (agent.evals && Array.isArray(agent.evals) && agent.evals.length > 0) {
97
+ for (const evalItem of agent.evals) {
98
+ const evalName = evalItem.metadata?.name ?? evalItem.name;
99
+ if (!evalName)
100
+ continue;
101
+ const evalDescription = evalItem.metadata?.description ?? evalItem.description;
102
+ const evalVersion = version; // same file version
103
+ const evalId = getEvalId(projectId, deploymentId, relativeFilename, evalName, evalVersion);
104
+ const evalIdentifier = generateStableEvalId(projectId, agentId, evalName);
105
+ logger.trace('Found eval "%s" in %s (identifier: %s)', evalName, relativeFilename, evalIdentifier);
106
+ evals.push({
107
+ id: evalId,
108
+ identifier: evalIdentifier,
109
+ name: evalName,
110
+ filename: relativeFilename,
111
+ version: evalVersion,
112
+ description: evalDescription,
113
+ agentIdentifier: agentId,
114
+ projectId,
115
+ });
389
116
  }
390
- if (isCreateEvalCall) {
391
- const callExpr = n;
392
- let evalName;
393
- let description;
394
- if (callExpr.arguments.length >= 2) {
395
- // Format: agent.createEval('name', { config })
396
- const nameArg = callExpr.arguments[0];
397
- evalName = String(nameArg.value);
398
- const callargexp = callExpr.arguments[1];
399
- if (callargexp.properties) {
400
- for (const prop of callargexp.properties) {
401
- if (prop.key.name === 'metadata' && prop.value.type === 'ObjectExpression') {
402
- const metadataMap = parseObjectExpressionToMap(prop.value);
403
- description = metadataMap.get('description');
404
- break;
405
- }
406
- }
407
- }
408
- }
409
- else if (callExpr.arguments.length === 1) {
410
- // Format: agent.createEval(presetEval({ name: '...', ... }))
411
- // or: agent.createEval(presetEval()) - uses preset's default name
412
- // or: agent.createEval({ name: '...', ... })
413
- const arg = callExpr.arguments[0];
414
- // Handle CallExpression: presetEval({ name: '...' }) or presetEval()
415
- if (arg.type === 'CallExpression') {
416
- const innerCall = arg;
417
- // Try to get name from the call arguments first
418
- if (innerCall.arguments.length >= 1) {
419
- const configArg = innerCall.arguments[0];
420
- if (configArg.type === 'ObjectExpression' && configArg.properties) {
421
- const configMap = parseObjectExpressionToMap(configArg);
422
- evalName = configMap.get('name');
423
- description = configMap.get('description');
424
- }
425
- }
426
- // Fallback: use the callee name as the eval name (e.g., politeness())
427
- if (!evalName && innerCall.callee) {
428
- const callee = innerCall.callee;
429
- if (callee.type === 'Identifier') {
430
- evalName = callee.name;
431
- }
432
- }
433
- }
434
- // Handle ObjectExpression: { name: '...', handler: ... }
435
- if (arg.type === 'ObjectExpression') {
436
- const configArg = arg;
437
- if (configArg.properties) {
438
- const configMap = parseObjectExpressionToMap(configArg);
439
- evalName = configMap.get('name');
440
- description = configMap.get('description');
117
+ }
118
+ // Also check for evals in separate eval.ts file in same directory
119
+ const agentDir = dirname(filePath);
120
+ const evalsPath = join(agentDir, 'eval.ts');
121
+ if (await Bun.file(evalsPath).exists()) {
122
+ const evalsSource = await Bun.file(evalsPath).text();
123
+ if (evalsSource.includes('createEval')) {
124
+ try {
125
+ await import(__rewriteRelativeImportExtension(evalsPath));
126
+ // After importing, the evals self-register on the agent via agent.createEval()
127
+ // Re-check agent.evals for any newly registered evals
128
+ if (agent.evals && Array.isArray(agent.evals)) {
129
+ const relativeEvalsPath = toForwardSlash(relative(join(rootDir), evalsPath));
130
+ const evalVersion = hash(evalsSource);
131
+ for (const evalItem of agent.evals) {
132
+ const evalName = evalItem.metadata?.name ?? evalItem.name;
133
+ if (!evalName)
134
+ continue;
135
+ // Skip if already collected from agent file
136
+ if (evals.some((e) => e.name === evalName))
137
+ continue;
138
+ const evalDescription = evalItem.metadata?.description ?? evalItem.description;
139
+ const evalId = getEvalId(projectId, deploymentId, relativeEvalsPath, evalName, evalVersion);
140
+ const evalIdentifier = generateStableEvalId(projectId, agentId, evalName);
141
+ logger.trace('Found eval "%s" in eval.ts for agent %s (identifier: %s)', evalName, name, evalIdentifier);
142
+ evals.push({
143
+ id: evalId,
144
+ identifier: evalIdentifier,
145
+ name: evalName,
146
+ filename: relativeEvalsPath,
147
+ version: evalVersion,
148
+ description: evalDescription,
149
+ agentIdentifier: agentId,
150
+ projectId,
151
+ });
441
152
  }
442
153
  }
443
154
  }
444
- if (evalName) {
445
- const id = getEvalId(projectId, deploymentId, filename, evalName, version);
446
- const identifier = generateStableEvalId(projectId, agentId, evalName);
447
- logger.trace(`Found eval '${evalName}' in ${filename} (identifier: ${identifier})`);
448
- evals.push({
449
- id,
450
- identifier,
451
- name: evalName,
452
- filename,
453
- version,
454
- description,
455
- agentIdentifier: agentId,
456
- projectId,
457
- });
155
+ catch (error) {
156
+ logger.warn('[agent-discovery] Failed to import evals from %s: %s', evalsPath, error instanceof Error ? error.message : String(error));
458
157
  }
459
158
  }
460
- // Recursively search child nodes
461
- for (const key of Object.keys(n)) {
462
- const value = n[key];
463
- if (Array.isArray(value)) {
464
- for (const item of value) {
465
- findCreateEvalCalls(item);
466
- }
467
- }
468
- else if (value && typeof value === 'object') {
469
- findCreateEvalCalls(value);
159
+ }
160
+ // Check for duplicate eval names across sources (agent file + eval.ts).
161
+ // Same name + same agent = same stable identifier → duplicate key error
162
+ // on the backend database.
163
+ if (evals.length > 1) {
164
+ const seen = new Map();
165
+ for (const evalItem of evals) {
166
+ const prev = seen.get(evalItem.name);
167
+ if (prev && prev !== evalItem.filename) {
168
+ throw new DuplicateEvalNameError({
169
+ message: `Duplicate eval name '${evalItem.name}' for agent '${name}': ` +
170
+ `defined in both '${prev}' and '${evalItem.filename}'. ` +
171
+ 'Eval names must be unique per agent.',
172
+ agent: name,
173
+ filename: evalItem.filename,
174
+ });
470
175
  }
176
+ seen.set(evalItem.name, evalItem.filename);
471
177
  }
472
178
  }
473
- findCreateEvalCalls(ast);
474
- return evals;
179
+ return {
180
+ filename: relativeFilename,
181
+ name,
182
+ id,
183
+ agentId,
184
+ version,
185
+ description,
186
+ inputSchemaCode,
187
+ outputSchemaCode,
188
+ evals: evals.length > 0 ? evals : undefined,
189
+ };
475
190
  }
476
191
  catch (error) {
477
- logger.warn(`Failed to parse evals from ${filename}: ${error}`);
478
- return [];
192
+ logger.warn('[agent-discovery] Failed to import agent %s: %s', filePath, error instanceof Error ? error.message : String(error));
193
+ return null;
479
194
  }
480
195
  }
481
196
  /**
482
- * Discover all agents in src/agent directory (READ-ONLY)
197
+ * Discover all agents in src/agent directory.
198
+ *
199
+ * Imports each agent file at build time — the agent instance already knows
200
+ * its own metadata, schemas, and evals. No AST parsing needed.
483
201
  */
484
202
  export async function discoverAgents(srcDir, projectId, deploymentId, logger) {
485
203
  const agentsDir = join(srcDir, 'agent');
486
204
  const agents = [];
487
- // Check if agent directory exists
488
- if (!existsSync(agentsDir)) {
205
+ const rootDir = join(srcDir, '..');
206
+ // Scan all .ts files in agent directory
207
+ const glob = new Bun.Glob('**/*.ts');
208
+ let files;
209
+ try {
210
+ files = await Array.fromAsync(glob.scan(agentsDir));
211
+ }
212
+ catch {
489
213
  logger.trace('No agent directory found at %s', agentsDir);
490
214
  return agents;
491
215
  }
492
- const transpiler = new Bun.Transpiler({ loader: 'ts', target: 'bun' });
493
- // Scan all .ts files in agent directory
494
- const glob = new Bun.Glob('**/*.ts');
495
- for await (const file of glob.scan(agentsDir)) {
216
+ // Track seen agent names to deduplicate — e.g., index.ts re-exporting agent.ts
217
+ const seenAgentNames = new Set();
218
+ for (const file of files) {
496
219
  const filePath = join(agentsDir, file);
497
- // Skip eval.ts files (processed separately)
220
+ // Skip eval.ts files (processed as part of agent discovery)
498
221
  if (file.endsWith('/eval.ts') || file === 'eval.ts') {
499
222
  continue;
500
223
  }
501
- try {
502
- const source = await Bun.file(filePath).text();
503
- const contents = transpiler.transformSync(source);
504
- // Use 'src/' prefix for consistency with bun bundler and registry imports
505
- const rootDir = join(srcDir, '..');
506
- const relativeFilename = toForwardSlash(relative(rootDir, filePath));
507
- const agentMetadata = extractAgentMetadata(contents, relativeFilename, projectId, deploymentId);
508
- if (agentMetadata) {
509
- logger.trace('Discovered agent: %s at %s', agentMetadata.name, relativeFilename);
510
- // Collect evals from multiple sources
511
- const allEvals = [];
512
- // 1. Extract evals from the agent file itself (agent.createEval() pattern)
513
- const evalsInAgentFile = extractEvalsFromSource(source, relativeFilename, agentMetadata.agentId, projectId, deploymentId, logger);
514
- if (evalsInAgentFile.length > 0) {
515
- logger.trace('Found %d eval(s) in agent file for %s', evalsInAgentFile.length, agentMetadata.name);
516
- allEvals.push(...evalsInAgentFile);
517
- }
518
- // 2. Check for evals in separate eval.ts file in same directory
519
- const agentDir = dirname(filePath);
520
- const evalsPath = join(agentDir, 'eval.ts');
521
- const relativeEvalsPath = toForwardSlash(relative(rootDir, evalsPath));
522
- const evalsInSeparateFile = await extractEvalMetadata(evalsPath, relativeEvalsPath, agentMetadata.agentId, projectId, deploymentId, logger);
523
- if (evalsInSeparateFile.length > 0) {
524
- logger.trace('Found %d eval(s) in eval.ts for agent %s', evalsInSeparateFile.length, agentMetadata.name);
525
- allEvals.push(...evalsInSeparateFile);
526
- }
527
- // Check for duplicate eval names across sources (agent file + eval.ts)
528
- // Same name + same agent = same stable identifier, which causes a
529
- // duplicate key error on the backend database.
530
- if (allEvals.length > 1) {
531
- const seen = new Map();
532
- for (const evalItem of allEvals) {
533
- const prev = seen.get(evalItem.name);
534
- if (prev && prev !== evalItem.filename) {
535
- throw new DuplicateEvalNameError({
536
- message: `Duplicate eval name '${evalItem.name}' for agent '${agentMetadata.name}': ` +
537
- `defined in both '${prev}' and '${evalItem.filename}'. ` +
538
- 'Eval names must be unique per agent.',
539
- agent: agentMetadata.name,
540
- filename: evalItem.filename,
541
- });
542
- }
543
- seen.set(evalItem.name, evalItem.filename);
544
- }
545
- }
546
- if (allEvals.length > 0) {
547
- agentMetadata.evals = allEvals;
548
- logger.trace('Total %d eval(s) for agent %s', allEvals.length, agentMetadata.name);
549
- }
550
- agents.push(agentMetadata);
224
+ const relativeFilename = toForwardSlash(relative(rootDir, filePath));
225
+ const agentMetadata = await importAgentMetadata(filePath, relativeFilename, rootDir, projectId, deploymentId, logger);
226
+ if (agentMetadata) {
227
+ if (seenAgentNames.has(agentMetadata.name)) {
228
+ logger.trace('Skipping duplicate agent %s from %s (already discovered)', agentMetadata.name, relativeFilename);
229
+ continue;
551
230
  }
552
- }
553
- catch (error) {
554
- logger.warn(`Failed to parse agent file ${filePath}: ${error}`);
231
+ seenAgentNames.add(agentMetadata.name);
232
+ logger.trace('Discovered agent: %s at %s', agentMetadata.name, relativeFilename);
233
+ agents.push(agentMetadata);
555
234
  }
556
235
  }
557
236
  logger.debug('Discovered %d agent(s)', agents.length);