@agentuity/cli 0.0.68 → 0.0.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +1 -1
  3. package/bin/cli.ts +20 -8
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +16 -0
  6. package/dist/cli.js.map +1 -1
  7. package/dist/cmd/ai/capabilities/show.d.ts.map +1 -1
  8. package/dist/cmd/ai/capabilities/show.js +0 -13
  9. package/dist/cmd/ai/capabilities/show.js.map +1 -1
  10. package/dist/cmd/ai/prompt/agent.d.ts.map +1 -1
  11. package/dist/cmd/ai/prompt/agent.js +23 -24
  12. package/dist/cmd/ai/prompt/agent.js.map +1 -1
  13. package/dist/cmd/ai/prompt/api.d.ts.map +1 -1
  14. package/dist/cmd/ai/prompt/api.js +12 -9
  15. package/dist/cmd/ai/prompt/api.js.map +1 -1
  16. package/dist/cmd/build/ast.d.ts +1 -2
  17. package/dist/cmd/build/ast.d.ts.map +1 -1
  18. package/dist/cmd/build/ast.js +261 -260
  19. package/dist/cmd/build/ast.js.map +1 -1
  20. package/dist/cmd/build/bundler.d.ts +2 -1
  21. package/dist/cmd/build/bundler.d.ts.map +1 -1
  22. package/dist/cmd/build/bundler.js +11 -2
  23. package/dist/cmd/build/bundler.js.map +1 -1
  24. package/dist/cmd/build/index.js +1 -1
  25. package/dist/cmd/build/index.js.map +1 -1
  26. package/dist/cmd/build/plugin.d.ts.map +1 -1
  27. package/dist/cmd/build/plugin.js +152 -416
  28. package/dist/cmd/build/plugin.js.map +1 -1
  29. package/dist/cmd/build/route-registry.d.ts +36 -0
  30. package/dist/cmd/build/route-registry.d.ts.map +1 -0
  31. package/dist/cmd/build/route-registry.js +151 -0
  32. package/dist/cmd/build/route-registry.js.map +1 -0
  33. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  34. package/dist/cmd/cloud/deploy.js +1 -0
  35. package/dist/cmd/cloud/deploy.js.map +1 -1
  36. package/dist/cmd/cloud/index.d.ts.map +1 -1
  37. package/dist/cmd/cloud/index.js +0 -2
  38. package/dist/cmd/cloud/index.js.map +1 -1
  39. package/dist/cmd/dev/index.js +3 -3
  40. package/dist/cmd/dev/index.js.map +1 -1
  41. package/dist/cmd/dev/sync.d.ts.map +1 -1
  42. package/dist/cmd/dev/sync.js +0 -15
  43. package/dist/cmd/dev/sync.js.map +1 -1
  44. package/dist/cmd/dev/templates.d.ts.map +1 -1
  45. package/dist/cmd/dev/templates.js +14 -31
  46. package/dist/cmd/dev/templates.js.map +1 -1
  47. package/dist/cmd/profile/create.d.ts.map +1 -1
  48. package/dist/cmd/profile/create.js +0 -1
  49. package/dist/cmd/profile/create.js.map +1 -1
  50. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  51. package/dist/cmd/project/template-flow.js +64 -53
  52. package/dist/cmd/project/template-flow.js.map +1 -1
  53. package/dist/config.d.ts.map +1 -1
  54. package/dist/config.js +0 -3
  55. package/dist/config.js.map +1 -1
  56. package/dist/keychain.d.ts.map +1 -1
  57. package/dist/keychain.js +8 -4
  58. package/dist/keychain.js.map +1 -1
  59. package/dist/tui/box.d.ts +19 -0
  60. package/dist/tui/box.d.ts.map +1 -0
  61. package/dist/tui/box.js +160 -0
  62. package/dist/tui/box.js.map +1 -0
  63. package/dist/tui/colors.d.ts +20 -0
  64. package/dist/tui/colors.d.ts.map +1 -0
  65. package/dist/tui/colors.js +52 -0
  66. package/dist/tui/colors.js.map +1 -0
  67. package/dist/tui/group.d.ts +25 -0
  68. package/dist/tui/group.d.ts.map +1 -0
  69. package/dist/tui/group.js +32 -0
  70. package/dist/tui/group.js.map +1 -0
  71. package/dist/tui/prompt.d.ts +65 -0
  72. package/dist/tui/prompt.d.ts.map +1 -0
  73. package/dist/tui/prompt.js +377 -0
  74. package/dist/tui/prompt.js.map +1 -0
  75. package/dist/tui/symbols.d.ts +32 -0
  76. package/dist/tui/symbols.d.ts.map +1 -0
  77. package/dist/tui/symbols.js +52 -0
  78. package/dist/tui/symbols.js.map +1 -0
  79. package/dist/tui.d.ts +6 -0
  80. package/dist/tui.d.ts.map +1 -1
  81. package/dist/tui.js +6 -0
  82. package/dist/tui.js.map +1 -1
  83. package/dist/types.d.ts +0 -24
  84. package/dist/types.d.ts.map +1 -1
  85. package/dist/types.js +0 -1
  86. package/dist/types.js.map +1 -1
  87. package/package.json +3 -3
  88. package/src/cli.ts +19 -0
  89. package/src/cmd/ai/capabilities/show.ts +0 -13
  90. package/src/cmd/ai/prompt/agent.ts +23 -24
  91. package/src/cmd/ai/prompt/api.ts +12 -9
  92. package/src/cmd/build/ast.ts +364 -334
  93. package/src/cmd/build/bundler.ts +16 -1
  94. package/src/cmd/build/index.ts +1 -1
  95. package/src/cmd/build/plugin.ts +171 -495
  96. package/src/cmd/build/route-registry.ts +198 -0
  97. package/src/cmd/cloud/deploy.ts +1 -0
  98. package/src/cmd/cloud/index.ts +0 -2
  99. package/src/cmd/dev/index.ts +3 -3
  100. package/src/cmd/dev/sync.ts +0 -28
  101. package/src/cmd/dev/templates.ts +14 -31
  102. package/src/cmd/profile/create.ts +0 -1
  103. package/src/cmd/project/template-flow.ts +77 -57
  104. package/src/config.ts +1 -5
  105. package/src/keychain.ts +9 -11
  106. package/src/tui/box.ts +202 -0
  107. package/src/tui/colors.ts +55 -0
  108. package/src/tui/group.ts +56 -0
  109. package/src/tui/prompt.ts +506 -0
  110. package/src/tui/symbols.ts +64 -0
  111. package/src/tui.ts +14 -0
  112. package/src/types.ts +0 -1
  113. package/dist/cmd/build/ast.test.d.ts +0 -2
  114. package/dist/cmd/build/ast.test.d.ts.map +0 -1
  115. package/dist/cmd/build/ast.test.js +0 -339
  116. package/dist/cmd/build/ast.test.js.map +0 -1
  117. package/dist/cmd/build/fix-duplicate-exports.test.d.ts +0 -2
  118. package/dist/cmd/build/fix-duplicate-exports.test.d.ts.map +0 -1
  119. package/dist/cmd/build/fix-duplicate-exports.test.js +0 -300
  120. package/dist/cmd/build/fix-duplicate-exports.test.js.map +0 -1
  121. package/dist/cmd/cloud/objectstore/delete-bucket.d.ts +0 -3
  122. package/dist/cmd/cloud/objectstore/delete-bucket.d.ts.map +0 -1
  123. package/dist/cmd/cloud/objectstore/delete-bucket.js +0 -72
  124. package/dist/cmd/cloud/objectstore/delete-bucket.js.map +0 -1
  125. package/dist/cmd/cloud/objectstore/delete.d.ts +0 -3
  126. package/dist/cmd/cloud/objectstore/delete.d.ts.map +0 -1
  127. package/dist/cmd/cloud/objectstore/delete.js +0 -65
  128. package/dist/cmd/cloud/objectstore/delete.js.map +0 -1
  129. package/dist/cmd/cloud/objectstore/get.d.ts +0 -3
  130. package/dist/cmd/cloud/objectstore/get.d.ts.map +0 -1
  131. package/dist/cmd/cloud/objectstore/get.js +0 -75
  132. package/dist/cmd/cloud/objectstore/get.js.map +0 -1
  133. package/dist/cmd/cloud/objectstore/index.d.ts +0 -3
  134. package/dist/cmd/cloud/objectstore/index.d.ts.map +0 -1
  135. package/dist/cmd/cloud/objectstore/index.js +0 -36
  136. package/dist/cmd/cloud/objectstore/index.js.map +0 -1
  137. package/dist/cmd/cloud/objectstore/list-buckets.d.ts +0 -3
  138. package/dist/cmd/cloud/objectstore/list-buckets.d.ts.map +0 -1
  139. package/dist/cmd/cloud/objectstore/list-buckets.js +0 -46
  140. package/dist/cmd/cloud/objectstore/list-buckets.js.map +0 -1
  141. package/dist/cmd/cloud/objectstore/list-keys.d.ts +0 -3
  142. package/dist/cmd/cloud/objectstore/list-keys.d.ts.map +0 -1
  143. package/dist/cmd/cloud/objectstore/list-keys.js +0 -58
  144. package/dist/cmd/cloud/objectstore/list-keys.js.map +0 -1
  145. package/dist/cmd/cloud/objectstore/put.d.ts +0 -3
  146. package/dist/cmd/cloud/objectstore/put.d.ts.map +0 -1
  147. package/dist/cmd/cloud/objectstore/put.js +0 -66
  148. package/dist/cmd/cloud/objectstore/put.js.map +0 -1
  149. package/dist/cmd/cloud/objectstore/repl.d.ts +0 -3
  150. package/dist/cmd/cloud/objectstore/repl.d.ts.map +0 -1
  151. package/dist/cmd/cloud/objectstore/repl.js +0 -224
  152. package/dist/cmd/cloud/objectstore/repl.js.map +0 -1
  153. package/dist/cmd/cloud/objectstore/url.d.ts +0 -3
  154. package/dist/cmd/cloud/objectstore/url.d.ts.map +0 -1
  155. package/dist/cmd/cloud/objectstore/url.js +0 -64
  156. package/dist/cmd/cloud/objectstore/url.js.map +0 -1
  157. package/dist/cmd/cloud/objectstore/util.d.ts +0 -11
  158. package/dist/cmd/cloud/objectstore/util.d.ts.map +0 -1
  159. package/dist/cmd/cloud/objectstore/util.js +0 -18
  160. package/dist/cmd/cloud/objectstore/util.js.map +0 -1
  161. package/dist/crypto/box.test.d.ts +0 -2
  162. package/dist/crypto/box.test.d.ts.map +0 -1
  163. package/dist/crypto/box.test.js +0 -317
  164. package/dist/crypto/box.test.js.map +0 -1
  165. package/dist/env-util.test.d.ts +0 -2
  166. package/dist/env-util.test.d.ts.map +0 -1
  167. package/dist/env-util.test.js +0 -146
  168. package/dist/env-util.test.js.map +0 -1
  169. package/src/cmd/build/ast.test.ts +0 -418
  170. package/src/cmd/build/fix-duplicate-exports.test.ts +0 -387
  171. package/src/cmd/cloud/objectstore/delete-bucket.ts +0 -77
  172. package/src/cmd/cloud/objectstore/delete.ts +0 -67
  173. package/src/cmd/cloud/objectstore/get.ts +0 -77
  174. package/src/cmd/cloud/objectstore/index.ts +0 -36
  175. package/src/cmd/cloud/objectstore/list-buckets.ts +0 -51
  176. package/src/cmd/cloud/objectstore/list-keys.ts +0 -63
  177. package/src/cmd/cloud/objectstore/put.ts +0 -74
  178. package/src/cmd/cloud/objectstore/repl.ts +0 -239
  179. package/src/cmd/cloud/objectstore/url.ts +0 -67
  180. package/src/cmd/cloud/objectstore/util.ts +0 -29
  181. package/src/crypto/box.test.ts +0 -431
  182. 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,103 +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
