@agentuity/cli 0.0.43 → 0.0.45

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 (209) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +1 -1
  3. package/dist/api.d.ts +3 -3
  4. package/dist/api.d.ts.map +1 -1
  5. package/dist/auth.d.ts +10 -2
  6. package/dist/auth.d.ts.map +1 -1
  7. package/dist/banner.d.ts.map +1 -1
  8. package/dist/cli.d.ts.map +1 -1
  9. package/dist/cmd/auth/api.d.ts +4 -4
  10. package/dist/cmd/auth/api.d.ts.map +1 -1
  11. package/dist/cmd/auth/index.d.ts.map +1 -1
  12. package/dist/cmd/auth/login.d.ts.map +1 -1
  13. package/dist/cmd/auth/signup.d.ts.map +1 -1
  14. package/dist/cmd/auth/ssh/add.d.ts +2 -0
  15. package/dist/cmd/auth/ssh/add.d.ts.map +1 -0
  16. package/dist/cmd/auth/ssh/api.d.ts +16 -0
  17. package/dist/cmd/auth/ssh/api.d.ts.map +1 -0
  18. package/dist/cmd/auth/ssh/delete.d.ts +2 -0
  19. package/dist/cmd/auth/ssh/delete.d.ts.map +1 -0
  20. package/dist/cmd/auth/ssh/index.d.ts +3 -0
  21. package/dist/cmd/auth/ssh/index.d.ts.map +1 -0
  22. package/dist/cmd/auth/ssh/list.d.ts +2 -0
  23. package/dist/cmd/auth/ssh/list.d.ts.map +1 -0
  24. package/dist/cmd/auth/whoami.d.ts.map +1 -1
  25. package/dist/cmd/bundle/ast.d.ts +14 -3
  26. package/dist/cmd/bundle/ast.d.ts.map +1 -1
  27. package/dist/cmd/bundle/ast.test.d.ts +2 -0
  28. package/dist/cmd/bundle/ast.test.d.ts.map +1 -0
  29. package/dist/cmd/bundle/bundler.d.ts +6 -1
  30. package/dist/cmd/bundle/bundler.d.ts.map +1 -1
  31. package/dist/cmd/bundle/file.d.ts.map +1 -1
  32. package/dist/cmd/bundle/fix-duplicate-exports.d.ts +2 -0
  33. package/dist/cmd/bundle/fix-duplicate-exports.d.ts.map +1 -0
  34. package/dist/cmd/bundle/fix-duplicate-exports.test.d.ts +2 -0
  35. package/dist/cmd/bundle/fix-duplicate-exports.test.d.ts.map +1 -0
  36. package/dist/cmd/bundle/plugin.d.ts +2 -0
  37. package/dist/cmd/bundle/plugin.d.ts.map +1 -1
  38. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  39. package/dist/cmd/cloud/domain.d.ts +17 -0
  40. package/dist/cmd/cloud/domain.d.ts.map +1 -0
  41. package/dist/cmd/cloud/index.d.ts.map +1 -1
  42. package/dist/cmd/cloud/resource/add.d.ts +2 -0
  43. package/dist/cmd/cloud/resource/add.d.ts.map +1 -0
  44. package/dist/cmd/cloud/resource/delete.d.ts +2 -0
  45. package/dist/cmd/cloud/resource/delete.d.ts.map +1 -0
  46. package/dist/cmd/cloud/resource/index.d.ts +3 -0
  47. package/dist/cmd/cloud/resource/index.d.ts.map +1 -0
  48. package/dist/cmd/cloud/resource/list.d.ts +2 -0
  49. package/dist/cmd/cloud/resource/list.d.ts.map +1 -0
  50. package/dist/cmd/cloud/scp/download.d.ts +2 -0
  51. package/dist/cmd/cloud/scp/download.d.ts.map +1 -0
  52. package/dist/cmd/cloud/scp/index.d.ts +3 -0
  53. package/dist/cmd/cloud/scp/index.d.ts.map +1 -0
  54. package/dist/cmd/cloud/scp/upload.d.ts +2 -0
  55. package/dist/cmd/cloud/scp/upload.d.ts.map +1 -0
  56. package/dist/cmd/cloud/ssh.d.ts +2 -0
  57. package/dist/cmd/cloud/ssh.d.ts.map +1 -0
  58. package/dist/cmd/dev/api.d.ts +18 -0
  59. package/dist/cmd/dev/api.d.ts.map +1 -0
  60. package/dist/cmd/dev/download.d.ts +11 -0
  61. package/dist/cmd/dev/download.d.ts.map +1 -0
  62. package/dist/cmd/dev/index.d.ts.map +1 -1
  63. package/dist/cmd/dev/templates.d.ts +3 -0
  64. package/dist/cmd/dev/templates.d.ts.map +1 -0
  65. package/dist/cmd/env/delete.d.ts.map +1 -1
  66. package/dist/cmd/env/get.d.ts.map +1 -1
  67. package/dist/cmd/env/import.d.ts.map +1 -1
  68. package/dist/cmd/env/list.d.ts.map +1 -1
  69. package/dist/cmd/env/pull.d.ts.map +1 -1
  70. package/dist/cmd/env/push.d.ts.map +1 -1
  71. package/dist/cmd/env/set.d.ts.map +1 -1
  72. package/dist/cmd/profile/show.d.ts.map +1 -1
  73. package/dist/cmd/project/create.d.ts.map +1 -1
  74. package/dist/cmd/project/delete.d.ts.map +1 -1
  75. package/dist/cmd/project/list.d.ts.map +1 -1
  76. package/dist/cmd/project/show.d.ts.map +1 -1
  77. package/dist/cmd/project/template-flow.d.ts +4 -0
  78. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  79. package/dist/cmd/secret/delete.d.ts.map +1 -1
  80. package/dist/cmd/secret/get.d.ts.map +1 -1
  81. package/dist/cmd/secret/import.d.ts.map +1 -1
  82. package/dist/cmd/secret/list.d.ts.map +1 -1
  83. package/dist/cmd/secret/pull.d.ts.map +1 -1
  84. package/dist/cmd/secret/push.d.ts.map +1 -1
  85. package/dist/cmd/secret/set.d.ts.map +1 -1
  86. package/dist/config.d.ts +9 -3
  87. package/dist/config.d.ts.map +1 -1
  88. package/dist/crypto/box.d.ts +65 -0
  89. package/dist/crypto/box.d.ts.map +1 -0
  90. package/dist/crypto/box.test.d.ts +2 -0
  91. package/dist/crypto/box.test.d.ts.map +1 -0
  92. package/dist/download.d.ts.map +1 -1
  93. package/dist/steps.d.ts +4 -1
  94. package/dist/steps.d.ts.map +1 -1
  95. package/dist/terminal.d.ts.map +1 -1
  96. package/dist/tui.d.ts +31 -1
  97. package/dist/tui.d.ts.map +1 -1
  98. package/dist/types.d.ts +249 -126
  99. package/dist/types.d.ts.map +1 -1
  100. package/dist/utils/detectSubagent.d.ts +15 -0
  101. package/dist/utils/detectSubagent.d.ts.map +1 -0
  102. package/dist/utils/zip.d.ts +7 -0
  103. package/dist/utils/zip.d.ts.map +1 -0
  104. package/package.json +11 -3
  105. package/src/api-errors.md +2 -2
  106. package/src/api.ts +12 -7
  107. package/src/auth.ts +116 -7
  108. package/src/banner.ts +13 -6
  109. package/src/cli.ts +695 -63
  110. package/src/cmd/auth/api.ts +10 -16
  111. package/src/cmd/auth/index.ts +2 -1
  112. package/src/cmd/auth/login.ts +24 -8
  113. package/src/cmd/auth/signup.ts +15 -11
  114. package/src/cmd/auth/ssh/add.ts +263 -0
  115. package/src/cmd/auth/ssh/api.ts +94 -0
  116. package/src/cmd/auth/ssh/delete.ts +102 -0
  117. package/src/cmd/auth/ssh/index.ts +10 -0
  118. package/src/cmd/auth/ssh/list.ts +74 -0
  119. package/src/cmd/auth/whoami.ts +13 -13
  120. package/src/cmd/bundle/ast.test.ts +565 -0
  121. package/src/cmd/bundle/ast.ts +457 -44
  122. package/src/cmd/bundle/bundler.ts +255 -57
  123. package/src/cmd/bundle/file.ts +6 -12
  124. package/src/cmd/bundle/fix-duplicate-exports.test.ts +387 -0
  125. package/src/cmd/bundle/fix-duplicate-exports.ts +204 -0
  126. package/src/cmd/bundle/index.ts +9 -9
  127. package/src/cmd/bundle/patch/aisdk.ts +1 -1
  128. package/src/cmd/bundle/plugin.ts +373 -53
  129. package/src/cmd/cloud/deploy.ts +300 -93
  130. package/src/cmd/cloud/domain.ts +92 -0
  131. package/src/cmd/cloud/index.ts +4 -1
  132. package/src/cmd/cloud/resource/add.ts +56 -0
  133. package/src/cmd/cloud/resource/delete.ts +120 -0
  134. package/src/cmd/cloud/resource/index.ts +11 -0
  135. package/src/cmd/cloud/resource/list.ts +69 -0
  136. package/src/cmd/cloud/scp/download.ts +59 -0
  137. package/src/cmd/cloud/scp/index.ts +9 -0
  138. package/src/cmd/cloud/scp/upload.ts +62 -0
  139. package/src/cmd/cloud/ssh.ts +68 -0
  140. package/src/cmd/dev/api.ts +46 -0
  141. package/src/cmd/dev/download.ts +111 -0
  142. package/src/cmd/dev/index.ts +360 -34
  143. package/src/cmd/dev/templates.ts +84 -0
  144. package/src/cmd/env/delete.ts +5 -20
  145. package/src/cmd/env/get.ts +5 -18
  146. package/src/cmd/env/import.ts +5 -20
  147. package/src/cmd/env/list.ts +5 -18
  148. package/src/cmd/env/pull.ts +10 -23
  149. package/src/cmd/env/push.ts +5 -23
  150. package/src/cmd/env/set.ts +5 -20
  151. package/src/cmd/index.ts +2 -2
  152. package/src/cmd/profile/show.ts +15 -6
  153. package/src/cmd/project/create.ts +7 -2
  154. package/src/cmd/project/delete.ts +75 -18
  155. package/src/cmd/project/download.ts +2 -2
  156. package/src/cmd/project/list.ts +8 -8
  157. package/src/cmd/project/show.ts +3 -7
  158. package/src/cmd/project/template-flow.ts +170 -72
  159. package/src/cmd/secret/delete.ts +5 -20
  160. package/src/cmd/secret/get.ts +5 -18
  161. package/src/cmd/secret/import.ts +5 -20
  162. package/src/cmd/secret/list.ts +5 -18
  163. package/src/cmd/secret/pull.ts +10 -23
  164. package/src/cmd/secret/push.ts +5 -23
  165. package/src/cmd/secret/set.ts +5 -20
  166. package/src/config.ts +224 -24
  167. package/src/crypto/box.test.ts +431 -0
  168. package/src/crypto/box.ts +477 -0
  169. package/src/download.ts +1 -0
  170. package/src/env-util.test.ts +1 -1
  171. package/src/steps.ts +65 -6
  172. package/src/terminal.ts +24 -23
  173. package/src/tui.ts +192 -61
  174. package/src/types.ts +291 -201
  175. package/src/utils/detectSubagent.ts +31 -0
  176. package/src/utils/zip.ts +38 -0
  177. package/dist/cmd/example/create-user.d.ts +0 -2
  178. package/dist/cmd/example/create-user.d.ts.map +0 -1
  179. package/dist/cmd/example/create.d.ts +0 -2
  180. package/dist/cmd/example/create.d.ts.map +0 -1
  181. package/dist/cmd/example/deploy.d.ts +0 -2
  182. package/dist/cmd/example/deploy.d.ts.map +0 -1
  183. package/dist/cmd/example/index.d.ts +0 -2
  184. package/dist/cmd/example/index.d.ts.map +0 -1
  185. package/dist/cmd/example/list.d.ts +0 -2
  186. package/dist/cmd/example/list.d.ts.map +0 -1
  187. package/dist/cmd/example/optional-auth.d.ts +0 -3
  188. package/dist/cmd/example/optional-auth.d.ts.map +0 -1
  189. package/dist/cmd/example/run-command.d.ts +0 -2
  190. package/dist/cmd/example/run-command.d.ts.map +0 -1
  191. package/dist/cmd/example/sound.d.ts +0 -3
  192. package/dist/cmd/example/sound.d.ts.map +0 -1
  193. package/dist/cmd/example/spinner.d.ts +0 -2
  194. package/dist/cmd/example/spinner.d.ts.map +0 -1
  195. package/dist/cmd/example/steps.d.ts +0 -2
  196. package/dist/cmd/example/steps.d.ts.map +0 -1
  197. package/dist/cmd/example/version.d.ts +0 -2
  198. package/dist/cmd/example/version.d.ts.map +0 -1
  199. package/src/cmd/example/create-user.ts +0 -38
  200. package/src/cmd/example/create.ts +0 -31
  201. package/src/cmd/example/deploy.ts +0 -36
  202. package/src/cmd/example/index.ts +0 -29
  203. package/src/cmd/example/list.ts +0 -32
  204. package/src/cmd/example/optional-auth.ts +0 -38
  205. package/src/cmd/example/run-command.ts +0 -45
  206. package/src/cmd/example/sound.ts +0 -14
  207. package/src/cmd/example/spinner.ts +0 -44
  208. package/src/cmd/example/steps.ts +0 -66
  209. package/src/cmd/example/version.ts +0 -13
