@agentuity/cli 0.0.69 → 0.0.71

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 (159) 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 +4 -1
  13. package/dist/cmd/build/bundler.d.ts.map +1 -1
  14. package/dist/cmd/build/bundler.js +89 -6
  15. package/dist/cmd/build/bundler.js.map +1 -1
  16. package/dist/cmd/build/index.d.ts.map +1 -1
  17. package/dist/cmd/build/index.js +2 -1
  18. package/dist/cmd/build/index.js.map +1 -1
  19. package/dist/cmd/build/plugin.d.ts.map +1 -1
  20. package/dist/cmd/build/plugin.js +152 -414
  21. package/dist/cmd/build/plugin.js.map +1 -1
  22. package/dist/cmd/build/route-registry.d.ts +36 -0
  23. package/dist/cmd/build/route-registry.d.ts.map +1 -0
  24. package/dist/cmd/build/route-registry.js +151 -0
  25. package/dist/cmd/build/route-registry.js.map +1 -0
  26. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  27. package/dist/cmd/cloud/deploy.js +2 -0
  28. package/dist/cmd/cloud/deploy.js.map +1 -1
  29. package/dist/cmd/cloud/index.d.ts.map +1 -1
  30. package/dist/cmd/cloud/index.js +0 -2
  31. package/dist/cmd/cloud/index.js.map +1 -1
  32. package/dist/cmd/dev/index.d.ts.map +1 -1
  33. package/dist/cmd/dev/index.js +4 -3
  34. package/dist/cmd/dev/index.js.map +1 -1
  35. package/dist/cmd/dev/sync.d.ts.map +1 -1
  36. package/dist/cmd/dev/sync.js +0 -15
  37. package/dist/cmd/dev/sync.js.map +1 -1
  38. package/dist/cmd/dev/templates.d.ts.map +1 -1
  39. package/dist/cmd/dev/templates.js +11 -35
  40. package/dist/cmd/dev/templates.js.map +1 -1
  41. package/dist/cmd/profile/create.d.ts.map +1 -1
  42. package/dist/cmd/profile/create.js +0 -1
  43. package/dist/cmd/profile/create.js.map +1 -1
  44. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  45. package/dist/cmd/project/template-flow.js +64 -53
  46. package/dist/cmd/project/template-flow.js.map +1 -1
  47. package/dist/config.d.ts.map +1 -1
  48. package/dist/config.js +0 -3
  49. package/dist/config.js.map +1 -1
  50. package/dist/env-util.d.ts.map +1 -1
  51. package/dist/env-util.js +0 -3
  52. package/dist/env-util.js.map +1 -1
  53. package/dist/tui/box.d.ts +19 -0
  54. package/dist/tui/box.d.ts.map +1 -0
  55. package/dist/tui/box.js +160 -0
  56. package/dist/tui/box.js.map +1 -0
  57. package/dist/tui/colors.d.ts +20 -0
  58. package/dist/tui/colors.d.ts.map +1 -0
  59. package/dist/tui/colors.js +52 -0
  60. package/dist/tui/colors.js.map +1 -0
  61. package/dist/tui/group.d.ts +25 -0
  62. package/dist/tui/group.d.ts.map +1 -0
  63. package/dist/tui/group.js +32 -0
  64. package/dist/tui/group.js.map +1 -0
  65. package/dist/tui/prompt.d.ts +65 -0
  66. package/dist/tui/prompt.d.ts.map +1 -0
  67. package/dist/tui/prompt.js +377 -0
  68. package/dist/tui/prompt.js.map +1 -0
  69. package/dist/tui/symbols.d.ts +32 -0
  70. package/dist/tui/symbols.d.ts.map +1 -0
  71. package/dist/tui/symbols.js +52 -0
  72. package/dist/tui/symbols.js.map +1 -0
  73. package/dist/tui.d.ts +6 -0
  74. package/dist/tui.d.ts.map +1 -1
  75. package/dist/tui.js +6 -0
  76. package/dist/tui.js.map +1 -1
  77. package/dist/types.d.ts +0 -24
  78. package/dist/types.d.ts.map +1 -1
  79. package/dist/types.js +0 -1
  80. package/dist/types.js.map +1 -1
  81. package/package.json +3 -3
  82. package/src/cmd/ai/capabilities/show.ts +0 -13
  83. package/src/cmd/ai/prompt/agent.ts +1 -1
  84. package/src/cmd/ai/prompt/api.ts +1 -1
  85. package/src/cmd/build/ast.ts +364 -334
  86. package/src/cmd/build/bundler.ts +123 -9
  87. package/src/cmd/build/index.ts +2 -1
  88. package/src/cmd/build/plugin.ts +171 -493
  89. package/src/cmd/build/route-registry.ts +198 -0
  90. package/src/cmd/cloud/deploy.ts +2 -0
  91. package/src/cmd/cloud/index.ts +0 -2
  92. package/src/cmd/dev/index.ts +4 -3
  93. package/src/cmd/dev/sync.ts +0 -28
  94. package/src/cmd/dev/templates.ts +11 -35
  95. package/src/cmd/profile/create.ts +0 -1
  96. package/src/cmd/project/template-flow.ts +77 -57
  97. package/src/config.ts +0 -3
  98. package/src/env-util.ts +0 -3
  99. package/src/tui/box.ts +202 -0
  100. package/src/tui/colors.ts +55 -0
  101. package/src/tui/group.ts +56 -0
  102. package/src/tui/prompt.ts +506 -0
  103. package/src/tui/symbols.ts +64 -0
  104. package/src/tui.ts +14 -0
  105. package/src/types.ts +0 -1
  106. package/dist/cmd/cloud/objectstore/delete-bucket.d.ts +0 -3
  107. package/dist/cmd/cloud/objectstore/delete-bucket.d.ts.map +0 -1
  108. package/dist/cmd/cloud/objectstore/delete-bucket.js +0 -72
  109. package/dist/cmd/cloud/objectstore/delete-bucket.js.map +0 -1
  110. package/dist/cmd/cloud/objectstore/delete.d.ts +0 -3
  111. package/dist/cmd/cloud/objectstore/delete.d.ts.map +0 -1
  112. package/dist/cmd/cloud/objectstore/delete.js +0 -65
  113. package/dist/cmd/cloud/objectstore/delete.js.map +0 -1
  114. package/dist/cmd/cloud/objectstore/get.d.ts +0 -3
  115. package/dist/cmd/cloud/objectstore/get.d.ts.map +0 -1
  116. package/dist/cmd/cloud/objectstore/get.js +0 -75
  117. package/dist/cmd/cloud/objectstore/get.js.map +0 -1
  118. package/dist/cmd/cloud/objectstore/index.d.ts +0 -3
  119. package/dist/cmd/cloud/objectstore/index.d.ts.map +0 -1
  120. package/dist/cmd/cloud/objectstore/index.js +0 -36
  121. package/dist/cmd/cloud/objectstore/index.js.map +0 -1
  122. package/dist/cmd/cloud/objectstore/list-buckets.d.ts +0 -3
  123. package/dist/cmd/cloud/objectstore/list-buckets.d.ts.map +0 -1
  124. package/dist/cmd/cloud/objectstore/list-buckets.js +0 -46
  125. package/dist/cmd/cloud/objectstore/list-buckets.js.map +0 -1
  126. package/dist/cmd/cloud/objectstore/list-keys.d.ts +0 -3
  127. package/dist/cmd/cloud/objectstore/list-keys.d.ts.map +0 -1
  128. package/dist/cmd/cloud/objectstore/list-keys.js +0 -58
  129. package/dist/cmd/cloud/objectstore/list-keys.js.map +0 -1
  130. package/dist/cmd/cloud/objectstore/put.d.ts +0 -3
  131. package/dist/cmd/cloud/objectstore/put.d.ts.map +0 -1
  132. package/dist/cmd/cloud/objectstore/put.js +0 -66
  133. package/dist/cmd/cloud/objectstore/put.js.map +0 -1
  134. package/dist/cmd/cloud/objectstore/repl.d.ts +0 -3
  135. package/dist/cmd/cloud/objectstore/repl.d.ts.map +0 -1
  136. package/dist/cmd/cloud/objectstore/repl.js +0 -224
  137. package/dist/cmd/cloud/objectstore/repl.js.map +0 -1
  138. package/dist/cmd/cloud/objectstore/url.d.ts +0 -3
  139. package/dist/cmd/cloud/objectstore/url.d.ts.map +0 -1
  140. package/dist/cmd/cloud/objectstore/url.js +0 -64
  141. package/dist/cmd/cloud/objectstore/url.js.map +0 -1
  142. package/dist/cmd/cloud/objectstore/util.d.ts +0 -11
  143. package/dist/cmd/cloud/objectstore/util.d.ts.map +0 -1
  144. package/dist/cmd/cloud/objectstore/util.js +0 -18
  145. package/dist/cmd/cloud/objectstore/util.js.map +0 -1
  146. package/src/cmd/build/ast.test.ts +0 -418
  147. package/src/cmd/build/fix-duplicate-exports.test.ts +0 -387
  148. package/src/cmd/cloud/objectstore/delete-bucket.ts +0 -77
  149. package/src/cmd/cloud/objectstore/delete.ts +0 -67
  150. package/src/cmd/cloud/objectstore/get.ts +0 -77
  151. package/src/cmd/cloud/objectstore/index.ts +0 -36
  152. package/src/cmd/cloud/objectstore/list-buckets.ts +0 -51
  153. package/src/cmd/cloud/objectstore/list-keys.ts +0 -63
  154. package/src/cmd/cloud/objectstore/put.ts +0 -74
  155. package/src/cmd/cloud/objectstore/repl.ts +0 -239
  156. package/src/cmd/cloud/objectstore/url.ts +0 -67
  157. package/src/cmd/cloud/objectstore/util.ts +0 -29
  158. package/src/crypto/box.test.ts +0 -431
  159. package/src/env-util.test.ts +0 -194
