@agentuity/cli 1.0.48 → 2.0.0-beta.0
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.
- package/dist/cmd/build/app-router-detector.d.ts +2 -5
- package/dist/cmd/build/app-router-detector.d.ts.map +1 -1
- package/dist/cmd/build/app-router-detector.js +130 -154
- package/dist/cmd/build/app-router-detector.js.map +1 -1
- package/dist/cmd/build/ids.d.ts +11 -0
- package/dist/cmd/build/ids.d.ts.map +1 -0
- package/dist/cmd/build/ids.js +18 -0
- package/dist/cmd/build/ids.js.map +1 -0
- package/dist/cmd/build/vite/agent-discovery.d.ts +8 -4
- package/dist/cmd/build/vite/agent-discovery.d.ts.map +1 -1
- package/dist/cmd/build/vite/agent-discovery.js +166 -487
- package/dist/cmd/build/vite/agent-discovery.js.map +1 -1
- package/dist/cmd/build/vite/bun-dev-server.d.ts +10 -16
- package/dist/cmd/build/vite/bun-dev-server.d.ts.map +1 -1
- package/dist/cmd/build/vite/bun-dev-server.js +67 -134
- package/dist/cmd/build/vite/bun-dev-server.js.map +1 -1
- package/dist/cmd/build/vite/docs-generator.d.ts.map +1 -1
- package/dist/cmd/build/vite/docs-generator.js +0 -2
- package/dist/cmd/build/vite/docs-generator.js.map +1 -1
- package/dist/cmd/build/vite/index.d.ts.map +1 -1
- package/dist/cmd/build/vite/index.js +0 -36
- package/dist/cmd/build/vite/index.js.map +1 -1
- package/dist/cmd/build/vite/lifecycle-generator.d.ts +10 -2
- package/dist/cmd/build/vite/lifecycle-generator.d.ts.map +1 -1
- package/dist/cmd/build/vite/lifecycle-generator.js +302 -23
- package/dist/cmd/build/vite/lifecycle-generator.js.map +1 -1
- package/dist/cmd/build/vite/route-discovery.d.ts +11 -38
- package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -1
- package/dist/cmd/build/vite/route-discovery.js +97 -177
- package/dist/cmd/build/vite/route-discovery.js.map +1 -1
- package/dist/cmd/build/vite/server-bundler.js +1 -1
- package/dist/cmd/build/vite/server-bundler.js.map +1 -1
- package/dist/cmd/build/vite/static-renderer.d.ts.map +1 -1
- package/dist/cmd/build/vite/static-renderer.js +1 -9
- package/dist/cmd/build/vite/static-renderer.js.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server-config.d.ts +6 -3
- package/dist/cmd/build/vite/vite-asset-server-config.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server-config.js +171 -21
- package/dist/cmd/build/vite/vite-asset-server-config.js.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server.d.ts +8 -3
- package/dist/cmd/build/vite/vite-asset-server.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server.js +14 -13
- package/dist/cmd/build/vite/vite-asset-server.js.map +1 -1
- package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-builder.js +6 -36
- package/dist/cmd/build/vite/vite-builder.js.map +1 -1
- package/dist/cmd/build/vite/ws-proxy.d.ts +53 -0
- package/dist/cmd/build/vite/ws-proxy.d.ts.map +1 -0
- package/dist/cmd/build/vite/ws-proxy.js +95 -0
- package/dist/cmd/build/vite/ws-proxy.js.map +1 -0
- package/dist/cmd/build/vite-bundler.d.ts.map +1 -1
- package/dist/cmd/build/vite-bundler.js +0 -3
- package/dist/cmd/build/vite-bundler.js.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.js +0 -1
- package/dist/cmd/cloud/deploy.js.map +1 -1
- package/dist/cmd/dev/file-watcher.d.ts.map +1 -1
- package/dist/cmd/dev/file-watcher.js +2 -8
- package/dist/cmd/dev/file-watcher.js.map +1 -1
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/index.js +369 -720
- package/dist/cmd/dev/index.js.map +1 -1
- package/package.json +6 -8
- package/src/cmd/ai/prompt/agent.md +0 -1
- package/src/cmd/ai/prompt/api.md +0 -7
- package/src/cmd/ai/prompt/web.md +51 -213
- package/src/cmd/build/app-router-detector.ts +152 -182
- package/src/cmd/build/ids.ts +19 -0
- package/src/cmd/build/vite/agent-discovery.ts +208 -679
- package/src/cmd/build/vite/bun-dev-server.ts +78 -154
- package/src/cmd/build/vite/docs-generator.ts +0 -2
- package/src/cmd/build/vite/index.ts +1 -42
- package/src/cmd/build/vite/lifecycle-generator.ts +345 -21
- package/src/cmd/build/vite/route-discovery.ts +116 -274
- package/src/cmd/build/vite/server-bundler.ts +1 -1
- package/src/cmd/build/vite/static-renderer.ts +1 -11
- package/src/cmd/build/vite/vite-asset-server-config.ts +196 -23
- package/src/cmd/build/vite/vite-asset-server.ts +25 -15
- package/src/cmd/build/vite/vite-builder.ts +6 -53
- package/src/cmd/build/vite/ws-proxy.ts +126 -0
- package/src/cmd/build/vite-bundler.ts +0 -4
- package/src/cmd/cloud/deploy.ts +0 -1
- package/src/cmd/dev/file-watcher.ts +2 -9
- package/src/cmd/dev/index.ts +409 -832
- package/dist/cmd/build/ast.d.ts +0 -78
- package/dist/cmd/build/ast.d.ts.map +0 -1
- package/dist/cmd/build/ast.js +0 -2703
- package/dist/cmd/build/ast.js.map +0 -1
- package/dist/cmd/build/entry-generator.d.ts +0 -25
- package/dist/cmd/build/entry-generator.d.ts.map +0 -1
- package/dist/cmd/build/entry-generator.js +0 -695
- package/dist/cmd/build/entry-generator.js.map +0 -1
- package/dist/cmd/build/vite/api-mount-path.d.ts +0 -61
- package/dist/cmd/build/vite/api-mount-path.d.ts.map +0 -1
- package/dist/cmd/build/vite/api-mount-path.js +0 -83
- package/dist/cmd/build/vite/api-mount-path.js.map +0 -1
- package/dist/cmd/build/vite/registry-generator.d.ts +0 -19
- package/dist/cmd/build/vite/registry-generator.d.ts.map +0 -1
- package/dist/cmd/build/vite/registry-generator.js +0 -1108
- package/dist/cmd/build/vite/registry-generator.js.map +0 -1
- package/dist/cmd/build/vite/tailwind-source-plugin.d.ts +0 -13
- package/dist/cmd/build/vite/tailwind-source-plugin.d.ts.map +0 -1
- package/dist/cmd/build/vite/tailwind-source-plugin.js +0 -44
- package/dist/cmd/build/vite/tailwind-source-plugin.js.map +0 -1
- package/dist/cmd/build/webanalytics-generator.d.ts +0 -16
- package/dist/cmd/build/webanalytics-generator.d.ts.map +0 -1
- package/dist/cmd/build/webanalytics-generator.js +0 -178
- package/dist/cmd/build/webanalytics-generator.js.map +0 -1
- package/dist/cmd/build/workbench.d.ts +0 -7
- package/dist/cmd/build/workbench.d.ts.map +0 -1
- package/dist/cmd/build/workbench.js +0 -55
- package/dist/cmd/build/workbench.js.map +0 -1
- package/dist/utils/route-migration.d.ts +0 -62
- package/dist/utils/route-migration.d.ts.map +0 -1
- package/dist/utils/route-migration.js +0 -630
- package/dist/utils/route-migration.js.map +0 -1
- package/src/cmd/build/ast.ts +0 -3529
- package/src/cmd/build/entry-generator.ts +0 -760
- package/src/cmd/build/vite/api-mount-path.ts +0 -87
- package/src/cmd/build/vite/registry-generator.ts +0 -1267
- package/src/cmd/build/vite/tailwind-source-plugin.ts +0 -54
- package/src/cmd/build/webanalytics-generator.ts +0 -197
- package/src/cmd/build/workbench.ts +0 -58
- package/src/utils/route-migration.ts +0 -757
|
@@ -1,125 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Agent Discovery
|
|
2
|
+
* Agent Discovery — import-based
|
|
3
3
|
*
|
|
4
|
-
* Discovers agents by scanning src/agent/**\/*.ts files
|
|
5
|
-
*
|
|
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
|
-
|
|
8
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
139
|
-
*
|
|
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
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
|
|
344
|
-
|
|
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.
|
|
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
|
-
*
|
|
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
|
|
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
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
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
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
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
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
-
|
|
445
|
-
|
|
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
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
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
|
-
|
|
474
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
488
|
-
|
|
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
|
-
|
|
493
|
-
|
|
494
|
-
const
|
|
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
|
|
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
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
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
|
-
|
|
554
|
-
|
|
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);
|