@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.
- package/dist/agents/agent/agent.types.d.ts +2 -2
- package/dist/agents/loader/index.d.ts +2 -2
- package/dist/agents/loader/loader.d.ts +3 -5
- package/dist/agents/loader/loader.schema.d.ts +226 -13
- package/dist/agents/loader/loader.types.d.ts +9 -8
- package/dist/agents/registry/agent-registry.constants.d.ts +1 -0
- package/dist/agents/registry/agent-registry.d.ts +38 -0
- package/dist/agents/registry/agent-registry.types.d.ts +58 -0
- package/dist/agents/registry/index.d.ts +2 -0
- package/dist/credentials/backends/json-file.d.ts +1 -1
- package/dist/credentials/credentials.constants.d.ts +2 -0
- package/dist/credentials/credentials.utils.d.ts +0 -19
- package/dist/credentials/index.d.ts +1 -1
- package/dist/data-directory/data-directory.d.ts +11 -0
- package/dist/data-directory/index.d.ts +1 -0
- package/dist/defaults/defaults.d.ts +1 -1
- package/dist/flows/index.d.ts +2 -0
- package/dist/flows/loader/loader.schema.d.ts +2 -195
- package/dist/flows/loader/loader.utils.d.ts +5 -0
- package/dist/flows/registry/flow-registry.constants.d.ts +1 -0
- package/dist/flows/registry/flow-registry.d.ts +45 -0
- package/dist/flows/registry/flow-registry.types.d.ts +31 -0
- package/dist/flows/registry/index.d.ts +2 -0
- package/dist/hub/archive/archive.d.ts +2 -0
- package/dist/hub/archive/index.d.ts +1 -0
- package/dist/hub/comma-project.schema.json +171 -0
- package/dist/hub/hub.constants.d.ts +5 -0
- package/dist/hub/hub.d.ts +13 -0
- package/dist/hub/hub.schema.d.ts +1093 -0
- package/dist/hub/hub.types.d.ts +50 -0
- package/dist/hub/hub.utils.d.ts +3 -0
- package/dist/hub/index.d.ts +3 -0
- package/dist/hub/index.js +404 -0
- package/dist/hub/installed-packages/index.d.ts +2 -0
- package/dist/hub/installed-packages/installed-packages.d.ts +3 -0
- package/dist/hub/installed-packages/installed-packages.types.d.ts +14 -0
- package/dist/hub/package-installer/index.d.ts +2 -0
- package/dist/hub/package-installer/package-installer.d.ts +3 -0
- package/dist/hub/package-installer/package-installer.types.d.ts +11 -0
- package/dist/hub/registry-client/index.d.ts +2 -0
- package/dist/hub/registry-client/registry-client.d.ts +3 -0
- package/dist/hub/registry-client/registry-client.types.d.ts +10 -0
- package/dist/index.d.ts +13 -10
- package/dist/index.js +1386 -769
- package/dist/model/providers/catalog/catalog.utils.d.ts +2 -9
- package/dist/skills/skills.constants.d.ts +2 -2
- package/dist/skills/skills.types.d.ts +1 -1
- package/dist/skills/skills.utils.d.ts +0 -10
- package/dist/strategies/@comma/core-strategies/README.md +9 -0
- package/dist/strategies/@comma/core-strategies/build/build.json +69 -0
- package/dist/strategies/@comma/core-strategies/build/prompts/coder.md +56 -0
- package/dist/strategies/@comma/core-strategies/build/prompts/tester.md +39 -0
- package/dist/strategies/@comma/core-strategies/comma-project.json +49 -0
- package/dist/strategies/@comma/core-strategies/plan/plan.json +66 -0
- package/dist/strategies/@comma/core-strategies/plan/prompts/planner.md +59 -0
- package/dist/strategies/@comma/core-strategies/plan/prompts/reviewer.md +34 -0
- package/dist/strategies/@comma/core-strategies/qa.json +36 -0
- package/dist/strategies/@comma/core-strategies/reduce-complexity/reduce-complexity.jsonc +24 -0
- package/dist/strategies/@comma/core-strategies/standardize/manager.jsonc +54 -0
- package/dist/strategies/@comma/core-strategies/standardize/prompts/manager.md +278 -0
- package/dist/strategies/@comma/core-strategies/standardize/prompts/worker-auditor.md +131 -0
- package/dist/strategies/@comma/core-strategies/standardize/prompts/worker-reviewer.md +58 -0
- package/dist/strategies/@comma/core-strategies/standardize/worker.jsonc +69 -0
- package/dist/strategies/@comma/core-strategies/talk.json +42 -0
- package/dist/strategy/discover/discover.d.ts +10 -2
- package/dist/strategy/discover/discover.types.d.ts +6 -5
- package/dist/strategy/discover/discover.utils.d.ts +2 -13
- package/dist/strategy/discover/index.d.ts +1 -1
- package/dist/strategy/index.d.ts +3 -3
- package/dist/strategy/loader/loader.types.d.ts +2 -70
- package/dist/strategy/loader/loader.utils.d.ts +1 -8
- package/dist/strategy/loader/project-loader.d.ts +7 -1
- package/dist/strategy/schema.d.ts +154 -60
- package/dist/tools/built-in/list-strategy/list-strategy.d.ts +2 -2
- 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
|
|
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
|
|
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 =
|
|
512
|
-
type:
|
|
513
|
-
key:
|
|
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 =
|
|
516
|
-
type:
|
|
517
|
-
accessToken:
|
|
518
|
-
refreshToken:
|
|
519
|
-
expiresAt:
|
|
520
|
-
accountId:
|
|
521
|
-
metadata:
|
|
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 =
|
|
524
|
-
type:
|
|
525
|
-
data:
|
|
916
|
+
var CustomCredentialSchema = z2.object({
|
|
917
|
+
type: z2.literal("custom"),
|
|
918
|
+
data: z2.record(z2.unknown())
|
|
526
919
|
});
|
|
527
|
-
var CredentialSchema =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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 {
|
|
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
|
|
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
|
|
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 {
|
|
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(
|
|
891
|
-
|
|
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(
|
|
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 =
|
|
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(
|
|
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 =
|
|
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
|
|
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 !==
|
|
2057
|
+
while (current !== resolve3(current, "..") || suffix.length === 0) {
|
|
1688
2058
|
try {
|
|
1689
2059
|
const real = realpathSync(current);
|
|
1690
|
-
return suffix.length > 0 ?
|
|
2060
|
+
return suffix.length > 0 ? resolve3(real, ...suffix.reverse()) : real;
|
|
1691
2061
|
} catch {
|
|
1692
|
-
const parent =
|
|
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 =
|
|
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
|
|
1711
|
-
const jailRoot = realCwd.endsWith(
|
|
2080
|
+
const sep2 = "/";
|
|
2081
|
+
const jailRoot = realCwd.endsWith(sep2) ? realCwd : `${realCwd}${sep2}`;
|
|
1712
2082
|
if (realResolved !== realCwd && !realResolved.startsWith(jailRoot)) {
|
|
1713
|
-
throw new SandboxViolationError(
|
|
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
|
|
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",
|
|
2158
|
+
addPolicy(pathSessionPolicy(mode, "allow", relative2));
|
|
1789
2159
|
} else {
|
|
1790
|
-
addPolicy(pathSessionPolicy(mode, "deny",
|
|
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,
|
|
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}-${
|
|
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(`/${
|
|
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}${
|
|
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(
|
|
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
|
|
1895
|
-
const cwdWithSep = cwd2.endsWith(
|
|
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
|
|
2269
|
+
const relative2 = absolutePath === cwd2 ? "." : absolutePath.slice(cwdWithSep.length);
|
|
1900
2270
|
for (const pattern of patterns) {
|
|
1901
|
-
if (new Bun.Glob(pattern).match(
|
|
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 =
|
|
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
|
|
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 =
|
|
2459
|
+
const auditDir = join7(workspaceRoot, ".comma", "audit");
|
|
2090
2460
|
const maxDiffBytes = options.maxDiffBytes ?? DEFAULT_MAX_DIFF_BYTES;
|
|
2091
2461
|
function fileForSession(sessionId) {
|
|
2092
|
-
return
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
2286
|
-
question:
|
|
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
|
|
2336
|
-
import { dirname as
|
|
2337
|
-
import { z as
|
|
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
|
|
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 =
|
|
2714
|
+
const dir = dirname6(absolutePath);
|
|
2345
2715
|
const tempName = `.${randomBytes(8).toString("hex")}.tmp`;
|
|
2346
|
-
const tempPath =
|
|
2716
|
+
const tempPath = join8(dir, tempName);
|
|
2347
2717
|
let effectiveMode = options.mode;
|
|
2348
2718
|
if (effectiveMode === undefined) {
|
|
2349
2719
|
try {
|
|
2350
|
-
const existing = await
|
|
2720
|
+
const existing = await stat2(absolutePath);
|
|
2351
2721
|
effectiveMode = existing.mode & 511;
|
|
2352
2722
|
} catch {}
|
|
2353
2723
|
}
|
|
2354
2724
|
try {
|
|
2355
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2912
|
+
mkdir as mkdir5,
|
|
2543
2913
|
readdir as readdir2,
|
|
2544
|
-
readFile as
|
|
2545
|
-
rm,
|
|
2546
|
-
stat as
|
|
2547
|
-
writeFile as
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2563
|
-
const relativePath =
|
|
2564
|
-
const content = await
|
|
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 =
|
|
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((
|
|
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", () =>
|
|
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
|
|
2963
|
+
await writeFile3(archivePath, new Uint8Array(compressed));
|
|
2594
2964
|
try {
|
|
2595
|
-
await
|
|
2965
|
+
await rm3(absSourcePath, { force: true });
|
|
2596
2966
|
} catch {
|
|
2597
|
-
await
|
|
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 =
|
|
2984
|
+
const full = join9(bucket, name);
|
|
2615
2985
|
try {
|
|
2616
|
-
const fileStat = await
|
|
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
|
|
3001
|
+
const compressed = await readFile5(archivePath);
|
|
2632
3002
|
const decompressed = gunzipSync(compressed);
|
|
2633
|
-
return await new Promise((
|
|
2634
|
-
const ext =
|
|
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", () =>
|
|
2654
|
-
ext.on("error", () =>
|
|
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 =
|
|
2668
|
-
const relativeToWorkspace =
|
|
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
|
|
2673
|
-
const compressed = await
|
|
3042
|
+
await mkdir5(dirname7(restoreAbsolute), { recursive: true });
|
|
3043
|
+
const compressed = await readFile5(trashPath);
|
|
2674
3044
|
const decompressed = gunzipSync(compressed);
|
|
2675
|
-
await new Promise((
|
|
2676
|
-
const ext =
|
|
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
|
|
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",
|
|
3067
|
+
ext.on("finish", resolve6);
|
|
2698
3068
|
ext.on("error", reject);
|
|
2699
3069
|
ext.end(Buffer.from(decompressed));
|
|
2700
3070
|
});
|
|
2701
|
-
await
|
|
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
|
|
3084
|
+
const fileStat = await stat3(join9(bucket, name));
|
|
2715
3085
|
totalBytes += fileStat.size;
|
|
2716
3086
|
count += 1;
|
|
2717
3087
|
} catch {}
|
|
2718
3088
|
}
|
|
2719
|
-
await
|
|
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 =
|
|
2725
|
-
path:
|
|
2726
|
-
content:
|
|
2727
|
-
createParentDirectories:
|
|
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
|
|
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 =
|
|
3208
|
+
const parent = dirname8(absolutePath);
|
|
2839
3209
|
try {
|
|
2840
|
-
const parentStat = await
|
|
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
|
|
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
|
|
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
|
|
2893
|
-
var deleteFileParams =
|
|
2894
|
-
path:
|
|
2895
|
-
expectedSha256:
|
|
2896
|
-
permanent:
|
|
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
|
|
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
|
|
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
|
|
3072
|
-
import { z as
|
|
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 =
|
|
3362
|
-
oldText:
|
|
3363
|
-
newText:
|
|
3364
|
-
expectedOccurrences:
|
|
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 =
|
|
3367
|
-
path:
|
|
3368
|
-
expectedSha256:
|
|
3369
|
-
edits:
|
|
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
|
|
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
|
|
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
|
|
3605
|
-
import { join as
|
|
3606
|
-
import { z as
|
|
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
|
|
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 =
|
|
3649
|
-
pattern:
|
|
3650
|
-
root:
|
|
3651
|
-
excludeGlobs:
|
|
3652
|
-
maxResults:
|
|
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
|
|
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 =
|
|
3795
|
-
const relFromRoot = toForwardSlash(
|
|
3796
|
-
const relFromWorkspace = toForwardSlash(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
3854
|
-
import {
|
|
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
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
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 =
|
|
3874
|
-
temperature:
|
|
3875
|
-
topP:
|
|
3876
|
-
topK:
|
|
3877
|
-
maxOutputTokens:
|
|
3878
|
-
maxRetries:
|
|
3879
|
-
frequencyPenalty:
|
|
3880
|
-
presencePenalty:
|
|
3881
|
-
seed:
|
|
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 =
|
|
3884
|
-
var ConversationContextSchema =
|
|
3885
|
-
rollingWindow:
|
|
3886
|
-
|
|
3887
|
-
|
|
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:
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
keepRecent:
|
|
3893
|
-
threshold:
|
|
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
|
|
3898
|
-
name:
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
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:
|
|
3904
|
-
providerOptions:
|
|
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:
|
|
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 =
|
|
3913
|
-
type:
|
|
3914
|
-
description:
|
|
3915
|
-
config:
|
|
3916
|
-
requireInput:
|
|
3917
|
-
presetMessage:
|
|
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 =
|
|
3921
|
-
type:
|
|
3922
|
-
description:
|
|
3923
|
-
model:
|
|
3924
|
-
systemPrompt:
|
|
3925
|
-
systemPromptTemplate:
|
|
3926
|
-
template:
|
|
3927
|
-
variables:
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
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:
|
|
3936
|
-
skills:
|
|
3937
|
-
providerOptions:
|
|
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:
|
|
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 =
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
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 =
|
|
4352
|
+
var FlowStepSchema = z10.lazy(() => z10.union([AgentStepSchema, FlowDefSchema]));
|
|
3949
4353
|
var BaseFlowFields = {
|
|
3950
|
-
name:
|
|
3951
|
-
description:
|
|
3952
|
-
steps:
|
|
4354
|
+
name: z10.string().min(1),
|
|
4355
|
+
description: z10.string().optional(),
|
|
4356
|
+
steps: z10.array(FlowStepSchema).min(1)
|
|
3953
4357
|
};
|
|
3954
|
-
var SequentialFlowDefSchema =
|
|
4358
|
+
var SequentialFlowDefSchema = z10.object({
|
|
4359
|
+
...BaseFlowFields,
|
|
4360
|
+
type: z10.literal("sequential")
|
|
4361
|
+
}).strict();
|
|
4362
|
+
var CycleFlowDefSchema = z10.object({
|
|
3955
4363
|
...BaseFlowFields,
|
|
3956
|
-
type:
|
|
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
|
|
4370
|
+
var BroadcastFlowDefSchema = z10.object({
|
|
3959
4371
|
...BaseFlowFields,
|
|
3960
|
-
type:
|
|
3961
|
-
|
|
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
|
|
4375
|
+
var CustomFlowDefSchema = z10.object({
|
|
3967
4376
|
...BaseFlowFields,
|
|
3968
|
-
type:
|
|
3969
|
-
|
|
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
|
|
4380
|
+
var BuiltInFlowDefSchema = z10.discriminatedUnion("type", [
|
|
3972
4381
|
SequentialFlowDefSchema,
|
|
3973
4382
|
CycleFlowDefSchema,
|
|
3974
4383
|
BroadcastFlowDefSchema
|
|
3975
4384
|
]);
|
|
3976
|
-
var
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
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 =
|
|
4079
|
-
const strategyPaths =
|
|
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) =>
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
4231
|
-
import
|
|
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((
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
4577
|
-
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
|
|
4589
|
-
import { join as
|
|
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 = "
|
|
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 {
|
|
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
|
|
5273
|
+
return join13(resolveDataDir(), GLOBAL_SKILLS_SUBDIR);
|
|
4637
5274
|
}
|
|
4638
5275
|
function resolveDefaultProjectSkillsDir(workspaceRoot) {
|
|
4639
|
-
return
|
|
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 =
|
|
4729
|
-
const skillFile =
|
|
5365
|
+
const skillDir = join14(skillsDirectory, entry.name);
|
|
5366
|
+
const skillFile = join14(skillDir, SKILL_FILENAME);
|
|
4730
5367
|
let fileStat;
|
|
4731
5368
|
try {
|
|
4732
|
-
fileStat = await
|
|
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
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
registry[name] = await
|
|
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 (!
|
|
5538
|
+
if (!isAbsolute4(resolvedSystemPrompt)) {
|
|
4884
5539
|
if (options.strategyDir) {
|
|
4885
|
-
resolvedPath =
|
|
5540
|
+
resolvedPath = resolve8(options.strategyDir, resolvedSystemPrompt);
|
|
4886
5541
|
} else {
|
|
4887
|
-
resolvedPath =
|
|
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 (!
|
|
5554
|
+
if (!isAbsolute4(resolvedTemplate)) {
|
|
4900
5555
|
if (options.strategyDir) {
|
|
4901
|
-
resolvedPath =
|
|
5556
|
+
resolvedPath = resolve8(options.strategyDir, resolvedTemplate);
|
|
4902
5557
|
} else {
|
|
4903
|
-
resolvedPath =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
5107
|
-
name:
|
|
5108
|
-
input:
|
|
5109
|
-
modelOverride:
|
|
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
|
|
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((
|
|
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
|
|
5225
|
-
import { join as
|
|
5226
|
-
import { z as
|
|
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
|
|
5971
|
+
import { sep as sep3 } from "path";
|
|
5235
5972
|
function toForwardSlash2(pathString) {
|
|
5236
|
-
return
|
|
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 =
|
|
5258
|
-
path:
|
|
5259
|
-
recursive:
|
|
5260
|
-
maxDepth:
|
|
5261
|
-
includeHidden:
|
|
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
|
|
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 =
|
|
6146
|
+
const childAbs = join15(node.absolutePath, dirent.name);
|
|
5410
6147
|
const childRelFromRoot = node.relativeFromRoot ? `${node.relativeFromRoot}/${dirent.name}` : dirent.name;
|
|
5411
|
-
const childRelFromWorkspace = toForwardSlash2(
|
|
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
|
|
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
|
|
5472
|
-
var listSkillsParams =
|
|
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
|
|
5525
|
-
var listStrategyParams =
|
|
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
|
|
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 (
|
|
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
|
|
5592
|
-
var loadSkillParams =
|
|
5593
|
-
name:
|
|
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
|
|
5654
|
-
var lspRequestParams =
|
|
5655
|
-
method:
|
|
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:
|
|
5666
|
-
path:
|
|
5667
|
-
line:
|
|
5668
|
-
character:
|
|
5669
|
-
query:
|
|
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
|
|
5855
|
-
import { z as
|
|
5856
|
-
var { copyFile, readFile:
|
|
5857
|
-
var moveFileParams =
|
|
5858
|
-
fromPath:
|
|
5859
|
-
toPath:
|
|
5860
|
-
expectedSha256:
|
|
5861
|
-
overwrite:
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
6772
|
+
const parent = dirname12(toAbs);
|
|
6036
6773
|
try {
|
|
6037
|
-
const parentStat = await
|
|
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
|
|
6118
|
-
import { z as
|
|
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 =
|
|
6142
|
-
path:
|
|
6143
|
-
startLine:
|
|
6144
|
-
endLine:
|
|
6145
|
-
maxBytes:
|
|
6146
|
-
allowBinary:
|
|
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
|
|
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
|
|
6360
|
-
import { z as
|
|
6361
|
-
var restoreFileParams =
|
|
6362
|
-
trashedPath:
|
|
6363
|
-
targetPath:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
6500
|
-
switch (
|
|
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
|
|
7253
|
+
return platform;
|
|
6517
7254
|
}
|
|
6518
7255
|
}
|
|
6519
|
-
function resolveShell(
|
|
6520
|
-
if (
|
|
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
|
|
6533
|
-
const { shellPath, shellFlag } = resolveShell(
|
|
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
|
|
6538
|
-
osName: friendlyOsName(
|
|
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 =
|
|
6616
|
-
command:
|
|
6617
|
-
cwd:
|
|
6618
|
-
timeoutMs:
|
|
6619
|
-
env:
|
|
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
|
|
6879
|
-
import { join as
|
|
6880
|
-
import { z as
|
|
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
|
|
7635
|
+
import { sep as sep4 } from "path";
|
|
6899
7636
|
function toForwardSlash3(pathString) {
|
|
6900
|
-
return
|
|
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 =
|
|
6934
|
-
query:
|
|
6935
|
-
mode:
|
|
6936
|
-
root:
|
|
6937
|
-
includeGlobs:
|
|
6938
|
-
excludeGlobs:
|
|
6939
|
-
maxResults:
|
|
6940
|
-
contextLines:
|
|
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
|
|
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 =
|
|
7119
|
-
const relFromRoot = toForwardSlash3(
|
|
7120
|
-
const relFromWorkspace = toForwardSlash3(
|
|
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
|
|
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
|
|
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 =
|
|
7254
|
-
content:
|
|
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 =
|
|
7277
|
-
id:
|
|
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 =
|
|
7305
|
-
id:
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
7441
|
-
url:
|
|
7442
|
-
format:
|
|
7443
|
-
timeout:
|
|
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
|
|
7524
|
-
import { z as
|
|
7525
|
-
var writeFileParams =
|
|
7526
|
-
path:
|
|
7527
|
-
content:
|
|
7528
|
-
expectedSha256:
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
8978
|
+
return await loadAgentFromString(content, format, {
|
|
8979
|
+
...options,
|
|
8980
|
+
strategyDir: options.strategyDir ?? dirname13(filePath)
|
|
8981
|
+
});
|
|
8235
8982
|
}
|
|
8236
|
-
async function loadAgentFromString(content, format,
|
|
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 =
|
|
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.
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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,
|