@@ -1,11 +1,11 @@
1
- import { dirname, basename, join, resolve } from 'node:path';
1
+ import { dirname, join, resolve } from 'node:path';
2
2
  import { existsSync, writeFileSync, mkdirSync, unlinkSync } from 'node:fs';
3
- import { parseAgentMetadata, parseRoute, parseEvalMetadata, analyzeWorkbench, checkRouteConflicts, generateLifecycleTypes, findCreateAppEndPosition, } from './ast';
3
+ import { parseAgentMetadata, parseEvalMetadata, parseRoute, analyzeWorkbench, generateLifecycleTypes, findCreateAppEndPosition, } from './ast';
4
4
  import { StructuredError } from '@agentuity/core';
5
5
  import { applyPatch, generatePatches } from './patch';
6
- import { detectSubagent } from '../../utils/detectSubagent';
7
6
  import { createLogger } from '@agentuity/server';
8
7
  import { toCamelCase, toPascalCase } from '../../utils/string';
8
+ import { generateRouteRegistry } from './route-registry';
9
9
  /**
10
10
  * Setup lifecycle types by analyzing app.ts for setup() function
11
11
  */
@@ -53,44 +53,17 @@ async function setupWorkbench(srcDir) {
53
53
  return null;
54
54
  }
55
55
  const workbenchConfig = analysis.config;
