@comma-agents/core 2.0.0-rc.0 → 2.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/agents/agent/agent.types.d.ts +2 -2
  2. package/dist/agents/loader/index.d.ts +2 -2
  3. package/dist/agents/loader/loader.d.ts +3 -5
  4. package/dist/agents/loader/loader.schema.d.ts +226 -13
  5. package/dist/agents/loader/loader.types.d.ts +9 -8
  6. package/dist/agents/registry/agent-registry.constants.d.ts +1 -0
  7. package/dist/agents/registry/agent-registry.d.ts +38 -0
  8. package/dist/agents/registry/agent-registry.types.d.ts +58 -0
  9. package/dist/agents/registry/index.d.ts +2 -0
  10. package/dist/credentials/backends/json-file.d.ts +1 -1
  11. package/dist/credentials/credentials.constants.d.ts +2 -0
  12. package/dist/credentials/credentials.utils.d.ts +0 -19
  13. package/dist/credentials/index.d.ts +1 -1
  14. package/dist/data-directory/data-directory.d.ts +11 -0
  15. package/dist/data-directory/index.d.ts +1 -0
  16. package/dist/defaults/defaults.d.ts +1 -1
  17. package/dist/flows/index.d.ts +2 -0
  18. package/dist/flows/loader/loader.schema.d.ts +2 -195
  19. package/dist/flows/loader/loader.utils.d.ts +5 -0
  20. package/dist/flows/registry/flow-registry.constants.d.ts +1 -0
  21. package/dist/flows/registry/flow-registry.d.ts +45 -0
  22. package/dist/flows/registry/flow-registry.types.d.ts +31 -0
  23. package/dist/flows/registry/index.d.ts +2 -0
  24. package/dist/hub/archive/archive.d.ts +2 -0
  25. package/dist/hub/archive/index.d.ts +1 -0
  26. package/dist/hub/comma-project.schema.json +171 -0
  27. package/dist/hub/hub.constants.d.ts +5 -0
  28. package/dist/hub/hub.d.ts +13 -0
  29. package/dist/hub/hub.schema.d.ts +1093 -0
  30. package/dist/hub/hub.types.d.ts +50 -0
  31. package/dist/hub/hub.utils.d.ts +3 -0
  32. package/dist/hub/index.d.ts +3 -0
  33. package/dist/hub/index.js +404 -0
  34. package/dist/hub/installed-packages/index.d.ts +2 -0
  35. package/dist/hub/installed-packages/installed-packages.d.ts +3 -0
  36. package/dist/hub/installed-packages/installed-packages.types.d.ts +14 -0
  37. package/dist/hub/package-installer/index.d.ts +2 -0
  38. package/dist/hub/package-installer/package-installer.d.ts +3 -0
  39. package/dist/hub/package-installer/package-installer.types.d.ts +11 -0
  40. package/dist/hub/registry-client/index.d.ts +2 -0
  41. package/dist/hub/registry-client/registry-client.d.ts +3 -0
  42. package/dist/hub/registry-client/registry-client.types.d.ts +10 -0
  43. package/dist/index.d.ts +13 -10
  44. package/dist/index.js +1386 -769
  45. package/dist/model/providers/catalog/catalog.utils.d.ts +2 -9
  46. package/dist/skills/skills.constants.d.ts +2 -2
  47. package/dist/skills/skills.types.d.ts +1 -1
  48. package/dist/skills/skills.utils.d.ts +0 -10
  49. package/dist/strategies/@comma/core-strategies/README.md +9 -0
  50. package/dist/strategies/@comma/core-strategies/build/build.json +69 -0
  51. package/dist/strategies/@comma/core-strategies/build/prompts/coder.md +56 -0
  52. package/dist/strategies/@comma/core-strategies/build/prompts/tester.md +39 -0
  53. package/dist/strategies/@comma/core-strategies/comma-project.json +49 -0
  54. package/dist/strategies/@comma/core-strategies/plan/plan.json +66 -0
  55. package/dist/strategies/@comma/core-strategies/plan/prompts/planner.md +59 -0
  56. package/dist/strategies/@comma/core-strategies/plan/prompts/reviewer.md +34 -0
  57. package/dist/strategies/@comma/core-strategies/qa.json +36 -0
  58. package/dist/strategies/@comma/core-strategies/reduce-complexity/reduce-complexity.jsonc +24 -0
  59. package/dist/strategies/@comma/core-strategies/standardize/manager.jsonc +54 -0
  60. package/dist/strategies/@comma/core-strategies/standardize/prompts/manager.md +278 -0
  61. package/dist/strategies/@comma/core-strategies/standardize/prompts/worker-auditor.md +131 -0
  62. package/dist/strategies/@comma/core-strategies/standardize/prompts/worker-reviewer.md +58 -0
  63. package/dist/strategies/@comma/core-strategies/standardize/worker.jsonc +69 -0
  64. package/dist/strategies/@comma/core-strategies/talk.json +42 -0
  65. package/dist/strategy/discover/discover.d.ts +10 -2
  66. package/dist/strategy/discover/discover.types.d.ts +6 -5
  67. package/dist/strategy/discover/discover.utils.d.ts +2 -13
  68. package/dist/strategy/discover/index.d.ts +1 -1
  69. package/dist/strategy/index.d.ts +3 -3
  70. package/dist/strategy/loader/loader.types.d.ts +2 -70
  71. package/dist/strategy/loader/loader.utils.d.ts +1 -8
  72. package/dist/strategy/loader/project-loader.d.ts +7 -1
  73. package/dist/strategy/schema.d.ts +154 -60
  74. package/dist/tools/built-in/list-strategy/list-strategy.d.ts +2 -2
  75. package/package.json +18 -7
package/dist/index.js CHANGED
@@ -1,6 +1,399 @@
1
1
  // @bun
2
2
  var __require = import.meta.require;
3
3
 
4
+ // src/hub/hub.ts
5
+ import { join as join3 } from "path";
6
+
7
+ // src/data-directory/data-directory.ts
8
+ import { homedir } from "os";
9
+ import { join } from "path";
10
+ function resolveDataDir() {
11
+ return join(homedir(), ".comma");
12
+ }
13
+ // src/hub/hub.constants.ts
14
+ var DEFAULT_HUB_REPOSITORY = {
15
+ owner: "CloAI",
16
+ repository: "CommaAgentsHub",
17
+ branch: "main"
18
+ };
19
+ var HUB_MAX_FILES = 2000;
20
+ var HUB_MAX_UNCOMPRESSED_BYTES = 50 * 1024 * 1024;
21
+ var HUB_INSTALLED_STATE_FILENAME = "installed-packages.json";
22
+
23
+ // src/hub/hub.utils.ts
24
+ function findAvailableHubPackage(snapshot, name) {
25
+ const project = snapshot.registry.packages.find((availablePackage) => availablePackage.name === name);
26
+ if (!project)
27
+ throw new Error(`Hub package not found: ${name}`);
28
+ return project;
29
+ }
30
+
31
+ // src/hub/installed-packages/installed-packages.ts
32
+ import { mkdir, readFile, rename, rm, writeFile } from "fs/promises";
33
+ import { dirname, resolve } from "path";
34
+ function createInstalledPackageStore({
35
+ statePath
36
+ }) {
37
+ async function readState() {
38
+ try {
39
+ const parsed = JSON.parse(await readFile(statePath, "utf8"));
40
+ return {
41
+ packages: Array.isArray(parsed.packages) ? parsed.packages : []
42
+ };
43
+ } catch (error) {
44
+ if (error.code === "ENOENT")
45
+ return { packages: [] };
46
+ throw error;
47
+ }
48
+ }
49
+ async function writeState(packages) {
50
+ await mkdir(dirname(statePath), { recursive: true });
51
+ const temporaryPath = `${statePath}.${crypto.randomUUID()}.tmp`;
52
+ await writeFile(temporaryPath, `${JSON.stringify({ packages: [...packages].sort((firstPackage, secondPackage) => firstPackage.name.localeCompare(secondPackage.name)) }, null, 2)}
53
+ `);
54
+ await rename(temporaryPath, statePath);
55
+ }
56
+ async function list() {
57
+ return (await readState()).packages;
58
+ }
59
+ async function get(name) {
60
+ return (await list()).find((installedPackage) => installedPackage.name === name);
61
+ }
62
+ return {
63
+ list,
64
+ get,
65
+ async replace(installedPackage) {
66
+ const state = await readState();
67
+ await writeState([
68
+ ...state.packages.filter((statePackage) => statePackage.name !== installedPackage.name),
69
+ installedPackage
70
+ ]);
71
+ },
72
+ async remove(name) {
73
+ const installedPackage = await get(name);
74
+ if (!installedPackage)
75
+ return false;
76
+ const backupPath = `${installedPackage.path}.${crypto.randomUUID()}.removing`;
77
+ const state = await readState();
78
+ await rename(installedPackage.path, backupPath);
79
+ try {
80
+ await writeState(state.packages.filter((statePackage) => statePackage.name !== name));
81
+ await rm(backupPath, { recursive: true, force: true });
82
+ return true;
83
+ } catch (error) {
84
+ await rename(backupPath, installedPackage.path);
85
+ throw error;
86
+ }
87
+ },
88
+ async isExecutableCodeApproved(manifestPath) {
89
+ const installedPackage = (await list()).find((statePackage) => resolve(statePackage.path, "comma-project.json") === resolve(manifestPath));
90
+ return installedPackage?.executableCodeApproved ?? false;
91
+ }
92
+ };
93
+ }
94
+ // src/hub/package-installer/package-installer.ts
95
+ import { mkdir as mkdir3, mkdtemp, readFile as readFile2, rename as rename2, rm as rm2, stat } from "fs/promises";
96
+ import { tmpdir } from "os";
97
+ import { dirname as dirname3, join as join2 } from "path";
98
+
99
+ // src/hub/archive/archive.ts
100
+ import { createWriteStream } from "fs";
101
+ import { mkdir as mkdir2 } from "fs/promises";
102
+ import { dirname as dirname2, relative, resolve as resolve2, sep } from "path";
103
+ import { extract } from "tar-stream";
104
+ async function extractHubPackageArchive(compressedArchive, packageName, destination) {
105
+ const unpackedArchive = Bun.gunzipSync(compressedArchive.buffer);
106
+ const archive = extract();
107
+ const packageMarker = `/packages/${packageName}/`;
108
+ let fileCount = 0;
109
+ let byteCount = 0;
110
+ let foundManifest = false;
111
+ await new Promise((resolveExtraction, rejectExtraction) => {
112
+ archive.on("entry", (header, stream, next) => {
113
+ const markerIndex = header.name.indexOf(packageMarker);
114
+ if (markerIndex === -1) {
115
+ stream.resume();
116
+ stream.once("end", next);
117
+ return;
118
+ }
119
+ const relativePath = header.name.slice(markerIndex + packageMarker.length);
120
+ if (relativePath.length === 0 || header.type === "directory") {
121
+ stream.resume();
122
+ stream.once("end", next);
123
+ return;
124
+ }
125
+ if (header.type !== "file") {
126
+ rejectExtraction(new Error(`Hub archive contains unsupported ${header.type} entry: ${relativePath}`));
127
+ stream.resume();
128
+ return;
129
+ }
130
+ const outputPath = resolve2(destination, relativePath);
131
+ const relativeOutputPath = relative(resolve2(destination), resolve2(outputPath));
132
+ if (relativeOutputPath === "" || relativeOutputPath === ".." || relativeOutputPath.startsWith(`..${sep}`)) {
133
+ rejectExtraction(new Error(`Hub archive path escapes package root: ${relativePath}`));
134
+ stream.resume();
135
+ return;
136
+ }
137
+ fileCount += 1;
138
+ byteCount += header.size ?? 0;
139
+ if (fileCount > HUB_MAX_FILES || byteCount > HUB_MAX_UNCOMPRESSED_BYTES) {
140
+ rejectExtraction(new Error("Hub package exceeds extraction limits"));
141
+ stream.resume();
142
+ return;
143
+ }
144
+ mkdir2(dirname2(outputPath), { recursive: true }).then(() => {
145
+ if (relativePath === "comma-project.json")
146
+ foundManifest = true;
147
+ const output = createWriteStream(outputPath, { flags: "wx" });
148
+ output.once("error", rejectExtraction);
149
+ output.once("finish", next);
150
+ stream.once("error", rejectExtraction);
151
+ stream.pipe(output);
152
+ }).catch(rejectExtraction);
153
+ });
154
+ archive.once("finish", () => {
155
+ if (!foundManifest) {
156
+ rejectExtraction(new Error(`Package ${packageName} was not found in the Hub archive`));
157
+ return;
158
+ }
159
+ resolveExtraction();
160
+ });
161
+ archive.once("error", rejectExtraction);
162
+ archive.end(unpackedArchive);
163
+ });
164
+ }
165
+ // src/hub/hub.schema.ts
166
+ import { z } from "zod";
167
+ var PACKAGE_NAME_PATTERN = /^@[a-z0-9][a-z0-9-]*\/[a-z0-9][a-z0-9-]*$/;
168
+ var ARTIFACT_NAME_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
169
+ var SEMVER_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
170
+ var HubPersonSchema = z.object({
171
+ name: z.string().min(1),
172
+ email: z.string().email().optional(),
173
+ url: z.string().url().optional()
174
+ }).strict();
175
+ var HubEnvironmentVariableSchema = z.object({
176
+ description: z.string().optional(),
177
+ required: z.boolean().optional(),
178
+ default: z.string().optional(),
179
+ example: z.string().optional()
180
+ }).strict();
181
+ var HubPermissionsSchema = z.object({
182
+ network: z.boolean().optional(),
183
+ filesystem: z.boolean().optional(),
184
+ shell: z.boolean().optional(),
185
+ executesCode: z.boolean().optional()
186
+ }).strict();
187
+ var ProjectArtifactEntrySchema = z.object({
188
+ path: z.string().min(1),
189
+ expose: z.boolean().optional(),
190
+ description: z.string().optional()
191
+ }).strict();
192
+ var ArtifactMapSchema = z.record(z.string().regex(ARTIFACT_NAME_PATTERN), ProjectArtifactEntrySchema);
193
+ var CommaProjectManifestSchema = z.object({
194
+ name: z.string().regex(PACKAGE_NAME_PATTERN),
195
+ version: z.string().regex(SEMVER_PATTERN, "Expected semantic version x.y.z"),
196
+ description: z.string().optional(),
197
+ license: z.string().optional(),
198
+ author: HubPersonSchema.optional(),
199
+ contributors: z.array(HubPersonSchema).optional(),
200
+ keywords: z.array(z.string()).optional(),
201
+ strategies: ArtifactMapSchema.optional(),
202
+ agents: ArtifactMapSchema.optional(),
203
+ flows: ArtifactMapSchema.optional(),
204
+ tools: ArtifactMapSchema.optional(),
205
+ entry: z.string().min(1).optional(),
206
+ dependencies: z.record(z.string(), z.string()).optional(),
207
+ environment: z.record(z.string().regex(/^[A-Z_][A-Z0-9_]*$/), HubEnvironmentVariableSchema).optional(),
208
+ permissions: HubPermissionsSchema.optional(),
209
+ links: z.object({
210
+ homepage: z.string().url().optional(),
211
+ repository: z.string().url().optional(),
212
+ docs: z.string().url().optional(),
213
+ issues: z.string().url().optional()
214
+ }).strict().optional()
215
+ }).strict();
216
+ var HubRegistryArtifactSchema = z.object({
217
+ id: z.string().regex(ARTIFACT_NAME_PATTERN),
218
+ ref: z.string().min(1),
219
+ path: z.string().min(1),
220
+ description: z.string().optional()
221
+ }).strict();
222
+ var RegistryArtifactListSchema = z.array(HubRegistryArtifactSchema);
223
+ var HubPackageSchema = z.object({
224
+ name: z.string().regex(PACKAGE_NAME_PATTERN),
225
+ version: z.string().regex(SEMVER_PATTERN),
226
+ description: z.string().optional(),
227
+ license: z.string().optional(),
228
+ path: z.string().min(1),
229
+ author: HubPersonSchema.optional(),
230
+ contributors: z.array(HubPersonSchema).optional(),
231
+ keywords: z.array(z.string()).optional(),
232
+ exports: z.object({
233
+ strategies: RegistryArtifactListSchema,
234
+ agents: RegistryArtifactListSchema,
235
+ flows: RegistryArtifactListSchema,
236
+ tools: RegistryArtifactListSchema
237
+ }).strict(),
238
+ environment: z.record(HubEnvironmentVariableSchema).optional(),
239
+ permissions: HubPermissionsSchema.optional(),
240
+ links: z.object({
241
+ homepage: z.string().url().optional(),
242
+ repository: z.string().url().optional(),
243
+ docs: z.string().url().optional(),
244
+ issues: z.string().url().optional()
245
+ }).strict().optional()
246
+ }).strict();
247
+ var HubRegistrySchema = z.object({
248
+ version: z.literal(1),
249
+ packages: z.array(HubPackageSchema)
250
+ }).strict();
251
+
252
+ // src/hub/package-installer/package-installer.ts
253
+ function createHubPackageInstaller({
254
+ packagesRoot,
255
+ installedPackages,
256
+ registryClient
257
+ }) {
258
+ return {
259
+ async install(project, snapshot, options, replace) {
260
+ if (project.permissions?.executesCode === true && options.allowCode !== true) {
261
+ throw new Error(`Package ${project.name} contains executable code; pass allowCode: true to install it`);
262
+ }
263
+ const existingPackage = await installedPackages.get(project.name);
264
+ if (existingPackage && !replace)
265
+ throw new Error(`Package ${project.name} is already installed`);
266
+ const stagingRoot = await mkdtemp(join2(tmpdir(), "comma-hub-"));
267
+ const stagedPackagePath = join2(stagingRoot, "package");
268
+ const destinationPath = join2(packagesRoot, ...project.name.split("/"));
269
+ const backupPath = `${destinationPath}.${crypto.randomUUID()}.backup`;
270
+ let destinationReplaced = false;
271
+ try {
272
+ await mkdir3(stagedPackagePath, { recursive: true });
273
+ await extractHubPackageArchive(await registryClient.fetchArchive(snapshot.commit), project.name, stagedPackagePath);
274
+ const manifest = CommaProjectManifestSchema.safeParse(JSON.parse(await readFile2(join2(stagedPackagePath, "comma-project.json"), "utf8")));
275
+ if (!manifest.success || manifest.data.name !== project.name || manifest.data.version !== project.version) {
276
+ throw new Error(`Installed manifest does not match registry entry for ${project.name}`);
277
+ }
278
+ if (manifest.data.permissions?.executesCode !== project.permissions?.executesCode) {
279
+ throw new Error(`Installed manifest permissions do not match registry entry for ${project.name}`);
280
+ }
281
+ if (manifest.data.dependencies && Object.keys(manifest.data.dependencies).length > 0) {
282
+ throw new Error(`Package dependencies are not supported in v1: ${project.name}`);
283
+ }
284
+ await mkdir3(dirname3(destinationPath), { recursive: true });
285
+ if (existingPackage)
286
+ await rename2(destinationPath, backupPath);
287
+ await rename2(stagedPackagePath, destinationPath);
288
+ destinationReplaced = true;
289
+ const installedPackage = {
290
+ name: project.name,
291
+ version: project.version,
292
+ commit: snapshot.commit,
293
+ path: destinationPath,
294
+ executableCodeApproved: project.permissions?.executesCode === true
295
+ };
296
+ await installedPackages.replace(installedPackage);
297
+ await rm2(backupPath, { recursive: true, force: true });
298
+ return installedPackage;
299
+ } catch (error) {
300
+ try {
301
+ if (destinationReplaced) {
302
+ await rm2(destinationPath, { recursive: true, force: true });
303
+ }
304
+ if (existingPackage && (await stat(backupPath)).isDirectory()) {
305
+ await rename2(backupPath, destinationPath);
306
+ }
307
+ } catch {}
308
+ throw error;
309
+ } finally {
310
+ await rm2(stagingRoot, { recursive: true, force: true });
311
+ await rm2(backupPath, { recursive: true, force: true });
312
+ }
313
+ }
314
+ };
315
+ }
316
+ // src/hub/registry-client/registry-client.ts
317
+ function createHubRegistryClient({
318
+ repository,
319
+ fetch: fetch2
320
+ }) {
321
+ let registrySnapshot;
322
+ async function fetchRequired(url) {
323
+ const response = await fetch2(url, {
324
+ headers: { Accept: "application/vnd.github+json" }
325
+ });
326
+ if (!response.ok)
327
+ throw new Error(`Hub request failed (${response.status}): ${url}`);
328
+ return response;
329
+ }
330
+ async function refresh() {
331
+ const repositoryUrl = `https://api.github.com/repos/${repository.owner}/${repository.repository}`;
332
+ const commitResponse = await fetchRequired(`${repositoryUrl}/commits/${repository.branch}`);
333
+ const commitJson = await commitResponse.json();
334
+ if (typeof commitJson.sha !== "string")
335
+ throw new Error("Hub commit response did not contain a SHA");
336
+ const registryResponse = await fetchRequired(`https://raw.githubusercontent.com/${repository.owner}/${repository.repository}/${commitJson.sha}/registry.json`);
337
+ const parsedRegistry = HubRegistrySchema.safeParse(await registryResponse.json());
338
+ if (!parsedRegistry.success) {
339
+ throw new Error(`Hub registry validation failed: ${parsedRegistry.error.message}`);
340
+ }
341
+ registrySnapshot = {
342
+ commit: commitJson.sha,
343
+ registry: parsedRegistry.data
344
+ };
345
+ return registrySnapshot;
346
+ }
347
+ return {
348
+ refresh,
349
+ async getSnapshot() {
350
+ return registrySnapshot ?? refresh();
351
+ },
352
+ async fetchArchive(commit) {
353
+ const archiveResponse = await fetchRequired(`https://codeload.github.com/${repository.owner}/${repository.repository}/tar.gz/${commit}`);
354
+ return new Uint8Array(await archiveResponse.arrayBuffer());
355
+ }
356
+ };
357
+ }
358
+ // src/hub/hub.ts
359
+ function createHubManager(options = {}) {
360
+ const dataDirectory = options.dataDir ?? resolveDataDir();
361
+ const repository = options.repository ?? DEFAULT_HUB_REPOSITORY;
362
+ const registryClient = createHubRegistryClient({
363
+ repository,
364
+ fetch: options.fetch ?? globalThis.fetch
365
+ });
366
+ const installedPackages = createInstalledPackageStore({
367
+ statePath: join3(dataDirectory, "hub", HUB_INSTALLED_STATE_FILENAME)
368
+ });
369
+ const packageInstaller = createHubPackageInstaller({
370
+ packagesRoot: join3(dataDirectory, "packages"),
371
+ installedPackages,
372
+ registryClient
373
+ });
374
+ return {
375
+ refreshRegistry: registryClient.refresh,
376
+ async listAvailable() {
377
+ return (await registryClient.getSnapshot()).registry.packages;
378
+ },
379
+ listInstalled: installedPackages.list,
380
+ async install(name, installOptions = {}) {
381
+ const snapshot = await registryClient.getSnapshot();
382
+ const project = findAvailableHubPackage(snapshot, name);
383
+ return packageInstaller.install(project, snapshot, installOptions, false);
384
+ },
385
+ async update(name, installOptions = {}) {
386
+ if (!await installedPackages.get(name))
387
+ throw new Error(`Package ${name} is not installed`);
388
+ const snapshot = await registryClient.refresh();
389
+ const project = findAvailableHubPackage(snapshot, name);
390
+ return packageInstaller.install(project, snapshot, installOptions, true);
391
+ },
392
+ remove: installedPackages.remove,
393
+ getInstalled: installedPackages.get,
394
+ isExecutableCodeApproved: installedPackages.isExecutableCodeApproved
395
+ };
396
+ }
4
397
  // src/abortable/abortable.ts