421
- .replace(/\/route$/, '/*')
422
- .replace('./', '/');
423
- const routePath = route
424
- .replace(/\/route$/, '')
425
- .replace('/web/', '/api/')
426
- .replace('/web', '/api')
427
- .replace('./', '/');
428
- const definitions = await parseRoute(rootDir, join(srcDir, `${route}.ts`), projectId, deploymentId);
429
- let agentDetail = {};
430
- if (hasAgent) {
431
- const md = agentMetadata.get(name);
432
- if (!md) {
433
- throw new MetadataMissingError({
434
- message: `Couldn't find agent metadata for ${route}`,
435
- });
436
- }
437
- agentDetail = {
438
- name,
439
- id: md.get('id'),
440
- path: `.${agent}`,
441
- filename: md.get('filename'),
442
- identifier: md.get('identifier'),
443
- description: md.get('description') ?? '',
444
- agentId: md.get('agentId'),
445
- };
446
- if (isSubagent && parentName) {
447
- agentDetail.parent = parentName;
448
- }
449
- agentInfo.push(agentDetail);
450
- for (const def of definitions) {
451
- def.agentIds = [agentDetail.agentId, agentDetail.id];
452
- }
453
- }
454
- // do this after handling the agent association (if any)
455
- routeDefinitions = [...routeDefinitions, ...definitions];
456
- let buffer = `await (async() => {
457
- const { createAgentMiddleware, getRouter, registerAgent } = await import('@agentuity/runtime');
458
- const router = getRouter()!;
459
- const route = require('./src/${route}').default;`;
460
- if (hasAgent) {
461
- const agentRegistrationName = isSubagent && parentName ? `${parentName}.${name}` : name;
462
- // Build evals path from agent path (e.g., 'agent/eval/agent' -> 'agent/eval/eval.ts')
463
- const agentDirPath = agent.replace(/\/agent$/, '');
464
- const evalsPath = join(srcDir, agentDirPath, 'eval.ts');
465
- const evalsImport = existsSync(evalsPath)
466
- ? `\n require('./src/${agentDirPath}/eval');`
467
- : '';
468
- buffer += `
469
- const agent = require('./src/${agent}').default;
470
- router.all("${agentPath}", createAgentMiddleware('${agentRegistrationName}'));
471
- registerAgent("${agentRegistrationName}", agent);${evalsImport}`;
472
- }
473
- buffer += `
474
- router.route("${routePath}", route);
475
- })();`;
476
- inserts.push(buffer);
477
- for (const def of definitions) {
478
- routeMapping[`${def.method} ${def.path}`] = def.id;
479
- }
480
- }
481
- // Register standalone agents (agents without routes)
482
- const routeAgentNames = new Set(agentInfo
483
- .filter((a) => {
484
- // Check if this agent was added via a route (has a corresponding route file)
485
- const agentPath = a.path.replace(/^\./, '').replace(/\/agent$/, '/route');
486
- return routes.has(agentPath.replace(/^\/src\//, './'));
487
- })
488
- .map((a) => a.name));
489
- for (const agentDetail of agentInfo) {
490
- if (!routeAgentNames.has(agentDetail.name)) {
491
- // This is a standalone agent - register it without a route
492
- const agentPath = agentDetail.path;
493
- const agentDirPath = agentPath.replace(/\/agent$/, '');
494
- const evalsPath = join(srcDir, agentDirPath.replace(/^\./, ''), 'eval.ts');
495
- const evalsImport = existsSync(evalsPath)
496
- ? `\n require('./src/${agentDirPath.replace(/^\.\//, '')}/eval');`
497
- : '';
498
- const isSubagent = !!agentDetail.parent;
499
- const agentRegistrationName = isSubagent
500
- ? `${agentDetail.parent}.${agentDetail.name}`
501
- : agentDetail.name;
502
- const buffer = `await (async() => {
503
- const { registerAgent } = await import('@agentuity/runtime');
504
- const agent = require('./src${agentPath}').default;
505
- registerAgent("${agentRegistrationName}", agent);${evalsImport}
506
- })();`;
507
- inserts.push(buffer);
508
- }
509
- }
510
259
  const indexFile = join(srcDir, 'web', 'index.html');
511
260
  // Setup workbench configuration - evaluate fresh each time during builds
512
261
  const workbenchConfig = await setupWorkbench(srcDir);
@@ -556,35 +305,41 @@ import { readFileSync, existsSync } from 'node:fs';
556
305
  router.get('/public/*', webstatic);
557
306
  })();`);
558
307
  }
559
- // Add standalone agents (agents without routes) to agentInfo
560
- // These agents can still be called by other agents or routes via ctx.agent
561
- const registeredIdentifiers = new Set(agentInfo.map((a) => a.identifier));
562
- for (const [identifier, md] of agentMetadata) {
563
- if (!registeredIdentifiers.has(identifier)) {
564
- // md.get('filename') can be either absolute or relative to rootDir
565
- const filename = md.get('filename');
566
- const absolutePath = filename.startsWith('/')
567
- ? filename
568
- : join(rootDir, filename);
569
- // Convert to path relative to srcDir like route-based agents
570
- // e.g., /path/to/src/agent/lifecycle/agent.ts -> ./agent/lifecycle/agent
571
- const agentPath = absolutePath.replace(srcDir, '.').replace('.ts', '');
572
- // Extract folder name as agent name (same as route-based logic)
573
- const folderName = basename(dirname(absolutePath));
574
- const { isSubagent, parentName } = detectSubagent(absolutePath, srcDir);
575
- const agentDetail = {
576
- name: folderName,
577
- id: md.get('id'),
578
- path: agentPath,
579
- filename: absolutePath,
580
- identifier: md.get('identifier'),
581
- description: md.get('description') ?? '',
582
- agentId: md.get('agentId'),
583
- };
584
- if (isSubagent && parentName) {
585
- 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)`);
586
342
  }