56
- // Check for route conflicts if workbench is being used
57
- if (workbenchConfig?.route) {
58
- const hasConflict = checkRouteConflicts(appContent, workbenchConfig.route);
59
- if (hasConflict) {
60
- const logger = createLogger(process.env.AGENTUITY_LOG_LEVEL || 'info');
61
- logger.error(`🚨 Route conflict detected!\n`);
62
- logger.error(` Workbench route '${workbenchConfig.route}' conflicts with existing application route`);
63
- logger.error(` Please use a different route or remove the conflicting route.\n`);
64
- }
65
- }
66
56
  return workbenchConfig;
67
57
  }
68
58
  const AgentIdentifierCollisionError = StructuredError('AgentIdentifierCollisionError');
69
- const SubAgentNameCollisionError = StructuredError('SubAgentNameCollisionError');
70
59
  function generateAgentRegistry(srcDir, agentInfo) {
71
- // Separate parent agents and subagents
72
- const parentAgents = agentInfo.filter((a) => !a.parent);
73
- const subagents = agentInfo.filter((a) => a.parent);
74
- // Group subagents by parent
75
- const subagentsByParent = new Map();
76
- for (const subagent of subagents) {
77
- const parentName = subagent.parent;
78
- if (!subagentsByParent.has(parentName)) {
79
- subagentsByParent.set(parentName, []);
80
- }
81
- subagentsByParent.get(parentName).push(subagent);
82
- }
83
60
  // Detect naming collisions in generated identifiers
84
- // Naming strategy: parent + child names are combined as `${parent}_${name}` then converted to camelCase
85
- // Example: parent="user", child="profile" → "user_profile" → "userProfile"
86
- // Potential collision: parent="user_profile", child="info" and parent="user", child="profile_info" both → "userProfileInfo"
87
61
  const generatedNames = new Set();
88
62
  const collisions = [];
89
63
  for (const agent of agentInfo) {
90
- const fullName = agent.parent ? `${agent.parent}_${agent.name}` : agent.name;
91
- const camelName = toCamelCase(fullName);
64
+ const camelName = toCamelCase(agent.name);
92
65
  if (generatedNames.has(camelName)) {
93
- collisions.push(`Identifier collision detected: "${camelName}" (from "${fullName}")`);
66
+ collisions.push(`Identifier collision detected: "${camelName}" (from "${agent.name}")`);
94
67
  }
95
68
  generatedNames.add(camelName);
96
69
  }
@@ -102,130 +75,41 @@ function generateAgentRegistry(srcDir, agentInfo) {
102
75
  });
103
76
  }
104
77
  // Generate imports for all agents
78
+ // Registry is now in .agentuity/, so imports need to go up one level and into src/
105
79
  const imports = agentInfo