5
398
  function createAbortablePromise(executor) {
6
399
  const controller = new AbortController;
@@ -494,7 +887,7 @@ import { tool as aiTool, generateText, Output, stepCountIs } from "ai";
494
887
  // src/defaults/defaults.ts
495
888
  import { existsSync as existsSync2, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
496
889
  import { createRequire } from "module";
497
- import { join as join3 } from "path";
890
+ import { join as join6 } from "path";
498
891
  // ../../node_modules/.bun/@comma-agents+utils@file+packages+utils/node_modules/@comma-agents/utils/src/string.ts
499
892
  function capitalize(text) {
500
893
  if (text.length === 0)
@@ -503,35 +896,35 @@ function capitalize(text) {
503
896
  }
504
897
  // src/credentials/backends/json-file.ts
505
898
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
506
- import { dirname } from "path";
507
- import { z as z2 } from "zod";
899
+ import { dirname as dirname4 } from "path";
900
+ import { z as z3 } from "zod";
508
901
 
509
902
  // src/credentials/credentials.schema.ts
510
- import { z } from "zod";
511
- var ApiCredentialSchema = z.object({
512
- type: z.literal("api"),
513
- key: z.string().min(1)
903
+ import { z as z2 } from "zod";
904
+ var ApiCredentialSchema = z2.object({
905
+ type: z2.literal("api"),
906
+ key: z2.string().min(1)
514
907
  });
515
- var OAuthCredentialSchema = z.object({
516
- type: z.literal("oauth"),
517
- accessToken: z.string().min(1),
518
- refreshToken: z.string().optional(),
519
- expiresAt: z.string().datetime().optional(),
520
- accountId: z.string().optional(),
521
- metadata: z.record(z.unknown()).optional()
908
+ var OAuthCredentialSchema = z2.object({
909
+ type: z2.literal("oauth"),
910
+ accessToken: z2.string().min(1),
911
+ refreshToken: z2.string().optional(),
912
+ expiresAt: z2.string().datetime().optional(),
913
+ accountId: z2.string().optional(),
914
+ metadata: z2.record(z2.unknown()).optional()
522
915
  });
523
- var CustomCredentialSchema = z.object({
524
- type: z.literal("custom"),
525
- data: z.record(z.unknown())
916
+ var CustomCredentialSchema = z2.object({
917
+ type: z2.literal("custom"),
918
+ data: z2.record(z2.unknown())
526
919
  });
527
- var CredentialSchema = z.discriminatedUnion("type", [
920
+ var CredentialSchema = z2.discriminatedUnion("type", [
528
921
  ApiCredentialSchema,
529
922
  OAuthCredentialSchema,
530
923
  CustomCredentialSchema
531
924
  ]);
532
925
 
533
926
  // src/credentials/backends/json-file.ts
534
- var CredentialFileSchema = z2.record(z2.record(CredentialSchema));
927
+ var CredentialFileSchema = z3.record(z3.record(CredentialSchema));
535
928
  function createJsonFileBackend(options) {
536
929
  const { filePath } = options;
537
930
  return {
@@ -562,7 +955,7 @@ function createJsonFileBackend(options) {
562
955
  },
563
956
  async writeAll(data) {
564
957
  CredentialFileSchema.parse(data);
565
- const dir = dirname(filePath);
958
+ const dir = dirname4(filePath);
566
959
  if (!existsSync(dir)) {
567
960
  mkdirSync(dir, { recursive: true });
568
961
  }
@@ -573,6 +966,7 @@ function createJsonFileBackend(options) {
573
966
  }
574
967
 
575
968
  // src/credentials/credentials.constants.ts
969
+ var CREDENTIALS_FILENAME = "credentials.json";
576
970
  var WELL_KNOWN_ENV_VARS = {
577
971
  openai: ["OPENAI_API_KEY"],
578
972
  anthropic: ["ANTHROPIC_API_KEY"],
@@ -612,7 +1006,7 @@ function createCredentialStore(options) {
612
1006
  const { backend } = options;
613
1007
  const envVarMap = buildEnvVarMap(options.envVarOverrides);
614
1008
  const env = options.env ?? process.env;
615
- async function resolve(providerId, scope) {
1009
+ async function resolve3(providerId, scope) {
616
1010
  const data = await backend.readAll();
617
1011
  if (scope && scope !== "$global") {
618
1012
  const scopedCred = data[scope]?.[providerId];
@@ -628,7 +1022,7 @@ function createCredentialStore(options) {
628
1022
  return;
629
1023
  }
630
1024
  return {
631
- resolve,
1025
+ resolve: resolve3,
632
1026
  async get(providerId, scope) {
633
1027
  const data = await backend.readAll();
634
1028
  return data[scope]?.[providerId];
@@ -672,35 +1066,21 @@ function createCredentialStore(options) {
672
1066
  return Object.keys(data);
673
1067
  },
674
1068
  async getAuthStatus(providerId, scope) {
675
- const credential = await resolve(providerId, scope);
1069
+ const credential = await resolve3(providerId, scope);
676
1070
  return credential ? "configured" : "none";
677
1071
  }
678
1072
  };
679
1073
  }
680
1074
 
681
1075
  // src/credentials/credentials.utils.ts
682
- import { homedir } from "os";
683
- import { join } from "path";
684
- function resolveDataDir() {
685
- const platform = process.platform;
686
- if (platform === "win32") {
687
- const base2 = process.env.LOCALAPPDATA ?? join(homedir(), "AppData", "Local");
688
- return join(base2, "comma-agents");
689
- }
690
- if (platform === "darwin") {
691
- return join(homedir(), "Library", "Application Support", "comma-agents");
692
- }
693
- const base = process.env.XDG_DATA_HOME ?? join(homedir(), ".local", "share");
694
- return join(base, "comma-agents");
695
- }
696
- var CREDENTIALS_FILENAME = "credentials.json";
1076
+ import { join as join4 } from "path";
697
1077
  function resolveCredentialsPath() {
698
- return join(resolveDataDir(), CREDENTIALS_FILENAME);
1078
+ return join4(resolveDataDir(), CREDENTIALS_FILENAME);
699
1079
  }
700
1080
 
701
1081
  // src/model/providers/catalog/catalog.ts
702
1082
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
703
- import { dirname as dirname2 } from "path";
1083
+ import { dirname as dirname5 } from "path";
704
1084
  // src/model/providers/catalog/catalog.data.json
705
1085
  var catalog_data_default = {
706
1086
  "302ai": { id: "302ai", env: ["302AI_API_KEY"], npm: "@ai-sdk/openai-compatible", api: "https://api.302.ai/v1", name: "302.AI", doc: "https://doc.302.ai", models: { "qwen3-235b-a22b": { id: "qwen3-235b-a22b", name: "Qwen3-235B-A22B", family: "qwen", attachment: false, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-04", release_date: "2025-04-29", last_updated: "2025-04-29", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.29, output: 2.86 }, limit: { context: 128000, output: 16384 } }, "grok-4.1": { id: "grok-4.1", name: "grok-4.1", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-06", release_date: "2025-11-18", last_updated: "2025-11-18", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 2, output: 10 }, limit: { context: 200000, output: 64000 } }, "MiniMax-M2": { id: "MiniMax-M2", name: "MiniMax-M2", attachment: false, reasoning: false, tool_call: true, temperature: true, release_date: "2025-10-26", last_updated: "2025-10-26", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.33, output: 1.32 }, limit: { context: 1e6, output: 128000 } }, "grok-4-1-fast-reasoning": { id: "grok-4-1-fast-reasoning", name: "grok-4-1-fast-reasoning", attachment: true, reasoning: true, tool_call: true, temperature: true, knowledge: "2025-06", release_date: "2025-11-20", last_updated: "2025-11-20", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.2, output: 0.5 }, limit: { context: 2000000, output: 30000 } }, "gemini-2.5-flash-nothink": { id: "gemini-2.5-flash-nothink", name: "gemini-2.5-flash-nothink", family: "gemini-flash", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-01", release_date: "2025-06-24", last_updated: "2025-06-24", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.3, output: 2.5 }, limit: { context: 1e6, output: 65536 } }, "grok-4.20-multi-agent-beta-0309": { id: "grok-4.20-multi-agent-beta-0309", name: "grok-4.20-multi-agent-beta-0309", attachment: true, reasoning: true, tool_call: true, temperature: true, release_date: "2026-03-16", last_updated: "2026-03-16", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 2, output: 6 }, limit: { context: 2000000, output: 30000 } }, "kimi-k2-0905-preview": { id: "kimi-k2-0905-preview", name: "kimi-k2-0905-preview", attachment: false, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-06", release_date: "2025-09-05", last_updated: "2025-09-05", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.632, output: 2.53 }, limit: { context: 262144, output: 262144 } }, "claude-haiku-4-5": { id: "claude-haiku-4-5", name: "claude-haiku-4-5", attachment: true, reasoning: true, tool_call: true, temperature: true, knowledge: "2025-02", release_date: "2025-10-16", last_updated: "2025-10-16", modalities: { input: ["text", "image", "pdf"], output: ["text"] }, open_weights: false, cost: { input: 1, output: 5 }, limit: { context: 200000, output: 64000 } }, "claude-opus-4-5-20251101": { id: "claude-opus-4-5-20251101", name: "claude-opus-4-5-20251101", attachment: true, reasoning: true, tool_call: true, temperature: true, knowledge: "2025-03", release_date: "2025-11-25", last_updated: "2025-11-25", modalities: { input: ["text", "image", "pdf"], output: ["text"] }, open_weights: false, cost: { input: 5, output: 25 }, limit: { context: 200000, output: 64000 } }, "gemini-2.5-flash-lite-preview-09-2025": { id: "gemini-2.5-flash-lite-preview-09-2025", name: "gemini-2.5-flash-lite-preview-09-2025", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-01", release_date: "2025-09-26", last_updated: "2025-09-26", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.1, output: 0.4 }, limit: { context: 1e6, output: 65536 } }, "qwen3-235b-a22b-instruct-2507": { id: "qwen3-235b-a22b-instruct-2507", name: "qwen3-235b-a22b-instruct-2507", attachment: false, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-04", release_date: "2025-07-30", last_updated: "2025-07-30", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.29, output: 1.143 }, limit: { context: 128000, output: 65536 } }, "glm-5v-turbo": { id: "glm-5v-turbo", name: "glm-5v-turbo", attachment: true, reasoning: true, tool_call: true, temperature: true, release_date: "2026-04-02", last_updated: "2026-04-02", modalities: { input: ["text", "image", "video", "audio", "pdf"], output: ["text"] }, open_weights: false, cost: { input: 0.72, output: 3.2 }, limit: { context: 200000, output: 131072 } }, "mistral-large-2512": { id: "mistral-large-2512", name: "mistral-large-2512", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2024-12", release_date: "2025-12-16", last_updated: "2025-12-16", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 1.1, output: 3.3 }, limit: { context: 128000, output: 262144 } }, "glm-4.7": { id: "glm-4.7", name: "glm-4.7", attachment: false, reasoning: true, tool_call: true, temperature: true, knowledge: "2025-06", release_date: "2025-12-22", last_updated: "2025-12-22", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.286, output: 1.142 }, limit: { context: 200000, output: 131072 } }, "claude-3-5-haiku-20241022": { id: "claude-3-5-haiku-20241022", name: "claude-3-5-haiku-20241022", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2024-07", release_date: "2024-10-22", last_updated: "2024-10-22", modalities: { input: ["text", "image", "pdf"], output: ["text"] }, open_weights: false, cost: { input: 0.8, output: 4 }, limit: { context: 200000, output: 8192 } }, "doubao-seed-1-8-251215": { id: "doubao-seed-1-8-251215", name: "doubao-seed-1-8-251215", attachment: true, reasoning: false, tool_call: true, temperature: true, release_date: "2025-12-18", last_updated: "2025-12-18", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.114, output: 0.286 }, limit: { context: 224000, output: 64000 } }, "chatgpt-4o-latest": { id: "chatgpt-4o-latest", name: "chatgpt-4o-latest", family: "gpt", attachment: true, reasoning: false, tool_call: false, temperature: true, knowledge: "2023-09", release_date: "2024-08-08", last_updated: "2024-08-08", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 5, output: 15 }, limit: { context: 128000, output: 16384 } }, "glm-5": { id: "glm-5", name: "glm-5", attachment: false, reasoning: true, tool_call: true, temperature: true, release_date: "2026-02-12", last_updated: "2026-02-12", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.6, output: 2.6 }, limit: { context: 200000, output: 131072 } }, "deepseek-chat": { id: "deepseek-chat", name: "Deepseek-Chat", family: "deepseek", attachment: false, reasoning: false, tool_call: true, temperature: true, knowledge: "2024-07", release_date: "2024-11-29", last_updated: "2024-11-29", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.29, output: 0.43 }, limit: { context: 128000, output: 8192 } }, "deepseek-v3.2-thinking": { id: "deepseek-v3.2-thinking", name: "DeepSeek-V3.2-Thinking", attachment: false, reasoning: true, tool_call: true, temperature: true, knowledge: "2024-12", release_date: "2025-12-01", last_updated: "2025-12-01", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.29, output: 0.43 }, limit: { context: 128000, output: 128000 } }, "claude-sonnet-4-6": { id: "claude-sonnet-4-6", name: "claude-sonnet-4-6", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-08", release_date: "2026-02-18", last_updated: "2026-03-13", modalities: { input: ["text", "image", "pdf"], output: ["text"] }, open_weights: false, cost: { input: 3, output: 15 }, limit: { context: 1e6, output: 64000 } }, "gpt-5-thinking": { id: "gpt-5-thinking", name: "gpt-5-thinking", attachment: true, reasoning: true, tool_call: true, temperature: true, knowledge: "2024-10", release_date: "2025-08-08", last_updated: "2025-08-08", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 1.25, output: 10 }, limit: { context: 400000, output: 128000 } }, "glm-4.7-flashx": { id: "glm-4.7-flashx", name: "glm-4.7-flashx", attachment: false, reasoning: true, tool_call: true, temperature: true, knowledge: "2025-04", release_date: "2026-01-20", last_updated: "2026-01-20", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.0715, output: 0.429 }, limit: { context: 200000, output: 131072 } }, "gemini-3-flash-preview": { id: "gemini-3-flash-preview", name: "gemini-3-flash-preview", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-06", release_date: "2025-12-18", last_updated: "2025-12-18", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.5, output: 3 }, limit: { context: 1e6, output: 65536 } }, "qwen-plus": { id: "qwen-plus", name: "Qwen-Plus", family: "qwen", attachment: false, reasoning: false, tool_call: true, temperature: true, knowledge: "2024-10", release_date: "2024-07-23", last_updated: "2024-07-23", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.12, output: 1.2 }, limit: { context: 1e6, output: 32768 } }, "grok-4.20-beta-0309-non-reasoning": { id: "grok-4.20-beta-0309-non-reasoning", name: "grok-4.20-beta-0309-non-reasoning", attachment: true, reasoning: false, tool_call: true, temperature: true, release_date: "2026-03-16", last_updated: "2026-03-16", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 2, output: 6 }, limit: { context: 2000000, output: 30000 } }, "claude-opus-4-7": { id: "claude-opus-4-7", name: "claude-opus-4-7", attachment: true, reasoning: true, tool_call: true, temperature: true, knowledge: "2025-05", release_date: "2026-04-17", last_updated: "2026-04-17", modalities: { input: ["text", "image", "pdf"], output: ["text"] }, open_weights: false, cost: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25, context_over_200k: { input: 10, output: 37.5, cache_read: 1, cache_write: 12.5 } }, limit: { context: 200000, output: 128000 } }, "gpt-5-mini": { id: "gpt-5-mini", name: "gpt-5-mini", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2024-10", release_date: "2025-08-08", last_updated: "2025-08-08", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.25, output: 2 }, limit: { context: 400000, output: 128000 } }, "gemini-3-pro-preview": { id: "gemini-3-pro-preview", name: "gemini-3-pro-preview", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-06", release_date: "2025-11-19", last_updated: "2025-11-19", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 2, output: 12 }, limit: { context: 1e6, output: 64000 } }, "MiniMax-M2.7": { id: "MiniMax-M2.7", name: "MiniMax-M2.7", attachment: false, reasoning: false, tool_call: true, temperature: true, release_date: "2026-03-19", last_updated: "2026-03-19", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.3, output: 1.2 }, limit: { context: 204800, output: 131072 } }, "qwen3-max-2025-09-23": { id: "qwen3-max-2025-09-23", name: "qwen3-max-2025-09-23", attachment: false, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-04", release_date: "2025-09-24", last_updated: "2025-09-24", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.86, output: 3.43 }, limit: { context: 258048, output: 65536 } }, "claude-sonnet-4-5-20250929": { id: "claude-sonnet-4-5-20250929", name: "claude-sonnet-4-5-20250929", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-03", release_date: "2025-09-30", last_updated: "2025-09-30", modalities: { input: ["text", "image", "pdf"], output: ["text"] }, open_weights: false, cost: { input: 3, output: 15 }, limit: { context: 200000, output: 64000 } }, "qwen-flash": { id: "qwen-flash", name: "Qwen-Flash", attachment: false, reasoning: false, tool_call: true, temperature: true, release_date: "2025-07-28", last_updated: "2025-07-28", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.022, output: 0.22 }, limit: { context: 1e6, output: 32768 } }, "gemini-2.5-pro": { id: "gemini-2.5-pro", name: "gemini-2.5-pro", family: "gemini-pro", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-01", release_date: "2025-06-17", last_updated: "2025-06-17", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 1.25, output: 10 }, limit: { context: 1e6, output: 65536 } }, "grok-4-1-fast-non-reasoning": { id: "grok-4-1-fast-non-reasoning", name: "grok-4-1-fast-non-reasoning", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-06", release_date: "2025-11-20", last_updated: "2025-11-20", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.2, output: 0.5 }, limit: { context: 2000000, output: 30000 } }, "claude-3-5-haiku-latest": { id: "claude-3-5-haiku-latest", name: "claude-3-5-haiku-latest", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2024-07", release_date: "2024-10-22", last_updated: "2024-10-22", modalities: { input: ["text", "image", "pdf"], output: ["text"] }, open_weights: false, cost: { input: 0.8, output: 4 }, limit: { context: 200000, output: 8192 } }, "claude-opus-4-5-20251101-thinking": { id: "claude-opus-4-5-20251101-thinking", name: "claude-opus-4-5-20251101-thinking", attachment: true, reasoning: true, tool_call: true, temperature: true, knowledge: "2025-03", release_date: "2025-11-25", last_updated: "2025-11-25", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 5, output: 25 }, limit: { context: 200000, output: 64000 } }, "gpt-5.2": { id: "gpt-5.2", name: "gpt-5.2", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2024-10", release_date: "2025-12-12", last_updated: "2025-12-12", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 1.75, output: 14 }, limit: { context: 400000, output: 128000 } }, "gpt-5.4-mini": { id: "gpt-5.4-mini", name: "gpt-5.4-mini", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-08", release_date: "2026-03-19", last_updated: "2026-03-19", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.75, output: 4.5 }, limit: { context: 400000, output: 128000 } }, "gemini-3-pro-image-preview": { id: "gemini-3-pro-image-preview", name: "gemini-3-pro-image-preview", attachment: true, reasoning: false, tool_call: false, temperature: true, knowledge: "2025-06", release_date: "2025-11-20", last_updated: "2025-11-20", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 2, output: 120 }, limit: { context: 32768, output: 64000 } }, "glm-5.1": { id: "glm-5.1", name: "glm-5.1", attachment: false, reasoning: true, tool_call: true, temperature: true, release_date: "2026-04-10", last_updated: "2026-04-10", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.86, output: 3.5 }, limit: { context: 200000, output: 131072 } }, "qwen-max-latest": { id: "qwen-max-latest", name: "Qwen-Max-Latest", family: "qwen", attachment: false, reasoning: false, tool_call: true, temperature: true, knowledge: "2024-11", release_date: "2024-04-03", last_updated: "2025-01-25", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.343, output: 1.372 }, limit: { context: 131072, output: 8192 } }, "gpt-5.4-nano": { id: "gpt-5.4-nano", name: "gpt-5.4-nano", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-08", release_date: "2026-03-19", last_updated: "2026-03-19", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.2, output: 1.25 }, limit: { context: 400000, output: 128000 } }, "gemini-2.5-flash-image": { id: "gemini-2.5-flash-image", name: "gemini-2.5-flash-image", attachment: true, reasoning: false, tool_call: false, temperature: true, knowledge: "2025-01", release_date: "2025-10-08", last_updated: "2025-10-08", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.3, output: 30 }, limit: { context: 32768, output: 32768 } }, "glm-4.5": { id: "glm-4.5", name: "GLM-4.5", attachment: false, reasoning: false, tool_call: true, temperature: true, knowledge: "2024-10", release_date: "2025-07-29", last_updated: "2025-07-29", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.286, output: 1.142 }, limit: { context: 128000, output: 98304 } }, "gpt-5.4-mini-2026-03-17": { id: "gpt-5.4-mini-2026-03-17", name: "gpt-5.4-mini-2026-03-17", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-08", release_date: "2026-03-19", last_updated: "2026-03-19", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.75, output: 4.5 }, limit: { context: 400000, output: 128000 } }, "gemini-2.5-flash": { id: "gemini-2.5-flash", name: "gemini-2.5-flash", family: "gemini-flash", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-01", release_date: "2025-06-17", last_updated: "2025-06-17", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.3, output: 2.5 }, limit: { context: 1e6, output: 65536 } }, "gpt-5.2-chat-latest": { id: "gpt-5.2-chat-latest", name: "gpt-5.2-chat-latest", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2024-10", release_date: "2025-12-12", last_updated: "2025-12-12", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 1.75, output: 14 }, limit: { context: 128000, output: 16384 } }, "doubao-seed-1-6-vision-250815": { id: "doubao-seed-1-6-vision-250815", name: "doubao-seed-1-6-vision-250815", attachment: true, reasoning: false, tool_call: true, temperature: true, release_date: "2025-09-30", last_updated: "2025-09-30", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.114, output: 1.143 }, limit: { context: 256000, output: 32000 } }, "gemini-3.1-flash-image-preview": { id: "gemini-3.1-flash-image-preview", name: "gemini-3.1-flash-image-preview", attachment: true, reasoning: false, tool_call: false, temperature: true, knowledge: "2025-01", release_date: "2026-02-27", last_updated: "2026-02-27", modalities: { input: ["text", "image", "pdf"], output: ["text", "image"] }, open_weights: false, cost: { input: 0.5, output: 60 }, limit: { context: 131072, output: 32768 } }, "MiniMax-M2.7-highspeed": { id: "MiniMax-M2.7-highspeed", name: "MiniMax-M2.7-highspeed", attachment: false, reasoning: false, tool_call: true, temperature: true, release_date: "2026-03-19", last_updated: "2026-03-19", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.6, output: 4.8 }, limit: { context: 204800, output: 131072 } }, "glm-4.5-x": { id: "glm-4.5-x", name: "glm-4.5-x", attachment: false, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-04", release_date: "2025-07-29", last_updated: "2025-07-29", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 1.143, output: 2.29 }, limit: { context: 128000, output: 16384 } }, "MiniMax-M2.1": { id: "MiniMax-M2.1", name: "MiniMax-M2.1", attachment: false, reasoning: false, tool_call: true, temperature: true, release_date: "2025-12-19", last_updated: "2025-12-19", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.3, output: 1.2 }, limit: { context: 1e6, output: 131072 } }, "gpt-5.1": { id: "gpt-5.1", name: "gpt-5.1", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2024-10", release_date: "2025-11-14", last_updated: "2025-11-14", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 1.25, output: 10 }, limit: { context: 400000, output: 128000 } }, "kimi-k2-thinking-turbo": { id: "kimi-k2-thinking-turbo", name: "kimi-k2-thinking-turbo", attachment: false, reasoning: true, tool_call: true, temperature: true, knowledge: "2025-06", release_date: "2025-09-05", last_updated: "2025-09-05", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 1.265, output: 9.119 }, limit: { context: 262144, output: 262144 } }, "deepseek-reasoner": { id: "deepseek-reasoner", name: "Deepseek-Reasoner", family: "deepseek-thinking", attachment: false, reasoning: true, tool_call: true, temperature: true, knowledge: "2024-07", release_date: "2025-01-20", last_updated: "2025-01-20", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.29, output: 0.43 }, limit: { context: 128000, output: 128000 } }, "grok-4-fast-reasoning": { id: "grok-4-fast-reasoning", name: "grok-4-fast-reasoning", attachment: true, reasoning: true, tool_call: true, temperature: true, knowledge: "2025-06", release_date: "2025-09-23", last_updated: "2025-09-23", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.2, output: 0.5 }, limit: { context: 2000000, output: 30000 } }, "claude-opus-4-1-20250805-thinking": { id: "claude-opus-4-1-20250805-thinking", name: "claude-opus-4-1-20250805-thinking", attachment: true, reasoning: true, tool_call: true, temperature: true, knowledge: "2025-03", release_date: "2025-05-27", last_updated: "2025-05-27", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 15, output: 75 }, limit: { context: 200000, output: 32000 } }, "glm-4.5-air": { id: "glm-4.5-air", name: "glm-4.5-air", attachment: false, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-04", release_date: "2025-07-29", last_updated: "2025-07-29", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.1143, output: 0.286 }, limit: { context: 128000, output: 98304 } }, "glm-5-turbo": { id: "glm-5-turbo", name: "glm-5-turbo", attachment: false, reasoning: true, tool_call: true, temperature: true, release_date: "2026-03-16", last_updated: "2026-03-16", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.72, output: 3.2 }, limit: { context: 200000, output: 131072 } }, "qwen3-30b-a3b": { id: "qwen3-30b-a3b", name: "Qwen3-30B-A3B", family: "qwen", attachment: false, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-04", release_date: "2025-04-29", last_updated: "2025-04-29", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.11, output: 1.08 }, limit: { context: 128000, output: 8192 } }, "claude-opus-4-5": { id: "claude-opus-4-5", name: "claude-opus-4-5", attachment: true, reasoning: true, tool_call: true, temperature: true, knowledge: "2025-03", release_date: "2025-11-25", last_updated: "2025-11-25", modalities: { input: ["text", "image", "pdf"], output: ["text"] }, open_weights: false, cost: { input: 5, output: 25 }, limit: { context: 200000, output: 64000 } }, "glm-4.5v": { id: "glm-4.5v", name: "GLM-4.5V", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2024-10", release_date: "2025-08-12", last_updated: "2025-08-12", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.29, output: 0.86 }, limit: { context: 64000, output: 16384 } }, "glm-4.6": { id: "glm-4.6", name: "glm-4.6", attachment: false, reasoning: true, tool_call: true, temperature: true, knowledge: "2025-03", release_date: "2025-09-30", last_updated: "2025-09-30", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.286, output: 1.142 }, limit: { context: 200000, output: 131072 } }, "claude-opus-4-6-thinking": { id: "claude-opus-4-6-thinking", name: "claude-opus-4-6-thinking", attachment: true, reasoning: true, tool_call: true, temperature: true, knowledge: "2025-05", release_date: "2026-02-06", last_updated: "2026-03-13", modalities: { input: ["text", "image", "pdf"], output: ["text"] }, open_weights: false, cost: { input: 5, output: 25 }, limit: { context: 1e6, output: 128000 } }, "gemini-2.5-flash-preview-09-2025": { id: "gemini-2.5-flash-preview-09-2025", name: "gemini-2.5-flash-preview-09-2025", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-01", release_date: "2025-09-26", last_updated: "2025-09-26", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.3, output: 2.5 }, limit: { context: 1e6, output: 65536 } }, "claude-sonnet-4-6-thinking": { id: "claude-sonnet-4-6-thinking", name: "claude-sonnet-4-6-thinking", attachment: true, reasoning: true, tool_call: true, temperature: true, knowledge: "2025-08", release_date: "2026-02-18", last_updated: "2026-03-13", modalities: { input: ["text", "image", "pdf"], output: ["text"] }, open_weights: false, cost: { input: 3, output: 15 }, limit: { context: 1e6, output: 64000 } }, "glm-4.6v": { id: "glm-4.6v", name: "GLM-4.6V", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-03", release_date: "2025-12-08", last_updated: "2025-12-08", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.145, output: 0.43 }, limit: { context: 128000, output: 32768 } }, "claude-opus-4-1-20250805": { id: "claude-opus-4-1-20250805", name: "claude-opus-4-1-20250805", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-03", release_date: "2025-08-05", last_updated: "2025-08-05", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 15, output: 75 }, limit: { context: 200000, output: 32000 } }, "gpt-5.1-chat-latest": { id: "gpt-5.1-chat-latest", name: "gpt-5.1-chat-latest", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2024-10", release_date: "2025-11-14", last_updated: "2025-11-14", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 1.25, output: 10 }, limit: { context: 128000, output: 16384 } }, "claude-haiku-4-5-20251001": { id: "claude-haiku-4-5-20251001", name: "claude-haiku-4-5-20251001", attachment: true, reasoning: true, tool_call: true, temperature: true, knowledge: "2025-03", release_date: "2025-10-16", last_updated: "2025-10-16", modalities: { input: ["text", "image", "pdf"], output: ["text"] }, open_weights: false, cost: { input: 1, output: 5 }, limit: { context: 200000, output: 64000 } }, "MiniMax-M1": { id: "MiniMax-M1", name: "MiniMax-M1", family: "minimax", attachment: false, reasoning: false, tool_call: true, temperature: true, release_date: "2025-06-16", last_updated: "2025-06-16", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.132, output: 1.254 }, limit: { context: 1e6, output: 128000 } }, "gpt-5.4-nano-2026-03-17": { id: "gpt-5.4-nano-2026-03-17", name: "gpt-5.4-nano-2026-03-17", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-08", release_date: "2026-03-19", last_updated: "2026-03-19", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.2, output: 1.25 }, limit: { context: 400000, output: 128000 } }, "claude-sonnet-4-20250514": { id: "claude-sonnet-4-20250514", name: "claude-sonnet-4-20250514", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-03", release_date: "2025-05-22", last_updated: "2025-05-22", modalities: { input: ["text", "image", "pdf"], output: ["text"] }, open_weights: false, cost: { input: 3, output: 15 }, limit: { context: 200000, output: 64000 } }, "qwen3-coder-480b-a35b-instruct": { id: "qwen3-coder-480b-a35b-instruct", name: "qwen3-coder-480b-a35b-instruct", attachment: false, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-04", release_date: "2025-07-23", last_updated: "2025-07-23", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.86, output: 3.43 }, limit: { context: 262144, output: 65536 } }, "claude-opus-4-6": { id: "claude-opus-4-6", name: "claude-opus-4-6", attachment: true, reasoning: true, tool_call: true, temperature: true, knowledge: "2025-05", release_date: "2026-02-06", last_updated: "2026-03-13", modalities: { input: ["text", "image", "pdf"], output: ["text"] }, open_weights: false, cost: { input: 5, output: 25 }, limit: { context: 1e6, output: 128000 } }, "doubao-seed-code-preview-251028": { id: "doubao-seed-code-preview-251028", name: "doubao-seed-code-preview-251028", attachment: true, reasoning: false, tool_call: true, temperature: true, release_date: "2025-11-11", last_updated: "2025-11-11", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.17, output: 1.14 }, limit: { context: 256000, output: 32000 } }, "gpt-4.1-nano": { id: "gpt-4.1-nano", name: "gpt-4.1-nano", family: "gpt-nano", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2024-04", release_date: "2025-04-14", last_updated: "2025-04-14", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.1, output: 0.4 }, limit: { context: 1e6, output: 32768 } }, "deepseek-v3.2": { id: "deepseek-v3.2", name: "deepseek-v3.2", attachment: false, reasoning: false, tool_call: true, temperature: true, knowledge: "2024-12", release_date: "2025-12-01", last_updated: "2025-12-01", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.29, output: 0.43 }, limit: { context: 128000, output: 8192 } }, "gpt-5-pro": { id: "gpt-5-pro", name: "gpt-5-pro", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2024-10", release_date: "2025-10-08", last_updated: "2025-10-08", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 15, output: 120 }, limit: { context: 400000, output: 272000 } }, "gpt-4o": { id: "gpt-4o", name: "gpt-4o", family: "gpt", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2023-09", release_date: "2024-05-13", last_updated: "2024-05-13", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 2.5, output: 10 }, limit: { context: 128000, output: 16384 } }, "claude-sonnet-4-5": { id: "claude-sonnet-4-5", name: "claude-sonnet-4-5", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-07", release_date: "2025-09-30", last_updated: "2025-09-30", modalities: { input: ["text", "image", "pdf"], output: ["text"] }, open_weights: false, cost: { input: 3, output: 15 }, limit: { context: 200000, output: 64000 } }, "gpt-5": { id: "gpt-5", name: "gpt-5", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2024-10", release_date: "2025-08-08", last_updated: "2025-08-08", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 1.25, output: 10 }, limit: { context: 400000, output: 128000 } }, "grok-4.20-beta-0309-reasoning": { id: "grok-4.20-beta-0309-reasoning", name: "grok-4.20-beta-0309-reasoning", attachment: true, reasoning: true, tool_call: true, temperature: true, release_date: "2026-03-16", last_updated: "2026-03-16", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 2, output: 6 }, limit: { context: 2000000, output: 30000 } }, "claude-opus-4-20250514": { id: "claude-opus-4-20250514", name: "claude-opus-4-20250514", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-03", release_date: "2025-05-22", last_updated: "2025-05-22", modalities: { input: ["text", "image", "pdf"], output: ["text"] }, open_weights: false, cost: { input: 15, output: 75 }, limit: { context: 200000, output: 32000 } }, "glm-for-coding": { id: "glm-for-coding", name: "glm-for-coding", attachment: false, reasoning: true, tool_call: true, temperature: true, release_date: "2025-09-30", last_updated: "2025-09-30", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.086, output: 0.343 }, limit: { context: 200000, output: 131072 } }, "claude-sonnet-4-5-20250929-thinking": { id: "claude-sonnet-4-5-20250929-thinking", name: "claude-sonnet-4-5-20250929-thinking", attachment: true, reasoning: true, tool_call: true, temperature: true, knowledge: "2025-03", release_date: "2025-09-30", last_updated: "2025-09-30", modalities: { input: ["text", "image", "pdf"], output: ["text"] }, open_weights: false, cost: { input: 3, output: 15 }, limit: { context: 200000, output: 64000 } }, "glm-4.5-airx": { id: "glm-4.5-airx", name: "glm-4.5-airx", attachment: false, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-04", release_date: "2025-07-29", last_updated: "2025-07-29", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.572, output: 1.714 }, limit: { context: 128000, output: 16384 } }, "gpt-4.1": { id: "gpt-4.1", name: "gpt-4.1", family: "gpt", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2024-04", release_date: "2025-04-14", last_updated: "2025-04-14", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 2, output: 8 }, limit: { context: 1e6, output: 32768 } }, "kimi-k2-thinking": { id: "kimi-k2-thinking", name: "kimi-k2-thinking", attachment: false, reasoning: true, tool_call: true, temperature: true, knowledge: "2025-06", release_date: "2025-09-05", last_updated: "2025-09-05", modalities: { input: ["text"], output: ["text"] }, open_weights: false, cost: { input: 0.575, output: 2.3 }, limit: { context: 262144, output: 262144 } }, "gemini-2.0-flash-lite": { id: "gemini-2.0-flash-lite", name: "gemini-2.0-flash-lite", family: "gemini-flash-lite", attachment: true, reasoning: false, tool_call: false, temperature: true, knowledge: "2024-11", release_date: "2025-06-16", last_updated: "2025-06-16", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.075, output: 0.3 }, limit: { context: 2000000, output: 8192 } }, "gpt-4.1-mini": { id: "gpt-4.1-mini", name: "gpt-4.1-mini", family: "gpt-mini", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2024-04", release_date: "2025-04-14", last_updated: "2025-04-14", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.4, output: 1.6 }, limit: { context: 1e6, output: 32768 } }, "grok-4-fast-non-reasoning": { id: "grok-4-fast-non-reasoning", name: "grok-4-fast-non-reasoning", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2025-06", release_date: "2025-09-23", last_updated: "2025-09-23", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.2, output: 0.5 }, limit: { context: 2000000, output: 30000 } }, "doubao-seed-1-6-thinking-250715": { id: "doubao-seed-1-6-thinking-250715", name: "doubao-seed-1-6-thinking-250715", attachment: true, reasoning: true, tool_call: true, temperature: true, release_date: "2025-07-15", last_updated: "2025-07-15", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.121, output: 1.21 }, limit: { context: 256000, output: 16000 } }, "ministral-14b-2512": { id: "ministral-14b-2512", name: "ministral-14b-2512", attachment: true, reasoning: false, tool_call: true, temperature: true, knowledge: "2024-12", release_date: "2025-12-16", last_updated: "2025-12-16", modalities: { input: ["text", "image"], output: ["text"] }, open_weights: false, cost: { input: 0.33, output: 0.33 }, limit: { context: 128000, output: 128000 } } } },
@@ -821,8 +1201,7 @@ var catalog_data_default = {
821
1201
  };
822
1202
 
823
1203
  // src/model/providers/catalog/catalog.utils.ts
824
- import { homedir as homedir2 } from "os";
825
- import { join as join2 } from "path";
1204
+ import { join as join5 } from "path";
826
1205
  var KNOWN_MODALITIES = new Set([
827
1206
  "text",
828
1207
  "image",
@@ -887,17 +1266,8 @@ function toModelInfo(catalogModel) {
887
1266
  };
888
1267
  }
889
1268
  var CATALOG_CACHE_FILENAME = "models-catalog.json";
890
- function resolveCatalogCachePath(env = process.env, platform = process.platform) {
891
- if (platform === "win32") {
892
- const base2 = env.LOCALAPPDATA && env.LOCALAPPDATA.length > 0 ? env.LOCALAPPDATA : join2(homedir2(), "AppData", "Local");
893
- return join2(base2, "comma-agents", "Cache", CATALOG_CACHE_FILENAME);
894
- }
895
- if (platform === "darwin") {
896
- return join2(homedir2(), "Library", "Caches", "comma-agents", CATALOG_CACHE_FILENAME);
897
- }
898
- const xdgCacheHome = env.XDG_CACHE_HOME;
899
- const base = xdgCacheHome && xdgCacheHome.length > 0 ? xdgCacheHome : join2(homedir2(), ".cache");
900
- return join2(base, "comma-agents", CATALOG_CACHE_FILENAME);
1269
+ function resolveCatalogCachePath() {
1270
+ return join5(resolveDataDir(), "cache", CATALOG_CACHE_FILENAME);
901
1271
  }
902
1272
 
903
1273
  // src/model/providers/catalog/catalog.ts
@@ -1002,7 +1372,7 @@ function readCache(cachePath) {
1002
1372
  }
1003
1373
  function writeCache(cachePath, payload) {
1004
1374
  try {
1005
- mkdirSync2(dirname2(cachePath), { recursive: true });
1375
+ mkdirSync2(dirname5(cachePath), { recursive: true });
1006
1376
  writeFileSync2(cachePath, JSON.stringify(payload), "utf8");
1007
1377
  } catch {}
1008
1378
  }
@@ -1309,18 +1679,18 @@ function unregisterProvider(providerId) {
1309
1679
  }
1310
1680
  function ensureCacheDir(dir) {
1311
1681
  mkdirSync3(dir, { recursive: true });
1312
- const pkgJsonPath = join3(dir, "package.json");
1682
+ const pkgJsonPath = join6(dir, "package.json");
1313
1683
  if (!existsSync2(pkgJsonPath)) {
1314
1684
  writeFileSync3(pkgJsonPath, JSON.stringify({ name: "comma-providers", private: true }, null, 2));
1315
1685
  }
1316
1686
  }
1317
1687
  function requireFromCache(cacheDir, packageName) {
1318
- const _require = createRequire(join3(cacheDir, "package.json"));
1688
+ const _require = createRequire(join6(cacheDir, "package.json"));
1319
1689
  return _require(packageName);
1320
1690
  }
1321
1691
  function isPackageCached(cacheDir, packageName) {
1322
1692
  const parts = packageName.split("/");
1323
- const pkgJsonPath = join3(cacheDir, "node_modules", ...parts, "package.json");
1693
+ const pkgJsonPath = join6(cacheDir, "node_modules", ...parts, "package.json");
1324
1694
  return existsSync2(pkgJsonPath);
1325
1695
  }
1326
1696
  async function ensurePackageInCache(dir, packageName) {
@@ -1676,20 +2046,20 @@ async function resolveSystemPrompt(options) {
1676
2046
  }
1677
2047
 
1678
2048
  // src/sandbox/sandbox.ts
1679
- import { resolve as resolve2 } from "path";
2049
+ import { resolve as resolve4 } from "path";
1680
2050
 
1681
2051
  // src/guard/guard.ts
1682
2052
  import { realpathSync } from "fs";
1683
- import { isAbsolute, resolve } from "path";
2053
+ import { isAbsolute, resolve as resolve3 } from "path";
1684
2054
  function resolveSymlinks(absolutePath) {
1685
2055
  let current = absolutePath;
1686
2056
  const suffix = [];
1687
- while (current !== resolve(current, "..") || suffix.length === 0) {
2057
+ while (current !== resolve3(current, "..") || suffix.length === 0) {
1688
2058
  try {
1689
2059
  const real = realpathSync(current);
1690
- return suffix.length > 0 ? resolve(real, ...suffix.reverse()) : real;
2060
+ return suffix.length > 0 ? resolve3(real, ...suffix.reverse()) : real;
1691
2061
  } catch {
1692
- const parent = resolve(current, "..");
2062
+ const parent = resolve3(current, "..");
1693
2063
  if (parent === current)
1694
2064
  break;
1695
2065
  suffix.push(current.slice(parent.length + 1));
@@ -1702,15 +2072,15 @@ function resolvePath(cwd2, inputPath, allowAbsolutePaths, jail) {
1702
2072
  if (!allowAbsolutePaths && isAbsolute(inputPath)) {
1703
2073
  throw new SandboxViolationError(inputPath, "absolute-path", `Absolute paths are not allowed; use a path relative to "${cwd2}".`);
1704
2074
  }
1705
- const resolved = resolve(cwd2, inputPath);
2075
+ const resolved = resolve3(cwd2, inputPath);
1706
2076
  if (!jail)
1707
2077
  return resolved;
1708
2078
  const realResolved = resolveSymlinks(resolved);
1709
2079
  const realCwd = resolveSymlinks(cwd2);
1710
- const sep = "/";
1711
- const jailRoot = realCwd.endsWith(sep) ? realCwd : `${realCwd}${sep}`;
2080
+ const sep2 = "/";
2081
+ const jailRoot = realCwd.endsWith(sep2) ? realCwd : `${realCwd}${sep2}`;
1712
2082
  if (realResolved !== realCwd && !realResolved.startsWith(jailRoot)) {
1713
- throw new SandboxViolationError(resolve(cwd2, inputPath), "jail", `Path escapes sandbox jail (cwd: ${cwd2})`);
2083
+ throw new SandboxViolationError(resolve3(cwd2, inputPath), "jail", `Path escapes sandbox jail (cwd: ${cwd2})`);
1714
2084
  }
1715
2085
  return realResolved;
1716
2086
  }
@@ -1783,11 +2153,11 @@ function createGuard(toolName, cwd2, allowAbsolutePaths, jail, policies, callbac
1783
2153
  }
1784
2154
  if (decision === "allow" || decision === "allow-session" || decision === "deny-session") {
1785
2155
  const mode = request.type === "fs.write" ? "write" : "read";
1786
- const relative = resolvedPath.startsWith(`${cwd2}/`) ? resolvedPath.slice(cwd2.length + 1) : resolvedPath;
2156
+ const relative2 = resolvedPath.startsWith(`${cwd2}/`) ? resolvedPath.slice(cwd2.length + 1) : resolvedPath;
1787
2157
  if (decision === "allow" || decision === "allow-session") {
1788
- addPolicy(pathSessionPolicy(mode, "allow", relative));
2158
+ addPolicy(pathSessionPolicy(mode, "allow", relative2));
1789
2159
  } else {
1790
- addPolicy(pathSessionPolicy(mode, "deny", relative));
2160
+ addPolicy(pathSessionPolicy(mode, "deny", relative2));
1791
2161
  }
1792
2162
  }
1793
2163
  return decision;
@@ -1862,25 +2232,25 @@ function createGuard(toolName, cwd2, allowAbsolutePaths, jail, policies, callbac
1862
2232
  askQuestion
1863
2233
  };
1864
2234
  }
1865
- function pathSessionPolicy(mode, decision, relative) {
2235
+ function pathSessionPolicy(mode, decision, relative2) {
1866
2236
  const fsType = mode === "write" ? "fs.write" : "fs.read";
1867
2237
  const decisionName = decision === "allow" ? "allow" : "deny";
1868
2238
  return {
1869
- name: `session-${decisionName}-${mode}-${relative.replace(/[^a-zA-Z0-9]/g, "-")}`,
2239
+ name: `session-${decisionName}-${mode}-${relative2.replace(/[^a-zA-Z0-9]/g, "-")}`,
1870
2240
  evaluate: (req) => {
1871
2241
  if (req.type !== fsType)
1872
2242
  return "pass";
1873
- if (req.resource.endsWith(`/${relative}`) || req.resource === `${cwd}/${relative}` || req.resource === `${cwd}/${relative}`) {
2243
+ if (req.resource.endsWith(`/${relative2}`) || req.resource === `${cwd}/${relative2}` || req.resource === `${cwd}/${relative2}`) {
1874
2244
  return decision;
1875
2245
  }
1876
2246
  const cwdSep = cwd.endsWith("/") ? cwd : `${cwd}/`;
1877
- const pattern = `${cwdSep}${relative}`;
2247
+ const pattern = `${cwdSep}${relative2}`;
1878
2248
  if (req.resource === pattern || req.resource.startsWith(`${pattern}/`)) {
1879
2249
  return decision;
1880
2250
  }
1881
2251
  if (req.resource.startsWith(cwdSep)) {
1882
2252
  const rel = req.resource.slice(cwdSep.length);
1883
- if (new Bun.Glob(relative).match(rel)) {
2253
+ if (new Bun.Glob(relative2).match(rel)) {
1884
2254
  return decision;
1885
2255
  }
1886
2256
  }
@@ -1891,14 +2261,14 @@ function pathSessionPolicy(mode, decision, relative) {
1891
2261
 
1892
2262
  // src/guard/policies.ts
1893
2263
  function matchesAny(absolutePath, patterns, cwd2) {
1894
- const sep = "/";
1895
- const cwdWithSep = cwd2.endsWith(sep) ? cwd2 : `${cwd2}${sep}`;
2264
+ const sep2 = "/";
2265
+ const cwdWithSep = cwd2.endsWith(sep2) ? cwd2 : `${cwd2}${sep2}`;
1896
2266
  if (absolutePath !== cwd2 && !absolutePath.startsWith(cwdWithSep)) {
1897
2267
  return false;
1898
2268
  }
1899
- const relative = absolutePath === cwd2 ? "." : absolutePath.slice(cwdWithSep.length);
2269
+ const relative2 = absolutePath === cwd2 ? "." : absolutePath.slice(cwdWithSep.length);
1900
2270
  for (const pattern of patterns) {
1901
- if (new Bun.Glob(pattern).match(relative))
2271
+ if (new Bun.Glob(pattern).match(relative2))
1902
2272
  return true;
1903
2273
  }
1904
2274
  return false;
@@ -1994,7 +2364,7 @@ function buildDefaultPolicies(forbiddenGlobs, read, write, cwd2) {
1994
2364
  // src/sandbox/sandbox.ts
1995
2365
  function createSandbox(config = {}, callbacks) {
1996
2366
  const cwd2 = config.cwd ?? process.cwd();
1997
- const sandboxCwd = resolve2(cwd2);
2367
+ const sandboxCwd = resolve4(cwd2);
1998
2368
  const jail = config.jail ?? false;
1999
2369
  const allowAbsolutePaths = config.allowAbsolutePaths ?? true;
2000
2370
  const forbiddenGlobs = config.forbiddenGlobs ?? [];
@@ -2066,8 +2436,8 @@ var DEFAULT_DAEMON_SANDBOX_CONFIG = {
2066
2436
  };
2067
2437
 
2068
2438
  // src/tools/io/audit-sink.ts
2069
- import { appendFile, mkdir, open, readdir, readFile } from "fs/promises";
2070
- import { join as join4 } from "path";
2439
+ import { appendFile, mkdir as mkdir4, open, readdir, readFile as readFile3 } from "fs/promises";
2440
+ import { join as join7 } from "path";
2071
2441
  function createMemoryAuditSink() {
2072
2442
  const entries = [];
2073
2443
  return {
@@ -2086,10 +2456,10 @@ function createMemoryAuditSink() {
2086
2456
  }
2087
2457
  var DEFAULT_MAX_DIFF_BYTES = 64 * 1024;
2088
2458
  function createFileAuditSink(workspaceRoot, options = {}) {
2089
- const auditDir = join4(workspaceRoot, ".comma", "audit");
2459
+ const auditDir = join7(workspaceRoot, ".comma", "audit");
2090
2460
  const maxDiffBytes = options.maxDiffBytes ?? DEFAULT_MAX_DIFF_BYTES;
2091
2461
  function fileForSession(sessionId) {
2092
- return join4(auditDir, `${sessionId ?? "default"}.jsonl`);
2462
+ return join7(auditDir, `${sessionId ?? "default"}.jsonl`);
2093
2463
  }
2094
2464
  function truncateDiff(entry) {
2095
2465
  if (entry.diff === undefined)
@@ -2113,7 +2483,7 @@ function createFileAuditSink(workspaceRoot, options = {}) {
2113
2483
  for (const filename of files) {
2114
2484
  if (!filename.endsWith(".jsonl"))
2115
2485
  continue;
2116
- const entries = await readJsonl(join4(auditDir, filename));
2486
+ const entries = await readJsonl(join7(auditDir, filename));
2117
2487
  out.push(...entries);
2118
2488
  }
2119
2489
  return out;
@@ -2121,7 +2491,7 @@ function createFileAuditSink(workspaceRoot, options = {}) {
2121
2491
  async function readJsonl(absolutePath) {
2122
2492
  let content;
2123
2493
  try {
2124
- content = await readFile(absolutePath, "utf8");
2494
+ content = await readFile3(absolutePath, "utf8");
2125
2495
  } catch {
2126
2496
  return [];
2127
2497
  }
@@ -2138,7 +2508,7 @@ function createFileAuditSink(workspaceRoot, options = {}) {
2138
2508
  }
2139
2509
  return {
2140
2510
  async append(entry) {
2141
- await mkdir(auditDir, { recursive: true });
2511
+ await mkdir4(auditDir, { recursive: true });
2142
2512
  const targetPath = fileForSession(entry.sessionId);
2143
2513
  const line = `${JSON.stringify(truncateDiff(entry))}
2144
2514
  `;
@@ -2212,7 +2582,7 @@ function sandboxErrorToToolError(error) {
2212
2582
  }
2213
2583
 
2214
2584
  // src/tools/built-in/ask-question/ask-question.ts
2215
- import { z as z3 } from "zod";
2585
+ import { z as z4 } from "zod";
2216
2586
 
2217
2587
  // src/tools/define/define-tool.ts
2218
2588
  function defineTool(config) {
@@ -2282,8 +2652,8 @@ function formatInput(input) {
2282
2652
  }
2283
2653
 
2284
2654
  // src/tools/built-in/ask-question/ask-question.ts
2285
- var askQuestionParams = z3.object({
2286
- question: z3.string().min(1).describe("The question to ask the user for feedback or clarification.")
2655
+ var askQuestionParams = z4.object({
2656
+ question: z4.string().min(1).describe("The question to ask the user for feedback or clarification.")
2287
2657
  });
2288
2658
  function createAskQuestionTool() {
2289
2659
  return defineTool({
@@ -2332,27 +2702,27 @@ function createAskQuestionTool() {
2332
2702
  });
2333
2703
  }
2334
2704
  // src/tools/built-in/create-file/create-file.ts
2335
- import { mkdir as mkdir3, stat as stat3 } from "fs/promises";
2336
- import { dirname as dirname5 } from "path";
2337
- import { z as z4 } from "zod";
2705
+ import { mkdir as mkdir6, stat as stat4 } from "fs/promises";
2706
+ import { dirname as dirname8 } from "path";
2707
+ import { z as z5 } from "zod";
2338
2708
 
2339
2709
  // src/tools/io/atomic-write.ts
2340
2710
  import { randomBytes } from "crypto";
2341
- import { chmod, open as open2, rename, stat, unlink, writeFile } from "fs/promises";
2342
- import { dirname as dirname3, join as join5 } from "path";
2711
+ import { chmod, open as open2, rename as rename3, stat as stat2, unlink, writeFile as writeFile2 } from "fs/promises";
2712
+ import { dirname as dirname6, join as join8 } from "path";
2343
2713
  async function writeAtomic(absolutePath, content, options = {}) {
2344
- const dir = dirname3(absolutePath);
2714
+ const dir = dirname6(absolutePath);
2345
2715
  const tempName = `.${randomBytes(8).toString("hex")}.tmp`;
2346
- const tempPath = join5(dir, tempName);
2716
+ const tempPath = join8(dir, tempName);
2347
2717
  let effectiveMode = options.mode;
2348
2718
  if (effectiveMode === undefined) {
2349
2719
  try {
2350
- const existing = await stat(absolutePath);
2720
+ const existing = await stat2(absolutePath);
2351
2721
  effectiveMode = existing.mode & 511;
2352
2722
  } catch {}
2353
2723
  }
2354
2724
  try {
2355
- await writeFile(tempPath, content);
2725
+ await writeFile2(tempPath, content);
2356
2726
  if (options.fsync !== false) {
2357
2727
  const handle = await open2(tempPath, "r+");
2358
2728
  try {
@@ -2364,7 +2734,7 @@ async function writeAtomic(absolutePath, content, options = {}) {
2364
2734
  if (effectiveMode !== undefined) {
2365
2735
  await chmod(tempPath, effectiveMode);
2366
2736
  }
2367
- await rename(tempPath, absolutePath);
2737
+ await rename3(tempPath, absolutePath);
2368
2738
  } catch (error) {
2369
2739
  try {
2370
2740
  await unlink(tempPath);
@@ -2430,14 +2800,14 @@ function unifiedDiff(before, after, options) {
2430
2800
  return patch;
2431
2801
  }
2432
2802
  // src/tools/io/hash.ts
2433
- import { readFile as readFile2 } from "fs/promises";
2803
+ import { readFile as readFile4 } from "fs/promises";
2434
2804
  function sha256OfBuffer(data) {
2435
2805
  const hasher = new Bun.CryptoHasher("sha256");
2436
2806
  hasher.update(data);
2437
2807
  return hasher.digest("hex");
2438
2808
  }
2439
2809
  async function sha256OfFile(absolutePath) {
2440
- const buffer = await readFile2(absolutePath);
2810
+ const buffer = await readFile4(absolutePath);
2441
2811
  return sha256OfBuffer(buffer);
2442
2812
  }
2443
2813
  // src/tools/io/newline.ts
@@ -2539,29 +2909,29 @@ var STALE_FILE_RECOVERY_HINT = "Re-read the file to obtain the current sha256 an
2539
2909
  // src/tools/io/trash.ts
2540
2910
  import { createHash } from "crypto";
2541
2911
  import {
2542
- mkdir as mkdir2,
2912
+ mkdir as mkdir5,
2543
2913
  readdir as readdir2,
2544
- readFile as readFile3,
2545
- rm,
2546
- stat as stat2,
2547
- writeFile as writeFile2
2914
+ readFile as readFile5,
2915
+ rm as rm3,
2916
+ stat as stat3,
2917
+ writeFile as writeFile3
2548
2918
  } from "fs/promises";
2549
- import { basename, dirname as dirname4, join as join6, relative, resolve as resolve3 } from "path";
2919
+ import { basename, dirname as dirname7, join as join9, relative as relative2, resolve as resolve5 } from "path";
2550
2920
  import { gunzipSync, gzipSync } from "zlib";
2551
- import extract from "tar-stream/extract";
2921
+ import extract2 from "tar-stream/extract";
2552
2922
  import pack from "tar-stream/pack";
2553
2923
  function trashWorkspaceDir(workspaceRoot) {
2554
2924
  const key = createHash("sha256").update(workspaceRoot).digest("hex");
2555
- return join6(resolveDataDir(), "trash", key);
2925
+ return join9(resolveDataDir(), "trash", key);
2556
2926
  }
2557
2927
  function shortHash(input, length) {
2558
2928
  return createHash("sha256").update(input).digest("hex").slice(0, length);
2559
2929
  }
2560
2930
  async function moveToTrash(workspaceRoot, absSourcePath, metadata) {
2561
2931
  const bucket = trashWorkspaceDir(workspaceRoot);
2562
- await mkdir2(bucket, { recursive: true });
2563
- const relativePath = relative(workspaceRoot, absSourcePath);
2564
- const content = await readFile3(absSourcePath);
2932
+ await mkdir5(bucket, { recursive: true });
2933
+ const relativePath = relative2(workspaceRoot, absSourcePath);
2934
+ const content = await readFile5(absSourcePath);
2565
2935
  const contentBuffer = Buffer.from(content);
2566
2936
  const sha256 = createHash("sha256").update(content).digest("hex");
2567
2937
  const stamp = new Date().toISOString().replace(/[:.]/g, "-");
@@ -2569,7 +2939,7 @@ async function moveToTrash(workspaceRoot, absSourcePath, metadata) {
2569
2939
  const runPart = metadata?.runId ? `${shortHash(metadata.runId, 8)}-` : "";
2570
2940
  const randomSuffix = !sessionPart && !runPart ? `${Math.random().toString(36).slice(2, 8)}-` : "";
2571
2941
  const archiveName = `${stamp}-${sessionPart}${runPart}${randomSuffix}${basename(absSourcePath)}.tar.gz`;
2572
- const archivePath = join6(bucket, archiveName);
2942
+ const archivePath = join9(bucket, archiveName);
2573
2943
  const metaEntry = {
2574
2944
  trashedAt: new Date().toISOString(),
2575
2945
  originalPath: relativePath,
@@ -2579,22 +2949,22 @@ async function moveToTrash(workspaceRoot, absSourcePath, metadata) {
2579
2949
  agentName: metadata?.agentName
2580
2950
  };
2581
2951
  const metaBuffer = Buffer.from(JSON.stringify(metaEntry, null, 2), "utf-8");
2582
- const packed = await new Promise((resolve4, reject) => {
2952
+ const packed = await new Promise((resolve6, reject) => {
2583
2953
  const p = pack();
2584
2954
  const chunks = [];
2585
2955
  p.on("error", reject);
2586
2956
  p.on("data", (chunk) => chunks.push(chunk));
2587
- p.on("end", () => resolve4(Buffer.concat(chunks)));
2957
+ p.on("end", () => resolve6(Buffer.concat(chunks)));
2588
2958
  p.entry({ name: "metadata.json", mode: 420, size: metaBuffer.length }, metaBuffer);
2589
2959
  p.entry({ name: relativePath, mode: 420, size: contentBuffer.length }, contentBuffer);
2590
2960
  p.finalize();
2591
2961
  });
2592
2962
  const compressed = gzipSync(packed);
2593
- await writeFile2(archivePath, new Uint8Array(compressed));
2963
+ await writeFile3(archivePath, new Uint8Array(compressed));
2594
2964
  try {
2595
- await rm(absSourcePath, { force: true });
2965
+ await rm3(absSourcePath, { force: true });
2596
2966
  } catch {
2597
- await rm(archivePath, { force: true });
2967
+ await rm3(archivePath, { force: true });
2598
2968
  throw new Error(`Failed to remove source file after archiving: ${absSourcePath}`);
2599
2969
  }
2600
2970
  return archivePath;
@@ -2611,9 +2981,9 @@ async function listTrash(workspaceRoot) {
2611
2981
  for (const name of entries) {
2612
2982
  if (!name.endsWith(".tar.gz"))
2613
2983
  continue;
2614
- const full = join6(bucket, name);
2984
+ const full = join9(bucket, name);
2615
2985
  try {
2616
- const fileStat = await stat2(full);
2986
+ const fileStat = await stat3(full);
2617
2987
  const metaResult = await readTrashMetadata(full);
2618
2988
  if (!metaResult)
2619
2989
  continue;
@@ -2628,10 +2998,10 @@ async function listTrash(workspaceRoot) {
2628
2998
  }
2629
2999
  async function readTrashMetadata(archivePath) {
2630
3000
  try {
2631
- const compressed = await readFile3(archivePath);
3001
+ const compressed = await readFile5(archivePath);
2632
3002
  const decompressed = gunzipSync(compressed);
2633
- return await new Promise((resolve4, _reject) => {
2634
- const ext = extract();
3003
+ return await new Promise((resolve6, _reject) => {
3004
+ const ext = extract2();
2635
3005
  let found;
2636
3006
  ext.on("entry", (header, stream, next) => {
2637
3007
  if (header.name === "metadata.json") {
@@ -2650,8 +3020,8 @@ async function readTrashMetadata(archivePath) {
2650
3020
  stream.on("error", () => next());
2651
3021
  }
2652
3022
  });
2653
- ext.on("finish", () => resolve4(found));
2654
- ext.on("error", () => resolve4(undefined));
3023
+ ext.on("finish", () => resolve6(found));
3024
+ ext.on("error", () => resolve6(undefined));
2655
3025
  ext.end(Buffer.from(decompressed));
2656
3026
  });
2657
3027
  } catch {
@@ -2664,23 +3034,23 @@ async function restoreFromTrash(workspaceRoot, trashPath, targetPath) {
2664
3034
  throw new Error(`Could not read metadata from trash archive: ${trashPath}`);
2665
3035
  }
2666
3036
  const restoreRelative = targetPath ?? metadata.originalPath;
2667
- const restoreAbsolute = resolve3(workspaceRoot, restoreRelative);
2668
- const relativeToWorkspace = relative(workspaceRoot, restoreAbsolute);
3037
+ const restoreAbsolute = resolve5(workspaceRoot, restoreRelative);
3038
+ const relativeToWorkspace = relative2(workspaceRoot, restoreAbsolute);
2669
3039
  if (relativeToWorkspace === ".." || relativeToWorkspace.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`)) {
2670
3040
  throw new Error(`Restore target escapes workspace: ${restoreRelative}`);
2671
3041
  }
2672
- await mkdir2(dirname4(restoreAbsolute), { recursive: true });
2673
- const compressed = await readFile3(trashPath);
3042
+ await mkdir5(dirname7(restoreAbsolute), { recursive: true });
3043
+ const compressed = await readFile5(trashPath);
2674
3044
  const decompressed = gunzipSync(compressed);
2675
- await new Promise((resolve4, reject) => {
2676
- const ext = extract();
3045
+ await new Promise((resolve6, reject) => {
3046
+ const ext = extract2();
2677
3047
  ext.on("entry", (header, stream, next) => {
2678
3048
  if (header.name === metadata.originalPath) {
2679
3049
  const chunks = [];
2680
3050
  stream.on("data", (chunk) => chunks.push(chunk));
2681
3051
  stream.on("end", async () => {
2682
3052
  try {
2683
- await writeFile2(restoreAbsolute, new Uint8Array(Buffer.concat(chunks)));
3053
+ await writeFile3(restoreAbsolute, new Uint8Array(Buffer.concat(chunks)));
2684
3054
  } catch (error) {
2685
3055
  reject(error);
2686
3056
  return;
@@ -2694,11 +3064,11 @@ async function restoreFromTrash(workspaceRoot, trashPath, targetPath) {
2694
3064
  stream.on("error", () => next());
2695
3065
  }
2696
3066
  });
2697
- ext.on("finish", resolve4);
3067
+ ext.on("finish", resolve6);
2698
3068
  ext.on("error", reject);
2699
3069
  ext.end(Buffer.from(decompressed));
2700
3070
  });
2701
- await rm(trashPath, { force: true });
3071
+ await rm3(trashPath, { force: true });
2702
3072
  return restoreAbsolute;
2703
3073
  }
2704
3074
  async function clearTrash(workspaceRoot) {
@@ -2711,20 +3081,20 @@ async function clearTrash(workspaceRoot) {
2711
3081
  if (!name.endsWith(".tar.gz"))
2712
3082
  continue;
2713
3083
  try {
2714
- const fileStat = await stat2(join6(bucket, name));
3084
+ const fileStat = await stat3(join9(bucket, name));
2715
3085
  totalBytes += fileStat.size;
2716
3086
  count += 1;
2717
3087
  } catch {}
2718
3088
  }
2719
- await rm(bucket, { recursive: true, force: true });
3089
+ await rm3(bucket, { recursive: true, force: true });
2720
3090
  } catch {}
2721
3091
  return { cleared: count, bytesFreed: totalBytes };
2722
3092
  }
2723
3093
  // src/tools/built-in/create-file/create-file.ts
2724
- var createFileParams = z4.object({
2725
- path: z4.string().min(1).describe("Workspace-relative path of the file to create. Absolute paths are rejected unless " + "the sandbox is configured with `allowAbsolutePaths: true`."),
2726
- content: z4.string().describe("Full UTF-8 content for the new file. May be empty to create a zero-byte file."),
2727
- createParentDirectories: z4.boolean().optional().describe("When true, missing parent directories are created (mkdir -p). When false (default), " + "a missing parent yields `not_found` \u2014 the LLM should re-call with this flag set.")
3094
+ var createFileParams = z5.object({
3095
+ path: z5.string().min(1).describe("Workspace-relative path of the file to create. Absolute paths are rejected unless " + "the sandbox is configured with `allowAbsolutePaths: true`."),
3096
+ content: z5.string().describe("Full UTF-8 content for the new file. May be empty to create a zero-byte file."),
3097
+ createParentDirectories: z5.boolean().optional().describe("When true, missing parent directories are created (mkdir -p). When false (default), " + "a missing parent yields `not_found` \u2014 the LLM should re-call with this flag set.")
2728
3098
  });
2729
3099
  function createCreateFileTool(config) {
2730
3100
  const defaultSink = config?.defaultAuditSink;
@@ -2824,7 +3194,7 @@ After every successful \`create_file\` call, **run the project's configured veri
2824
3194
  throw caught;
2825
3195
  }
2826
3196
  try {
2827
- await stat3(absolutePath);
3197
+ await stat4(absolutePath);
2828
3198
  return errorResult(toolError("already_exists", `File already exists: ${validatedArguments.path}`, {
2829
3199
  path: validatedArguments.path,
2830
3200
  recoverable: true,
@@ -2835,9 +3205,9 @@ After every successful \`create_file\` call, **run the project's configured veri
2835
3205
  if (code !== "ENOENT" && code !== "ENOTDIR")
2836
3206
  throw statError;
2837
3207
  }
2838
- const parent = dirname5(absolutePath);
3208
+ const parent = dirname8(absolutePath);
2839
3209
  try {
2840
- const parentStat = await stat3(parent);
3210
+ const parentStat = await stat4(parent);
2841
3211
  if (!parentStat.isDirectory()) {
2842
3212
  return errorResult(toolError("not_found", `Parent path is not a directory: ${validatedArguments.path}`, { path: validatedArguments.path, recoverable: false }));
2843
3213
  }
@@ -2852,7 +3222,7 @@ After every successful \`create_file\` call, **run the project's configured veri
2852
3222
  suggestedNextAction: "Re-call create_file with `createParentDirectories: true`."
2853
3223
  }));
2854
3224
  }
2855
- await mkdir3(parent, { recursive: true });
3225
+ await mkdir6(parent, { recursive: true });
2856
3226
  }
2857
3227
  const contentBytes = new TextEncoder().encode(validatedArguments.content);
2858
3228
  const sha256 = sha256OfBuffer(contentBytes);
@@ -2886,14 +3256,14 @@ After every successful \`create_file\` call, **run the project's configured veri
2886
3256
  });
2887
3257
  }
2888
3258
  // src/tools/built-in/delete-file/delete-file.ts
2889
- import { readFile as readFile4, stat as stat4, unlink as unlink2 } from "fs/promises";
3259
+ import { readFile as readFile6, stat as stat5, unlink as unlink2 } from "fs/promises";
2890
3260
 
2891
3261
  // src/tools/built-in/delete-file/delete-file.constants.ts
2892
- import { z as z5 } from "zod";
2893
- var deleteFileParams = z5.object({
2894
- path: z5.string().min(1).describe("Workspace-relative path of the file to delete. Absolute paths are rejected unless " + "the sandbox is configured with `allowAbsolutePaths: true`."),
2895
- expectedSha256: z5.string().length(64).regex(/^[0-9a-f]{64}$/, "expectedSha256 must be a 64-character lowercase hex string").optional().describe("Optional. SHA-256 of the file's current on-disk bytes, as returned by `read_file`. " + "When present, a mismatch yields `stale_file` so concurrent edits are caught. " + "When omitted, the file is deleted without staleness protection."),
2896
- permanent: z5.boolean().optional().describe("When true, the file is unlinked directly with no recovery path. When false (default), " + "the file is moved to a workspace-scoped trash bucket under the OS temp directory.")
3262
+ import { z as z6 } from "zod";
3263
+ var deleteFileParams = z6.object({
3264
+ path: z6.string().min(1).describe("Workspace-relative path of the file to delete. Absolute paths are rejected unless " + "the sandbox is configured with `allowAbsolutePaths: true`."),
3265
+ expectedSha256: z6.string().length(64).regex(/^[0-9a-f]{64}$/, "expectedSha256 must be a 64-character lowercase hex string").optional().describe("Optional. SHA-256 of the file's current on-disk bytes, as returned by `read_file`. " + "When present, a mismatch yields `stale_file` so concurrent edits are caught. " + "When omitted, the file is deleted without staleness protection."),
3266
+ permanent: z6.boolean().optional().describe("When true, the file is unlinked directly with no recovery path. When false (default), " + "the file is moved to a workspace-scoped trash bucket under the OS temp directory.")
2897
3267
  });
2898
3268
 
2899
3269
  // src/tools/built-in/delete-file/delete-file.ts
@@ -2988,7 +3358,7 @@ After every successful \`delete_file\` call, **run the project's configured veri
2988
3358
  }
2989
3359
  let targetStat;
2990
3360
  try {
2991
- targetStat = await stat4(absolutePath);
3361
+ targetStat = await stat5(absolutePath);
2992
3362
  } catch (statError) {
2993
3363
  const code = statError.code;
2994
3364
  if (code === "ENOENT" || code === "ENOTDIR") {
@@ -3002,7 +3372,7 @@ After every successful \`delete_file\` call, **run the project's configured veri
3002
3372
  if (targetStat.isDirectory()) {
3003
3373
  return errorResult(toolError("not_found", `Path is a directory, not a file: ${validatedArguments.path}`, { path: validatedArguments.path, recoverable: false }));
3004
3374
  }
3005
- const beforeBytes = await readFile4(absolutePath);
3375
+ const beforeBytes = await readFile6(absolutePath);
3006
3376
  const beforeSha256 = sha256OfBuffer(beforeBytes);
3007
3377
  if (validatedArguments.expectedSha256 !== undefined && beforeSha256 !== validatedArguments.expectedSha256) {
3008
3378
  return errorResult(toolError("stale_file", `File has changed since last read: ${validatedArguments.path}`, {
@@ -3068,8 +3438,8 @@ After every successful \`delete_file\` call, **run the project's configured veri
3068
3438
  });
3069
3439
  }
3070
3440
  // src/tools/built-in/edit-file/edit-file.ts
3071
- import { readFile as readFile5, stat as stat5 } from "fs/promises";
3072
- import { z as z6 } from "zod";
3441
+ import { readFile as readFile7, stat as stat6 } from "fs/promises";
3442
+ import { z as z7 } from "zod";
3073
3443
 
3074
3444
  // src/tools/built-in/edit-file/edit-file.replacers.ts
3075
3445
  var exactReplacer = function* (content, find) {
@@ -3358,15 +3728,15 @@ function stripBomToLF(text) {
3358
3728
  }
3359
3729
 
3360
3730
  // src/tools/built-in/edit-file/edit-file.ts
3361
- var editSchema = z6.object({
3362
- oldText: z6.string().min(1).describe("Exact substring to match in the current file content. Matching is literal " + "(no regex). Compared against logical (LF, no-BOM) content \u2014 provide LF newlines."),
3363
- newText: z6.string().describe("Replacement text. May be empty to delete the matched range. Provide LF newlines; " + "the tool re-applies the file's existing newline style and BOM on write."),
3364
- expectedOccurrences: z6.number().int().min(1).optional().describe("Expected number of matches for `oldText` (default 1). Any other count yields " + "`multiple_matches` with the actual match ranges in `error.details`.")
3731
+ var editSchema = z7.object({
3732
+ oldText: z7.string().min(1).describe("Exact substring to match in the current file content. Matching is literal " + "(no regex). Compared against logical (LF, no-BOM) content \u2014 provide LF newlines."),
3733
+ newText: z7.string().describe("Replacement text. May be empty to delete the matched range. Provide LF newlines; " + "the tool re-applies the file's existing newline style and BOM on write."),
3734
+ expectedOccurrences: z7.number().int().min(1).optional().describe("Expected number of matches for `oldText` (default 1). Any other count yields " + "`multiple_matches` with the actual match ranges in `error.details`.")
3365
3735
  });
3366
- var editFileParams = z6.object({
3367
- path: z6.string().min(1).describe("Workspace-relative path of the file to edit. Absolute paths are rejected unless " + "the sandbox is configured with `allowAbsolutePaths: true`."),
3368
- expectedSha256: z6.string().length(64).regex(/^[0-9a-f]{64}$/, "expectedSha256 must be a 64-character lowercase hex string").optional().describe("Optional. SHA-256 of the file's current on-disk bytes, as returned by `read_file`. " + "When present, a mismatch yields `stale_file` so concurrent edits are caught. " + "When omitted, the tool reads the file at edit time and proceeds without staleness " + "protection \u2014 fine for typical single-agent runs."),
3369
- edits: z6.array(editSchema).min(1).describe("One or more (oldText, newText) replacements. All edits are evaluated against the " + "ORIGINAL file snapshot, so order is irrelevant and overlapping replacements " + "are reported as `overlapping_edits` rather than silently clobbering each other.")
3736
+ var editFileParams = z7.object({
3737
+ path: z7.string().min(1).describe("Workspace-relative path of the file to edit. Absolute paths are rejected unless " + "the sandbox is configured with `allowAbsolutePaths: true`."),
3738
+ expectedSha256: z7.string().length(64).regex(/^[0-9a-f]{64}$/, "expectedSha256 must be a 64-character lowercase hex string").optional().describe("Optional. SHA-256 of the file's current on-disk bytes, as returned by `read_file`. " + "When present, a mismatch yields `stale_file` so concurrent edits are caught. " + "When omitted, the tool reads the file at edit time and proceeds without staleness " + "protection \u2014 fine for typical single-agent runs."),
3739
+ edits: z7.array(editSchema).min(1).describe("One or more (oldText, newText) replacements. All edits are evaluated against the " + "ORIGINAL file snapshot, so order is irrelevant and overlapping replacements " + "are reported as `overlapping_edits` rather than silently clobbering each other.")
3370
3740
  });
3371
3741
  function createEditFileTool(config) {
3372
3742
  const defaultSink = config?.defaultAuditSink;
@@ -3500,7 +3870,7 @@ After every successful \`edit_file\` call, **run the project's configured verifi
3500
3870
  }
3501
3871
  let targetStat;
3502
3872
  try {
3503
- targetStat = await stat5(absolutePath);
3873
+ targetStat = await stat6(absolutePath);
3504
3874
  } catch (statError) {
3505
3875
  const code = statError.code;
3506
3876
  if (code === "ENOENT" || code === "ENOTDIR") {
@@ -3518,7 +3888,7 @@ After every successful \`edit_file\` call, **run the project's configured verifi
3518
3888
  recoverable: false
3519
3889
  }));
3520
3890
  }
3521
- const beforeBytes = await readFile5(absolutePath);
3891
+ const beforeBytes = await readFile7(absolutePath);
3522
3892
  if (isLikelyBinary(beforeBytes)) {
3523
3893
  return errorResult(toolError("binary_file", `Cannot edit binary file: ${validatedArguments.path}`, {
3524
3894
  path: validatedArguments.path,
@@ -3601,9 +3971,9 @@ After every successful \`edit_file\` call, **run the project's configured verifi
3601
3971
  });
3602
3972
  }
3603
3973
  // src/tools/built-in/glob/glob.ts
3604
- import { readdir as readdir3, stat as stat6 } from "fs/promises";
3605
- import { join as join7, relative as relative2 } from "path";
3606
- import { z as z7 } from "zod";
3974
+ import { readdir as readdir3, stat as stat7 } from "fs/promises";
3975
+ import { join as join10, relative as relative3 } from "path";
3976
+ import { z as z8 } from "zod";
3607
3977
 
3608
3978
  // src/tools/built-in/glob/glob.constants.ts
3609
3979
  var DEFAULT_EXCLUDE_GLOBS = [
@@ -3619,9 +3989,9 @@ var DEFAULT_MAX_RESULTS = 1000;
3619
3989
  var DEFAULT_TRAVERSAL_DEPTH = 32;
3620
3990
 
3621
3991
  // src/tools/built-in/glob/glob.utils.ts
3622
- import { sep } from "path";
3992
+ import { sep as sep2 } from "path";
3623
3993
  function toForwardSlash(pathString) {
3624
- return sep === "/" ? pathString : pathString.split(sep).join("/");
3994
+ return sep2 === "/" ? pathString : pathString.split(sep2).join("/");
3625
3995
  }
3626
3996
  function matchesAnyGlob(relPath, patterns) {
3627
3997
  for (const pattern of patterns) {
@@ -3645,11 +4015,11 @@ ${lines.join(`
3645
4015
  }
3646
4016
 
3647
4017
  // src/tools/built-in/glob/glob.ts
3648
- var globParams = z7.object({
3649
- pattern: z7.string().min(1).describe('The glob pattern to match files and folders against (e.g., "src/**/*.ts" or "**/*.json").'),
3650
- root: z7.string().optional().describe('Workspace-relative directory to search under. Defaults to "." (the workspace root).'),
3651
- excludeGlobs: z7.array(z7.string()).optional().describe("Glob patterns to exclude. Replaces the default exclude set " + "(node_modules, .git, dist, build, .next, .turbo, coverage). Pass [] to disable."),
3652
- maxResults: z7.number().int().positive().optional().describe("Maximum number of matches to return. Defaults to 1000.")
4018
+ var globParams = z8.object({
4019
+ pattern: z8.string().min(1).describe('The glob pattern to match files and folders against (e.g., "src/**/*.ts" or "**/*.json").'),
4020
+ root: z8.string().optional().describe('Workspace-relative directory to search under. Defaults to "." (the workspace root).'),
4021
+ excludeGlobs: z8.array(z8.string()).optional().describe("Glob patterns to exclude. Replaces the default exclude set " + "(node_modules, .git, dist, build, .next, .turbo, coverage). Pass [] to disable."),
4022
+ maxResults: z8.number().int().positive().optional().describe("Maximum number of matches to return. Defaults to 1000.")
3653
4023
  });
3654
4024
  function createGlobTool(config) {
3655
4025
  const maxResultsCap = config?.maxResults ?? DEFAULT_MAX_RESULTS;
@@ -3750,7 +4120,7 @@ function createGlobTool(config) {
3750
4120
  }
3751
4121
  let rootStat;
3752
4122
  try {
3753
- rootStat = await stat6(absoluteRoot);
4123
+ rootStat = await stat7(absoluteRoot);
3754
4124
  } catch (statError) {
3755
4125
  const code = statError.code;
3756
4126
  if (code === "ENOENT" || code === "ENOTDIR") {
@@ -3791,9 +4161,9 @@ function createGlobTool(config) {
3791
4161
  for (const dirent of dirents) {
3792
4162
  if (abort.aborted)
3793
4163
  break outer;
3794
- const childAbs = join7(node.absolutePath, dirent.name);
3795
- const relFromRoot = toForwardSlash(relative2(absoluteRoot, childAbs));
3796
- const relFromWorkspace = toForwardSlash(relative2(guard.cwd, childAbs));
4164
+ const childAbs = join10(node.absolutePath, dirent.name);
4165
+ const relFromRoot = toForwardSlash(relative3(absoluteRoot, childAbs));
4166
+ const relFromWorkspace = toForwardSlash(relative3(guard.cwd, childAbs));
3797
4167
  if (matchesAnyGlob(relFromWorkspace, excludeGlobs))
3798
4168
  continue;
3799
4169
  const isDir = dirent.isDirectory();
@@ -3815,7 +4185,7 @@ function createGlobTool(config) {
3815
4185
  let mtime = new Date(0).toISOString();
3816
4186
  if (type !== "symlink") {
3817
4187
  try {
3818
- const childStat = await stat6(childAbs);
4188
+ const childStat = await stat7(childAbs);
3819
4189
  size = type === "directory" ? 0 : childStat.size;
3820
4190
  mtime = childStat.mtime.toISOString();
3821
4191
  } catch {
@@ -3843,158 +4213,195 @@ function createGlobTool(config) {
3843
4213
  });
3844
4214
  }
3845
4215
  // src/tools/built-in/launch-strategy/launch-strategy.ts
3846
- import { z as z10 } from "zod";
4216
+ import { z as z11 } from "zod";
3847
4217
 
3848
4218
  // src/strategy/discover/discover.ts
3849
4219
  import { existsSync as existsSync4 } from "fs";
3850
- import { join as join9 } from "path";
4220
+ import { isAbsolute as isAbsolute3, join as join12, relative as relative5, resolve as resolve7 } from "path";
4221
+ import stripJsonComments2 from "strip-json-comments";
3851
4222
 
3852
4223
  // src/strategy/discover/discover.utils.ts
3853
- import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3 } from "fs";
3854
- import { dirname as dirname6, join as join8, resolve as resolve4 } from "path";
4224
+ import { existsSync as existsSync3, readdirSync } from "fs";
4225
+ import { realpath, stat as stat8 } from "fs/promises";
4226
+ import { dirname as dirname9, isAbsolute as isAbsolute2, join as join11, relative as relative4, resolve as resolve6 } from "path";
3855
4227
  import stripJsonComments from "strip-json-comments";
3856
4228
  import YAML2 from "yaml";
3857
4229
 
3858
4230
  // src/strategy/schema.ts
4231
+ import { z as z10 } from "zod";
4232
+
4233
+ // src/agents/loader/loader.schema.ts
3859
4234
  import { z as z9 } from "zod";
3860
4235
 
4236
+ // src/agents/registry/agent-registry.constants.ts
4237
+ var BUILT_IN_AGENT_NAMES = ["llm", "user"];
4238
+
3861
4239
  // src/agents/loader/loader.schema.ts
3862
- import { z as z8 } from "zod";
3863
- var SystemPromptTemplateSchema = z8.object({
3864
- template: z8.string(),
3865
- variables: z8.record(z8.union([
3866
- z8.string(),
3867
- z8.number(),
3868
- z8.boolean(),
3869
- z8.array(z8.string()),
3870
- z8.record(z8.string())
4240
+ var SystemPromptTemplateSchema = z9.object({
4241
+ template: z9.string(),
4242
+ variables: z9.record(z9.union([
4243
+ z9.string(),
4244
+ z9.number(),
4245
+ z9.boolean(),
4246
+ z9.array(z9.string()),
4247
+ z9.record(z9.string())
3871
4248
  ])).optional()
3872
4249
  }).strict();
3873
- var ModelOptionsSchema = z8.object({
3874
- temperature: z8.number().optional(),
3875
- topP: z8.number().optional(),
3876
- topK: z8.number().optional(),
3877
- maxOutputTokens: z8.number().optional(),
3878
- maxRetries: z8.number().optional(),
3879
- frequencyPenalty: z8.number().optional(),
3880
- presencePenalty: z8.number().optional(),
3881
- seed: z8.number().optional()
4250
+ var ModelOptionsSchema = z9.object({
4251
+ temperature: z9.number().optional(),
4252
+ topP: z9.number().optional(),
4253
+ topK: z9.number().optional(),
4254
+ maxOutputTokens: z9.number().optional(),
4255
+ maxRetries: z9.number().optional(),
4256
+ frequencyPenalty: z9.number().optional(),
4257
+ presencePenalty: z9.number().optional(),
4258
+ seed: z9.number().optional()
3882
4259
  }).strict();
3883
- var OutputSchemaSchema = z8.record(z8.unknown());
3884
- var ConversationContextSchema = z8.object({
3885
- rollingWindow: z8.union([
3886
- z8.number().int().nonnegative(),
3887
- z8.object({ maxRecords: z8.number().int().nonnegative() }).strict()
4260
+ var OutputSchemaSchema = z9.record(z9.unknown());
4261
+ var ConversationContextSchema = z9.object({
4262
+ rollingWindow: z9.union([
4263
+ z9.number().int().nonnegative(),
4264
+ z9.object({ maxRecords: z9.number().int().nonnegative() }).strict()
3888
4265
  ]).optional(),
3889
- compaction: z8.union([
3890
- z8.boolean(),
3891
- z8.object({
3892
- keepRecent: z8.number().int().nonnegative().optional(),
3893
- threshold: z8.number().int().positive().optional()
4266
+ compaction: z9.union([
4267
+ z9.boolean(),
4268
+ z9.object({
4269
+ keepRecent: z9.number().int().nonnegative().optional(),
4270
+ threshold: z9.number().int().positive().optional()
3894
4271
  }).strict()
3895
4272
  ]).optional()
3896
4273
  }).strict();
3897
- var AgentDescriptionSchema = z8.object({
3898
- name: z8.string().min(1),
3899
- description: z8.string().optional(),
3900
- model: z8.string().min(1),
3901
- systemPrompt: z8.string().optional(),
4274
+ var LLMAgentDescriptionSchema = z9.object({
4275
+ name: z9.string().min(1),
4276
+ type: z9.literal("llm").optional(),
4277
+ description: z9.string().optional(),
4278
+ model: z9.string().min(1),
4279
+ systemPrompt: z9.string().optional(),
3902
4280
  systemPromptTemplate: SystemPromptTemplateSchema.optional(),
3903
- tools: z8.array(z8.string()).optional(),
3904
- providerOptions: z8.record(z8.record(z8.unknown())).optional(),
4281
+ tools: z9.array(z9.string()).optional(),
4282
+ providerOptions: z9.record(z9.record(z9.unknown())).optional(),
3905
4283
  modelOptions: ModelOptionsSchema.optional(),
3906
4284
  outputSchema: OutputSchemaSchema.optional(),
3907
4285
  context: ConversationContextSchema.optional(),
3908
- maxSteps: z8.number().int().positive().optional()
4286
+ maxSteps: z9.number().int().positive().optional()
4287
+ }).strict();
4288
+ var CustomAgentDescriptionSchema = z9.object({
4289
+ name: z9.string().min(1),
4290
+ description: z9.string().optional(),
4291
+ type: z9.string().min(1).refine((type) => !BUILT_IN_AGENT_NAMES.includes(type), "Built-in agent types must use their built-in schema."),
4292
+ config: z9.record(z9.unknown()).optional()
3909
4293
  }).strict();
4294
+ var AgentDescriptionSchema = z9.union([
4295
+ LLMAgentDescriptionSchema,
4296
+ CustomAgentDescriptionSchema
4297
+ ]);
4298
+
4299
+ // src/flows/registry/flow-registry.constants.ts
4300
+ var BUILT_IN_FLOW_NAMES = [
4301
+ "sequential",
4302
+ "cycle",
4303
+ "broadcast"
4304
+ ];
3910
4305
 
3911
4306
  // src/strategy/schema.ts
3912
- var UserAgentDefSchema = z9.object({
3913
- type: z9.literal("user"),
3914
- description: z9.string().optional(),
3915
- config: z9.object({
3916
- requireInput: z9.boolean().optional(),
3917
- presetMessage: z9.string().optional()
4307
+ var UserAgentDefSchema = z10.object({
4308
+ type: z10.literal("user"),
4309
+ description: z10.string().optional(),
4310
+ config: z10.object({
4311
+ requireInput: z10.boolean().optional(),
4312
+ presetMessage: z10.string().optional()
3918
4313
  }).strict().optional()
3919
4314
  }).strict();
3920
- var LLMAgentDefSchema = z9.object({
3921
- type: z9.literal("llm").optional(),
3922
- description: z9.string().optional(),
3923
- model: z9.string().optional(),
3924
- systemPrompt: z9.string().optional(),
3925
- systemPromptTemplate: z9.object({
3926
- template: z9.string(),
3927
- variables: z9.record(z9.union([
3928
- z9.string(),
3929
- z9.number(),
3930
- z9.boolean(),
3931
- z9.array(z9.string()),
3932
- z9.record(z9.string())
4315
+ var LLMAgentDefSchema = z10.object({
4316
+ type: z10.literal("llm").optional(),
4317
+ description: z10.string().optional(),
4318
+ model: z10.string().optional(),
4319
+ systemPrompt: z10.string().optional(),
4320
+ systemPromptTemplate: z10.object({
4321
+ template: z10.string(),
4322
+ variables: z10.record(z10.union([
4323
+ z10.string(),
4324
+ z10.number(),
4325
+ z10.boolean(),
4326
+ z10.array(z10.string()),
4327
+ z10.record(z10.string())
3933
4328
  ])).optional()
3934
4329
  }).strict().optional(),
3935
- tools: z9.array(z9.string()).optional(),
3936
- skills: z9.array(z9.string().min(1)).optional(),
3937
- providerOptions: z9.record(z9.record(z9.unknown())).optional(),
4330
+ tools: z10.array(z10.string()).optional(),
4331
+ skills: z10.array(z10.string().min(1)).optional(),
4332
+ providerOptions: z10.record(z10.record(z10.unknown())).optional(),
3938
4333
  modelOptions: ModelOptionsSchema.optional(),
3939
4334
  outputSchema: OutputSchemaSchema.optional(),
3940
4335
  context: ConversationContextSchema.optional(),
3941
- maxSteps: z9.number().int().positive().optional()
4336
+ maxSteps: z10.number().int().positive().optional()
4337
+ }).strict();
4338
+ var CustomAgentDefSchema = z10.object({
4339
+ type: z10.string().min(1).refine((type) => !BUILT_IN_AGENT_NAMES.includes(type), "Built-in agent types must use their built-in schema."),
4340
+ description: z10.string().optional(),
4341
+ config: z10.record(z10.unknown()).optional()
3942
4342
  }).strict();
3943
- var AgentDefSchema = z9.union([UserAgentDefSchema, LLMAgentDefSchema]);
3944
- var AgentStepSchema = z9.object({
3945
- agent: z9.string(),
3946
- description: z9.string().optional()
4343
+ var AgentDefSchema = z10.union([
4344
+ UserAgentDefSchema,
4345
+ LLMAgentDefSchema,
4346
+ CustomAgentDefSchema
4347
+ ]);
4348
+ var AgentStepSchema = z10.object({
4349
+ agent: z10.string(),
4350
+ description: z10.string().optional()
3947
4351
  }).strict();
3948
- var FlowStepSchema = z9.lazy(() => z9.union([AgentStepSchema, FlowDefSchema]));
4352
+ var FlowStepSchema = z10.lazy(() => z10.union([AgentStepSchema, FlowDefSchema]));
3949
4353
  var BaseFlowFields = {
3950
- name: z9.string(),
3951
- description: z9.string().optional(),
3952
- steps: z9.array(FlowStepSchema).min(1)
4354
+ name: z10.string().min(1),
4355
+ description: z10.string().optional(),
4356
+ steps: z10.array(FlowStepSchema).min(1)
3953
4357
  };
3954
- var SequentialFlowDefSchema = z9.object({
4358
+ var SequentialFlowDefSchema = z10.object({
4359
+ ...BaseFlowFields,
4360
+ type: z10.literal("sequential")
4361
+ }).strict();
4362
+ var CycleFlowDefSchema = z10.object({
3955
4363
  ...BaseFlowFields,
3956
- type: z9.literal("sequential")
4364
+ type: z10.literal("cycle"),
4365
+ cycles: z10.union([z10.number().int().positive(), z10.literal("Infinity")]).optional(),
4366
+ observer: z10.string().optional(),
4367
+ breakCycleSignals: z10.array(z10.string().min(1)).min(1).optional(),
4368
+ breakCycleSignalMatch: z10.enum(["substring", "first-line", "any-line", "exact"]).optional()
3957
4369
  }).strict();
3958
- var CycleFlowDefSchema = z9.object({
4370
+ var BroadcastFlowDefSchema = z10.object({
3959
4371
  ...BaseFlowFields,
3960
- type: z9.literal("cycle"),
3961
- cycles: z9.union([z9.number().int().positive(), z9.literal("Infinity")]).optional(),
3962
- observer: z9.string().optional(),
3963
- breakCycleSignals: z9.array(z9.string().min(1)).min(1).optional(),
3964
- breakCycleSignalMatch: z9.enum(["substring", "first-line", "any-line", "exact"]).optional()
4372
+ type: z10.literal("broadcast"),
4373
+ separator: z10.string().optional()
3965
4374
  }).strict();
3966
- var BroadcastFlowDefSchema = z9.object({
4375
+ var CustomFlowDefSchema = z10.object({
3967
4376
  ...BaseFlowFields,
3968
- type: z9.literal("broadcast"),
3969
- separator: z9.string().optional()
4377
+ type: z10.string().min(1).refine((type) => !BUILT_IN_FLOW_NAMES.includes(type), "Built-in flow types must use their built-in schema."),
4378
+ config: z10.record(z10.unknown()).optional()
3970
4379
  }).strict();
3971
- var FlowDefSchema = z9.discriminatedUnion("type", [
4380
+ var BuiltInFlowDefSchema = z10.discriminatedUnion("type", [
3972
4381
  SequentialFlowDefSchema,
3973
4382
  CycleFlowDefSchema,
3974
4383
  BroadcastFlowDefSchema
3975
4384
  ]);
3976
- var StrategySchema = z9.object({
3977
- name: z9.string().min(1),
3978
- version: z9.string().min(1),
3979
- description: z9.string().optional(),
3980
- agents: z9.record(AgentDefSchema),
4385
+ var FlowDefSchema = z10.union([
4386
+ BuiltInFlowDefSchema,
4387
+ CustomFlowDefSchema
4388
+ ]);
4389
+ var StrategySchema = z10.object({
4390
+ name: z10.string().min(1),
4391
+ version: z10.string().min(1),
4392
+ description: z10.string().optional(),
4393
+ agents: z10.record(AgentDefSchema),
3981
4394
  flow: FlowDefSchema
3982
4395
  }).strict();
3983
- var CommaProjectManifestSchema = z9.object({
3984
- name: z9.string().min(1),
3985
- version: z9.string().optional(),
3986
- description: z9.string().optional(),
3987
- strategies: z9.array(z9.string()).min(1),
3988
- tools: z9.array(z9.string()).optional(),
3989
- entry: z9.string().optional(),
3990
- dependencies: z9.array(z9.string()).optional()
3991
- }).strict();
3992
4396
  function isUserAgentDef(agentDefinition) {
3993
4397
  return "type" in agentDefinition && agentDefinition.type === "user";
3994
4398
  }
3995
4399
  function isLLMAgentDef(agentDefinition) {
3996
4400
  return !("type" in agentDefinition) || agentDefinition.type === undefined || agentDefinition.type === "llm";
3997
4401
  }
4402
+ function isCustomAgentDef(agentDefinition) {
4403
+ return !isUserAgentDef(agentDefinition) && !isLLMAgentDef(agentDefinition);
4404
+ }
3998
4405
  function isAgentStep(step) {
3999
4406
  return typeof step === "object" && step !== null && "agent" in step && typeof step.agent === "string";
4000
4407
  }
@@ -4075,8 +4482,50 @@ async function parseProjectManifest(manifestPath) {
4075
4482
  const detail = firstIssue ? `${firstIssue.path.join(".")}: ${firstIssue.message}` : "validation failed";
4076
4483
  return { ok: false, reason: `Manifest invalid (${detail})` };
4077
4484
  }
4078
- const manifestDir = dirname6(manifestPath);
4079
- const strategyPaths = result.data.strategies.map((relativePath) => resolve4(manifestDir, relativePath));
4485
+ const manifestDir = dirname9(manifestPath);
4486
+ const strategyPaths = [];
4487
+ let realManifestDir;
4488
+ try {
4489
+ realManifestDir = await realpath(manifestDir);
4490
+ for (const entry of Object.values(result.data.strategies ?? {})) {
4491
+ if (entry.expose !== true)
4492
+ continue;
4493
+ if (isAbsolute2(entry.path)) {
4494
+ return {
4495
+ ok: false,
4496
+ reason: `Strategy path must be relative: ${entry.path}`
4497
+ };
4498
+ }
4499
+ const candidate = resolve6(manifestDir, entry.path);
4500
+ const lexicalRelative = relative4(manifestDir, candidate);
4501
+ if (lexicalRelative.startsWith("..") || isAbsolute2(lexicalRelative)) {
4502
+ return {
4503
+ ok: false,
4504
+ reason: `Strategy path escapes project: ${entry.path}`
4505
+ };
4506
+ }
4507
+ const resolvedPath = await realpath(candidate);
4508
+ const realRelative = relative4(realManifestDir, resolvedPath);
4509
+ if (realRelative.startsWith("..") || isAbsolute2(realRelative)) {
4510
+ return {
4511
+ ok: false,
4512
+ reason: `Strategy path escapes project: ${entry.path}`
4513
+ };
4514
+ }
4515
+ if (!(await stat8(resolvedPath)).isFile()) {
4516
+ return {
4517
+ ok: false,
4518
+ reason: `Strategy path is not a regular file: ${entry.path}`
4519
+ };
4520
+ }
4521
+ strategyPaths.push(candidate);
4522
+ }
4523
+ } catch (error) {
4524
+ return {
4525
+ ok: false,
4526
+ reason: `Strategy path is missing or unreadable: ${error instanceof Error ? error.message : String(error)}`
4527
+ };
4528
+ }
4080
4529
  return {
4081
4530
  ok: true,
4082
4531
  name: result.data.name,
@@ -4088,7 +4537,7 @@ function listStrategyFiles(dir) {
4088
4537
  if (!existsSync3(dir))
4089
4538
  return [];
4090
4539
  try {
4091
- return readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && hasStrategyExtension(entry.name)).map((entry) => join8(dir, entry.name));
4540
+ return readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && hasStrategyExtension(entry.name)).map((entry) => join11(dir, entry.name));
4092
4541
  } catch {
4093
4542
  return [];
4094
4543
  }
@@ -4097,7 +4546,17 @@ function listProjectManifests(dir) {
4097
4546
  if (!existsSync3(dir))
4098
4547
  return [];
4099
4548
  try {
4100
- return readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && existsSync3(join8(dir, entry.name, "comma-project.json"))).map((entry) => join8(dir, entry.name, "comma-project.json"));
4549
+ return readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && existsSync3(join11(dir, entry.name, "comma-project.json"))).map((entry) => join11(dir, entry.name, "comma-project.json"));
4550
+ } catch {
4551
+ return [];
4552
+ }
4553
+ }
4554
+ function listInstalledProjectManifests(dataDir) {
4555
+ const packagesDir = join11(dataDir, "packages");
4556
+ if (!existsSync3(packagesDir))
4557
+ return [];
4558
+ try {
4559
+ return readdirSync(packagesDir, { withFileTypes: true }).filter((scope) => scope.isDirectory() && scope.name.startsWith("@")).flatMap((scope) => listProjectManifests(join11(packagesDir, scope.name)));
4101
4560
  } catch {
4102
4561
  return [];
4103
4562
  }
@@ -4115,31 +4574,6 @@ function buildDiscoveredStrategy(input) {
4115
4574
  ...manifestPath !== undefined ? { manifestPath } : {}
4116
4575
  };
4117
4576
  }
4118
- function findCorePackageRoot() {
4119
- let dir;
4120
- try {
4121
- dir = import.meta.dir;
4122
- } catch {
4123
- return null;
4124
- }
4125
- for (let i = 0;i < 12; i += 1) {
4126
- const manifestPath = join8(dir, "package.json");
4127
- if (existsSync3(manifestPath)) {
4128
- try {
4129
- const content = readFileSync3(manifestPath, "utf8");
4130
- const parsed = JSON.parse(content);
4131
- if (parsed && parsed.name === "@comma-agents/core") {
4132
- return dir;
4133
- }
4134
- } catch {}
4135
- }
4136
- const parent = dirname6(dir);
4137
- if (parent === dir)
4138
- break;
4139
- dir = parent;
4140
- }
4141
- return null;
4142
- }
4143
4577
 
4144
4578
  // src/strategy/discover/discover.ts
4145
4579
  async function discoverStrategies(options = {}) {
@@ -4186,38 +4620,55 @@ async function discoverStrategies(options = {}) {
4186
4620
  }));
4187
4621
  }
4188
4622
  }
4189
- if (includeBundled) {
4190
- const corePackageRoot = findCorePackageRoot();
4191
- if (corePackageRoot) {
4192
- const bundledRoot = join9(corePackageRoot, "strategies");
4193
- await collectSingleFiles(bundledRoot, "bundled");
4194
- for (const manifestPath of listProjectManifests(bundledRoot)) {
4195
- await collectProjectManifest(manifestPath, "bundled-project");
4196
- }
4197
- const bundledRootManifest = join9(bundledRoot, "comma-project.json");
4198
- if (existsSync4(bundledRootManifest)) {
4199
- await collectProjectManifest(bundledRootManifest, "bundled-project");
4200
- }
4201
- }
4202
- }
4203
- const cwdStrategiesDir = join9(cwd2, ".comma", "strategies");
4623
+ const cwdStrategiesDir = join12(cwd2, ".comma", "strategies");
4204
4624
  await collectSingleFiles(cwdStrategiesDir, "cwd");
4205
4625
  for (const manifestPath of listProjectManifests(cwdStrategiesDir)) {
4206
4626
  await collectProjectManifest(manifestPath, "cwd-project");
4207
4627
  }
4208
- const cwdRootManifest = join9(cwd2, ".comma", "comma-project.json");
4628
+ const cwdRootManifest = join12(cwd2, ".comma", "comma-project.json");
4209
4629
  if (existsSync4(cwdRootManifest)) {
4210
4630
  await collectProjectManifest(cwdRootManifest, "cwd-root-project");
4211
4631
  }
4212
4632
  if (dataDir) {
4213
- const dataStrategiesDir = join9(dataDir, "strategies");
4633
+ const dataStrategiesDir = join12(dataDir, "strategies");
4214
4634
  await collectSingleFiles(dataStrategiesDir, "data");
4215
4635
  for (const manifestPath of listProjectManifests(dataStrategiesDir)) {
4216
4636
  await collectProjectManifest(manifestPath, "data-project");
4217
4637
  }
4638
+ for (const manifestPath of listInstalledProjectManifests(dataDir)) {
4639
+ await collectProjectManifest(manifestPath, "hub-package");
4640
+ }
4641
+ }
4642
+ if (includeBundled) {
4643
+ for (const manifestPath of getBundledProjectManifestCandidates()) {
4644
+ if (!existsSync4(manifestPath))
4645
+ continue;
4646
+ await collectProjectManifest(manifestPath, "bundled");
4647
+ break;
4648
+ }
4218
4649
  }
4219
4650
  return { strategies, warnings };
4220
4651
  }
4652
+ async function resolveInstalledStrategyReference(reference, dataDir = safeResolveDataDir()) {
4653
+ const match = /^(@[^/]+\/[^/]+)\/strategies\/([^/]+)$/.exec(reference);
4654
+ if (!match)
4655
+ return;
4656
+ const [, packageName, artifactId] = match;
4657
+ if (!packageName || !artifactId)
4658
+ return;
4659
+ if (dataDir) {
4660
+ const installedManifestPath = join12(dataDir, "packages", packageName, "comma-project.json");
4661
+ const installed = await resolveProjectStrategyReference(installedManifestPath, packageName, artifactId, "hub-package");
4662
+ if (installed)
4663
+ return installed;
4664
+ }
4665
+ for (const manifestPath of getBundledProjectManifestCandidates()) {
4666
+ const bundled = await resolveProjectStrategyReference(manifestPath, packageName, artifactId, "bundled");
4667
+ if (bundled)
4668
+ return bundled;
4669
+ }
4670
+ return;
4671
+ }
4221
4672
  function safeResolveDataDir() {
4222
4673
  try {
4223
4674
  return resolveDataDir();
@@ -4225,65 +4676,49 @@ function safeResolveDataDir() {
4225
4676
  return;
4226
4677
  }
4227
4678
  }
4679
+ function getBundledProjectManifestCandidates() {
4680
+ return [
4681
+ resolve7(import.meta.dir, "strategies", "@comma", "core-strategies", "comma-project.json"),
4682
+ resolve7(import.meta.dir, "..", "..", "..", "strategies", "@comma", "core-strategies", "comma-project.json"),
4683
+ resolve7(import.meta.dir, "..", "..", "strategies", "@comma", "core-strategies", "comma-project.json")
4684
+ ];
4685
+ }
4686
+ async function resolveProjectStrategyReference(manifestPath, packageName, artifactId, origin) {
4687
+ if (!existsSync4(manifestPath))
4688
+ return;
4689
+ try {
4690
+ const raw = JSON.parse(stripJsonComments2(await Bun.file(manifestPath).text()));
4691
+ const manifest = CommaProjectManifestSchema.parse(raw);
4692
+ if (manifest.name !== packageName)
4693
+ return;
4694
+ const artifact = manifest.strategies?.[artifactId];
4695
+ if (!artifact || isAbsolute3(artifact.path))
4696
+ return;
4697
+ const manifestDir = resolve7(manifestPath, "..");
4698
+ const strategyPath = resolve7(manifestDir, artifact.path);
4699
+ const rel = relative5(manifestDir, strategyPath);
4700
+ if (rel.startsWith("..") || isAbsolute3(rel))
4701
+ return;
4702
+ const header = await parseStrategyHeader(strategyPath);
4703
+ if (!header.ok)
4704
+ return;
4705
+ return buildDiscoveredStrategy({
4706
+ header,
4707
+ path: strategyPath,
4708
+ origin,
4709
+ projectName: manifest.name,
4710
+ manifestPath
4711
+ });
4712
+ } catch {
4713
+ return;
4714
+ }
4715
+ }
4228
4716
 
4229
4717
  // src/strategy/loader/loader.ts
4230
- import { dirname as dirname7 } from "path";
4231
- import stripJsonComments2 from "strip-json-comments";
4718
+ import { dirname as dirname10 } from "path";
4719
+ import stripJsonComments3 from "strip-json-comments";
4232
4720
  import YAML3 from "yaml";
4233
4721
 
4234
- // src/strategy/loader/loader.utils.ts
4235
- import { isAbsolute as isAbsolute2, resolve as resolve5 } from "path";
4236
- import { jsonSchema } from "ai";
4237
-
4238
- // src/agents/built-in/user/user-agent.utils.ts
4239
- var defaultInputCollector = async (request) => {
4240
- if (typeof globalThis.prompt === "function") {
4241
- return globalThis.prompt(request.prompt) ?? "";
4242
- }
4243
- const { createInterface } = await import("readline");
4244
- const readlineInterface = createInterface({
4245
- input: process.stdin,
4246
- output: process.stdout
4247
- });
4248
- return new Promise((resolve5, reject) => {
4249
- if (request.signal?.aborted) {
4250
- readlineInterface.close();
4251
- reject(new DOMException("Input collection aborted", "AbortError"));
4252
- return;
4253
- }
4254
- const onAbort = () => {
4255
- readlineInterface.close();
4256
- reject(new DOMException("Input collection aborted", "AbortError"));
4257
- };
4258
- request.signal?.addEventListener("abort", onAbort, { once: true });
4259
- readlineInterface.question(`${request.prompt}
4260
- > `, (answer) => {
4261
- request.signal?.removeEventListener("abort", onAbort);
4262
- readlineInterface.close();
4263
- resolve5(answer);
4264
- });
4265
- });
4266
- };
4267
-
4268
- // src/agents/built-in/user/user-agent.ts
4269
- function createUserAgent(config) {
4270
- const requireInput = config.requireInput ?? true;
4271
- const collector = config.inputCollector ?? defaultInputCollector;
4272
- const agent = createAgent({
4273
- name: config.name,
4274
- execute: async (message) => {
4275
- if (requireInput && message === "") {
4276
- return collector({
4277
- agentName: config.name,
4278
- prompt: message
4279
- });
4280
- }
4281
- return config.presetMessage ?? message;
4282
- }
4283
- });
4284
- return agent;
4285
- }
4286
-
4287
4722
  // src/flows/flow/flow.utils.ts
4288
4723
  function buildFlowResult(text, stepResults) {
4289
4724
  let promptTokens = 0;
@@ -4453,7 +4888,7 @@ function createCycleFlow(config) {
4453
4888
  let current = message;
4454
4889
  for (let cycle = 0;cycle < cycles; cycle++) {
4455
4890
  if (cycle > 0) {
4456
- await new Promise((resolve5) => setTimeout(resolve5, 0));
4891
+ await new Promise((resolve8) => setTimeout(resolve8, 0));
4457
4892
  }
4458
4893
  current = await runTransformHooks(store.alterMessageBeforeCycle, current);
4459
4894
  for (const step of steps) {
@@ -4501,8 +4936,221 @@ function hookIntoFlow(flow, hooks) {
4501
4936
  }
4502
4937
  }
4503
4938
 
4939
+ // src/flows/registry/flow-registry.ts
4940
+ var flowRegistry = new Map;
4941
+ function defineFlowType(definition) {
4942
+ return definition;
4943
+ }
4944
+ function registerFlow(name, definition) {
4945
+ if (BUILT_IN_FLOW_NAMES.includes(name)) {
4946
+ console.warn(`[comma-agents] registerFlow("${name}"): overriding built-in flow "${name}". ` + "The custom flow will be used instead of the default.");
4947
+ } else if (flowRegistry.has(name)) {
4948
+ console.warn(`[comma-agents] registerFlow("${name}"): overriding previously registered flow "${name}".`);
4949
+ }
4950
+ flowRegistry.set(name, {
4951
+ create(context) {
4952
+ const result = definition.configSchema.safeParse(context.config);
4953
+ if (!result.success) {
4954
+ const issues = result.error.issues.map((issue) => {
4955
+ const path = issue.path.length ? `config.${issue.path.join(".")}` : "config";
4956
+ return ` - ${path}: ${issue.message}`;
4957
+ }).join(`
4958
+ `);
4959
+ throw new StrategyValidationError(`Flow "${context.name}" configuration validation failed for type "${name}":
4960
+ ${issues}`, { cause: result.error });
4961
+ }
4962
+ return definition.create({
4963
+ name: context.name,
4964
+ steps: context.steps,
4965
+ config: result.data,
4966
+ resolveAgent: context.resolveAgent
4967
+ });
4968
+ }
4969
+ });
4970
+ }
4971
+ function unregisterFlow(name) {
4972
+ return flowRegistry.delete(name);
4973
+ }
4974
+ function getRegisteredFlowNames() {
4975
+ return [...flowRegistry.keys()];
4976
+ }
4977
+ function resolveRegisteredFlow(name) {
4978
+ return flowRegistry.get(name);
4979
+ }
4980
+ function resetFlowRegistry() {
4981
+ flowRegistry = new Map;
4982
+ }
4983
+
4984
+ // src/flows/loader/loader.utils.ts
4985
+ var builtInFlowFactories = {
4986
+ sequential(definition, steps) {
4987
+ return createSequentialFlow({ name: definition.name, steps });
4988
+ },
4989
+ cycle(definition, steps, resolveAgent) {
4990
+ const cycleDefinition = definition;
4991
+ const cycles = cycleDefinition.cycles === "Infinity" ? Infinity : cycleDefinition.cycles ?? 1;
4992
+ const observer = cycleDefinition.observer ? resolveAgent(cycleDefinition.observer) : undefined;
4993
+ return createCycleFlow({
4994
+ name: cycleDefinition.name,
4995
+ steps,
4996
+ cycles,
4997
+ ...observer ? { observer } : {},
4998
+ ...cycleDefinition.breakCycleSignals ? { breakCycleSignals: cycleDefinition.breakCycleSignals } : {},
4999
+ ...cycleDefinition.breakCycleSignalMatch ? { breakCycleSignalMatch: cycleDefinition.breakCycleSignalMatch } : {}
5000
+ });
5001
+ },
5002
+ broadcast(definition, steps) {
5003
+ const broadcastDefinition = definition;
5004
+ return createBroadcastFlow({
5005
+ name: broadcastDefinition.name,
5006
+ steps,
5007
+ separator: broadcastDefinition.separator
5008
+ });
5009
+ }
5010
+ };
5011
+ function buildFlowFromDescription(description, options) {
5012
+ const resolveAgent = (name) => resolveAgentReference(name, description.name, options.agents);
5013
+ const steps = description.steps.map((step, stepIndex) => {
5014
+ if (isAgentStep(step)) {
5015
+ return resolveAgentReference(step.agent, description.name, options.agents);
5016
+ }
5017
+ if (isFlowDef(step)) {
5018
+ return buildFlowFromDescription(step, options);
5019
+ }
5020
+ throw new StrategyValidationError(`Flow "${description.name}" step ${stepIndex} is neither an agent reference nor a flow definition.`);
5021
+ });
5022
+ const registeredFlow = resolveRegisteredFlow(description.type);
5023
+ const builtInFactory = builtInFlowFactories[description.type];
5024
+ let flow;
5025
+ if (registeredFlow) {
5026
+ flow = registeredFlow.create({
5027
+ name: description.name,
5028
+ steps,
5029
+ config: "config" in description ? description.config ?? {} : {},
5030
+ resolveAgent
5031
+ });
5032
+ } else if (builtInFactory) {
5033
+ flow = builtInFactory(description, steps, resolveAgent);
5034
+ } else {
5035
+ throw new StrategyValidationError(`Flow "${description.name}" references unknown flow type "${description.type}". ` + `Built-in flows: [${BUILT_IN_FLOW_NAMES.join(", ")}]. ` + `Registered flows: [${getRegisteredFlowNames().join(", ") || "(none)"}].`);
5036
+ }
5037
+ if (options.flowHooks) {
5038
+ hookIntoFlow(flow, options.flowHooks);
5039
+ }
5040
+ return flow;
5041
+ }
5042
+ function resolveAgentReference(agentName, flowName, agents) {
5043
+ const agent = agents[agentName];
5044
+ if (!agent) {
5045
+ const available = Object.keys(agents).join(", ");
5046
+ throw new StrategyValidationError(`Flow "${flowName}" references agent "${agentName}" which is not defined. ` + `Available agents: [${available}].`);
5047
+ }
5048
+ return agent;
5049
+ }
5050
+
5051
+ // src/strategy/loader/loader.utils.ts
5052
+ import { isAbsolute as isAbsolute4, resolve as resolve8 } from "path";
5053
+ import { jsonSchema } from "ai";
5054
+
5055
+ // src/agents/built-in/user/user-agent.utils.ts
5056
+ var defaultInputCollector = async (request) => {
5057
+ if (typeof globalThis.prompt === "function") {
5058
+ return globalThis.prompt(request.prompt) ?? "";
5059
+ }
5060
+ const { createInterface } = await import("readline");
5061
+ const readlineInterface = createInterface({
5062
+ input: process.stdin,
5063
+ output: process.stdout
5064
+ });
5065
+ return new Promise((resolve8, reject) => {
5066
+ if (request.signal?.aborted) {
5067
+ readlineInterface.close();
5068
+ reject(new DOMException("Input collection aborted", "AbortError"));
5069
+ return;
5070
+ }
5071
+ const onAbort = () => {
5072
+ readlineInterface.close();
5073
+ reject(new DOMException("Input collection aborted", "AbortError"));
5074
+ };
5075
+ request.signal?.addEventListener("abort", onAbort, { once: true });
5076
+ readlineInterface.question(`${request.prompt}
5077
+ > `, (answer) => {
5078
+ request.signal?.removeEventListener("abort", onAbort);
5079
+ readlineInterface.close();
5080
+ resolve8(answer);
5081
+ });
5082
+ });
5083
+ };
5084
+
5085
+ // src/agents/built-in/user/user-agent.ts
5086
+ function createUserAgent(config) {
5087
+ const requireInput = config.requireInput ?? true;
5088
+ const collector = config.inputCollector ?? defaultInputCollector;
5089
+ const agent = createAgent({
5090
+ name: config.name,
5091
+ execute: async (message) => {
5092
+ if (requireInput && message === "") {
5093
+ return collector({
5094
+ agentName: config.name,
5095
+ prompt: message
5096
+ });
5097
+ }
5098
+ return config.presetMessage ?? message;
5099
+ }
5100
+ });
5101
+ return agent;
5102
+ }
5103
+
5104
+ // src/agents/registry/agent-registry.ts
5105
+ var agentRegistry = new Map;
5106
+ function defineAgentType(definition) {
5107
+ return definition;
5108
+ }
5109
+ function registerAgent(name, definition) {
5110
+ if (BUILT_IN_AGENT_NAMES.includes(name)) {
5111
+ throw new StrategyValidationError(`Cannot register reserved built-in agent type "${name}". ` + `Choose a custom name other than: [${BUILT_IN_AGENT_NAMES.join(", ")}].`);
5112
+ }
5113
+ if (name.length === 0) {
5114
+ throw new StrategyValidationError("Custom agent type names cannot be empty.");
5115
+ }
5116
+ if (agentRegistry.has(name)) {
5117
+ console.warn(`[comma-agents] registerAgent("${name}"): overriding previously registered agent "${name}".`);
5118
+ }
5119
+ agentRegistry.set(name, {
5120
+ async create(context) {
5121
+ const result = definition.configSchema.safeParse(context.config);
5122
+ if (!result.success) {
5123
+ const issues = result.error.issues.map((issue) => {
5124
+ const path = issue.path.length ? `config.${issue.path.join(".")}` : "config";
5125
+ return ` - ${path}: ${issue.message}`;
5126
+ }).join(`
5127
+ `);
5128
+ throw new StrategyValidationError(`Agent "${context.name}" configuration validation failed for type "${name}":
5129
+ ${issues}`, { cause: result.error });
5130
+ }
5131
+ return await definition.create({
5132
+ name: context.name,
5133
+ config: result.data,
5134
+ runtime: context.runtime
5135
+ });
5136
+ }
5137
+ });
5138
+ }
5139
+ function unregisterAgent(name) {
5140
+ return agentRegistry.delete(name);
5141
+ }
5142
+ function getRegisteredAgentNames() {
5143
+ return [...agentRegistry.keys()];
5144
+ }
5145
+ function resolveRegisteredAgent(name) {
5146
+ return agentRegistry.get(name);
5147
+ }
5148
+ function resetAgentRegistry() {
5149
+ agentRegistry = new Map;
5150
+ }
5151
+
4504
5152
  // src/prompts/template/prompt-template.ts
4505
- import { readFile as readFile6 } from "fs/promises";
5153
+ import { readFile as readFile8 } from "fs/promises";
4506
5154
  import { Liquid } from "liquidjs";
4507
5155
  var engine = new Liquid({
4508
5156
  strictVariables: false,
@@ -4523,7 +5171,7 @@ engine.registerFilter("file", async (filePath) => {
4523
5171
  if (!resolvedPath)
4524
5172
  throw new Error('{{ "/path" | file }} requires a file path');
4525
5173
  try {
4526
- const content = await readFile6(resolvedPath, "utf-8");
5174
+ const content = await readFile8(resolvedPath, "utf-8");
4527
5175
  return content.split(`
4528
5176
  `)[0]?.trim() ?? "";
4529
5177
  } catch {
@@ -4565,7 +5213,7 @@ function createPromptTemplate(config) {
4565
5213
  template: config.template,
4566
5214
  defaults,
4567
5215
  async render(overrides) {
4568
- const resolve5 = async (vars) => {
5216
+ const resolve8 = async (vars) => {
4569
5217
  const out = {};
4570
5218
  await Promise.all(Object.entries(vars).map(async ([key, variableValue]) => {
4571
5219
  out[key] = typeof variableValue === "function" ? await variableValue() : variableValue;
@@ -4573,8 +5221,8 @@ function createPromptTemplate(config) {
4573
5221
  return out;
4574
5222
  };
4575
5223
  const [base, over] = await Promise.all([
4576
- resolve5(defaults),
4577
- overrides ? resolve5(overrides) : {}
5224
+ resolve8(defaults),
5225
+ overrides ? resolve8(overrides) : {}
4578
5226
  ]);
4579
5227
  return engine.parseAndRender(config.template, { ...base, ...over });
4580
5228
  },
@@ -4585,11 +5233,11 @@ function createPromptTemplate(config) {
4585
5233
  }
4586
5234
 
4587
5235
  // src/skills/skills.loader.ts
4588
- import { readdir as readdir4, stat as stat7 } from "fs/promises";
4589
- import { join as join11 } from "path";
5236
+ import { readdir as readdir4, stat as stat9 } from "fs/promises";
5237
+ import { join as join14 } from "path";
4590
5238
 
4591
5239
  // src/skills/skills.constants.ts
4592
- var GLOBAL_SKILLS_SUBDIR = "comma-agents/skills";
5240
+ var GLOBAL_SKILLS_SUBDIR = "skills";
4593
5241
  var PROJECT_SKILLS_SUBDIR = ".comma/skills";
4594
5242
  var SKILL_FILENAME = "SKILL.md";
4595
5243
  var SKILL_MAX_BYTES = 256 * 1024;
@@ -4620,23 +5268,12 @@ function createSkillRegistry() {
4620
5268
  }
4621
5269
 
4622
5270
  // src/skills/skills.utils.ts
4623
- import { homedir as homedir3, platform } from "os";
4624
- import { join as join10 } from "path";
4625
- function resolveUserConfigRoot() {
4626
- const currentPlatform = platform();
4627
- if (currentPlatform === "darwin") {
4628
- return join10(homedir3(), "Library", "Application Support");
4629
- }
4630
- if (currentPlatform === "win32") {
4631
- return process.env.APPDATA ?? join10(homedir3(), "AppData", "Roaming");
4632
- }
4633
- return process.env.XDG_CONFIG_HOME ?? join10(homedir3(), ".config");
4634
- }
5271
+ import { join as join13 } from "path";
4635
5272
  function resolveDefaultGlobalSkillsDir() {
4636
- return join10(resolveUserConfigRoot(), GLOBAL_SKILLS_SUBDIR);
5273
+ return join13(resolveDataDir(), GLOBAL_SKILLS_SUBDIR);
4637
5274
  }
4638
5275
  function resolveDefaultProjectSkillsDir(workspaceRoot) {
4639
- return join10(workspaceRoot, PROJECT_SKILLS_SUBDIR);
5276
+ return join13(workspaceRoot, PROJECT_SKILLS_SUBDIR);
4640
5277
  }
4641
5278
  function parseSkillFile(rawContent) {
4642
5279
  const normalised = rawContent.replace(/^\uFEFF/, "");
@@ -4725,11 +5362,11 @@ async function scanDirectoryInto(skillsDirectory, origin, registry, warnings) {
4725
5362
  for (const entry of entries) {
4726
5363
  if (!entry.isDirectory())
4727
5364
  continue;
4728
- const skillDir = join11(skillsDirectory, entry.name);
4729
- const skillFile = join11(skillDir, SKILL_FILENAME);
5365
+ const skillDir = join14(skillsDirectory, entry.name);
5366
+ const skillFile = join14(skillDir, SKILL_FILENAME);
4730
5367
  let fileStat;
4731
5368
  try {
4732
- fileStat = await stat7(skillFile);
5369
+ fileStat = await stat9(skillFile);
4733
5370
  } catch (statError) {
4734
5371
  const code = statError.code;
4735
5372
  if (code === "ENOENT" || code === "ENOTDIR")
@@ -4822,14 +5459,32 @@ function mergeSystemPrompts(prompts) {
4822
5459
  }
4823
5460
 
4824
5461
  // src/strategy/loader/loader.utils.ts
5462
+ var builtInAgentFactories = {
5463
+ user(name, agentDefinition, options) {
5464
+ return buildUserAgent(name, agentDefinition, options);
5465
+ },
5466
+ llm(name, agentDefinition, options) {
5467
+ return buildLLMAgent(name, agentDefinition, options);
5468
+ }
5469
+ };
4825
5470
  async function buildAgentRegistry(strategy, options) {
4826
5471
  const registry = {};
4827
5472
  for (const [name, agentDefinition] of Object.entries(strategy.agents)) {
4828
- if (isUserAgentDef(agentDefinition)) {
4829
- registry[name] = buildUserAgent(name, agentDefinition, options);
4830
- } else if (isLLMAgentDef(agentDefinition)) {
4831
- registry[name] = await buildLLMAgent(name, agentDefinition, options);
5473
+ const agentType = agentDefinition.type ?? "llm";
5474
+ const builtInFactory = builtInAgentFactories[agentType];
5475
+ if (builtInFactory) {
5476
+ registry[name] = await builtInFactory(name, agentDefinition, options);
5477
+ continue;
4832
5478
  }
5479
+ const registeredAgent = resolveRegisteredAgent(agentType);
5480
+ if (!registeredAgent) {
5481
+ throw new StrategyValidationError(`Agent "${name}" references unknown agent type "${agentType}". ` + `Built-in agents: [${BUILT_IN_AGENT_NAMES.join(", ")}]. ` + `Registered agents: [${getRegisteredAgentNames().join(", ") || "(none)"}].`);
5482
+ }
5483
+ registry[name] = await registeredAgent.create({
5484
+ name,
5485
+ config: "config" in agentDefinition ? agentDefinition.config ?? {} : {},
5486
+ runtime: options
5487
+ });
4833
5488
  }
4834
5489
  return registry;
4835
5490
  }
@@ -4880,11 +5535,11 @@ async function buildLLMAgent(name, agentDefinition, options) {
4880
5535
  let resolvedSystemPrompt = agentDefinition.systemPrompt;
4881
5536
  if (resolvedSystemPrompt && (resolvedSystemPrompt.endsWith(".txt") || resolvedSystemPrompt.endsWith(".md"))) {
4882
5537
  let resolvedPath = resolvedSystemPrompt;
4883
- if (!isAbsolute2(resolvedSystemPrompt)) {
5538
+ if (!isAbsolute4(resolvedSystemPrompt)) {
4884
5539
  if (options.strategyDir) {
4885
- resolvedPath = resolve5(options.strategyDir, resolvedSystemPrompt);
5540
+ resolvedPath = resolve8(options.strategyDir, resolvedSystemPrompt);
4886
5541
  } else {
4887
- resolvedPath = resolve5(resolvedSystemPrompt);
5542
+ resolvedPath = resolve8(resolvedSystemPrompt);
4888
5543
  }
4889
5544
  }
4890
5545
  const file = Bun.file(resolvedPath);
@@ -4896,11 +5551,11 @@ async function buildLLMAgent(name, agentDefinition, options) {
4896
5551
  let resolvedTemplate = agentDefinition.systemPromptTemplate?.template;
4897
5552
  if (resolvedTemplate && (resolvedTemplate.endsWith(".txt") || resolvedTemplate.endsWith(".md"))) {
4898
5553
  let resolvedPath = resolvedTemplate;
4899
- if (!isAbsolute2(resolvedTemplate)) {
5554
+ if (!isAbsolute4(resolvedTemplate)) {
4900
5555
  if (options.strategyDir) {
4901
- resolvedPath = resolve5(options.strategyDir, resolvedTemplate);
5556
+ resolvedPath = resolve8(options.strategyDir, resolvedTemplate);
4902
5557
  } else {
4903
- resolvedPath = resolve5(resolvedTemplate);
5558
+ resolvedPath = resolve8(resolvedTemplate);
4904
5559
  }
4905
5560
  }
4906
5561
  const file = Bun.file(resolvedPath);
@@ -4990,62 +5645,6 @@ function prependSkillsHeader(systemPrompt, skillsHeader) {
4990
5645
 
4991
5646
  ${skillsHeader}`;
4992
5647
  }
4993
- function buildFlow(flowDef, agents, options) {
4994
- const steps = resolveSteps(flowDef.steps, flowDef.name, agents, options);
4995
- let flow;
4996
- switch (flowDef.type) {
4997
- case "sequential":
4998
- flow = createSequentialFlow({
4999
- name: flowDef.name,
5000
- steps
5001
- });
5002
- break;
5003
- case "cycle": {
5004
- const cycleDef = flowDef;
5005
- const cycles = cycleDef.cycles === "Infinity" ? Infinity : cycleDef.cycles ?? 1;
5006
- const observer = cycleDef.observer ? resolveAgentRef(cycleDef.observer, flowDef.name, agents) : undefined;
5007
- flow = createCycleFlow({
5008
- name: flowDef.name,
5009
- steps,
5010
- cycles,
5011
- ...observer ? { observer } : {},
5012
- ...cycleDef.breakCycleSignals ? { breakCycleSignals: cycleDef.breakCycleSignals } : {},
5013
- ...cycleDef.breakCycleSignalMatch ? { breakCycleSignalMatch: cycleDef.breakCycleSignalMatch } : {}
5014
- });
5015
- break;
5016
- }
5017
- case "broadcast":
5018
- flow = createBroadcastFlow({
5019
- name: flowDef.name,
5020
- steps,
5021
- separator: flowDef.separator
5022
- });
5023
- break;
5024
- }
5025
- if (options.flowHooks) {
5026
- hookIntoFlow(flow, options.flowHooks);
5027
- }
5028
- return flow;
5029
- }
5030
- function resolveSteps(steps, flowName, agents, options) {
5031
- return steps.map((step, index) => {
5032
- if (isAgentStep(step)) {
5033
- return resolveAgentRef(step.agent, flowName, agents);
5034
- }
5035
- if (isFlowDef(step)) {
5036
- return buildFlow(step, agents, options);
5037
- }
5038
- throw new StrategyValidationError(`Flow "${flowName}" step ${index} is neither an agent reference nor a flow definition.`);
5039
- });
5040
- }
5041
- function resolveAgentRef(agentName, flowName, agents) {
5042
- const agent = agents[agentName];
5043
- if (!agent) {
5044
- const available = Object.keys(agents).join(", ");
5045
- throw new StrategyValidationError(`Flow "${flowName}" references agent "${agentName}" which is not defined. ` + `Available agents: [${available}].`);
5046
- }
5047
- return agent;
5048
- }
5049
5648
 
5050
5649
  // src/strategy/loader/loader.ts
5051
5650
  async function loadStrategy(filePath, options = {}) {
@@ -5063,7 +5662,7 @@ async function loadStrategy(filePath, options = {}) {
5063
5662
  throw new StrategyValidationError(`Strategy file not found: ${filePath}`);
5064
5663
  }
5065
5664
  const content = await file.text();
5066
- const strategyDir = dirname7(filePath);
5665
+ const strategyDir = dirname10(filePath);
5067
5666
  return await loadStrategyFromString(content, format, {
5068
5667
  ...options,
5069
5668
  strategyDir: options.strategyDir ?? strategyDir
@@ -5073,7 +5672,7 @@ async function loadStrategyFromString(content, format, options = {}) {
5073
5672
  let raw;
5074
5673
  try {
5075
5674
  if (format === "json") {
5076
- raw = JSON.parse(stripJsonComments2(content));
5675
+ raw = JSON.parse(stripJsonComments3(content));
5077
5676
  } else {
5078
5677
  raw = YAML3.parse(content);
5079
5678
  }
@@ -5091,7 +5690,10 @@ ${issues}`, {
5091
5690
  }
5092
5691
  const strategy = result.data;
5093
5692
  const agents = await buildAgentRegistry(strategy, options);
5094
- const flow = buildFlow(strategy.flow, agents, options);
5693
+ const flow = buildFlowFromDescription(strategy.flow, {
5694
+ agents,
5695
+ flowHooks: options.flowHooks
5696
+ });
5095
5697
  return {
5096
5698
  name: strategy.name,
5097
5699
  version: strategy.version,
@@ -5102,11 +5704,143 @@ ${issues}`, {
5102
5704
  };
5103
5705
  }
5104
5706
 
5707
+ // src/strategy/loader/project-loader.ts
5708
+ import { realpath as realpath2, stat as stat10 } from "fs/promises";
5709
+ import { dirname as dirname11, isAbsolute as isAbsolute5, relative as relative6, resolve as resolve9 } from "path";
5710
+ import stripJsonComments4 from "strip-json-comments";
5711
+ import YAML4 from "yaml";
5712
+ var FILESYSTEM_TOOLS = new Set([
5713
+ "read_file",
5714
+ "list_directory",
5715
+ "search_files",
5716
+ "glob",
5717
+ "create_file",
5718
+ "write_file",
5719
+ "edit_file",
5720
+ "delete_file",
5721
+ "restore_file",
5722
+ "move_file"
5723
+ ]);
5724
+ async function resolveProjectFile(manifestDir, declaredPath, label) {
5725
+ if (isAbsolute5(declaredPath)) {
5726
+ throw new StrategyValidationError(`${label} must use a relative path: ${declaredPath}`);
5727
+ }
5728
+ const candidate = resolve9(manifestDir, declaredPath);
5729
+ const lexicalRelative = relative6(manifestDir, candidate);
5730
+ if (lexicalRelative.startsWith("..") || isAbsolute5(lexicalRelative)) {
5731
+ throw new StrategyValidationError(`${label} escapes the project directory: ${declaredPath}`);
5732
+ }
5733
+ let resolvedPath;
5734
+ try {
5735
+ resolvedPath = await realpath2(candidate);
5736
+ } catch {
5737
+ throw new StrategyValidationError(`${label} not found: ${candidate}`);
5738
+ }
5739
+ const realManifestDir = await realpath2(manifestDir);
5740
+ const realRelative = relative6(realManifestDir, resolvedPath);
5741
+ if (realRelative.startsWith("..") || isAbsolute5(realRelative)) {
5742
+ throw new StrategyValidationError(`${label} escapes the project directory: ${declaredPath}`);
5743
+ }
5744
+ if (!(await stat10(resolvedPath)).isFile()) {
5745
+ throw new StrategyValidationError(`${label} must be a regular file: ${candidate}`);
5746
+ }
5747
+ if (!await Bun.file(resolvedPath).exists()) {
5748
+ throw new StrategyValidationError(`${label} not found: ${candidate}`);
5749
+ }
5750
+ return resolvedPath;
5751
+ }
5752
+ async function importProjectFile(filePath, label) {
5753
+ try {
5754
+ await import(filePath);
5755
+ } catch (importError) {
5756
+ throw new StrategyValidationError(`Failed to import ${label} "${filePath}": ${importError instanceof Error ? importError.message : String(importError)}`, { cause: importError });
5757
+ }
5758
+ }
5759
+ async function loadProject(manifestPath) {
5760
+ const file = Bun.file(manifestPath);
5761
+ if (!await file.exists()) {
5762
+ throw new StrategyValidationError(`Project manifest not found: ${manifestPath}`);
5763
+ }
5764
+ const content = await file.text();
5765
+ let raw;
5766
+ try {
5767
+ raw = JSON.parse(stripJsonComments4(content));
5768
+ } catch (parseError) {
5769
+ throw new StrategyValidationError(`Failed to parse comma-project.json: ${parseError instanceof Error ? parseError.message : String(parseError)}`, { cause: parseError });
5770
+ }
5771
+ const result = CommaProjectManifestSchema.safeParse(raw);
5772
+ if (!result.success) {
5773
+ const issues = result.error.issues.map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`).join(`
5774
+ `);
5775
+ throw new StrategyValidationError(`Project manifest validation failed:
5776
+ ${issues}`, { cause: result.error });
5777
+ }
5778
+ const manifest = result.data;
5779
+ const manifestDir = dirname11(manifestPath);
5780
+ if ((manifest.entry || Object.keys(manifest.tools ?? {}).length > 0 || Object.keys(manifest.flows ?? {}).length > 0) && manifest.permissions?.executesCode !== true) {
5781
+ throw new StrategyValidationError("Projects with entry, tool, or flow modules must set permissions.executesCode to true");
5782
+ }
5783
+ const strategyPaths = [];
5784
+ for (const artifact of Object.values(manifest.strategies ?? {})) {
5785
+ strategyPaths.push(await resolveProjectFile(manifestDir, artifact.path, "Strategy file"));
5786
+ }
5787
+ for (const artifact of Object.values(manifest.agents ?? {})) {
5788
+ await resolveProjectFile(manifestDir, artifact.path, "Agent file");
5789
+ }
5790
+ const usedTools = new Set;
5791
+ for (const strategyPath of strategyPaths) {
5792
+ const strategyFile = await readStrategyFile(strategyPath);
5793
+ const raw2 = strategyFile.format === "json" ? JSON.parse(stripJsonComments4(strategyFile.content)) : YAML4.parse(strategyFile.content);
5794
+ const strategy = StrategySchema.safeParse(raw2);
5795
+ if (!strategy.success) {
5796
+ throw new StrategyValidationError(`Strategy file validation failed: ${strategyPath}`, { cause: strategy.error });
5797
+ }
5798
+ for (const agent of Object.values(strategy.data.agents)) {
5799
+ if ("tools" in agent && Array.isArray(agent.tools)) {
5800
+ for (const tool of agent.tools)
5801
+ usedTools.add(tool);
5802
+ }
5803
+ }
5804
+ }
5805
+ if ([...usedTools].some((tool) => FILESYSTEM_TOOLS.has(tool)) && manifest.permissions?.filesystem !== true) {
5806
+ throw new StrategyValidationError("Project strategies use filesystem tools but permissions.filesystem is not true");
5807
+ }
5808
+ if (usedTools.has("run_command") && manifest.permissions?.shell !== true) {
5809
+ throw new StrategyValidationError("Project strategies use run_command but permissions.shell is not true");
5810
+ }
5811
+ if (usedTools.has("webfetch") && manifest.permissions?.network !== true) {
5812
+ throw new StrategyValidationError("Project strategies use webfetch but permissions.network is not true");
5813
+ }
5814
+ if (manifest.entry) {
5815
+ const entryPath = await resolveProjectFile(manifestDir, manifest.entry, "Entry file");
5816
+ await importProjectFile(entryPath, "Entry file");
5817
+ }
5818
+ if (manifest.tools) {
5819
+ for (const tool of Object.values(manifest.tools)) {
5820
+ const resolvedToolPath = await resolveProjectFile(manifestDir, tool.path, "Tool file");
5821
+ await importProjectFile(resolvedToolPath, "Tool file");
5822
+ }
5823
+ }
5824
+ if (manifest.flows) {
5825
+ for (const flow of Object.values(manifest.flows)) {
5826
+ const resolvedFlowPath = await resolveProjectFile(manifestDir, flow.path, "Flow file");
5827
+ await importProjectFile(resolvedFlowPath, "Flow file");
5828
+ }
5829
+ }
5830
+ return {
5831
+ name: manifest.name,
5832
+ version: manifest.version,
5833
+ description: manifest.description,
5834
+ manifest,
5835
+ manifestDir
5836
+ };
5837
+ }
5838
+
5105
5839
  // src/tools/built-in/launch-strategy/launch-strategy.ts
5106
- var launchStrategyParams = z10.object({
5107
- name: z10.string().min(1).describe("Strategy name as advertised by list_strategy. Must match a `name` field exactly."),
5108
- input: z10.string().describe("Initial message passed to the strategy's entry flow. Use an empty string when the entry flow is a user agent that prompts for input on its own."),
5109
- modelOverride: z10.string().optional().describe("Optional provider/model override (`providerID/modelID`) applied to every LLM agent in the sub-strategy.")
5840
+ var launchStrategyParams = z11.object({
5841
+ name: z11.string().min(1).describe("Strategy name as advertised by list_strategy. Must match a `name` field exactly."),
5842
+ input: z11.string().describe("Initial message passed to the strategy's entry flow. Use an empty string when the entry flow is a user agent that prompts for input on its own."),
5843
+ modelOverride: z11.string().optional().describe("Optional provider/model override (`providerID/modelID`) applied to every LLM agent in the sub-strategy.")
5110
5844
  });
5111
5845
  function createLaunchStrategyTool() {
5112
5846
  return defineTool({
@@ -5147,7 +5881,7 @@ function createLaunchStrategyTool() {
5147
5881
  }
5148
5882
  ],
5149
5883
  notes: [
5150
- "When multiple discovered strategies share the same name (rare \u2014 possible when the same file is reachable via multiple sources), the highest-priority match (bundled > cwd > data) is launched.",
5884
+ "When multiple discovered strategies share the same name, the highest-priority match (cwd > data > Hub packages > bundled defaults) is launched.",
5151
5885
  "Sub-strategies inherit the parent run's sandbox state; tools inside them are subject to the same policies as the caller."
5152
5886
  ]
5153
5887
  }),
@@ -5160,7 +5894,7 @@ function createLaunchStrategyTool() {
5160
5894
  } catch (discoveryError) {
5161
5895
  return errorResult(toolError("unknown", `Strategy discovery failed: ${discoveryError instanceof Error ? discoveryError.message : String(discoveryError)}`, { recoverable: false }));
5162
5896
  }
5163
- const match = discovered.strategies.find((s) => s.name === name);
5897
+ const match = discovered.strategies.find((strategy) => strategy.name === name) ?? await resolveInstalledStrategyReference(name);
5164
5898
  if (!match) {
5165
5899
  const available = discovered.strategies.map((s) => s.name);
5166
5900
  return errorResult(toolError("not_found", `No strategy named "${name}" is available. Known names: [${available.join(", ")}].`, {
@@ -5189,6 +5923,9 @@ function createLaunchStrategyTool() {
5189
5923
  }
5190
5924
  }
5191
5925
  try {
5926
+ if (match.manifestPath) {
5927
+ await loadProject(match.manifestPath);
5928
+ }
5192
5929
  const { content, format } = await readStrategyFile(match.path);
5193
5930
  let seed = input.length > 0 ? input : null;
5194
5931
  const baseCollector = toolContext.inputCollector;
@@ -5221,9 +5958,9 @@ function createLaunchStrategyTool() {
5221
5958
  });
5222
5959
  }
5223
5960
  // src/tools/built-in/list-directory/list-directory.ts
5224
- import { readdir as readdir5, stat as stat8 } from "fs/promises";
5225
- import { join as join12, relative as relative3 } from "path";
5226
- import { z as z11 } from "zod";
5961
+ import { readdir as readdir5, stat as stat11 } from "fs/promises";
5962
+ import { join as join15, relative as relative7 } from "path";
5963
+ import { z as z12 } from "zod";
5227
5964
 
5228
5965
  // src/tools/built-in/list-directory/list-directory.constants.ts
5229
5966
  var DEFAULT_ABSOLUTE_MAX_DEPTH = 32;
@@ -5231,9 +5968,9 @@ var DEFAULT_RECURSIVE_DEPTH = 8;
5231
5968
  var DEFAULT_MAX_ENTRIES = 5000;
5232
5969
 
5233
5970
  // src/tools/built-in/list-directory/list-directory.utils.ts
5234
- import { sep as sep2 } from "path";
5971
+ import { sep as sep3 } from "path";
5235
5972
  function toForwardSlash2(pathString) {
5236
- return sep2 === "/" ? pathString : pathString.split(sep2).join("/");
5973
+ return sep3 === "/" ? pathString : pathString.split(sep3).join("/");
5237
5974
  }
5238
5975
  function formatListing(data) {
5239
5976
  if (data.entries.length === 0) {
@@ -5254,11 +5991,11 @@ function typeRank(type) {
5254
5991
  }
5255
5992
 
5256
5993
  // src/tools/built-in/list-directory/list-directory.ts
5257
- var listDirectoryParams = z11.object({
5258
- path: z11.string().min(1).describe('Workspace-relative path to the directory to list. Use "." for the workspace root.'),
5259
- recursive: z11.boolean().optional().describe("Recurse into subdirectories. Defaults to false (depth=1)."),
5260
- maxDepth: z11.number().int().positive().optional().describe("Maximum recursion depth (only meaningful with recursive:true). " + "Defaults to 8; capped at 32."),
5261
- includeHidden: z11.boolean().optional().describe('Include entries whose name starts with ".". Defaults to false.')
5994
+ var listDirectoryParams = z12.object({
5995
+ path: z12.string().min(1).describe('Workspace-relative path to the directory to list. Use "." for the workspace root.'),
5996
+ recursive: z12.boolean().optional().describe("Recurse into subdirectories. Defaults to false (depth=1)."),
5997
+ maxDepth: z12.number().int().positive().optional().describe("Maximum recursion depth (only meaningful with recursive:true). " + "Defaults to 8; capped at 32."),
5998
+ includeHidden: z12.boolean().optional().describe('Include entries whose name starts with ".". Defaults to false.')
5262
5999
  });
5263
6000
  function createListDirectoryTool(config) {
5264
6001
  const absoluteMaxDepth = config?.absoluteMaxDepth ?? DEFAULT_ABSOLUTE_MAX_DEPTH;
@@ -5353,7 +6090,7 @@ The result includes \`entries: [{ name, relativePath, type, size, mtime, depth }
5353
6090
  }
5354
6091
  let rootStat;
5355
6092
  try {
5356
- rootStat = await stat8(absoluteRoot);
6093
+ rootStat = await stat11(absoluteRoot);
5357
6094
  } catch (statError) {
5358
6095
  const code = statError.code;
5359
6096
  if (code === "ENOENT" || code === "ENOTDIR") {
@@ -5406,9 +6143,9 @@ The result includes \`entries: [{ name, relativePath, type, size, mtime, depth }
5406
6143
  for (const dirent of dirents) {
5407
6144
  if (!includeHidden && dirent.name.startsWith("."))
5408
6145
  continue;
5409
- const childAbs = join12(node.absolutePath, dirent.name);
6146
+ const childAbs = join15(node.absolutePath, dirent.name);
5410
6147
  const childRelFromRoot = node.relativeFromRoot ? `${node.relativeFromRoot}/${dirent.name}` : dirent.name;
5411
- const childRelFromWorkspace = toForwardSlash2(relative3(guard.cwd, childAbs));
6148
+ const childRelFromWorkspace = toForwardSlash2(relative7(guard.cwd, childAbs));
5412
6149
  if (!guard.canAccess({
5413
6150
  type: "fs.read",
5414
6151
  resource: childRelFromWorkspace
@@ -5419,7 +6156,7 @@ The result includes \`entries: [{ name, relativePath, type, size, mtime, depth }
5419
6156
  let mtime = new Date(0).toISOString();
5420
6157
  if (type !== "symlink") {
5421
6158
  try {
5422
- const childStat = await stat8(childAbs);
6159
+ const childStat = await stat11(childAbs);
5423
6160
  size = type === "directory" ? 0 : childStat.size;
5424
6161
  mtime = childStat.mtime.toISOString();
5425
6162
  } catch {
@@ -5468,8 +6205,8 @@ The result includes \`entries: [{ name, relativePath, type, size, mtime, depth }
5468
6205
  });
5469
6206
  }
5470
6207
  // src/tools/built-in/list-skills/list-skills.ts
5471
- import { z as z12 } from "zod";
5472
- var listSkillsParams = z12.object({});
6208
+ import { z as z13 } from "zod";
6209
+ var listSkillsParams = z13.object({});
5473
6210
  function createListSkillsTool() {
5474
6211
  return defineTool({
5475
6212
  description: describeTool({
@@ -5521,14 +6258,14 @@ function createListSkillsTool() {
5521
6258
  });
5522
6259
  }
5523
6260
  // src/tools/built-in/list-strategy/list-strategy.ts
5524
- import { z as z13 } from "zod";
5525
- var listStrategyParams = z13.object({});
6261
+ import { z as z14 } from "zod";
6262
+ var listStrategyParams = z14.object({});
5526
6263
  function createListStrategyTool() {
5527
6264
  return defineTool({
5528
6265
  description: describeTool({
5529
6266
  purpose: [
5530
6267
  "List every strategy currently available to launch with launch_strategy.",
5531
- "Strategies are agent-orchestration files (JSON, JSONC, or YAML) discovered from the bundled set shipped with @comma-agents/core, the workspace `.comma/strategies/` directory, and the platform data dir."
6268
+ "Strategies are agent-orchestration files (JSON, JSONC, or YAML) discovered from the workspace `.comma/strategies/` directory, the shared `~/.comma` data directory, installed Hub packages, and the bundled defaults shipped with @comma-agents/core."
5532
6269
  ],
5533
6270
  inputs: [
5534
6271
  {
@@ -5538,7 +6275,7 @@ function createListStrategyTool() {
5538
6275
  description: "This tool takes no parameters."
5539
6276
  }
5540
6277
  ],
5541
- outputs: "`{ strategies, count, warnings }` where each strategy is `{ name, version, description?, path, origin, manifestPath?, label }`. Entries are ordered by discovery priority (bundled \u2192 cwd \u2192 data) with duplicate paths removed.",
6278
+ outputs: "`{ strategies, count, warnings }` where each strategy is `{ name, version, description?, path, origin, manifestPath?, label }`. Entries are ordered by discovery priority (cwd \u2192 data \u2192 Hub packages \u2192 bundled defaults) with duplicate paths removed.",
5542
6279
  errors: [],
5543
6280
  notes: [
5544
6281
  "Pass the `name` of any returned entry to `launch_strategy` to run it.",
@@ -5588,9 +6325,9 @@ function createListStrategyTool() {
5588
6325
  });
5589
6326
  }
5590
6327
  // src/tools/built-in/load-skill/load-skill.ts
5591
- import { z as z14 } from "zod";
5592
- var loadSkillParams = z14.object({
5593
- name: z14.string().min(1).describe("Name of the skill to load. Must match one of the names advertised in the agent's system prompt under '## Available Skills'.")
6328
+ import { z as z15 } from "zod";
6329
+ var loadSkillParams = z15.object({
6330
+ name: z15.string().min(1).describe("Name of the skill to load. Must match one of the names advertised in the agent's system prompt under '## Available Skills'.")
5594
6331
  });
5595
6332
  function createLoadSkillTool() {
5596
6333
  return defineTool({
@@ -5650,9 +6387,9 @@ ${skill.content}` : "";
5650
6387
  });
5651
6388
  }
5652
6389
  // src/tools/built-in/lsp-request/lsp-request.schema.ts
5653
- import { z as z15 } from "zod";
5654
- var lspRequestParams = z15.object({
5655
- method: z15.enum([
6390
+ import { z as z16 } from "zod";
6391
+ var lspRequestParams = z16.object({
6392
+ method: z16.enum([
5656
6393
  "textDocument/diagnostic",
5657
6394
  "textDocument/hover",
5658
6395
  "textDocument/definition",
@@ -5662,11 +6399,11 @@ var lspRequestParams = z15.object({
5662
6399
  "textDocument/documentSymbol",
5663
6400
  "workspace/symbol"
5664
6401
  ]),
5665
- languageId: z15.string().min(1).optional(),
5666
- path: z15.string().min(1).optional(),
5667
- line: z15.number().int().positive().optional(),
5668
- character: z15.number().int().positive().optional(),
5669
- query: z15.string().min(1).optional()
6402
+ languageId: z16.string().min(1).optional(),
6403
+ path: z16.string().min(1).optional(),
6404
+ line: z16.number().int().positive().optional(),
6405
+ character: z16.number().int().positive().optional(),
6406
+ query: z16.string().min(1).optional()
5670
6407
  });
5671
6408
 
5672
6409
  // src/tools/built-in/lsp-request/lsp-request.ts
@@ -5851,14 +6588,14 @@ function formatSymbol(symbol) {
5851
6588
  }
5852
6589
  // src/tools/built-in/move-file/move-file.ts
5853
6590
  import * as fsp from "fs/promises";
5854
- import { dirname as dirname8 } from "path";
5855
- import { z as z16 } from "zod";
5856
- var { copyFile, readFile: readFile7, stat: stat9, unlink: unlink3 } = fsp;
5857
- var moveFileParams = z16.object({
5858
- fromPath: z16.string().min(1).describe("Workspace-relative path of the existing file to move. Absolute paths are rejected " + "unless the sandbox is configured with `allowAbsolutePaths: true`."),
5859
- toPath: z16.string().min(1).describe("Workspace-relative destination path. Parent directories must already exist (use " + "`create_file` to create directories on the fly). Directories are never valid as " + "a destination."),
5860
- expectedSha256: z16.string().length(64).regex(/^[0-9a-f]{64}$/, "expectedSha256 must be a 64-character lowercase hex string").describe("SHA-256 of the source file's current on-disk bytes, as returned by `read_file`. " + "Required to detect concurrent edits \u2014 a mismatch yields `stale_file`."),
5861
- overwrite: z16.boolean().optional().describe("When true, an existing regular file at `toPath` is moved to the workspace trash " + "before the rename. When false (default), an existing destination yields " + "`already_exists`. Destination directories are NEVER overwritten.")
6591
+ import { dirname as dirname12 } from "path";
6592
+ import { z as z17 } from "zod";
6593
+ var { copyFile, readFile: readFile9, stat: stat12, unlink: unlink3 } = fsp;
6594
+ var moveFileParams = z17.object({
6595
+ fromPath: z17.string().min(1).describe("Workspace-relative path of the existing file to move. Absolute paths are rejected " + "unless the sandbox is configured with `allowAbsolutePaths: true`."),
6596
+ toPath: z17.string().min(1).describe("Workspace-relative destination path. Parent directories must already exist (use " + "`create_file` to create directories on the fly). Directories are never valid as " + "a destination."),
6597
+ expectedSha256: z17.string().length(64).regex(/^[0-9a-f]{64}$/, "expectedSha256 must be a 64-character lowercase hex string").describe("SHA-256 of the source file's current on-disk bytes, as returned by `read_file`. " + "Required to detect concurrent edits \u2014 a mismatch yields `stale_file`."),
6598
+ overwrite: z17.boolean().optional().describe("When true, an existing regular file at `toPath` is moved to the workspace trash " + "before the rename. When false (default), an existing destination yields " + "`already_exists`. Destination directories are NEVER overwritten.")
5862
6599
  });
5863
6600
  function createMoveFileTool(config) {
5864
6601
  const defaultSink = config?.defaultAuditSink;
@@ -5983,7 +6720,7 @@ After every \`move_file\` call, **run the project's configured verifier with \`r
5983
6720
  }
5984
6721
  let fromStat;
5985
6722
  try {
5986
- fromStat = await stat9(fromAbs);
6723
+ fromStat = await stat12(fromAbs);
5987
6724
  } catch (statError) {
5988
6725
  const code = statError.code;
5989
6726
  if (code === "ENOENT" || code === "ENOTDIR") {
@@ -5997,7 +6734,7 @@ After every \`move_file\` call, **run the project's configured verifier with \`r
5997
6734
  if (fromStat.isDirectory()) {
5998
6735
  return errorResult(toolError("not_found", `Source path is a directory, not a file: ${validatedArguments.fromPath}`, { path: validatedArguments.fromPath, recoverable: false }));
5999
6736
  }
6000
- const beforeBytes = await readFile7(fromAbs);
6737
+ const beforeBytes = await readFile9(fromAbs);
6001
6738
  const beforeSha256 = sha256OfBuffer(beforeBytes);
6002
6739
  if (beforeSha256 !== validatedArguments.expectedSha256) {
6003
6740
  return errorResult(toolError("stale_file", `File has changed since last read: ${validatedArguments.fromPath}`, {
@@ -6012,7 +6749,7 @@ After every \`move_file\` call, **run the project's configured verifier with \`r
6012
6749
  }
6013
6750
  let overwroteTrashedTo;
6014
6751
  try {
6015
- const toStat = await stat9(toAbs);
6752
+ const toStat = await stat12(toAbs);
6016
6753
  if (toStat.isDirectory()) {
6017
6754
  return errorResult(toolError("already_exists", `Destination is a directory: ${validatedArguments.toPath}`, { path: validatedArguments.toPath, recoverable: false }));
6018
6755
  }
@@ -6032,9 +6769,9 @@ After every \`move_file\` call, **run the project's configured verifier with \`r
6032
6769
  const code = caughtError.code;
6033
6770
  if (code !== "ENOENT" && code !== "ENOTDIR")
6034
6771
  throw caughtError;
6035
- const parent = dirname8(toAbs);
6772
+ const parent = dirname12(toAbs);
6036
6773
  try {
6037
- const parentStat = await stat9(parent);
6774
+ const parentStat = await stat12(parent);
6038
6775
  if (!parentStat.isDirectory()) {
6039
6776
  return errorResult(toolError("not_found", `Destination parent is not a directory: ${validatedArguments.toPath}`, { path: validatedArguments.toPath, recoverable: false }));
6040
6777
  }
@@ -6114,8 +6851,8 @@ After every \`move_file\` call, **run the project's configured verifier with \`r
6114
6851
  });
6115
6852
  }
6116
6853
  // src/tools/built-in/read-file/read-file.ts
6117
- import { stat as stat10 } from "fs/promises";
6118
- import { z as z17 } from "zod";
6854
+ import { stat as stat13 } from "fs/promises";
6855
+ import { z as z18 } from "zod";
6119
6856
 
6120
6857
  // src/tools/built-in/read-file/read-file.constants.ts
6121
6858
  var DEFAULT_MAX_BYTES = 256 * 1024;
@@ -6138,12 +6875,12 @@ function truncateUtf8(content, maxBytes) {
6138
6875
  }
6139
6876
 
6140
6877
  // src/tools/built-in/read-file/read-file.ts
6141
- var readFileParams = z17.object({
6142
- path: z17.string().min(1).describe("Workspace-relative path to the file. Absolute paths are rejected unless the sandbox " + "is configured with `allowAbsolutePaths: true`."),
6143
- startLine: z17.number().int().positive().optional().describe("1-indexed inclusive line to start at. Defaults to 1."),
6144
- endLine: z17.number().int().positive().optional().describe("1-indexed inclusive line to end at. Clamped to the file's lineCount when larger. " + "Defaults to the end of the file."),
6145
- maxBytes: z17.number().int().positive().optional().describe("Maximum UTF-8 byte length of the returned content. When the slice exceeds this, " + "content is truncated and `data.truncated` is set to true. Defaults to 262144."),
6146
- allowBinary: z17.boolean().optional().describe("Opt in to receiving the contents of a binary file as base64. Required for any file " + "whose first 8 KiB contains a NUL byte. Defaults to false.")
6878
+ var readFileParams = z18.object({
6879
+ path: z18.string().min(1).describe("Workspace-relative path to the file. Absolute paths are rejected unless the sandbox " + "is configured with `allowAbsolutePaths: true`."),
6880
+ startLine: z18.number().int().positive().optional().describe("1-indexed inclusive line to start at. Defaults to 1."),
6881
+ endLine: z18.number().int().positive().optional().describe("1-indexed inclusive line to end at. Clamped to the file's lineCount when larger. " + "Defaults to the end of the file."),
6882
+ maxBytes: z18.number().int().positive().optional().describe("Maximum UTF-8 byte length of the returned content. When the slice exceeds this, " + "content is truncated and `data.truncated` is set to true. Defaults to 262144."),
6883
+ allowBinary: z18.boolean().optional().describe("Opt in to receiving the contents of a binary file as base64. Required for any file " + "whose first 8 KiB contains a NUL byte. Defaults to false.")
6147
6884
  });
6148
6885
  function createReadFileTool(config) {
6149
6886
  const defaultMaxBytes = config?.defaultMaxBytes ?? DEFAULT_MAX_BYTES;
@@ -6250,7 +6987,7 @@ If a subsequent edit/write returns \`stale_file\`, the file changed under you \u
6250
6987
  }
6251
6988
  let statResult;
6252
6989
  try {
6253
- statResult = await stat10(absolutePath);
6990
+ statResult = await stat13(absolutePath);
6254
6991
  } catch (statError) {
6255
6992
  const code = statError.code;
6256
6993
  if (code === "ENOENT" || code === "ENOTDIR") {
@@ -6356,11 +7093,11 @@ ${content}` : ""), { data });
6356
7093
  });
6357
7094
  }
6358
7095
  // src/tools/built-in/restore-file/restore-file.ts
6359
- import { stat as stat11 } from "fs/promises";
6360
- import { z as z18 } from "zod";
6361
- var restoreFileParams = z18.object({
6362
- trashedPath: z18.string().min(1).describe("Absolute path to the .tar.gz trash archive. Obtain from a previous `delete_file` " + "result's `trashedTo` field or from the audit log."),
6363
- targetPath: z18.string().optional().describe("Optional workspace-relative path for the restored file. If omitted, the file is " + "restored to its original path (as recorded in the archive's metadata).")
7096
+ import { stat as stat14 } from "fs/promises";
7097
+ import { z as z19 } from "zod";
7098
+ var restoreFileParams = z19.object({
7099
+ trashedPath: z19.string().min(1).describe("Absolute path to the .tar.gz trash archive. Obtain from a previous `delete_file` " + "result's `trashedTo` field or from the audit log."),
7100
+ targetPath: z19.string().optional().describe("Optional workspace-relative path for the restored file. If omitted, the file is " + "restored to its original path (as recorded in the archive's metadata).")
6364
7101
  });
6365
7102
  function createRestoreFileTool() {
6366
7103
  return defineTool({
@@ -6413,7 +7150,7 @@ function createRestoreFileTool() {
6413
7150
  }));
6414
7151
  }
6415
7152
  try {
6416
- await stat11(validatedArguments.trashedPath);
7153
+ await stat14(validatedArguments.trashedPath);
6417
7154
  } catch {
6418
7155
  return errorResult(toolError("not_found", `Trash archive not found: ${validatedArguments.trashedPath}`, { path: validatedArguments.trashedPath, recoverable: false }));
6419
7156
  }
@@ -6436,7 +7173,7 @@ function createRestoreFileTool() {
6436
7173
  throw caught;
6437
7174
  }
6438
7175
  try {
6439
- await stat11(targetAbsolute);
7176
+ await stat14(targetAbsolute);
6440
7177
  return errorResult(toolError("already_exists", `Restore target already exists: ${targetPath}`, {
6441
7178
  path: targetPath,
6442
7179
  recoverable: true,
@@ -6450,7 +7187,7 @@ function createRestoreFileTool() {
6450
7187
  let restoredAbsolute;
6451
7188
  try {
6452
7189
  restoredAbsolute = await restoreFromTrash(guard.cwd, validatedArguments.trashedPath, targetPath);
6453
- const contentStat = await stat11(restoredAbsolute);
7190
+ const contentStat = await stat14(restoredAbsolute);
6454
7191
  const sizeBytes = contentStat.size;
6455
7192
  return okResult(`Restored ${validatedArguments.trashedPath} to ${restoredAbsolute} (${sizeBytes} bytes)`, {
6456
7193
  data: {
@@ -6472,7 +7209,7 @@ function createRestoreFileTool() {
6472
7209
  }
6473
7210
  // src/tools/built-in/run-command/run-command.ts
6474
7211
  import { spawn } from "child_process";
6475
- import { z as z19 } from "zod";
7212
+ import { z as z20 } from "zod";
6476
7213
 
6477
7214
  // src/tools/built-in/run-command/run-command.constants.ts
6478
7215
  var RUN_COMMAND_DEFAULT_TIMEOUT_MS = 60000;
@@ -6496,8 +7233,8 @@ var RUN_COMMAND_TRUNCATION_MARKER = `
6496
7233
 
6497
7234
  // src/tools/built-in/run-command/run-command.utils.ts
6498
7235
  import { release } from "os";
6499
- function friendlyOsName(platform2) {
6500
- switch (platform2) {
7236
+ function friendlyOsName(platform) {
7237
+ switch (platform) {
6501
7238
  case "darwin":
6502
7239
  return "macOS";
6503
7240
  case "linux":
@@ -6513,11 +7250,11 @@ function friendlyOsName(platform2) {
6513
7250
  case "aix":
6514
7251
  return "AIX";
6515
7252
  default:
6516
- return platform2;
7253
+ return platform;
6517
7254
  }
6518
7255
  }
6519
- function resolveShell(platform2, env = process.env) {
6520
- if (platform2 === "win32") {
7256
+ function resolveShell(platform, env = process.env) {
7257
+ if (platform === "win32") {
6521
7258
  return {
6522
7259
  shellPath: env.ComSpec ?? "cmd.exe",
6523
7260
  shellFlag: "/d /s /c"
@@ -6529,13 +7266,13 @@ function resolveShell(platform2, env = process.env) {
6529
7266
  };
6530
7267
  }
6531
7268
  function detectPlatformInfo() {
6532
- const platform2 = process.platform;
6533
- const { shellPath, shellFlag } = resolveShell(platform2);
7269
+ const platform = process.platform;
7270
+ const { shellPath, shellFlag } = resolveShell(platform);
6534
7271
  const bunVersion = typeof globalThis.Bun !== "undefined" ? globalThis.Bun.version : undefined;
6535
7272
  const runtime = bunVersion ? `bun ${bunVersion}` : `node ${process.versions.node}`;
6536
7273
  return {
6537
- platform: platform2,
6538
- osName: friendlyOsName(platform2),
7274
+ platform,
7275
+ osName: friendlyOsName(platform),
6539
7276
  osRelease: release(),
6540
7277
  arch: process.arch,
6541
7278
  shellPath,
@@ -6612,11 +7349,11 @@ function truncateOutput(buffer, maxBytes) {
6612
7349
  }
6613
7350
 
6614
7351
  // src/tools/built-in/run-command/run-command.ts
6615
- var runCommandParams = z19.object({
6616
- command: z19.string().min(1).describe("Shell command line to execute. Runs through the host's default shell \u2014 see the tool description for the resolved shell path."),
6617
- cwd: z19.string().optional().describe("Workspace-relative working directory. Defaults to the workspace root. Must resolve inside the sandbox or `outside_workspace` is returned."),
6618
- timeoutMs: z19.number().int().positive().max(RUN_COMMAND_MAX_TIMEOUT_MS).optional().describe(`Kill the process after this many milliseconds. Defaults to ${RUN_COMMAND_DEFAULT_TIMEOUT_MS}; hard-capped at ${RUN_COMMAND_MAX_TIMEOUT_MS}.`),
6619
- env: z19.record(z19.string(), z19.string()).optional().describe("Extra environment variables, merged on top of the daemon's environment. Existing variables are overwritten by entries supplied here; nothing is removed.")
7352
+ var runCommandParams = z20.object({
7353
+ command: z20.string().min(1).describe("Shell command line to execute. Runs through the host's default shell \u2014 see the tool description for the resolved shell path."),
7354
+ cwd: z20.string().optional().describe("Workspace-relative working directory. Defaults to the workspace root. Must resolve inside the sandbox or `outside_workspace` is returned."),
7355
+ timeoutMs: z20.number().int().positive().max(RUN_COMMAND_MAX_TIMEOUT_MS).optional().describe(`Kill the process after this many milliseconds. Defaults to ${RUN_COMMAND_DEFAULT_TIMEOUT_MS}; hard-capped at ${RUN_COMMAND_MAX_TIMEOUT_MS}.`),
7356
+ env: z20.record(z20.string(), z20.string()).optional().describe("Extra environment variables, merged on top of the daemon's environment. Existing variables are overwritten by entries supplied here; nothing is removed.")
6620
7357
  });
6621
7358
  function formatOutputWithCapturedStreams(summaryParts, data) {
6622
7359
  const outputParts = [...summaryParts];
@@ -6875,9 +7612,9 @@ function createRunCommandTool(config) {
6875
7612
  });
6876
7613
  }
6877
7614
  // src/tools/built-in/search-files/search-files.ts
6878
- import { readdir as readdir6, stat as stat12 } from "fs/promises";
6879
- import { join as join13, relative as relative4 } from "path";
6880
- import { z as z20 } from "zod";
7615
+ import { readdir as readdir6, stat as stat15 } from "fs/promises";
7616
+ import { join as join16, relative as relative8 } from "path";
7617
+ import { z as z21 } from "zod";
6881
7618
 
6882
7619
  // src/tools/built-in/search-files/search-files.constants.ts
6883
7620
  var DEFAULT_EXCLUDE_GLOBS2 = [
@@ -6895,9 +7632,9 @@ var DEFAULT_MAX_FILE_BYTES = 4 * 1024 * 1024;
6895
7632
  var DEFAULT_TRAVERSAL_DEPTH2 = 32;
6896
7633
 
6897
7634
  // src/tools/built-in/search-files/search-files.utils.ts
6898
- import { sep as sep3 } from "path";
7635
+ import { sep as sep4 } from "path";
6899
7636
  function toForwardSlash3(pathString) {
6900
- return sep3 === "/" ? pathString : pathString.split(sep3).join("/");
7637
+ return sep4 === "/" ? pathString : pathString.split(sep4).join("/");
6901
7638
  }
6902
7639
  function matchesAnyGlob2(relPath, patterns) {
6903
7640
  for (const pattern of patterns) {
@@ -6930,14 +7667,14 @@ function compileRegex(query) {
6930
7667
  }
6931
7668
 
6932
7669
  // src/tools/built-in/search-files/search-files.ts
6933
- var searchFilesParams = z20.object({
6934
- query: z20.string().min(1).describe("Search query. Interpretation depends on `mode`."),
6935
- mode: z20.enum(["path", "text", "regex"]).describe('Search mode. "path" \u2192 glob-match file paths. "text" \u2192 literal substring search in ' + 'file contents. "regex" \u2192 JavaScript regex search in file contents (compiled with the "m" flag).'),
6936
- root: z20.string().optional().describe('Workspace-relative directory to search under. Defaults to ".".'),
6937
- includeGlobs: z20.array(z20.string()).optional().describe("When set, only paths matching at least one of these Bun.Glob patterns are considered."),
6938
- excludeGlobs: z20.array(z20.string()).optional().describe("Bun.Glob patterns to exclude. Replaces the default exclude set " + "(node_modules, .git, dist, build, .next, .turbo, coverage). Pass [] to disable."),
6939
- maxResults: z20.number().int().positive().optional().describe("Maximum number of matches to return. Defaults to 100."),
6940
- contextLines: z20.number().int().min(0).optional().describe("Number of context lines to include on either side of each text/regex match. Defaults to 0.")
7670
+ var searchFilesParams = z21.object({
7671
+ query: z21.string().min(1).describe("Search query. Interpretation depends on `mode`."),
7672
+ mode: z21.enum(["path", "text", "regex"]).describe('Search mode. "path" \u2192 glob-match file paths. "text" \u2192 literal substring search in ' + 'file contents. "regex" \u2192 JavaScript regex search in file contents (compiled with the "m" flag).'),
7673
+ root: z21.string().optional().describe('Workspace-relative directory to search under. Defaults to ".".'),
7674
+ includeGlobs: z21.array(z21.string()).optional().describe("When set, only paths matching at least one of these Bun.Glob patterns are considered."),
7675
+ excludeGlobs: z21.array(z21.string()).optional().describe("Bun.Glob patterns to exclude. Replaces the default exclude set " + "(node_modules, .git, dist, build, .next, .turbo, coverage). Pass [] to disable."),
7676
+ maxResults: z21.number().int().positive().optional().describe("Maximum number of matches to return. Defaults to 100."),
7677
+ contextLines: z21.number().int().min(0).optional().describe("Number of context lines to include on either side of each text/regex match. Defaults to 0.")
6941
7678
  });
6942
7679
  function createSearchFilesTool(config) {
6943
7680
  const maxResultsCap = config?.maxResults ?? DEFAULT_MAX_RESULTS2;
@@ -7075,7 +7812,7 @@ function createSearchFilesTool(config) {
7075
7812
  }
7076
7813
  let rootStat;
7077
7814
  try {
7078
- rootStat = await stat12(absoluteRoot);
7815
+ rootStat = await stat15(absoluteRoot);
7079
7816
  } catch (statError) {
7080
7817
  const code = statError.code;
7081
7818
  if (code === "ENOENT" || code === "ENOTDIR") {
@@ -7115,9 +7852,9 @@ function createSearchFilesTool(config) {
7115
7852
  for (const dirent of dirents) {
7116
7853
  if (abort.aborted)
7117
7854
  break outer;
7118
- const childAbs = join13(node.absolutePath, dirent.name);
7119
- const relFromRoot = toForwardSlash3(relative4(absoluteRoot, childAbs));
7120
- const relFromWorkspace = toForwardSlash3(relative4(guard.cwd, childAbs));
7855
+ const childAbs = join16(node.absolutePath, dirent.name);
7856
+ const relFromRoot = toForwardSlash3(relative8(absoluteRoot, childAbs));
7857
+ const relFromWorkspace = toForwardSlash3(relative8(guard.cwd, childAbs));
7121
7858
  if (matchesAnyGlob2(relFromWorkspace, excludeGlobs))
7122
7859
  continue;
7123
7860
  if (dirent.isDirectory()) {
@@ -7143,7 +7880,7 @@ function createSearchFilesTool(config) {
7143
7880
  }
7144
7881
  let childStat;
7145
7882
  try {
7146
- childStat = await stat12(childAbs);
7883
+ childStat = await stat15(childAbs);
7147
7884
  } catch {
7148
7885
  continue;
7149
7886
  }
@@ -7222,7 +7959,7 @@ ${lines.join(`
7222
7959
  });
7223
7960
  }
7224
7961
  // src/tools/built-in/todo/todo.ts
7225
- import { z as z21 } from "zod";
7962
+ import { z as z22 } from "zod";
7226
7963
  var todoStore = new Map;
7227
7964
  var idCounters = new Map;
7228
7965
  function storeKey(context) {
@@ -7250,8 +7987,8 @@ function resetTodoStateForAgent(key) {
7250
7987
  todoStore.delete(key);
7251
7988
  idCounters.delete(key);
7252
7989
  }
7253
- var todoAddParams = z21.object({
7254
- content: z21.string().min(1).describe("Description of the todo item to add.")
7990
+ var todoAddParams = z22.object({
7991
+ content: z22.string().min(1).describe("Description of the todo item to add.")
7255
7992
  });
7256
7993
  function createTodoAddTool() {
7257
7994
  return defineTool({
@@ -7273,8 +8010,8 @@ function createTodoAddTool() {
7273
8010
  }
7274
8011
  });
7275
8012
  }
7276
- var todoRemoveParams = z21.object({
7277
- id: z21.string().min(1).describe("The id of the todo item to remove from the list.")
8013
+ var todoRemoveParams = z22.object({
8014
+ id: z22.string().min(1).describe("The id of the todo item to remove from the list.")
7278
8015
  });
7279
8016
  function createTodoRemoveTool() {
7280
8017
  return defineTool({
@@ -7301,8 +8038,8 @@ function createTodoRemoveTool() {
7301
8038
  }
7302
8039
  });
7303
8040
  }
7304
- var todoCompleteParams = z21.object({
7305
- id: z21.string().min(1).describe("The id of the todo item to mark as completed.")
8041
+ var todoCompleteParams = z22.object({
8042
+ id: z22.string().min(1).describe("The id of the todo item to mark as completed.")
7306
8043
  });
7307
8044
  function createTodoCompleteTool() {
7308
8045
  return defineTool({
@@ -7333,7 +8070,7 @@ function createTodoCompleteTool() {
7333
8070
  }
7334
8071
  });
7335
8072
  }
7336
- var todoGetParams = z21.object({});
8073
+ var todoGetParams = z22.object({});
7337
8074
  function createTodoGetTool() {
7338
8075
  return defineTool({
7339
8076
  description: "Get all todo items for the current run, including completed and pending ones. " + "Use this to review progress or remember outstanding tasks.",
@@ -7354,7 +8091,7 @@ function createTodoGetTool() {
7354
8091
  }
7355
8092
  });
7356
8093
  }
7357
- var todoGetNextParams = z21.object({});
8094
+ var todoGetNextParams = z22.object({});
7358
8095
  function createTodoGetNextTool() {
7359
8096
  return defineTool({
7360
8097
  description: "Get the next pending todo item without modifying the list. " + "Returns a message indicating no pending items if the list is exhausted.",
@@ -7373,7 +8110,7 @@ function createTodoGetNextTool() {
7373
8110
  }
7374
8111
  });
7375
8112
  }
7376
- var todoClearParams = z21.object({});
8113
+ var todoClearParams = z22.object({});
7377
8114
  function createTodoClearTool() {
7378
8115
  return defineTool({
7379
8116
  description: "Clear all todo items for the current run. **WARNING: This destroys all progress tracking and should almost never be used.** " + "Only use when the entire goal has fundamentally changed and every existing item is obsolete. " + "For normal pivots, partial changes, or when some items become irrelevant, use todo_remove for specific obsolete items and todo_add for new ones. " + "Clearing loses the history of what was completed and what remains \u2014 this information is valuable for tracking progress and avoiding repeated work.",
@@ -7390,7 +8127,7 @@ function createTodoClearTool() {
7390
8127
  }
7391
8128
 
7392
8129
  // src/tools/built-in/webfetch/webfetch.ts
7393
- import { z as z22 } from "zod";
8130
+ import { z as z23 } from "zod";
7394
8131
 
7395
8132
  // src/tools/built-in/webfetch/webfetch.constants.ts
7396
8133
  var DEFAULT_TIMEOUT_SECONDS = 30;
@@ -7437,10 +8174,10 @@ function transformContent(html, format) {
7437
8174
  }
7438
8175
 
7439
8176
  // src/tools/built-in/webfetch/webfetch.ts
7440
- var webFetchParams = z22.object({
7441
- url: z22.string().url().describe("The fully-qualified URL to fetch."),
7442
- format: z22.enum(["text", "markdown", "html"]).optional().default("markdown").describe('Output format: "markdown" (HTML converted to Markdown), "text" (plain text only), ' + 'or "html" (raw HTML). Defaults to "markdown".'),
7443
- timeout: z22.number().positive().optional().describe("Optional request timeout in seconds. Defaults to 30.")
8177
+ var webFetchParams = z23.object({
8178
+ url: z23.string().url().describe("The fully-qualified URL to fetch."),
8179
+ format: z23.enum(["text", "markdown", "html"]).optional().default("markdown").describe('Output format: "markdown" (HTML converted to Markdown), "text" (plain text only), ' + 'or "html" (raw HTML). Defaults to "markdown".'),
8180
+ timeout: z23.number().positive().optional().describe("Optional request timeout in seconds. Defaults to 30.")
7444
8181
  });
7445
8182
  function createWebFetchTool(config) {
7446
8183
  const defaultTimeout = config?.defaultTimeout ?? DEFAULT_TIMEOUT_SECONDS;
@@ -7520,12 +8257,12 @@ function createWebFetchTool(config) {
7520
8257
  }
7521
8258
 
7522
8259
  // src/tools/built-in/write-file/write-file.ts
7523
- import { readFile as readFile8, stat as stat13 } from "fs/promises";
7524
- import { z as z23 } from "zod";
7525
- var writeFileParams = z23.object({
7526
- path: z23.string().min(1).describe("Workspace-relative path of the file to replace. Absolute paths are rejected unless " + "the sandbox is configured with `allowAbsolutePaths: true`."),
7527
- content: z23.string().describe("Full UTF-8 replacement content. Provide logical content with LF newlines and no BOM; " + "the tool re-applies the file's existing newline style and BOM on write."),
7528
- expectedSha256: z23.string().length(64).regex(/^[0-9a-f]{64}$/, "expectedSha256 must be a 64-character lowercase hex string").describe("SHA-256 of the file's current on-disk bytes, as returned by `read_file`. " + "Required to detect concurrent edits \u2014 a mismatch yields `stale_file`.")
8260
+ import { readFile as readFile10, stat as stat16 } from "fs/promises";
8261
+ import { z as z24 } from "zod";
8262
+ var writeFileParams = z24.object({
8263
+ path: z24.string().min(1).describe("Workspace-relative path of the file to replace. Absolute paths are rejected unless " + "the sandbox is configured with `allowAbsolutePaths: true`."),
8264
+ content: z24.string().describe("Full UTF-8 replacement content. Provide logical content with LF newlines and no BOM; " + "the tool re-applies the file's existing newline style and BOM on write."),
8265
+ expectedSha256: z24.string().length(64).regex(/^[0-9a-f]{64}$/, "expectedSha256 must be a 64-character lowercase hex string").describe("SHA-256 of the file's current on-disk bytes, as returned by `read_file`. " + "Required to detect concurrent edits \u2014 a mismatch yields `stale_file`.")
7529
8266
  });
7530
8267
  function createWriteFileTool(config) {
7531
8268
  const defaultSink = config?.defaultAuditSink;
@@ -7631,7 +8368,7 @@ After every successful \`write_file\` call, **run the project's configured verif
7631
8368
  }
7632
8369
  let targetStat;
7633
8370
  try {
7634
- targetStat = await stat13(absolutePath);
8371
+ targetStat = await stat16(absolutePath);
7635
8372
  } catch (statError) {
7636
8373
  const code = statError.code;
7637
8374
  if (code === "ENOENT" || code === "ENOTDIR") {
@@ -7649,7 +8386,7 @@ After every successful \`write_file\` call, **run the project's configured verif
7649
8386
  recoverable: false
7650
8387
  }));
7651
8388
  }
7652
- const beforeBytes = await readFile8(absolutePath);
8389
+ const beforeBytes = await readFile10(absolutePath);
7653
8390
  const beforeSha256 = sha256OfBuffer(beforeBytes);
7654
8391
  if (beforeSha256 !== validatedArguments.expectedSha256) {
7655
8392
  return errorResult(toolError("stale_file", `File has changed since last read: ${validatedArguments.path}`, {
@@ -8214,8 +8951,15 @@ function hookIntoAgent(agent, hooks) {
8214
8951
  }
8215
8952
  }
8216
8953
  // src/agents/loader/loader.ts
8954
+ import { dirname as dirname13 } from "path";
8217
8955
  import { jsonSchema as jsonSchema2 } from "ai";
8218
- import YAML4 from "yaml";
8956
+ import YAML5 from "yaml";
8957
+ function formatValidationIssue(issue) {
8958
+ if (issue.code === "invalid_union") {
8959
+ return issue.unionErrors.flatMap((unionError) => unionError.issues.flatMap(formatValidationIssue));
8960
+ }
8961
+ return [` - ${issue.path.join(".")}: ${issue.message}`];
8962
+ }
8219
8963
  async function loadAgent(filePath, options = {}) {
8220
8964
  const fileExtension = filePath.split(".").pop()?.toLowerCase();
8221
8965
  let format;
@@ -8231,22 +8975,25 @@ async function loadAgent(filePath, options = {}) {
8231
8975
  throw new StrategyValidationError(`Agent description file not found: ${filePath}`);
8232
8976
  }
8233
8977
  const content = await file.text();
8234
- return await loadAgentFromString(content, format, options);
8978
+ return await loadAgentFromString(content, format, {
8979
+ ...options,
8980
+ strategyDir: options.strategyDir ?? dirname13(filePath)
8981
+ });
8235
8982
  }
8236
- async function loadAgentFromString(content, format, _options = {}) {
8983
+ async function loadAgentFromString(content, format, options = {}) {
8237
8984
  let raw;
8238
8985
  try {
8239
8986
  if (format === "json") {
8240
8987
  raw = JSON.parse(content);
8241
8988
  } else {
8242
- raw = YAML4.parse(content);
8989
+ raw = YAML5.parse(content);
8243
8990
  }
8244
8991
  } catch (parseError) {
8245
8992
  throw new StrategyValidationError(`Failed to parse agent description ${format.toUpperCase()}: ${parseError instanceof Error ? parseError.message : String(parseError)}`, { cause: parseError });
8246
8993
  }
8247
8994
  const result = AgentDescriptionSchema.safeParse(raw);
8248
8995
  if (!result.success) {
8249
- const issues = result.error.issues.map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`).join(`
8996
+ const issues = result.error.issues.flatMap(formatValidationIssue).join(`
8250
8997
  `);
8251
8998
  throw new StrategyValidationError(`Agent description validation failed:
8252
8999
  ${issues}`, {
@@ -8254,59 +9001,40 @@ ${issues}`, {
8254
9001
  });
8255
9002
  }
8256
9003
  const description = result.data;
9004
+ if (description.type && description.type !== "llm") {
9005
+ const registeredAgent = resolveRegisteredAgent(description.type);
9006
+ if (!registeredAgent) {
9007
+ throw new StrategyValidationError(`Agent "${description.name}" references unknown agent type "${description.type}". ` + `Built-in agents: [${BUILT_IN_AGENT_NAMES.join(", ")}]. ` + `Registered agents: [${getRegisteredAgentNames().join(", ") || "(none)"}].`);
9008
+ }
9009
+ return await registeredAgent.create({
9010
+ name: description.name,
9011
+ config: description.config ?? {},
9012
+ runtime: options
9013
+ });
9014
+ }
8257
9015
  const systemPrompt = description.systemPromptTemplate ? createPromptTemplate({
8258
9016
  template: description.systemPromptTemplate.template,
8259
9017
  variables: description.systemPromptTemplate.variables
8260
9018
  }) : description.systemPrompt;
8261
9019
  return createAgent({
8262
9020
  name: description.name,
8263
- model: description.model,
9021
+ model: options.modelOverride ?? description.model,
8264
9022
  systemPrompt,
8265
9023
  tools: description.tools,
8266
9024
  maxSteps: description.maxSteps,
8267
9025
  ...description.providerOptions ? { providerOptions: description.providerOptions } : {},
8268
9026
  ...description.modelOptions ? { modelOptions: description.modelOptions } : {},
8269
9027
  ...description.outputSchema ? { outputSchema: jsonSchema2(description.outputSchema) } : {},
8270
- ...description.context ? { context: description.context } : {}
9028
+ ...description.context ? { context: description.context } : {},
9029
+ ...options.skillRegistry ? { skillRegistry: options.skillRegistry } : {},
9030
+ ...options.inputCollector ? { inputCollector: options.inputCollector } : {},
9031
+ ...options.launchStrategy ? { launchStrategy: options.launchStrategy } : {},
9032
+ ...options.languageService ? { languageService: options.languageService } : {},
9033
+ ...options.runId ? { runId: options.runId } : {}
8271
9034
  });
8272
9035
  }
8273
9036
  // src/flows/loader/loader.ts
8274
- import YAML5 from "yaml";
8275
-
8276
- // src/flows/loader/loader.schema.ts
8277
- import { z as z24 } from "zod";
8278
- var AgentStepDescriptionSchema = z24.object({
8279
- agent: z24.string().min(1),
8280
- description: z24.string().optional()
8281
- }).strict();
8282
- var FlowStepDescriptionSchema = z24.lazy(() => z24.union([AgentStepDescriptionSchema, FlowDescriptionSchema]));
8283
- var BaseFlowDescriptionFields = {
8284
- name: z24.string().min(1),
8285
- description: z24.string().optional(),
8286
- steps: z24.array(FlowStepDescriptionSchema).min(1)
8287
- };
8288
- var SequentialFlowDescriptionSchema = z24.object({
8289
- ...BaseFlowDescriptionFields,
8290
- type: z24.literal("sequential")
8291
- }).strict();
8292
- var CycleFlowDescriptionSchema = z24.object({
8293
- ...BaseFlowDescriptionFields,
8294
- type: z24.literal("cycle"),
8295
- cycles: z24.union([z24.number().int().positive(), z24.literal("Infinity")]).optional(),
8296
- observer: z24.string().optional()
8297
- }).strict();
8298
- var BroadcastFlowDescriptionSchema = z24.object({
8299
- ...BaseFlowDescriptionFields,
8300
- type: z24.literal("broadcast"),
8301
- separator: z24.string().optional()
8302
- }).strict();
8303
- var FlowDescriptionSchema = z24.discriminatedUnion("type", [
8304
- SequentialFlowDescriptionSchema,
8305
- CycleFlowDescriptionSchema,
8306
- BroadcastFlowDescriptionSchema
8307
- ]);
8308
-
8309
- // src/flows/loader/loader.ts
9037
+ import YAML6 from "yaml";
8310
9038
  async function loadFlow(filePath, options) {
8311
9039
  const fileExtension = filePath.split(".").pop()?.toLowerCase();
8312
9040
  let format;
@@ -8330,12 +9058,12 @@ function loadFlowFromString(content, format, options) {
8330
9058
  if (format === "json") {
8331
9059
  raw = JSON.parse(content);
8332
9060
  } else {
8333
- raw = YAML5.parse(content);
9061
+ raw = YAML6.parse(content);
8334
9062
  }
8335
9063
  } catch (parseError) {
8336
9064
  throw new StrategyValidationError(`Failed to parse flow description ${format.toUpperCase()}: ${parseError instanceof Error ? parseError.message : String(parseError)}`, { cause: parseError });
8337
9065
  }
8338
- const result = FlowDescriptionSchema.safeParse(raw);
9066
+ const result = FlowDefSchema.safeParse(raw);
8339
9067
  if (!result.success) {
8340
9068
  const issues = result.error.issues.map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`).join(`
8341
9069
  `);
@@ -8347,65 +9075,6 @@ ${issues}`, {
8347
9075
  const description = result.data;
8348
9076
  return buildFlowFromDescription(description, options);
8349
9077
  }
8350
- function buildFlowFromDescription(description, options) {
8351
- const steps = resolveSteps2(description.steps, description.name, options);
8352
- let flow;
8353
- switch (description.type) {
8354
- case "sequential":
8355
- flow = createSequentialFlow({
8356
- name: description.name,
8357
- steps
8358
- });
8359
- break;
8360
- case "cycle": {
8361
- const cycles = description.cycles === "Infinity" ? Infinity : description.cycles ?? 1;
8362
- const observer = description.observer ? resolveAgentReference(description.observer, description.name, options.agents) : undefined;
8363
- flow = createCycleFlow({
8364
- name: description.name,
8365
- steps,
8366
- cycles,
8367
- observer
8368
- });
8369
- break;
8370
- }
8371
- case "broadcast":
8372
- flow = createBroadcastFlow({
8373
- name: description.name,
8374
- steps,
8375
- separator: description.separator
8376
- });
8377
- break;
8378
- }
8379
- if (options.flowHooks) {
8380
- hookIntoFlow(flow, options.flowHooks);
8381
- }
8382
- return flow;
8383
- }
8384
- function isAgentStep2(step) {
8385
- return typeof step === "object" && step !== null && "agent" in step && typeof step.agent === "string";
8386
- }
8387
- function isNestedFlowStep(step) {
8388
- return typeof step === "object" && step !== null && "type" in step && "steps" in step && typeof step.name === "string";
8389
- }
8390
- function resolveSteps2(steps, flowName, options) {
8391
- return steps.map((step, stepIndex) => {
8392
- if (isAgentStep2(step)) {
8393
- return resolveAgentReference(step.agent, flowName, options.agents);
8394
- }
8395
- if (isNestedFlowStep(step)) {
8396
- return buildFlowFromDescription(step, options);
8397
- }
8398
- throw new StrategyValidationError(`Flow "${flowName}" step ${stepIndex} is neither an agent reference nor a flow definition.`);
8399
- });
8400
- }
8401
- function resolveAgentReference(agentName, flowName, agents) {
8402
- const agent = agents[agentName];
8403
- if (!agent) {
8404
- const available = Object.keys(agents).join(", ");
8405
- throw new StrategyValidationError(`Flow "${flowName}" references agent "${agentName}" which is not defined. ` + `Available agents: [${available}].`);
8406
- }
8407
- return agent;
8408
- }
8409
9078
  // src/hooks/built-in/token-tracking/token-tracking.constants.ts
8410
9079
  var MODEL_CATALOG = {
8411
9080
  "openai/gpt-4o": { contextWindow: 128000, maxOutputTokens: 16384 },
@@ -8592,81 +9261,15 @@ function getSandbox(boxed) {
8592
9261
  return boxed[SANDBOX_KEY];
8593
9262
  }
8594
9263
  // src/strategy/exporter/exporter.ts
8595
- import YAML6 from "yaml";
9264
+ import YAML7 from "yaml";
8596
9265
  function exportStrategy(strategy, options) {
8597
9266
  const format = options?.format ?? "json";
8598
9267
  const indent = options?.indent ?? 2;
8599
9268
  if (format === "yaml") {
8600
- return YAML6.stringify(strategy.raw, { indent });
9269
+ return YAML7.stringify(strategy.raw, { indent });
8601
9270
  }
8602
9271
  return JSON.stringify(strategy.raw, null, indent);
8603
9272
  }
8604
- // src/strategy/loader/project-loader.ts
8605
- import { dirname as dirname9, resolve as resolve6 } from "path";
8606
- import stripJsonComments3 from "strip-json-comments";
8607
- async function importIfExists(filePath, label) {
8608
- const file = Bun.file(filePath);
8609
- if (!await file.exists()) {
8610
- throw new StrategyValidationError(`${label} not found: ${filePath}`);
8611
- }
8612
- try {
8613
- await import(filePath);
8614
- } catch (importError) {
8615
- throw new StrategyValidationError(`Failed to import ${label} "${filePath}": ${importError instanceof Error ? importError.message : String(importError)}`, { cause: importError });
8616
- }
8617
- }
8618
- async function importIfPresent(filePath) {
8619
- const file = Bun.file(filePath);
8620
- if (!await file.exists())
8621
- return;
8622
- try {
8623
- await import(filePath);
8624
- } catch (importError) {
8625
- throw new StrategyValidationError(`Failed to import "${filePath}": ${importError instanceof Error ? importError.message : String(importError)}`, { cause: importError });
8626
- }
8627
- }
8628
- async function loadProject(manifestPath) {
8629
- const file = Bun.file(manifestPath);
8630
- if (!await file.exists()) {
8631
- throw new StrategyValidationError(`Project manifest not found: ${manifestPath}`);
8632
- }
8633
- const content = await file.text();
8634
- let raw;
8635
- try {
8636
- raw = JSON.parse(stripJsonComments3(content));
8637
- } catch (parseError) {
8638
- throw new StrategyValidationError(`Failed to parse comma-project.json: ${parseError instanceof Error ? parseError.message : String(parseError)}`, { cause: parseError });
8639
- }
8640
- const result = CommaProjectManifestSchema.safeParse(raw);
8641
- if (!result.success) {
8642
- const issues = result.error.issues.map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`).join(`
8643
- `);
8644
- throw new StrategyValidationError(`Project manifest validation failed:
8645
- ${issues}`, { cause: result.error });
8646
- }
8647
- const manifest = result.data;
8648
- const manifestDir = dirname9(manifestPath);
8649
- if (manifest.entry) {
8650
- const entryPath = resolve6(manifestDir, manifest.entry);
8651
- await importIfExists(entryPath, "Entry file");
8652
- } else {
8653
- const defaultEntryPath = resolve6(manifestDir, "index.ts");
8654
- await importIfPresent(defaultEntryPath);
8655
- }
8656
- if (manifest.tools) {
8657
- for (const toolPath of manifest.tools) {
8658
- const resolvedToolPath = resolve6(manifestDir, toolPath);
8659
- await importIfExists(resolvedToolPath, "Tool file");
8660
- }
8661
- }
8662
- return {
8663
- name: manifest.name,
8664
- version: manifest.version,
8665
- description: manifest.description,
8666
- manifest,
8667
- manifestDir
8668
- };
8669
- }
8670
9273
  // src/timeline/projections/conversation-context.ts
8671
9274
  function projectConversationContext(events, agentName) {
8672
9275
  const records = [];
@@ -8806,6 +9409,8 @@ export {
8806
9409
  unregisterProviderDefinition,
8807
9410
  unregisterProvider,
8808
9411
  unregisterModel,
9412
+ unregisterFlow,
9413
+ unregisterAgent,
8809
9414
  unifiedDiff,
8810
9415
  trashWorkspaceDir,
8811
9416
  toolError,
@@ -8832,11 +9437,15 @@ export {
8832
9437
  resetProviderRegistry,
8833
9438
  resetModelRegistry,
8834
9439
  resetGlobalDefaults,
9440
+ resetFlowRegistry,
8835
9441
  resetCatalog,
9442
+ resetAgentRegistry,
8836
9443
  registerTool,
8837
9444
  registerProviderDefinition,
8838
9445
  registerProvider,
8839
9446
  registerModel,
9447
+ registerFlow,
9448
+ registerAgent,
8840
9449
  refreshCatalog,
8841
9450
  recordsToMessages,
8842
9451
  recordToJsonlLine,
@@ -8872,6 +9481,7 @@ export {
8872
9481
  isLLMAgentDef,
8873
9482
  isKnownProvider,
8874
9483
  isFlowDef,
9484
+ isCustomAgentDef,
8875
9485
  isAgentStep,
8876
9486
  inSandbox,
8877
9487
  hookIntoFlow,
@@ -8880,6 +9490,8 @@ export {
8880
9490
  getSandbox,
8881
9491
  getReverseModelIndex,
8882
9492
  getRegisteredToolNames,
9493
+ getRegisteredFlowNames,
9494
+ getRegisteredAgentNames,
8883
9495
  getQualifiedModelMetadata,
8884
9496
  getProvidersForModel,
8885
9497
  getProviderPackage,
@@ -8902,6 +9514,8 @@ export {
8902
9514
  detectNewline,
8903
9515
  denyCommandsPolicy,
8904
9516
  defineTool,
9517
+ defineFlowType,
9518
+ defineAgentType,
8905
9519
  createUserAgent,
8906
9520
  createTokenTracker,
8907
9521
  createTimeline,
@@ -8943,15 +9557,18 @@ export {
8943
9557
  PERMISSIVE_SANDBOX_CONFIG,
8944
9558
  OAuthCredentialSchema,
8945
9559
  ModelResolutionError,
9560
+ LLMAgentDescriptionSchema,
8946
9561
  HookExecutionError,
8947
9562
  FlowExecutionError,
8948
- FlowDescriptionSchema,
9563
+ FlowDefSchema as FlowDescriptionSchema,
8949
9564
  DEFAULT_SANDBOX_CONFIG,
8950
9565
  DEFAULT_FORBIDDEN_GLOBS,
8951
9566
  DEFAULT_DAEMON_SANDBOX_CONFIG,
9567
+ CustomFlowDefSchema,
8952
9568
  CustomCredentialSchema,
9569
+ CustomAgentDescriptionSchema,
9570
+ CustomAgentDefSchema,
8953
9571
  CredentialSchema,
8954
- CommaProjectManifestSchema,
8955
9572
  CommaAgentsError,
8956
9573
  BOM,
8957
9574
  BINARY_DETECTION_SAMPLE_BYTES,