587
- agentInfo.push(agentDetail);
588
343
  }
589
344
  }
590
345
  // Only generate registry if there are agents
@@ -595,6 +350,63 @@ import { readFileSync, existsSync } from 'node:fs';
595
350
  generateAgentRegistry(srcDir, agentInfo);
596
351
  // Generate lifecycle types if setup() is present in app.ts
597
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
+ }
598
410
  // Only create the workbench routes if workbench is actually configured
599
411
  if (workbenchConfig) {
600
412
  inserts.push(`await (async() => {
@@ -641,12 +453,10 @@ await (async() => {
641
453
  }
642
454
  // generate the build metadata
643
455
  metadata = {
644
- routes: routeDefinitions,
456
+ routes: apiRoutesMetadata,
645
457
  agents: [],
646
458
  };
647
- // Group agents by parent/child relationship
648
- const parentAgentMetadata = new Map();
649
- const subagentsByParent = new Map();
459
+ // Validate required metadata properties and build agent metadata
650
460
  for (const [, v] of agentMetadata) {
651
461
  if (!v.has('filename')) {
652
462
  throw new MetadataPropertyMissingError({
@@ -660,12 +470,6 @@ await (async() => {
660
470
  message: 'agent metadata is missing expected id property',
661
471
  });
662
472
  }
663
- if (!v.has('identifier')) {
664
- throw new MetadataPropertyMissingError({
665
- name: 'identifier',
666
- message: 'agent metadata is missing expected identifier property',
667
- });
668
- }
669
473
  if (!v.has('version')) {
670
474
  throw new MetadataPropertyMissingError({
671
475
  name: 'version',
@@ -684,40 +488,13 @@ await (async() => {
684
488
  message: 'agent metadata is missing expected agentId property',
685
489
  });
686
490
  }
687
- const parentName = v.get('parent');
688
- if (parentName) {
689
- // This is a subagent
690
- if (!subagentsByParent.has(parentName)) {
691
- subagentsByParent.set(parentName, []);
692
- }
693
- subagentsByParent.get(parentName).push(v);
694
- }
695
- else {
696
- // This is a parent or standalone agent
697
- parentAgentMetadata.set(v.get('identifier'), v);
698
- }
699
- }
700
- // Validate that all subagents reference existing parent agents
701
- for (const [parentName, subagents] of subagentsByParent) {
702
- const parentExists = Array.from(parentAgentMetadata.values()).some((meta) => meta.get('name') === parentName || meta.get('identifier') === parentName);
703
- if (!parentExists) {
704
- const subagentPaths = subagents.map((s) => s.get('filename')).join(', ');
705
- throw new SubAgentMissingError({
706
- message: `Subagent(s) [${subagentPaths}] reference parent "${parentName}" which does not exist. ` +
707
- `Ensure the parent agent is defined.`,
708
- });
709
- }
710
- }
711
- // Build metadata with nested subagents
712
- for (const [_identifier, v] of parentAgentMetadata) {
713
491
  const agentData = {
714
492
  filename: v.get('filename'),
715
493
  id: v.get('id'),
716
- identifier: v.get('identifier'),
717
494
  agentId: v.get('agentId'),
718
495
  version: v.get('version'),
719
496
  name: v.get('name'),
720
- description: v.get('description') ?? '<no description provided>',
497
+ description: v.get('description') ?? '',
721
498
  projectId,
722
499
  };
723
500
  const evalsStr = v.get('evals');
@@ -740,49 +517,8 @@ await (async() => {
740
517
  else {
741
518
  logger.trace(`[plugin] No evals found for agent ${agentData.name}`);
742
519
  }
743
- // Add subagents if any (check both name and identifier)
744
- const subagents = subagentsByParent.get(agentData.name) ||
745
- subagentsByParent.get(agentData.identifier);
746
- if (subagents && subagents.length > 0) {
747
- agentData.subagents = subagents.map((sub) => {
748
- const subagentData = {
749
- filename: sub.get('filename'),
750
- id: sub.get('id'),
751
- identifier: sub.get('identifier'),
752
- agentId: sub.get('agentId'),
753
- version: sub.get('version'),
754
- name: sub.get('name'),
755
- description: sub.get('description') ?? '<no description provided>',
756
- projectId,
757
- };
758
- // Add evals for subagents if any
759
- const subEvalsStr = sub.get('evals');
760
- if (subEvalsStr) {
761
- logger.trace(`[plugin] Found evals string for subagent ${subagentData.name}, parsing...`);
762
- try {
763
- const parsedSubEvals = JSON.parse(subEvalsStr);
764
- subagentData.evals = parsedSubEvals.map((evalItem) => ({
765
- ...evalItem,
766
- agentIdentifier: subagentData.agentId,
767
- projectId,
768
- }));
769
- logger.trace(`[plugin] Successfully parsed ${subagentData.evals?.length ?? 0} eval(s) for subagent ${subagentData.name}`);
770
- }
771
- catch (e) {
772
- logger.trace(`[plugin] Failed to parse evals for subagent ${subagentData.name}: ${e}`);
773
- console.warn(`Failed to parse evals for subagent ${subagentData.name}: ${e}`);
774
- }
775
- }
776
- else {
777
- logger.trace(`[plugin] No evals found for subagent ${subagentData.name}`);
778
- }
779
- return subagentData;
780
- });
781
- }
782
520
  metadata.agents.push(agentData);
783
521
  }
784
- const routeMappingJSFile = Bun.file(join(outDir, '.routemapping.json'));
785
- await routeMappingJSFile.write(JSON.stringify(routeMapping));
786
522
  return {
787
523
  contents,
788
524
  loader: 'ts',