106
- .map(({ name, path, parent }) => {
107
- const fullName = parent ? `${parent}.${name}` : name;
108
- const camelName = toCamelCase(fullName.replace('.', '_'));
109
- const relativePath = path.replace(/^\.\/agent\//, './');
80
+ .map(({ name, path }) => {
81
+ const camelName = toCamelCase(name);
82
+ const relativePath = path.replace(/^\.\/agent\//, '../src/agent/');
110
83
  return `import ${camelName}Agent from '${relativePath}';`;
111
84
  })
112
85
  .join('\n');
113
- // Evals are now imported in plugin.ts when agents are registered
114
- // No need to import them in registry.generated.ts
115
- const evalsImportsStr = '';
116
- // Validate that child property names don't collide with parent agent properties
117
- const reservedAgentProperties = ['metadata', 'run', 'inputSchema', 'outputSchema', 'stream'];
118
- for (const parentAgent of parentAgents) {
119
- const children = subagentsByParent.get(parentAgent.name) || [];
120
- for (const child of children) {
121
- const childPropertyName = toCamelCase(child.name);
122
- // Check for collision with reserved agent properties
123
- if (reservedAgentProperties.includes(childPropertyName)) {
124
- throw new SubAgentNameCollisionError({
125
- message: `Subagent property name collision detected: "${childPropertyName}" in parent "${parentAgent.name}"\n` +
126
- `The child name "${child.name}" conflicts with a reserved agent property (${reservedAgentProperties.join(', ')}).\n` +
127
- `Please rename the subagent to avoid this collision.`,
128
- });
129
- }
130
- }
131
- }
132
- // Generate nested registry structure
133
- const registryLines = [];
134
- for (const parentAgent of parentAgents) {
135
- const parentCamelName = toCamelCase(parentAgent.name);
136
- const children = subagentsByParent.get(parentAgent.name) || [];
137
- if (children.length === 0) {
138
- // No subagents, simple assignment
139
- registryLines.push(` ${parentCamelName}: ${parentCamelName}Agent,`);
140
- }
141
- else {
142
- // Has subagents, create nested structure using object spread (no mutation)
143
- registryLines.push(` ${parentCamelName}: {`);
144
- registryLines.push(` ...${parentCamelName}Agent,`);
145
- for (const child of children) {
146
- const childCamelName = toCamelCase(`${parentAgent.name}_${child.name}`);
147
- registryLines.push(` ${toCamelCase(child.name)}: ${childCamelName}Agent,`);
148
- }
149
- registryLines.push(` },`);
150
- }
151
- }
152
- const registry = registryLines.join('\n');
86
+ // Generate flat registry structure (no subagents)
87
+ const registry = agentInfo
88
+ .map(({ name }) => {
89
+ const camelName = toCamelCase(name);
90
+ return ` ${camelName}: ${camelName}Agent,`;
91
+ })
92
+ .join('\n');
153
93
  // Generate type exports for all agents
154
94
  const typeExports = agentInfo
155
- .map(({ name, parent }) => {
156
- const fullName = parent ? `${parent}_${name}` : name;
157
- const camelName = toCamelCase(fullName);
158
- const pascalName = toPascalCase(fullName);
95
+ .map(({ name }) => {
96
+ const camelName = toCamelCase(name);
97
+ const pascalName = toPascalCase(name);
159
98
  return `export type ${pascalName}AgentRunner = AgentRunner<typeof ${camelName}Agent['inputSchema'], typeof ${camelName}Agent['outputSchema'], typeof ${camelName}Agent['stream'] extends true ? true : false>;`;
160
99
  })
161
100
  .join('\n');
162
- // Generate nested agent type definitions for Hono Context augmentation
163
- const honoAgentTypeLines = [];
164
- for (const parentAgent of parentAgents) {
165
- const parentCamelName = toCamelCase(parentAgent.name);
166
- const children = subagentsByParent.get(parentAgent.name) || [];
167
- if (children.length === 0) {
168
- // No subagents
169
- honoAgentTypeLines.push(` ${parentCamelName}: AgentRunner<LocalAgentRegistry['${parentCamelName}']['inputSchema'], LocalAgentRegistry['${parentCamelName}']['outputSchema'], LocalAgentRegistry['${parentCamelName}']['stream'] extends true ? true : false>;`);
170
- }
171
- else {
172
- // Has subagents - create intersection type
173
- honoAgentTypeLines.push(` ${parentCamelName}: AgentRunner<LocalAgentRegistry['${parentCamelName}']['inputSchema'], LocalAgentRegistry['${parentCamelName}']['outputSchema'], LocalAgentRegistry['${parentCamelName}']['stream'] extends true ? true : false> & {`);
174
- for (const child of children) {
175
- const childCamelName = toCamelCase(child.name);
176
- const fullChildName = toCamelCase(`${parentAgent.name}_${child.name}`);
177
- honoAgentTypeLines.push(` ${childCamelName}: AgentRunner<typeof ${fullChildName}Agent['inputSchema'], typeof ${fullChildName}Agent['outputSchema'], typeof ${fullChildName}Agent['stream'] extends true ? true : false>;`);
178
- }
179
- honoAgentTypeLines.push(` };`);
180
- }
181
- }
182
- const honoAgentTypes = honoAgentTypeLines.join('\n');
183
- // Generate agent type definitions for AgentRegistry interface augmentation
184
- const runtimeAgentTypeLines = [];
185
- for (const parentAgent of parentAgents) {
186
- const parentCamelName = toCamelCase(parentAgent.name);
187
- const children = subagentsByParent.get(parentAgent.name) || [];
188
- if (children.length === 0) {
189
- // No subagents - use typeof the imported agent
190
- runtimeAgentTypeLines.push(` ${parentCamelName}: AgentRunner<typeof ${parentCamelName}Agent['inputSchema'], typeof ${parentCamelName}Agent['outputSchema'], typeof ${parentCamelName}Agent['stream'] extends true ? true : false>;`);
191
- }
192
- else {
193
- // Has subagents - create intersection type using typeof
194
- runtimeAgentTypeLines.push(` ${parentCamelName}: AgentRunner<typeof ${parentCamelName}Agent['inputSchema'], typeof ${parentCamelName}Agent['outputSchema'], typeof ${parentCamelName}Agent['stream'] extends true ? true : false> & {`);
195
- for (const child of children) {
196
- const childCamelName = toCamelCase(child.name);
197
- const fullChildName = toCamelCase(`${parentAgent.name}_${child.name}`);
198
- runtimeAgentTypeLines.push(` ${childCamelName}: AgentRunner<typeof ${fullChildName}Agent['inputSchema'], typeof ${fullChildName}Agent['outputSchema'], typeof ${fullChildName}Agent['stream'] extends true ? true : false>;`);
199
- }
200
- runtimeAgentTypeLines.push(` };`);
201
- }
202
- }
203
- const runtimeAgentTypes = runtimeAgentTypeLines.join('\n');
204
- // Generate React client types with nested structure
205
- const clientAgentTypeLines = [];
206
- for (const parentAgent of parentAgents) {
207
- const parentCamelName = toCamelCase(parentAgent.name);
208
- const children = subagentsByParent.get(parentAgent.name) || [];
209
- if (children.length === 0) {
210
- // No subagents
211
- clientAgentTypeLines.push(` '${parentAgent.name}': Agent<typeof ${parentCamelName}Agent['inputSchema'], typeof ${parentCamelName}Agent['outputSchema']>;`);
212
- }
213
- else {
214
- // Has subagents - create nested type with subagent access via dot notation
215
- clientAgentTypeLines.push(` '${parentAgent.name}': Agent<typeof ${parentCamelName}Agent['inputSchema'], typeof ${parentCamelName}Agent['outputSchema']>;`);
216
- for (const child of children) {
217
- const fullChildName = toCamelCase(`${parentAgent.name}_${child.name}`);
218
- clientAgentTypeLines.push(` '${parentAgent.name}.${child.name}': Agent<typeof ${fullChildName}Agent['inputSchema'], typeof ${fullChildName}Agent['outputSchema']>;`);
219
- }
220
- }
221
- }
222
- const reactAgentTypes = clientAgentTypeLines.join('\n');
101
+ // Generate flat agent type definitions for AgentRegistry interface augmentation
102
+ const runtimeAgentTypes = agentInfo
103
+ .map(({ name }) => {
104
+ const camelName = toCamelCase(name);
105
+ return ` ${camelName}: AgentRunner<typeof ${camelName}Agent['inputSchema'], typeof ${camelName}Agent['outputSchema'], typeof ${camelName}Agent['stream'] extends true ? true : false>;`;
106
+ })
107
+ .join('\n');
223
108
  const generatedContent = `/// <reference types="hono" />
224
109
  // Auto-generated by Agentuity - do not edit manually
225
- ${imports}${evalsImportsStr}
110
+ ${imports}
226
111
  import type { AgentRunner, Logger } from '@agentuity/runtime';
227
- import type { KeyValueStorage, ObjectStorage, StreamStorage, VectorStorage } from '@agentuity/core';
228
- import type { Agent } from '@agentuity/react';
112
+ import type { KeyValueStorage, StreamStorage, VectorStorage } from '@agentuity/core';
229
113
 
230
114
  /**
231
115
  * Registry of all agents in this application.
@@ -251,36 +135,17 @@ ${runtimeAgentTypes}
251
135
  }
252
136
  }
253
137
 
254
- // Augment Hono Context to provide strongly-typed agents and runtime services
255
- // Note: Properties are added to Context via middleware in @agentuity/runtime
256
- declare module "hono" {
257
- interface Context {
258
- agentName: LocalAgentName;
259
- agent: {
260
- ${honoAgentTypes}
261
- };
262
- waitUntil: (promise: Promise<void> | (() => void | Promise<void>)) => void;
263
- logger: Logger;
264
- kv: KeyValueStorage;
265
- objectstore: ObjectStorage;
266
- stream: StreamStorage;
267
- vector: VectorStorage;
268
- }
269
- }
270
-
271
- // Augment @agentuity/react types with strongly-typed agents from this project
272
- declare module '@agentuity/react' {
273
- interface AgentRegistry {
274
- ${reactAgentTypes}
275
- }
276
- }
138
+ // NOTE: Hono Context properties are accessed via c.var (e.g., c.var.logger, c.var.kv)
139
+ // The Variables interface in @agentuity/runtime defines all available context properties
277
140
  `;
141
+ const projectRoot = join(srcDir, '..');
142
+ const agentuityDir = join(projectRoot, '.agentuity');
143
+ const registryPath = join(agentuityDir, 'registry.generated.ts');
278
144
  const agentsDir = join(srcDir, 'agent');
279
- const registryPath = join(agentsDir, 'registry.generated.ts');
280
145
  const legacyTypesPath = join(agentsDir, 'types.generated.d.ts');
281
- // Ensure agent directory exists
282
- if (!existsSync(agentsDir)) {
283
- mkdirSync(agentsDir, { recursive: true });
146
+ // Ensure .agentuity directory exists
147
+ if (!existsSync(agentuityDir)) {
148
+ mkdirSync(agentuityDir, { recursive: true });
284
149
  }
285
150
  writeFileSync(registryPath, generatedContent, 'utf-8');
286
151
  // Remove legacy types.generated.d.ts if it exists (now consolidated into registry.generated.ts)
@@ -293,9 +158,7 @@ export function getBuildMetadata() {
293
158
  return metadata;
294
159
  }
295
160
  const AgentNameDuplicateError = StructuredError('AgentNameDuplicateError');
296
- const MetadataMissingError = StructuredError('MetadataMissingError');
297
161
  const MetadataPropertyMissingError = StructuredError('MetadataPropertyMissingError')();
298
- const SubAgentMissingError = StructuredError('SubAgentMissingError');
299
162
  const AgentuityBundler = {
300
163
  name: 'Agentuity Bundler',
301
164
  setup(build) {
@@ -314,54 +177,37 @@ const AgentuityBundler = {
314
177
  const isDevMode = (build.config.define?.['process.env.NODE_ENV']
315
178
  ? JSON.parse(build.config.define['process.env.NODE_ENV'])
316
179
  : 'production') === 'development';
317
- const routes = new Set();
318
180
  const agentInfo = [];
319
181
  const agentMetadata = new Map();
320
182
  const transpiler = new Bun.Transpiler({ loader: 'ts', target: 'bun' });
321
- let routeDefinitions = [];
322
- build.onResolve({ filter: /\/route\.ts$/, namespace: 'file' }, async (args) => {
323
- if (args.path.startsWith(srcDir)) {
324
- const importPath = args.path
325
- .replace(rootDir, '')
326
- .replace('.ts', '')
327
- .replace('/src/', './');
328
- routes.add(importPath);
329
- }
330
- return args;
331
- });
332
- build.onLoad({ filter: /\/route\.ts$/, namespace: 'file' }, async (args) => {
333
- if (args.path.startsWith(srcDir)) {
334
- const importPath = args.path
335
- .replace(rootDir, '')
336
- .replace('.ts', '')
337
- .replace('/src/', './');
338
- routes.add(importPath);
339
- }
340
- // return undefined to let Bun handle loading normally
341
- return;
342
- });
343
- build.onLoad({ filter: /\/agent\.ts$/, namespace: 'file' }, async (args) => {
183
+ // Scan ALL .ts files in src/agent directory for agents
184
+ build.onLoad({ filter: /\/agent\/.*\.ts$/, namespace: 'file' }, async (args) => {
344
185
  let newsource = await Bun.file(args.path).text();
345
186
  if (args.path.startsWith(srcDir)) {
346
187
  const contents = transpiler.transformSync(newsource);
347
- const [ns, md] = await parseAgentMetadata(rootDir, args.path, contents, projectId, deploymentId);
348
- newsource = ns;
349
- // Detect if this is a subagent by checking path structure
350
- // Note: Path structure assumption - 4 segments: agent/parent/child/agent.ts
351
- const { isSubagent, parentName } = detectSubagent(args.path, srcDir);
352
- if (isSubagent && parentName) {
353
- md.set('parent', parentName);
188
+ const result = await parseAgentMetadata(rootDir, args.path, contents, projectId, deploymentId);
189
+ // Skip files that don't have a createAgent export
190
+ if (result === undefined) {
191
+ return {
192
+ contents: newsource,
193
+ loader: 'ts',
194
+ };
354
195
  }
355
- const newAgentName = md.get('name');
356
- for (const [, kv] of agentMetadata) {
357
- const found = kv.get('name');
358
- if (newAgentName === found) {
359
- throw new AgentNameDuplicateError({
360
- message: `The agent in ${kv.get('filename')} and the agent in ${md.get('filename')} have the same name (${found}). Agent Names must be unique within a project.`,
361
- });
196
+ const [ns, md] = result;
197
+ newsource = ns;
198
+ // Only process files that actually export an agent
199
+ if (md.has('name')) {
200
+ const newAgentName = md.get('name');
201
+ for (const [, kv] of agentMetadata) {
202
+ const found = kv.get('name');
203
+ if (newAgentName === found) {
204
+ throw new AgentNameDuplicateError({
205
+ message: `The agent in ${kv.get('filename')} and the agent in ${md.get('filename')} have the same name (${found}). Agent Names must be unique within a project.`,
206
+ });
207
+ }
362
208
  }
209
+ agentMetadata.set(md.get('name'), md);
363
210
  }
364
- agentMetadata.set(md.get('identifier'), md);
365
211
  }
366
212
  return {
367
213
  contents: newsource,
@@ -410,101 +256,6 @@ const AgentuityBundler = {
410
256
  }
411
257
  await args.defer();
412
258
  const inserts = [];
413
- const routeMapping = {};
414
- for (const route of routes) {
415
- const name = basename(dirname(route));
416
- const agent = route.replace(/\/route$/, '/agent');
417
- const hasAgent = existsSync(join(srcDir, agent + '.ts'));
418
- // Detect if this is a subagent route using shared utility
419
- const { isSubagent, parentName } = detectSubagent(route);
420
- const agentPath = route.replace(/\/route$/, '/*').replace('./', '/');
421
- const routePath = route
422
- .replace(/\/route$/, '')
423
- .replace('/web/', '/api/')
424
- .replace('/web', '/api')
425
- .replace('./', '/');
426
- const definitions = await parseRoute(rootDir, join(srcDir, `${route}.ts`), projectId, deploymentId);
427
- let agentDetail = {};
428
- if (hasAgent) {
429
- const md = agentMetadata.get(name);
430
- if (!md) {
431
- throw new MetadataMissingError({
432
- message: `Couldn't find agent metadata for ${route}`,
433
- });
434
- }
435
- agentDetail = {
436
- name,
437
- id: md.get('id'),
438
- path: `.${agent}`,
439
- filename: md.get('filename'),
440
- identifier: md.get('identifier'),
441
- description: md.get('description') ?? '',
442
- agentId: md.get('agentId'),
443
- };
444
- if (isSubagent && parentName) {
445
- agentDetail.parent = parentName;
446
- }
447
- agentInfo.push(agentDetail);
448
- for (const def of definitions) {
449
- def.agentIds = [agentDetail.agentId, agentDetail.id];
450
- }
451
- }
452
- // do this after handling the agent association (if any)
453
- routeDefinitions = [...routeDefinitions, ...definitions];
454
- let buffer = `await (async() => {
455
- const { createAgentMiddleware, getRouter, registerAgent } = await import('@agentuity/runtime');
456
- const router = getRouter()!;
457
- const route = require('./src/${route}').default;`;
458
- if (hasAgent) {
459
- const agentRegistrationName = isSubagent && parentName ? `${parentName}.${name}` : name;
460
- // Build evals path from agent path (e.g., 'agent/eval/agent' -> 'agent/eval/eval.ts')
461
- const agentDirPath = agent.replace(/\/agent$/, '');
462
- const evalsPath = join(srcDir, agentDirPath, 'eval.ts');
463
- const evalsImport = existsSync(evalsPath)
464
- ? `\n require('./src/${agentDirPath}/eval');`
465
- : '';
466
- buffer += `
467
- const agent = require('./src/${agent}').default;
468
- router.all("${agentPath}", createAgentMiddleware('${agentRegistrationName}'));
469
- registerAgent("${agentRegistrationName}", agent);${evalsImport}`;
470
- }
471
- buffer += `
472
- router.route("${routePath}", route);
473
- })();`;
474
- inserts.push(buffer);
475
- for (const def of definitions) {
476
- routeMapping[`${def.method} ${def.path}`] = def.id;
477
- }
478
- }
479
- // Register standalone agents (agents without routes)
480
- const routeAgentNames = new Set(agentInfo
481
- .filter((a) => {
482
- // Check if this agent was added via a route (has a corresponding route file)
483
- const agentPath = a.path.replace(/^\./, '').replace(/\/agent$/, '/route');
484
- return routes.has(agentPath.replace(/^\/src\//, './'));
485
- })
486
- .map((a) => a.name));
487
- for (const agentDetail of agentInfo) {
488
- if (!routeAgentNames.has(agentDetail.name)) {
489
- // This is a standalone agent - register it without a route
490
- const agentPath = agentDetail.path;
491
- const agentDirPath = agentPath.replace(/\/agent$/, '');
492
- const evalsPath = join(srcDir, agentDirPath.replace(/^\./, ''), 'eval.ts');
493
- const evalsImport = existsSync(evalsPath)
494
- ? `\n require('./src/${agentDirPath.replace(/^\.\//, '')}/eval');`
495
- : '';
496
- const isSubagent = !!agentDetail.parent;
497
- const agentRegistrationName = isSubagent
498
- ? `${agentDetail.parent}.${agentDetail.name}`
499
- : agentDetail.name;
500
- const buffer = `await (async() => {
501
- const { registerAgent } = await import('@agentuity/runtime');
502
- const agent = require('./src${agentPath}').default;
503
- registerAgent("${agentRegistrationName}", agent);${evalsImport}
504
- })();`;
505
- inserts.push(buffer);
506
- }
507
- }
508
259
  const indexFile = join(srcDir, 'web', 'index.html');
509
260
  // Setup workbench configuration - evaluate fresh each time during builds
510
261
  const workbenchConfig = await setupWorkbench(srcDir);
@@ -554,35 +305,41 @@ import { readFileSync, existsSync } from 'node:fs';
554
305
  router.get('/public/*', webstatic);
555
306
  })();`);
556
307
  }
557
- // Add standalone agents (agents without routes) to agentInfo
558
- // These agents can still be called by other agents or routes via ctx.agent
559
- const registeredIdentifiers = new Set(agentInfo.map((a) => a.identifier));
560
- for (const [identifier, md] of agentMetadata) {
561
- if (!registeredIdentifiers.has(identifier)) {
562
- // md.get('filename') can be either absolute or relative to rootDir
563
- const filename = md.get('filename');
564
- const absolutePath = filename.startsWith('/')
565
- ? filename
566
- : join(rootDir, filename);
567
- // Convert to path relative to srcDir like route-based agents
568
- // e.g., /path/to/src/agent/lifecycle/agent.ts -> ./agent/lifecycle/agent
569
- const agentPath = absolutePath.replace(srcDir, '.').replace('.ts', '');
570
- // Extract folder name as agent name (same as route-based logic)
571
- const folderName = basename(dirname(absolutePath));
572
- const { isSubagent, parentName } = detectSubagent(absolutePath, srcDir);
573
- const agentDetail = {
574
- name: folderName,
575
- id: md.get('id'),
576
- path: agentPath,
577
- filename: absolutePath,
578
- identifier: md.get('identifier'),
579
- description: md.get('description') ?? '',
580
- agentId: md.get('agentId'),
581
- };
582
- if (isSubagent && parentName) {
583
- agentDetail.parent = parentName;
308
+ // Build agentInfo from all discovered agents and track directories
309
+ const agentDirs = new Set();
310
+ for (const [_identifier, md] of agentMetadata) {
311
+ // md.get('filename') can be either absolute or relative to rootDir
312
+ const filename = md.get('filename');
313
+ const absolutePath = filename.startsWith('/') ? filename : join(rootDir, filename);
314
+ // Track which directories have agents
315
+ const dir = dirname(absolutePath);
316
+ agentDirs.add(dir);
317
+ // Convert to path relative to srcDir
318
+ // e.g., /path/to/src/agent/my-agent.ts -> ./agent/my-agent
319
+ const agentPath = absolutePath.replace(srcDir, '.').replace('.ts', '');
320
+ const agentDetail = {
321
+ name: md.get('name'),
322
+ id: md.get('id'),
323
+ path: agentPath,
324
+ filename: absolutePath,
325
+ description: md.get('description') ?? '',
326
+ agentId: md.get('agentId'),
327
+ };
328
+ agentInfo.push(agentDetail);
329
+ }
330
+ // Validate that all directories in src/agent have at least one agent
331
+ const agentBaseDir = join(srcDir, 'agent');
332
+ if (existsSync(agentBaseDir)) {
333
+ const { readdirSync, statSync } = await import('node:fs');
334
+ const subdirs = readdirSync(agentBaseDir).filter((name) => {
335
+ const fullPath = join(agentBaseDir, name);
336
+ return statSync(fullPath).isDirectory();
337
+ });
338
+ for (const subdir of subdirs) {
339
+ const fullPath = join(agentBaseDir, subdir);
340
+ if (!agentDirs.has(fullPath)) {
341
+ throw new Error(`Directory ${subdir} in src/agent must contain at least one agent (a file with a createAgent export)`);
584
342
  }
585
- agentInfo.push(agentDetail);
586
343
  }
587
344
  }
588
345
  // Only generate registry if there are agents
@@ -593,6 +350,63 @@ import { readFileSync, existsSync } from 'node:fs';
593
350
  generateAgentRegistry(srcDir, agentInfo);
594
351
  // Generate lifecycle types if setup() is present in app.ts
595
352
  await setupLifecycleTypes(rootDir, outDir, srcDir, logger);
353
+ // Parse routes from src/api directory
354
+ const apiRoutesMetadata = [];
355
+ const routeInfoList = [];
356
+ const apiDir = join(srcDir, 'api');
357
+ if (existsSync(apiDir)) {
358
+ const { readdirSync, statSync } = await import('node:fs');
359
+ const apiFiles = readdirSync(apiDir)
360
+ .filter((name) => name.endsWith('.ts') && !name.endsWith('.generated.ts'))
361
+ .map((name) => join(apiDir, name));
362
+ for (const apiFile of apiFiles) {
363
+ if (statSync(apiFile).isFile()) {
364
+ try {
365
+ const routes = await parseRoute(rootDir, apiFile, projectId, deploymentId);
366
+ apiRoutesMetadata.push(...routes);
367
+ // Collect route info for RouteRegistry generation
368
+ for (const route of routes) {
369
+ routeInfoList.push({
370
+ method: route.method.toUpperCase(),
371
+ path: route.path,
372
+ filename: route.filename,
373
+ hasValidator: route.config?.hasValidator === true,
374
+ routeType: route.type || 'api',
375
+ agentVariable: route.config?.agentVariable,
376
+ agentImportPath: route.config?.agentImportPath,
377
+ inputSchemaVariable: route.config?.inputSchemaVariable,
378
+ outputSchemaVariable: route.config?.outputSchemaVariable,
379
+ });
380
+ }
381
+ }
382
+ catch (error) {
383
+ // Skip files that don't have createRouter (they might be utilities)
384
+ if (error instanceof Error &&
385
+ error.message.includes('could not find an proper createRouter')) {
386
+ logger.trace(`Skipping ${apiFile}: no createRouter found`);
387
+ }
388
+ else {
389
+ throw error;
390
+ }
391
+ }
392
+ }
393
+ }
394
+ }
395
+ // Generate RouteRegistry for type-safe route access
396
+ if (routeInfoList.length > 0) {
397
+ logger.trace(`Generating RouteRegistry with ${routeInfoList.length} routes`);
398
+ generateRouteRegistry(srcDir, routeInfoList);
399
+ }
400
+ // Auto-mount src/api/index.ts if it exists
401
+ const apiIndexPath = join(srcDir, 'api', 'index.ts');
402
+ if (existsSync(apiIndexPath)) {
403
+ inserts.push(`await (async() => {
404
+ const { getRouter } = await import('@agentuity/runtime');
405
+ const router = getRouter()!;
406
+ const api = require('./src/api/index').default;
407
+ router.route('/api', api);
408
+ })();`);
409
+ }
596
410
  // Only create the workbench routes if workbench is actually configured
597
411
  if (workbenchConfig) {
598
412
  inserts.push(`await (async() => {
@@ -639,12 +453,10 @@ await (async() => {
639
453
  }
640
454
  // generate the build metadata
641
455
  metadata = {
642
- routes: routeDefinitions,
456
+ routes: apiRoutesMetadata,
643
457
  agents: [],
644
458
  };
645
- // Group agents by parent/child relationship
646
- const parentAgentMetadata = new Map();
647
- const subagentsByParent = new Map();
459
+ // Validate required metadata properties and build agent metadata
648
460
  for (const [, v] of agentMetadata) {
649
461
  if (!v.has('filename')) {
650
462
  throw new MetadataPropertyMissingError({
@@ -658,12 +470,6 @@ await (async() => {
658
470
  message: 'agent metadata is missing expected id property',
659
471
  });
660
472
  }
661
- if (!v.has('identifier')) {
662
- throw new MetadataPropertyMissingError({
663
- name: 'identifier',
664
- message: 'agent metadata is missing expected identifier property',
665
- });
666
- }
667
473
  if (!v.has('version')) {
668
474
  throw new MetadataPropertyMissingError({
669
475
  name: 'version',
@@ -682,40 +488,13 @@ await (async() => {
682
488
  message: 'agent metadata is missing expected agentId property',
683
489
  });
684
490
  }
685
- const parentName = v.get('parent');
686
- if (parentName) {
687
- // This is a subagent
688
- if (!subagentsByParent.has(parentName)) {
689
- subagentsByParent.set(parentName, []);
690
- }
691
- subagentsByParent.get(parentName).push(v);
692
- }
693
- else {
694
- // This is a parent or standalone agent
695
- parentAgentMetadata.set(v.get('identifier'), v);
696
- }
697
- }
698
- // Validate that all subagents reference existing parent agents
699
- for (const [parentName, subagents] of subagentsByParent) {
700
- const parentExists = Array.from(parentAgentMetadata.values()).some((meta) => meta.get('name') === parentName || meta.get('identifier') === parentName);
701
- if (!parentExists) {
702
- const subagentPaths = subagents.map((s) => s.get('filename')).join(', ');
703
- throw new SubAgentMissingError({
704
- message: `Subagent(s) [${subagentPaths}] reference parent "${parentName}" which does not exist. ` +
705
- `Ensure the parent agent is defined.`,
706
- });
707
- }
708
- }
709
- // Build metadata with nested subagents
710
- for (const [_identifier, v] of parentAgentMetadata) {
711
491
  const agentData = {
712
492
  filename: v.get('filename'),
713
493
  id: v.get('id'),
714
- identifier: v.get('identifier'),
715
494
  agentId: v.get('agentId'),
716
495
  version: v.get('version'),
717
496
  name: v.get('name'),
718
- description: v.get('description') ?? '<no description provided>',
497
+ description: v.get('description') ?? '',
719
498
  projectId,
720
499
  };
721
500
  const evalsStr = v.get('evals');
@@ -738,49 +517,8 @@ await (async() => {
738
517
  else {
739
518
  logger.trace(`[plugin] No evals found for agent ${agentData.name}`);
740
519
  }
741
- // Add subagents if any (check both name and identifier)
742
- const subagents = subagentsByParent.get(agentData.name) ||
743
- subagentsByParent.get(agentData.identifier);
744
- if (subagents && subagents.length > 0) {
745
- agentData.subagents = subagents.map((sub) => {
746
- const subagentData = {
747
- filename: sub.get('filename'),
748
- id: sub.get('id'),
749
- identifier: sub.get('identifier'),
750
- agentId: sub.get('agentId'),
751
- version: sub.get('version'),
752
- name: sub.get('name'),
753
- description: sub.get('description') ?? '<no description provided>',
754
- projectId,
755
- };
756
- // Add evals for subagents if any
757
- const subEvalsStr = sub.get('evals');
758
- if (subEvalsStr) {
759
- logger.trace(`[plugin] Found evals string for subagent ${subagentData.name}, parsing...`);
760
- try {
761
- const parsedSubEvals = JSON.parse(subEvalsStr);
762
- subagentData.evals = parsedSubEvals.map((evalItem) => ({
763
- ...evalItem,
764
- agentIdentifier: subagentData.agentId,
765
- projectId,
766
- }));
767
- logger.trace(`[plugin] Successfully parsed ${subagentData.evals?.length ?? 0} eval(s) for subagent ${subagentData.name}`);
768
- }
769
- catch (e) {
770
- logger.trace(`[plugin] Failed to parse evals for subagent ${subagentData.name}: ${e}`);
771
- console.warn(`Failed to parse evals for subagent ${subagentData.name}: ${e}`);
772
- }
773
- }
774
- else {
775
- logger.trace(`[plugin] No evals found for subagent ${subagentData.name}`);
776
- }
777
- return subagentData;
778
- });
779
- }
780
520
  metadata.agents.push(agentData);
781
521
  }
782
- const routeMappingJSFile = Bun.file(join(outDir, '.routemapping.json'));
783
- await routeMappingJSFile.write(JSON.stringify(routeMapping));
784
522
  return {
785
523
  contents,
786
524
  loader: 'ts',