@@ -1,9 +1,12 @@
1
1
  import type { BunPlugin } from 'bun';
2
- import { dirname, basename, join } from 'node:path';
3
- import { existsSync, writeFileSync } from 'node:fs';
4
- import type { BuildMetadata } from '../../types';
5
- import { parseAgentMetadata, parseRoute } from './ast';
2
+ import { dirname, basename, join, resolve } from 'node:path';
3
+ import { existsSync, writeFileSync, mkdirSync } from 'node:fs';
4
+ import type { BuildMetadata } from '@agentuity/server';
5
+ import { parseAgentMetadata, parseRoute, parseEvalMetadata } from './ast';
6
6
  import { applyPatch, generatePatches } from './patch';
7
+ import { detectSubagent } from '../../utils/detectSubagent';
8
+ import { createLogger } from '@agentuity/server';
9
+ import type { LogLevel } from '../../types';
7
10
 
8
11
  function toCamelCase(str: string): string {
9
12
  return str
@@ -17,31 +20,139 @@ function toPascalCase(str: string): string {
17
20
  }
18
21
 
19
22
  function generateAgentRegistry(srcDir: string, agentInfo: Array<Record<string, string>>) {
23
+ // Separate parent agents and subagents
24
+ const parentAgents = agentInfo.filter((a) => !a.parent);
25
+ const subagents = agentInfo.filter((a) => a.parent);
26
+
27
+ // Group subagents by parent
28
+ const subagentsByParent = new Map<string, Array<Record<string, string>>>();
29
+ for (const subagent of subagents) {
30
+ const parentName = subagent.parent!;
31
+ if (!subagentsByParent.has(parentName)) {
32
+ subagentsByParent.set(parentName, []);
33
+ }
34
+ subagentsByParent.get(parentName)!.push(subagent);
35
+ }
36
+
37
+ // Detect naming collisions in generated identifiers
38
+ // Naming strategy: parent + child names are combined as `${parent}_${name}` then converted to camelCase
39
+ // Example: parent="user", child="profile" → "user_profile" → "userProfile"
40
+ // Potential collision: parent="user_profile", child="info" and parent="user", child="profile_info" both → "userProfileInfo"
41
+ const generatedNames = new Set<string>();
42
+ const collisions: string[] = [];
43
+
44
+ for (const agent of agentInfo) {
45
+ const fullName = agent.parent ? `${agent.parent}_${agent.name}` : agent.name;
46
+ const camelName = toCamelCase(fullName);
47
+
48
+ if (generatedNames.has(camelName)) {
49
+ collisions.push(`Identifier collision detected: "${camelName}" (from "${fullName}")`);
50
+ }
51
+ generatedNames.add(camelName);
52
+ }
53
+
54
+ if (collisions.length > 0) {
55
+ throw new Error(
56
+ `Agent identifier naming collisions detected:\n${collisions.join('\n')}\n\n` +
57
+ `This occurs when different agent names produce the same camelCase identifier.\n` +
58
+ `Please rename your agents to avoid this collision.`
59
+ );
60
+ }
61
+
62
+ // Generate imports for all agents
20
63
  const imports = agentInfo
21
- .map(({ name, path }) => {
22
- const camelName = toCamelCase(name);
64
+ .map(({ name, path, parent }) => {
65
+ const fullName = parent ? `${parent}.${name}` : name;
66
+ const camelName = toCamelCase(fullName.replace('.', '_'));
23
67
  const relativePath = path.replace(/^\.\/agents\//, './');
24
68
  return `import ${camelName}Agent from '${relativePath}';`;
25
69
  })
26
70
  .join('\n');
27
71
 
28
- const registry = agentInfo
29
- .map(({ name }) => {
30
- const camelName = toCamelCase(name);
31
- return ` ${camelName}: ${camelName}Agent,`;
32
- })
33
- .join('\n');
72
+ // Evals are now imported in plugin.ts when agents are registered
73
+ // No need to import them in registry.generated.ts
74
+ const evalsImportsStr = '';
75
+
76
+ // Validate that child property names don't collide with parent agent properties
77
+ const reservedAgentProperties = ['metadata', 'run', 'inputSchema', 'outputSchema', 'stream'];
78
+ for (const parentAgent of parentAgents) {
79
+ const children = subagentsByParent.get(parentAgent.name) || [];
80
+ for (const child of children) {
81
+ const childPropertyName = toCamelCase(child.name);
82
+
83
+ // Check for collision with reserved agent properties
84
+ if (reservedAgentProperties.includes(childPropertyName)) {
85
+ throw new Error(
86
+ `Subagent property name collision detected: "${childPropertyName}" in parent "${parentAgent.name}"\n` +
87
+ `The child name "${child.name}" conflicts with a reserved agent property (${reservedAgentProperties.join(', ')}).\n` +
88
+ `Please rename the subagent to avoid this collision.`
89
+ );
90
+ }
91
+ }
92
+ }
93
+
94
+ // Generate nested registry structure
95
+ const registryLines: string[] = [];
96
+ for (const parentAgent of parentAgents) {
97
+ const parentCamelName = toCamelCase(parentAgent.name);
98
+ const children = subagentsByParent.get(parentAgent.name) || [];
99
+
100
+ if (children.length === 0) {
101
+ // No subagents, simple assignment
102
+ registryLines.push(` ${parentCamelName}: ${parentCamelName}Agent,`);
103
+ } else {
104
+ // Has subagents, create nested structure using object spread (no mutation)
105
+ registryLines.push(` ${parentCamelName}: {`);
106
+ registryLines.push(` ...${parentCamelName}Agent,`);
107
+ for (const child of children) {
108
+ const childCamelName = toCamelCase(`${parentAgent.name}_${child.name}`);
109
+ registryLines.push(` ${toCamelCase(child.name)}: ${childCamelName}Agent,`);
110
+ }
111
+ registryLines.push(` },`);
112
+ }
113
+ }
114
+ const registry = registryLines.join('\n');
34
115
 
116
+ // Generate type exports for all agents
35
117
  const typeExports = agentInfo
36
- .map(({ name }) => {
37
- const camelName = toCamelCase(name);
38
- const pascalName = toPascalCase(name);
118
+ .map(({ name, parent }) => {
119
+ const fullName = parent ? `${parent}_${name}` : name;
120
+ const camelName = toCamelCase(fullName);
121
+ const pascalName = toPascalCase(fullName);
39
122
  return `export type ${pascalName}AgentRunner = AgentRunner<typeof ${camelName}Agent['inputSchema'], typeof ${camelName}Agent['outputSchema'], typeof ${camelName}Agent['stream'] extends true ? true : false>;`;
40
123
  })
41
124
  .join('\n');
42
125
 
126
+ // Generate nested agent type definitions
127
+ const agentTypeLines: string[] = [];
128
+ for (const parentAgent of parentAgents) {
129
+ const parentCamelName = toCamelCase(parentAgent.name);
130
+ const children = subagentsByParent.get(parentAgent.name) || [];
131
+
132
+ if (children.length === 0) {
133
+ // No subagents
134
+ agentTypeLines.push(
135
+ ` ${parentCamelName}: AgentRunner<AgentRegistry['${parentCamelName}']['inputSchema'], AgentRegistry['${parentCamelName}']['outputSchema'], AgentRegistry['${parentCamelName}']['stream'] extends true ? true : false>;`
136
+ );
137
+ } else {
138
+ // Has subagents - create intersection type
139
+ agentTypeLines.push(
140
+ ` ${parentCamelName}: AgentRunner<AgentRegistry['${parentCamelName}']['inputSchema'], AgentRegistry['${parentCamelName}']['outputSchema'], AgentRegistry['${parentCamelName}']['stream'] extends true ? true : false> & {`
141
+ );
142
+ for (const child of children) {
143
+ const childCamelName = toCamelCase(child.name);
144
+ const fullChildName = toCamelCase(`${parentAgent.name}_${child.name}`);
145
+ agentTypeLines.push(
146
+ ` ${childCamelName}: AgentRunner<typeof ${fullChildName}Agent['inputSchema'], typeof ${fullChildName}Agent['outputSchema'], typeof ${fullChildName}Agent['stream'] extends true ? true : false>;`
147
+ );
148
+ }
149
+ agentTypeLines.push(` };`);
150
+ }
151
+ }
152
+ const agentTypes = agentTypeLines.join('\n');
153
+
43
154
  const generatedContent = `// Auto-generated by Agentuity - do not edit manually
44
- ${imports}
155
+ ${imports}${evalsImportsStr}
45
156
  import type { AgentRunner, Logger } from '@agentuity/runtime';
46
157
  import type { KeyValueStorage, ObjectStorage, StreamStorage, VectorStorage } from '@agentuity/core';
47
158
 
@@ -60,7 +171,7 @@ declare module "hono" {
60
171
  interface Context {
61
172
  agentName: AgentName;
62
173
  agent: {
63
- [K in AgentName]: AgentRunner<AgentRegistry[K]['inputSchema'], AgentRegistry[K]['outputSchema'], AgentRegistry[K]['stream'] extends true ? true : false>;
174
+ ${agentTypes}
64
175
  };
65
176
  waitUntil: (promise: Promise<void> | (() => void | Promise<void>)) => void;
66
177
  logger: Logger;
@@ -75,27 +186,55 @@ declare module "hono" {
75
186
  const agentsDir = join(srcDir, 'agents');
76
187
  const registryPath = join(agentsDir, 'registry.generated.ts');
77
188
 
189
+ // Ensure agents directory exists
190
+ if (!existsSync(agentsDir)) {
191
+ mkdirSync(agentsDir, { recursive: true });
192
+ }
193
+
78
194
  writeFileSync(registryPath, generatedContent, 'utf-8');
79
195
 
196
+ // Generate React client types with nested structure
197
+ const clientImports = agentInfo
198
+ .map(({ name, path, parent }) => {
199
+ const fullName = parent ? `${parent}_${name}` : name;
200
+ const camelName = toCamelCase(fullName);
201
+ const relativePath = path.replace(/^\.\/agents\//, './');
202
+ return `import type ${camelName}Agent from '${relativePath}';`;
203
+ })
204
+ .join('\n');
205
+
206
+ const clientAgentTypeLines: string[] = [];
207
+ for (const parentAgent of parentAgents) {
208
+ const parentCamelName = toCamelCase(parentAgent.name);
209
+ const children = subagentsByParent.get(parentAgent.name) || [];
210
+
211
+ if (children.length === 0) {
212
+ // No subagents
213
+ clientAgentTypeLines.push(
214
+ ` '${parentAgent.name}': Agent<typeof ${parentCamelName}Agent['inputSchema'], typeof ${parentCamelName}Agent['outputSchema']>;`
215
+ );
216
+ } else {
217
+ // Has subagents - create nested type with subagent access via dot notation
218
+ clientAgentTypeLines.push(
219
+ ` '${parentAgent.name}': Agent<typeof ${parentCamelName}Agent['inputSchema'], typeof ${parentCamelName}Agent['outputSchema']>;`
220
+ );
221
+ for (const child of children) {
222
+ const fullChildName = toCamelCase(`${parentAgent.name}_${child.name}`);
223
+ clientAgentTypeLines.push(
224
+ ` '${parentAgent.name}.${child.name}': Agent<typeof ${fullChildName}Agent['inputSchema'], typeof ${fullChildName}Agent['outputSchema']>;`
225
+ );
226
+ }
227
+ }
228
+ }
229
+
80
230
  const clientTypesContent = `// Auto-generated by Agentuity - do not edit manually
81
231
  // This file augments @agentuity/react with your project's agent types
82
- ${agentInfo
83
- .map(({ name, path }) => {
84
- const camelName = toCamelCase(name);
85
- const relativePath = path.replace(/^\.\/agents\//, './');
86
- return `import type ${camelName}Agent from '${relativePath}';`;
87
- })
88
- .join('\n')}
232
+ ${clientImports}
89
233
  import type { Agent } from '@agentuity/react';
90
234
 
91
235
  declare module '@agentuity/react' {
92
236
  interface AgentRegistry {
93
- ${agentInfo
94
- .map(({ name }) => {
95
- const camelName = toCamelCase(name);
96
- return ` '${name}': Agent<typeof ${camelName}Agent['inputSchema'], typeof ${camelName}Agent['outputSchema']>;`;
97
- })
98
- .join('\n')}
237
+ ${clientAgentTypeLines.join('\n')}
99
238
  }
100
239
  }
101
240
  `;
@@ -104,11 +243,23 @@ ${agentInfo
104
243
  writeFileSync(clientTypesPath, clientTypesContent, 'utf-8');
105
244
  }
106
245
 
246
+ let metadata: Partial<BuildMetadata>;
247
+
248
+ export function getBuildMetadata(): Partial<BuildMetadata> {
249
+ return metadata;
250
+ }
251
+
107
252
  const AgentuityBundler: BunPlugin = {
108
253
  name: 'Agentuity Bundler',
109
254
  setup(build) {
110
- const rootDir = build.config.root ?? '.';
255
+ const rootDir = resolve(build.config.root ?? '.');
111
256
  const srcDir = join(rootDir, 'src');
257
+ const projectId = build.config.define?.['process.env.AGENTUITY_CLOUD_PROJECT_ID']
258
+ ? JSON.parse(build.config.define['process.env.AGENTUITY_CLOUD_PROJECT_ID'])
259
+ : '';
260
+ const deploymentId = build.config.define?.['process.env.AGENTUITY_CLOUD_DEPLOYMENT_ID']
261
+ ? JSON.parse(build.config.define['process.env.AGENTUITY_CLOUD_DEPLOYMENT_ID'])
262
+ : '';
112
263
  const routes: Set<string> = new Set();
113
264
  const agentInfo: Array<Record<string, string>> = [];
114
265
  const agentMetadata: Map<string, Map<string, string>> = new Map<
@@ -129,12 +280,38 @@ const AgentuityBundler: BunPlugin = {
129
280
  return args;
130
281
  });
131
282
 
283
+ build.onLoad({ filter: /\/route\.ts$/, namespace: 'file' }, async (args) => {
284
+ if (args.path.startsWith(srcDir)) {
285
+ const importPath = args.path
286
+ .replace(rootDir, '')
287
+ .replace('.ts', '')
288
+ .replace('/src/', './');
289
+ routes.add(importPath);
290
+ }
291
+ // return undefined to let Bun handle loading normally
292
+ return;
293
+ });
294
+
132
295
  build.onLoad({ filter: /\/agent\.ts$/, namespace: 'file' }, async (args) => {
133
296
  let newsource = await Bun.file(args.path).text();
134
297
  if (args.path.startsWith(srcDir)) {
135
298
  const contents = transpiler.transformSync(newsource);
136
- const [ns, md] = parseAgentMetadata(rootDir, args.path, contents);
299
+ const [ns, md] = await parseAgentMetadata(
300
+ rootDir,
301
+ args.path,
302
+ contents,
303
+ projectId,
304
+ deploymentId
305
+ );
137
306
  newsource = ns;
307
+
308
+ // Detect if this is a subagent by checking path structure
309
+ // Note: Path structure assumption - 4 segments: agents/parent/child/agent.ts
310
+ const { isSubagent, parentName } = detectSubagent(args.path, srcDir);
311
+ if (isSubagent && parentName) {
312
+ md.set('parent', parentName);
313
+ }
314
+
138
315
  agentMetadata.set(md.get('identifier')!, md);
139
316
  }
140
317
  return {
@@ -143,6 +320,19 @@ const AgentuityBundler: BunPlugin = {
143
320
  };
144
321
  });
145
322
 
323
+ build.onLoad({ filter: /\/eval\.ts$/, namespace: 'file' }, async (args) => {
324
+ let newsource = await Bun.file(args.path).text();
325
+ if (args.path.startsWith(srcDir)) {
326
+ const contents = transpiler.transformSync(newsource);
327
+ const [ns] = parseEvalMetadata(rootDir, args.path, contents, projectId, deploymentId);
328
+ newsource = ns;
329
+ }
330
+ return {
331
+ contents: newsource,
332
+ loader: 'ts',
333
+ };
334
+ });
335
+
146
336
  const patches = generatePatches();
147
337
  for (const [, patch] of patches) {
148
338
  let modulePath = join('node_modules', patch.module, '.*');
@@ -173,17 +363,23 @@ const AgentuityBundler: BunPlugin = {
173
363
  namespace: 'file',
174
364
  },
175
365
  async (args) => {
366
+ const logger = createLogger((process.env.AGENTUITY_LOG_LEVEL as LogLevel) || 'info');
176
367
  if (build.config.target !== 'bun') {
177
368
  return;
178
369
  }
179
370
  await args.defer();
180
371
 
181
372
  const inserts: string[] = [];
373
+ const routeMapping: Record<string, string> = {};
182
374
 
183
375
  for (const route of routes) {
184
376
  const name = basename(dirname(route));
185
377
  const agent = route.replace(/\/route$/, '/agent');
186
378
  const hasAgent = existsSync(join(srcDir, agent + '.ts'));
379
+
380
+ // Detect if this is a subagent route using shared utility
381
+ const { isSubagent, parentName } = detectSubagent(route);
382
+
187
383
  const agentPath = route
188
384
  .replace(/\/route$/, '/*')
189
385
  .replace('/agents', '/agent')
@@ -196,7 +392,12 @@ const AgentuityBundler: BunPlugin = {
196
392
  .replace('/agents', '/agent')
197
393
  .replace('./', '/');
198
394
 
199
- const definitions = await parseRoute(rootDir, join(srcDir, route + '.ts'));
395
+ const definitions = await parseRoute(
396
+ rootDir,
397
+ join(srcDir, `${route}.ts`),
398
+ projectId,
399
+ deploymentId
400
+ );
200
401
  routeDefinitions = [...routeDefinitions, ...definitions];
201
402
 
202
403
  let agentDetail: Record<string, string> = {};
@@ -213,23 +414,38 @@ const AgentuityBundler: BunPlugin = {
213
414
  identifier: md.get('identifier')!,
214
415
  description: md.get('description') ?? '',
215
416
  };
417
+ if (isSubagent && parentName) {
418
+ agentDetail.parent = parentName;
419
+ }
216
420
  agentInfo.push(agentDetail);
217
421
  }
218
422
 
219
423
  let buffer = `await (async() => {
220
- const { createAgentMiddleware, getApp, registerAgent } = await import('@agentuity/runtime');
221
- const app = getApp()!;
424
+ const { createAgentMiddleware, getRouter, registerAgent } = await import('@agentuity/runtime');
425
+ const router = getRouter()!;
222
426
  const route = require('./src/${route}').default;`;
223
427
  if (hasAgent) {
428
+ const agentRegistrationName =
429
+ isSubagent && parentName ? `${parentName}.${name}` : name;
430
+ // Build evals path from agent path (e.g., 'agents/eval/agent' -> 'agents/eval/eval.ts')
431
+ const agentDirPath = agent.replace(/\/agent$/, '');
432
+ const evalsPath = join(srcDir, agentDirPath, 'eval.ts');
433
+ const evalsImport = existsSync(evalsPath)
434
+ ? `\n require('./src/${agentDirPath}/eval');`
435
+ : '';
224
436
  buffer += `
225
437
  const agent = require('./src/${agent}').default;
226
- app.all("${agentPath}", createAgentMiddleware('${name}'));
227
- registerAgent("${name}", agent);`;
438
+ router.all("${agentPath}", createAgentMiddleware('${agentRegistrationName}'));
439
+ registerAgent("${agentRegistrationName}", agent);${evalsImport}`;
228
440
  }
229
441
  buffer += `
230
- app.route("${routePath}", route);
442
+ router.route("${routePath}", route);
231
443
  })();`;
232
444
  inserts.push(buffer);
445
+
446
+ for (const def of definitions) {
447
+ routeMapping[`${def.method} ${def.path}`] = def.id;
448
+ }
233
449
  }
234
450
 
235
451
  const indexFile = join(srcDir, 'web', 'index.html');
@@ -237,18 +453,25 @@ const AgentuityBundler: BunPlugin = {
237
453
  if (existsSync(indexFile)) {
238
454
  inserts.unshift(`await (async () => {
239
455
  const { serveStatic } = require('hono/bun');
240
- const { getApp } = await import('@agentuity/runtime');
241
- const app = getApp()!;
242
- const index = await Bun.file(import.meta.dir + '/web/index.js').text();
456
+ const { getRouter } = await import('@agentuity/runtime');
457
+ const router = getRouter()!;
458
+ const index = await Bun.file(import.meta.dir + '/web/index.html').text();
243
459
  const webstatic = serveStatic({ root: import.meta.dir + '/web' });
244
- app.get('/', (c) => c.html(index));
245
- app.get('/chunk/*', webstatic);
246
- app.get('/asset/*', webstatic);
247
- app.get('/public/*', webstatic);
460
+ router.get('/', (c) => c.html(index));
461
+ router.get('/web/chunk/*', webstatic);
462
+ router.get('/web/asset/*', webstatic);
463
+ router.get('/public/*', webstatic);
248
464
  })();`);
249
465
  }
250
466
 
251
- generateAgentRegistry(srcDir, agentInfo);
467
+ // Only generate registry if there are agents
468
+ // Note: We don't import the registry here because:
469
+ // 1. Evals are already imported when agents are registered (see line 421-422)
470
+ // 2. The registry is for type definitions only, not runtime execution
471
+ // 3. Importing it causes bundler resolution issues since it's generated during build
472
+ if (agentInfo.length > 0) {
473
+ generateAgentRegistry(srcDir, agentInfo);
474
+ }
252
475
 
253
476
  const file = Bun.file(args.path);
254
477
  let contents = await file.text();
@@ -270,10 +493,15 @@ const AgentuityBundler: BunPlugin = {
270
493
  }
271
494
 
272
495
  // generate the build metadata
273
- const metadata: BuildMetadata = {
496
+ metadata = {
274
497
  routes: routeDefinitions,
275
498
  agents: [],
276
499
  };
500
+
501
+ // Group agents by parent/child relationship
502
+ const parentAgentMetadata = new Map<string, Map<string, string>>();
503
+ const subagentsByParent = new Map<string, Array<Map<string, string>>>();
504
+
277
505
  for (const [, v] of agentMetadata) {
278
506
  if (!v.has('filename')) {
279
507
  throw new Error('agent metadata is missing expected filename property');
@@ -290,20 +518,112 @@ const AgentuityBundler: BunPlugin = {
290
518
  if (!v.has('name')) {
291
519
  throw new Error('agent metadata is missing expected name property');
292
520
  }
293
- metadata.agents.push({
521
+
522
+ const parentName = v.get('parent');
523
+ if (parentName) {
524
+ // This is a subagent
525
+ if (!subagentsByParent.has(parentName)) {
526
+ subagentsByParent.set(parentName, []);
527
+ }
528
+ subagentsByParent.get(parentName)!.push(v);
529
+ } else {
530
+ // This is a parent or standalone agent
531
+ parentAgentMetadata.set(v.get('identifier')!, v);
532
+ }
533
+ }
534
+
535
+ // Validate that all subagents reference existing parent agents
536
+ for (const [parentName, subagents] of subagentsByParent) {
537
+ const parentExists = Array.from(parentAgentMetadata.values()).some(
538
+ (meta) => meta.get('name') === parentName || meta.get('identifier') === parentName
539
+ );
540
+ if (!parentExists) {
541
+ const subagentPaths = subagents.map((s) => s.get('filename')).join(', ');
542
+ throw new Error(
543
+ `Subagent(s) [${subagentPaths}] reference parent "${parentName}" which does not exist. ` +
544
+ `Ensure the parent agent is defined.`
545
+ );
546
+ }
547
+ }
548
+
549
+ // Build metadata with nested subagents
550
+ for (const [_identifier, v] of parentAgentMetadata) {
551
+ const agentData: BuildMetadata['agents'][number] = {
294
552
  filename: v.get('filename')!,
295
553
  id: v.get('id')!,
296
554
  identifier: v.get('identifier')!,
297
555
  version: v.get('version')!,
298
556
  name: v.get('name')!,
299
557
  description: v.get('description') ?? '<no description provided>',
300
- });
558
+ };
559
+
560
+ const evalsStr = v.get('evals');
561
+ if (evalsStr) {
562
+ logger.trace(
563
+ `[plugin] Found evals string for agent ${agentData.name}, parsing...`
564
+ );
565
+ try {
566
+ agentData.evals = JSON.parse(evalsStr);
567
+ logger.trace(
568
+ `[plugin] Successfully parsed ${agentData.evals?.length ?? 0} eval(s) for agent ${agentData.name}`
569
+ );
570
+ } catch (e) {
571
+ logger.trace(
572
+ `[plugin] Failed to parse evals for agent ${agentData.name}: ${e}`
573
+ );
574
+ console.warn(`Failed to parse evals for agent ${agentData.name}: ${e}`);
575
+ }
576
+ } else {
577
+ logger.trace(`[plugin] No evals found for agent ${agentData.name}`);
578
+ }
579
+
580
+ // Add subagents if any (check both name and identifier)
581
+ const subagents =
582
+ subagentsByParent.get(agentData.name) ||
583
+ subagentsByParent.get(agentData.identifier);
584
+ if (subagents && subagents.length > 0) {
585
+ agentData.subagents = subagents.map((sub) => {
586
+ const subagentData: BuildMetadata['agents'][number] = {
587
+ filename: sub.get('filename')!,
588
+ id: sub.get('id')!,
589
+ identifier: sub.get('identifier')!,
590
+ version: sub.get('version')!,
591
+ name: sub.get('name')!,
592
+ description: sub.get('description') ?? '<no description provided>',
593
+ };
594
+
595
+ // Add evals for subagents if any
596
+ const subEvalsStr = sub.get('evals');
597
+ if (subEvalsStr) {
598
+ logger.trace(
599
+ `[plugin] Found evals string for subagent ${subagentData.name}, parsing...`
600
+ );
601
+ try {
602
+ subagentData.evals = JSON.parse(subEvalsStr);
603
+ logger.trace(
604
+ `[plugin] Successfully parsed ${subagentData.evals?.length ?? 0} eval(s) for subagent ${subagentData.name}`
605
+ );
606
+ } catch (e) {
607
+ logger.trace(
608
+ `[plugin] Failed to parse evals for subagent ${subagentData.name}: ${e}`
609
+ );
610
+ console.warn(
611
+ `Failed to parse evals for subagent ${subagentData.name}: ${e}`
612
+ );
613
+ }
614
+ } else {
615
+ logger.trace(`[plugin] No evals found for subagent ${subagentData.name}`);
616
+ }
617
+
618
+ return subagentData;
619
+ });
620
+ }
621
+
622
+ metadata.agents!.push(agentData);
301
623
  }
302
624
 
303
- const metadataFilename = Bun.file(
304
- join(build.config.outdir!, 'agentuity.metadata.json')
305
- );
306
- await metadataFilename.write(JSON.stringify(metadata));
625
+ const routeMappingJSFile = Bun.file(join(build.config.outdir!, '.routemapping.json'));
626
+ await routeMappingJSFile.write(JSON.stringify(routeMapping));
307
627
 
308
628
  return {
309
629
  contents,