@polka-codes/core 0.9.80 → 0.9.82
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/README.md +3 -4
- package/dist/index.js +1707 -1286
- package/package.json +1 -1
- package/dist/_tsup-dts-rollup.d.ts +0 -1775
- package/dist/index.d.ts +0 -115
package/dist/index.js
CHANGED
|
@@ -127,12 +127,6 @@ function computeRateLimitBackoffSeconds(count, baseSeconds = 2, capSeconds = 60)
|
|
|
127
127
|
|
|
128
128
|
// src/config.ts
|
|
129
129
|
import { z } from "zod";
|
|
130
|
-
var providerModelSchema = z.object({
|
|
131
|
-
provider: z.string().optional(),
|
|
132
|
-
model: z.string().optional(),
|
|
133
|
-
parameters: z.record(z.string(), z.any()).optional(),
|
|
134
|
-
budget: z.number().positive().optional()
|
|
135
|
-
});
|
|
136
130
|
var ruleSchema = z.union([
|
|
137
131
|
z.string(),
|
|
138
132
|
z.object({ path: z.string() }).strict(),
|
|
@@ -145,6 +139,69 @@ var ruleSchema = z.union([
|
|
|
145
139
|
branch: z.string().optional()
|
|
146
140
|
}).strict()
|
|
147
141
|
]);
|
|
142
|
+
var providerConfigSchema = z.object({
|
|
143
|
+
apiKey: z.string().optional(),
|
|
144
|
+
defaultModel: z.string().optional(),
|
|
145
|
+
defaultParameters: z.record(z.string(), z.any()).optional(),
|
|
146
|
+
location: z.string().optional(),
|
|
147
|
+
project: z.string().optional(),
|
|
148
|
+
keyFile: z.string().optional(),
|
|
149
|
+
baseUrl: z.string().optional()
|
|
150
|
+
});
|
|
151
|
+
var providerModelSchema = z.object({
|
|
152
|
+
provider: z.string().optional(),
|
|
153
|
+
model: z.string().optional(),
|
|
154
|
+
parameters: z.record(z.string(), z.any()).optional(),
|
|
155
|
+
budget: z.number().positive().optional(),
|
|
156
|
+
rules: z.array(ruleSchema).optional().or(z.string()).optional()
|
|
157
|
+
});
|
|
158
|
+
var scriptSchema = z.union([
|
|
159
|
+
// Type 1: Simple shell command (backward compatible)
|
|
160
|
+
z.string(),
|
|
161
|
+
// Type 2: Object with command and description (backward compatible)
|
|
162
|
+
z.object({
|
|
163
|
+
command: z.string(),
|
|
164
|
+
description: z.string()
|
|
165
|
+
}).strict(),
|
|
166
|
+
// Type 3: Reference to dynamic workflow YAML
|
|
167
|
+
z.object({
|
|
168
|
+
workflow: z.string(),
|
|
169
|
+
// Path to .yml workflow file
|
|
170
|
+
description: z.string().optional(),
|
|
171
|
+
input: z.record(z.string(), z.any()).optional()
|
|
172
|
+
// Default workflow input
|
|
173
|
+
}).strict(),
|
|
174
|
+
// Type 4: TypeScript script file (NEW)
|
|
175
|
+
z.object({
|
|
176
|
+
script: z.string(),
|
|
177
|
+
// Path to .ts file
|
|
178
|
+
description: z.string().optional(),
|
|
179
|
+
permissions: z.object({
|
|
180
|
+
fs: z.enum(["read", "write", "none"]).optional(),
|
|
181
|
+
network: z.boolean().optional(),
|
|
182
|
+
subprocess: z.boolean().optional()
|
|
183
|
+
}).optional(),
|
|
184
|
+
timeout: z.number().int().positive().max(36e5).optional(),
|
|
185
|
+
// Max 1 hour in milliseconds
|
|
186
|
+
memory: z.number().int().positive().min(64).max(8192).optional()
|
|
187
|
+
// 64MB-8GB in MB
|
|
188
|
+
}).strict()
|
|
189
|
+
]);
|
|
190
|
+
var mcpServerConfigSchema = z.object({
|
|
191
|
+
command: z.string(),
|
|
192
|
+
args: z.array(z.string()).optional(),
|
|
193
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
194
|
+
tools: z.record(
|
|
195
|
+
z.string(),
|
|
196
|
+
z.boolean().or(
|
|
197
|
+
z.object({
|
|
198
|
+
provider: z.string().optional(),
|
|
199
|
+
model: z.string().optional(),
|
|
200
|
+
parameters: z.record(z.string(), z.unknown()).optional()
|
|
201
|
+
}).strict()
|
|
202
|
+
)
|
|
203
|
+
).optional()
|
|
204
|
+
}).strict();
|
|
148
205
|
var configSchema = z.object({
|
|
149
206
|
prices: z.record(
|
|
150
207
|
z.string(),
|
|
@@ -160,17 +217,7 @@ var configSchema = z.object({
|
|
|
160
217
|
})
|
|
161
218
|
)
|
|
162
219
|
).optional(),
|
|
163
|
-
providers: z.record(
|
|
164
|
-
z.string(),
|
|
165
|
-
z.object({
|
|
166
|
-
apiKey: z.string().optional(),
|
|
167
|
-
defaultModel: z.string().optional(),
|
|
168
|
-
defaultParameters: z.record(z.string(), z.any()).optional(),
|
|
169
|
-
location: z.string().optional(),
|
|
170
|
-
project: z.string().optional(),
|
|
171
|
-
keyFile: z.string().optional()
|
|
172
|
-
})
|
|
173
|
-
).optional(),
|
|
220
|
+
providers: z.record(z.string(), providerConfigSchema).optional(),
|
|
174
221
|
defaultProvider: z.string().optional(),
|
|
175
222
|
defaultModel: z.string().optional(),
|
|
176
223
|
defaultParameters: z.record(z.string(), z.any()).optional(),
|
|
@@ -179,42 +226,690 @@ var configSchema = z.object({
|
|
|
179
226
|
retryCount: z.number().int().min(0).optional(),
|
|
180
227
|
requestTimeoutSeconds: z.number().int().positive().optional(),
|
|
181
228
|
summaryThreshold: z.number().int().positive().optional(),
|
|
182
|
-
scripts: z.record(
|
|
183
|
-
z.string(),
|
|
184
|
-
z.string().or(
|
|
185
|
-
z.object({
|
|
186
|
-
command: z.string(),
|
|
187
|
-
description: z.string()
|
|
188
|
-
})
|
|
189
|
-
)
|
|
190
|
-
).optional(),
|
|
229
|
+
scripts: z.record(z.string(), scriptSchema).optional(),
|
|
191
230
|
commands: z.record(z.string(), providerModelSchema).optional(),
|
|
192
231
|
tools: z.object({
|
|
193
232
|
search: providerModelSchema.or(z.boolean()).optional()
|
|
194
233
|
}).optional(),
|
|
234
|
+
mcpServers: z.record(z.string(), mcpServerConfigSchema).optional(),
|
|
195
235
|
rules: z.array(ruleSchema).optional().or(z.string()).optional(),
|
|
196
236
|
excludeFiles: z.array(z.string()).optional()
|
|
197
237
|
}).strict().nullish();
|
|
198
238
|
|
|
199
|
-
// src/
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
239
|
+
// src/fs/node-provider.ts
|
|
240
|
+
import { existsSync } from "fs";
|
|
241
|
+
import { readdir, readFile, stat } from "fs/promises";
|
|
242
|
+
import { join, normalize } from "path";
|
|
243
|
+
var NodeFileSystemProvider = class {
|
|
244
|
+
constructor(_options = {}) {
|
|
245
|
+
this._options = _options;
|
|
246
|
+
}
|
|
247
|
+
exists(path) {
|
|
248
|
+
return existsSync(path);
|
|
249
|
+
}
|
|
250
|
+
async readdir(path) {
|
|
251
|
+
const entries = await readdir(path, { withFileTypes: true });
|
|
252
|
+
return entries.map((entry) => ({
|
|
253
|
+
name: entry.name,
|
|
254
|
+
isDirectory: entry.isDirectory(),
|
|
255
|
+
isFile: entry.isFile()
|
|
256
|
+
}));
|
|
257
|
+
}
|
|
258
|
+
async readFile(path) {
|
|
259
|
+
return readFile(path, "utf-8");
|
|
260
|
+
}
|
|
261
|
+
async readFileAsBuffer(path) {
|
|
262
|
+
const buffer = await readFile(path);
|
|
263
|
+
return new Uint8Array(buffer);
|
|
264
|
+
}
|
|
265
|
+
async stat(path) {
|
|
266
|
+
const stats = await stat(path);
|
|
267
|
+
return {
|
|
268
|
+
size: stats.size,
|
|
269
|
+
isDirectory: stats.isDirectory(),
|
|
270
|
+
isFile: stats.isFile()
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
join(...paths) {
|
|
274
|
+
return join(...paths);
|
|
275
|
+
}
|
|
276
|
+
normalize(path) {
|
|
277
|
+
return normalize(path);
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// src/skills/constants.ts
|
|
282
|
+
var SKILL_LIMITS = {
|
|
283
|
+
MAX_FILE_SIZE: 1024 * 1024,
|
|
284
|
+
// 1MB per file
|
|
285
|
+
MAX_SKILL_SIZE: 10 * 1024 * 1024,
|
|
286
|
+
// 10MB total
|
|
287
|
+
MAX_DEPTH: 10,
|
|
288
|
+
// Maximum directory recursion depth
|
|
289
|
+
MAX_FILES: 500,
|
|
290
|
+
// Maximum files to load per skill
|
|
291
|
+
MIN_DESCRIPTION_LENGTH: 20,
|
|
292
|
+
// Minimum description length
|
|
293
|
+
MAX_DESCRIPTION_LENGTH: 1024,
|
|
294
|
+
// Maximum description length
|
|
295
|
+
MAX_NAME_LENGTH: 64
|
|
296
|
+
// Maximum skill name length
|
|
297
|
+
};
|
|
298
|
+
var IGNORED_DIRECTORIES = [
|
|
299
|
+
".git",
|
|
300
|
+
"node_modules",
|
|
301
|
+
".next",
|
|
302
|
+
".turbo",
|
|
303
|
+
"dist",
|
|
304
|
+
"build",
|
|
305
|
+
"coverage",
|
|
306
|
+
".cache",
|
|
307
|
+
".vscode",
|
|
308
|
+
".idea",
|
|
309
|
+
"tmp",
|
|
310
|
+
"temp",
|
|
311
|
+
".DS_Store"
|
|
312
|
+
];
|
|
313
|
+
var SUSPICIOUS_PATTERNS = [
|
|
314
|
+
/<script[^>]*>[\s\S]*?<\/script>/i,
|
|
315
|
+
// Script tags (with dotAll for multiline)
|
|
316
|
+
/javascript:/i,
|
|
317
|
+
// JavaScript URLs
|
|
318
|
+
/on\w+\s*=/i
|
|
319
|
+
// Event handlers (onclick, onload, etc.)
|
|
320
|
+
];
|
|
321
|
+
var SKILL_ERROR_MESSAGES = {
|
|
322
|
+
MISSING_FRONTMATTER: "SKILL.md must begin with YAML frontmatter enclosed in ---",
|
|
323
|
+
FRONTMATTER_INVALID: "Invalid frontmatter: {message}",
|
|
324
|
+
SKILL_NOT_FOUND: "Skill not found",
|
|
325
|
+
CONTEXT_NOT_INITIALIZED: "Skill context not initialized"
|
|
326
|
+
};
|
|
327
|
+
var SOURCE_ICONS = {
|
|
328
|
+
project: "\u{1F4C1}",
|
|
329
|
+
personal: "\u{1F3E0}",
|
|
330
|
+
plugin: "\u{1F50C}"
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
// src/skills/discovery.ts
|
|
334
|
+
import { homedir } from "os";
|
|
335
|
+
import { parse } from "yaml";
|
|
336
|
+
import { ZodError } from "zod";
|
|
206
337
|
|
|
207
|
-
// src/
|
|
338
|
+
// src/skills/types.ts
|
|
208
339
|
import { z as z2 } from "zod";
|
|
209
|
-
var
|
|
210
|
-
|
|
211
|
-
|
|
340
|
+
var skillMetadataSchema = z2.object({
|
|
341
|
+
name: z2.string().regex(/^[a-z0-9-]+$/, "Skill name must be lowercase letters, numbers, and hyphens").max(64, "Skill name must be at most 64 characters"),
|
|
342
|
+
description: z2.string().max(1024, "Description must be at most 1024 characters"),
|
|
343
|
+
allowedTools: z2.array(z2.string()).optional()
|
|
344
|
+
});
|
|
345
|
+
var SkillDiscoveryError = class extends Error {
|
|
346
|
+
constructor(message, path) {
|
|
347
|
+
super(message);
|
|
348
|
+
this.path = path;
|
|
349
|
+
this.name = "SkillDiscoveryError";
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
var SkillValidationError = class extends Error {
|
|
353
|
+
constructor(message, path) {
|
|
354
|
+
super(message);
|
|
355
|
+
this.path = path;
|
|
356
|
+
this.name = "SkillValidationError";
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
// src/skills/discovery.ts
|
|
361
|
+
var BINARY_EXTENSIONS = [
|
|
362
|
+
".png",
|
|
363
|
+
".jpg",
|
|
364
|
+
".jpeg",
|
|
365
|
+
".gif",
|
|
366
|
+
".bmp",
|
|
367
|
+
".ico",
|
|
368
|
+
".webp",
|
|
369
|
+
".pdf",
|
|
370
|
+
".zip",
|
|
371
|
+
".tar",
|
|
372
|
+
".gz",
|
|
373
|
+
".exe",
|
|
374
|
+
".dll",
|
|
375
|
+
".so",
|
|
376
|
+
".dylib",
|
|
377
|
+
".bin",
|
|
378
|
+
".dat",
|
|
379
|
+
".db",
|
|
380
|
+
".sqlite",
|
|
381
|
+
".woff",
|
|
382
|
+
".woff2",
|
|
383
|
+
".ttf",
|
|
384
|
+
".eot"
|
|
385
|
+
];
|
|
386
|
+
function isBinaryFile(filename) {
|
|
387
|
+
const ext = filename.toLowerCase().slice(filename.lastIndexOf("."));
|
|
388
|
+
return BINARY_EXTENSIONS.includes(ext);
|
|
389
|
+
}
|
|
390
|
+
function isBinaryContent(buffer) {
|
|
391
|
+
const checkLength = Math.min(buffer.length, 8192);
|
|
392
|
+
for (let i = 0; i < checkLength; i++) {
|
|
393
|
+
if (buffer[i] === 0) {
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
async function tryReadTextFile(filePath, fs) {
|
|
400
|
+
try {
|
|
401
|
+
const buffer = await fs.readFileAsBuffer(filePath);
|
|
402
|
+
if (isBinaryContent(buffer)) {
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
try {
|
|
406
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(buffer);
|
|
407
|
+
} catch (_decodeError) {
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
} catch (error) {
|
|
411
|
+
if (error && typeof error === "object" && "code" in error && (error.code === "EINVAL" || error.code === "EISDIR")) {
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
throw error;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
var SkillDiscoveryService = class {
|
|
418
|
+
fs;
|
|
419
|
+
personalSkillsDir;
|
|
420
|
+
projectSkillsDir;
|
|
421
|
+
pluginSkillsDirs;
|
|
422
|
+
constructor(options) {
|
|
423
|
+
this.fs = options.fs ?? new NodeFileSystemProvider();
|
|
424
|
+
this.personalSkillsDir = options.personalSkillsDir ?? this.fs.join(homedir(), ".claude", "skills");
|
|
425
|
+
this.projectSkillsDir = this.fs.join(options.cwd, ".claude", "skills");
|
|
426
|
+
this.pluginSkillsDirs = options.pluginSkillsDirs ?? [];
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Discover all available skills from all sources
|
|
430
|
+
* Removes duplicates (project skills take priority over personal/plugin)
|
|
431
|
+
*/
|
|
432
|
+
async discoverAll() {
|
|
433
|
+
const skills = [];
|
|
434
|
+
const results = await Promise.allSettled([
|
|
435
|
+
this.discoverInDirectory(this.projectSkillsDir, "project"),
|
|
436
|
+
this.discoverInDirectory(this.personalSkillsDir, "personal"),
|
|
437
|
+
this.discoverPlugins()
|
|
438
|
+
]);
|
|
439
|
+
const projectSkills = results[0].status === "fulfilled" ? results[0].value : [];
|
|
440
|
+
const personalSkills = results[1].status === "fulfilled" ? results[1].value : [];
|
|
441
|
+
const pluginSkills = results[2].status === "fulfilled" ? results[2].value : [];
|
|
442
|
+
if (results[0].status === "rejected") {
|
|
443
|
+
console.warn(`Failed to load project skills: ${results[0].reason}`);
|
|
444
|
+
}
|
|
445
|
+
if (results[1].status === "rejected") {
|
|
446
|
+
console.warn(`Failed to load personal skills: ${results[1].reason}`);
|
|
447
|
+
}
|
|
448
|
+
if (results[2].status === "rejected") {
|
|
449
|
+
console.warn(`Failed to load plugin skills: ${results[2].reason}`);
|
|
450
|
+
}
|
|
451
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
452
|
+
const allSkills = [...projectSkills, ...personalSkills, ...pluginSkills];
|
|
453
|
+
for (const skill of allSkills) {
|
|
454
|
+
if (!seenNames.has(skill.metadata.name)) {
|
|
455
|
+
seenNames.add(skill.metadata.name);
|
|
456
|
+
skills.push(skill);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
return skills;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Discover skills in a specific directory
|
|
463
|
+
*/
|
|
464
|
+
async discoverInDirectory(dir, source) {
|
|
465
|
+
const exists = typeof this.fs.exists === "boolean" ? this.fs.exists(dir) : await this.fs.exists(dir);
|
|
466
|
+
if (!exists) {
|
|
467
|
+
return [];
|
|
468
|
+
}
|
|
469
|
+
const skills = [];
|
|
470
|
+
const entries = await this.fs.readdir(dir);
|
|
471
|
+
for (const entry of entries) {
|
|
472
|
+
if (!entry.isDirectory) {
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
const skillPath = this.fs.join(dir, entry.name);
|
|
476
|
+
const skillMdPath = this.fs.join(skillPath, "SKILL.md");
|
|
477
|
+
const skillMdExists = typeof this.fs.exists === "boolean" ? this.fs.exists(skillMdPath) : await this.fs.exists(skillMdPath);
|
|
478
|
+
if (!skillMdExists) {
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
try {
|
|
482
|
+
const content = await this.fs.readFile(skillMdPath);
|
|
483
|
+
const { metadata } = this.parseSkillMd(content);
|
|
484
|
+
skills.push({
|
|
485
|
+
metadata,
|
|
486
|
+
path: skillPath,
|
|
487
|
+
source
|
|
488
|
+
});
|
|
489
|
+
} catch (error) {
|
|
490
|
+
let message = "Unknown error";
|
|
491
|
+
let path = skillPath;
|
|
492
|
+
if (error instanceof SkillDiscoveryError) {
|
|
493
|
+
message = error.message;
|
|
494
|
+
path = error.path;
|
|
495
|
+
} else if (error instanceof ZodError) {
|
|
496
|
+
message = error.issues[0]?.message ?? "Invalid skill metadata";
|
|
497
|
+
} else if (error instanceof Error) {
|
|
498
|
+
message = error.message;
|
|
499
|
+
}
|
|
500
|
+
console.warn(`Warning: Failed to load skill at ${path}: ${message}`);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
return skills;
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Discover skills from plugin directories (node_modules)
|
|
507
|
+
*/
|
|
508
|
+
async discoverPlugins() {
|
|
509
|
+
const skills = [];
|
|
510
|
+
for (const pluginDir of this.pluginSkillsDirs) {
|
|
511
|
+
const exists = typeof this.fs.exists === "boolean" ? this.fs.exists(pluginDir) : await this.fs.exists(pluginDir);
|
|
512
|
+
if (!exists) {
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
const pluginSkills = await this.discoverInDirectory(pluginDir, "plugin");
|
|
516
|
+
skills.push(...pluginSkills);
|
|
517
|
+
}
|
|
518
|
+
return skills;
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Load a single skill from its directory
|
|
522
|
+
*/
|
|
523
|
+
async loadSkill(skillPath, source) {
|
|
524
|
+
const skillMdPath = this.fs.join(skillPath, "SKILL.md");
|
|
525
|
+
const skillMdExists = typeof this.fs.exists === "boolean" ? this.fs.exists(skillMdPath) : await this.fs.exists(skillMdPath);
|
|
526
|
+
if (!skillMdExists) {
|
|
527
|
+
throw new SkillDiscoveryError("SKILL.md not found", skillPath);
|
|
528
|
+
}
|
|
529
|
+
const content = await this.fs.readFile(skillMdPath);
|
|
530
|
+
const { metadata, content: instructions } = this.parseSkillMd(content);
|
|
531
|
+
const files = /* @__PURE__ */ new Map();
|
|
532
|
+
let totalSize = 0;
|
|
533
|
+
const entries = await this.fs.readdir(skillPath);
|
|
534
|
+
for (const entry of entries) {
|
|
535
|
+
if (entry.name === "SKILL.md") {
|
|
536
|
+
continue;
|
|
537
|
+
}
|
|
538
|
+
const filePath = this.fs.join(skillPath, entry.name);
|
|
539
|
+
if (entry.isFile) {
|
|
540
|
+
if (isBinaryFile(entry.name)) {
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
const fileStats = await this.fs.stat(filePath);
|
|
544
|
+
const fileSize = fileStats.size;
|
|
545
|
+
if (fileSize > SKILL_LIMITS.MAX_FILE_SIZE) {
|
|
546
|
+
throw new SkillDiscoveryError(`File size exceeds limit (${fileSize} > ${SKILL_LIMITS.MAX_FILE_SIZE}): ${entry.name}`, skillPath);
|
|
547
|
+
}
|
|
548
|
+
if (totalSize + fileSize > SKILL_LIMITS.MAX_SKILL_SIZE) {
|
|
549
|
+
throw new SkillDiscoveryError(
|
|
550
|
+
`Total skill size exceeds limit (${totalSize + fileSize} > ${SKILL_LIMITS.MAX_SKILL_SIZE}): ${skillPath}`,
|
|
551
|
+
skillPath
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
const fileContent = await tryReadTextFile(filePath, this.fs);
|
|
555
|
+
if (fileContent === null) {
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
totalSize += fileSize;
|
|
559
|
+
files.set(entry.name, fileContent);
|
|
560
|
+
} else if (entry.isDirectory) {
|
|
561
|
+
totalSize = await this.loadDirectoryFiles(filePath, entry.name, files, 0, totalSize);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
return {
|
|
565
|
+
metadata,
|
|
566
|
+
content: instructions,
|
|
567
|
+
files,
|
|
568
|
+
path: skillPath,
|
|
569
|
+
source
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Parse SKILL.md content and extract frontmatter
|
|
574
|
+
*/
|
|
575
|
+
parseSkillMd(content) {
|
|
576
|
+
const frontmatterRegex = /^---\r?\n([\s\S]+?)\r?\n---\r?\n([\s\S]*)$/;
|
|
577
|
+
const match = content.match(frontmatterRegex);
|
|
578
|
+
if (!match || match.length < 3) {
|
|
579
|
+
throw new SkillDiscoveryError(SKILL_ERROR_MESSAGES.MISSING_FRONTMATTER, "");
|
|
580
|
+
}
|
|
581
|
+
const frontmatter = match[1] ?? "";
|
|
582
|
+
const instructions = match[2] ?? "";
|
|
583
|
+
const metadata = this.parseMetadata(frontmatter);
|
|
584
|
+
return { metadata, content: instructions };
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Parse and validate YAML frontmatter
|
|
588
|
+
*/
|
|
589
|
+
parseMetadata(frontmatter) {
|
|
590
|
+
const parsed = parse(frontmatter);
|
|
591
|
+
return skillMetadataSchema.parse(parsed);
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Recursively load files from a directory into the files map
|
|
595
|
+
* @returns The total size of all files loaded (in bytes)
|
|
596
|
+
*/
|
|
597
|
+
async loadDirectoryFiles(dirPath, prefix, files, depth = 0, currentTotal = 0) {
|
|
598
|
+
const { MAX_DEPTH, MAX_FILES } = SKILL_LIMITS;
|
|
599
|
+
let totalSize = currentTotal;
|
|
600
|
+
if (depth > MAX_DEPTH) {
|
|
601
|
+
return totalSize;
|
|
602
|
+
}
|
|
603
|
+
if (files.size >= MAX_FILES) {
|
|
604
|
+
return totalSize;
|
|
605
|
+
}
|
|
606
|
+
const currentDirName = prefix.split("/").pop() ?? prefix;
|
|
607
|
+
if (IGNORED_DIRECTORIES.includes(currentDirName)) {
|
|
608
|
+
return totalSize;
|
|
609
|
+
}
|
|
610
|
+
const entries = await this.fs.readdir(dirPath);
|
|
611
|
+
for (const entry of entries) {
|
|
612
|
+
if (files.size >= MAX_FILES) {
|
|
613
|
+
break;
|
|
614
|
+
}
|
|
615
|
+
const filePath = this.fs.join(dirPath, entry.name);
|
|
616
|
+
const key = `${prefix}/${entry.name}`.replace(/\/+/g, "/");
|
|
617
|
+
if (entry.isFile) {
|
|
618
|
+
if (isBinaryFile(entry.name)) {
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
const fileStats = await this.fs.stat(filePath);
|
|
622
|
+
const fileSize = fileStats.size;
|
|
623
|
+
if (fileSize > SKILL_LIMITS.MAX_FILE_SIZE) {
|
|
624
|
+
throw new SkillDiscoveryError(`File size exceeds limit (${fileSize} > ${SKILL_LIMITS.MAX_FILE_SIZE}): ${key}`, dirPath);
|
|
625
|
+
}
|
|
626
|
+
if (totalSize + fileSize > SKILL_LIMITS.MAX_SKILL_SIZE) {
|
|
627
|
+
throw new SkillDiscoveryError(
|
|
628
|
+
`Total skill size exceeds limit (${totalSize + fileSize} > ${SKILL_LIMITS.MAX_SKILL_SIZE}): ${dirPath}`,
|
|
629
|
+
dirPath
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
const content = await tryReadTextFile(filePath, this.fs);
|
|
633
|
+
if (content === null) {
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
totalSize += fileSize;
|
|
637
|
+
files.set(key, content);
|
|
638
|
+
} else if (entry.isDirectory) {
|
|
639
|
+
if (IGNORED_DIRECTORIES.includes(entry.name)) {
|
|
640
|
+
continue;
|
|
641
|
+
}
|
|
642
|
+
totalSize = await this.loadDirectoryFiles(filePath, key, files, depth + 1, totalSize);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
return totalSize;
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Create an initial skill context object
|
|
649
|
+
*/
|
|
650
|
+
async createContext() {
|
|
651
|
+
const availableSkills = await this.discoverAll();
|
|
652
|
+
return {
|
|
653
|
+
activeSkill: null,
|
|
654
|
+
availableSkills,
|
|
655
|
+
skillLoadingHistory: [],
|
|
656
|
+
loadSkill: async (name) => {
|
|
657
|
+
const ref = availableSkills.find((s) => s.metadata.name === name);
|
|
658
|
+
if (!ref) return null;
|
|
659
|
+
return this.loadSkill(ref.path, ref.source);
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
// src/skills/validation.ts
|
|
666
|
+
import { join as join2, normalize as normalize2 } from "path";
|
|
667
|
+
function validateSkillSecurity(skill) {
|
|
668
|
+
const { MAX_FILE_SIZE, MAX_SKILL_SIZE } = SKILL_LIMITS;
|
|
669
|
+
let totalSize = 0;
|
|
670
|
+
const contentSize = Buffer.byteLength(skill.content, "utf8");
|
|
671
|
+
if (contentSize > MAX_FILE_SIZE) {
|
|
672
|
+
throw new SkillValidationError(`SKILL.md content exceeds size limit (${contentSize} > ${MAX_FILE_SIZE})`, join2(skill.path, "SKILL.md"));
|
|
673
|
+
}
|
|
674
|
+
totalSize += contentSize;
|
|
675
|
+
for (const [filename, content] of skill.files) {
|
|
676
|
+
const fileSize = Buffer.byteLength(content, "utf8");
|
|
677
|
+
if (fileSize > MAX_FILE_SIZE) {
|
|
678
|
+
throw new SkillValidationError(`File ${filename} exceeds size limit (${fileSize} > ${MAX_FILE_SIZE})`, join2(skill.path, filename));
|
|
679
|
+
}
|
|
680
|
+
totalSize += fileSize;
|
|
681
|
+
}
|
|
682
|
+
if (totalSize > MAX_SKILL_SIZE) {
|
|
683
|
+
throw new SkillValidationError(`Skill total size exceeds limit (${totalSize} > ${MAX_SKILL_SIZE})`, skill.path);
|
|
684
|
+
}
|
|
685
|
+
validateContentSecurity(skill.content, skill.path);
|
|
686
|
+
for (const [filename, content] of skill.files) {
|
|
687
|
+
validateContentSecurity(content, join2(skill.path, filename));
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
function validateContentSecurity(content, path) {
|
|
691
|
+
for (const pattern of SUSPICIOUS_PATTERNS) {
|
|
692
|
+
if (pattern.test(content)) {
|
|
693
|
+
throw new SkillValidationError("Suspicious content detected", path);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
function validateSkillReferences(skill) {
|
|
698
|
+
const warnings = [];
|
|
699
|
+
const externalRefs = skill.content.match(/https?:\/\/[^\s\])]+/g) || [];
|
|
700
|
+
if (externalRefs.length > 0 && !skill.metadata.description.toLowerCase().includes("external")) {
|
|
701
|
+
warnings.push(
|
|
702
|
+
`Skill '${skill.metadata.name}' contains external references. Consider adding 'external' to description for transparency.`
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
const codeBlocks = skill.content.match(/```[\s\S]*?```/g) || [];
|
|
706
|
+
for (const block of codeBlocks) {
|
|
707
|
+
const pathsInCode = block.match(/\/[a-zA-Z][\w./-]*/g) || [];
|
|
708
|
+
for (const path of pathsInCode) {
|
|
709
|
+
if (!path.startsWith("/dev") && !path.startsWith("/proc") && !path.startsWith("/sys") && !path.startsWith("//")) {
|
|
710
|
+
warnings.push(`Skill '${skill.metadata.name}' contains possible absolute path '${path}'. Use relative paths instead.`);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
const linkRegex = /\[[^\]]+\]\(([^)]+(?:\s+[^)]+)*)\)/g;
|
|
715
|
+
let match;
|
|
716
|
+
match = linkRegex.exec(skill.content);
|
|
717
|
+
while (match !== null) {
|
|
718
|
+
const filepath = match[1];
|
|
719
|
+
match = linkRegex.exec(skill.content);
|
|
720
|
+
if (filepath.startsWith("http://") || filepath.startsWith("https://") || filepath.startsWith("#")) {
|
|
721
|
+
continue;
|
|
722
|
+
}
|
|
723
|
+
const normalizedPath = normalize2(filepath).replace(/^\.?\//, "").replace(/\\/g, "/");
|
|
724
|
+
if (!skill.files.has(normalizedPath)) {
|
|
725
|
+
warnings.push(`Referenced file not found: ${filepath}`);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
return warnings;
|
|
729
|
+
}
|
|
730
|
+
function validateSkillMetadata(skill) {
|
|
731
|
+
const errors = [];
|
|
732
|
+
if (skill.metadata.description.length < 20) {
|
|
733
|
+
errors.push(`Description too short: ${skill.metadata.description.length} < 20`);
|
|
734
|
+
}
|
|
735
|
+
return errors;
|
|
736
|
+
}
|
|
737
|
+
function getSkillStats(skill) {
|
|
738
|
+
let totalSize = Buffer.byteLength(skill.content, "utf8");
|
|
739
|
+
let largestFile = { name: "SKILL.md", size: totalSize };
|
|
740
|
+
let fileCount = 1;
|
|
741
|
+
for (const [name, content] of skill.files) {
|
|
742
|
+
const size = Buffer.byteLength(content, "utf8");
|
|
743
|
+
totalSize += size;
|
|
744
|
+
fileCount++;
|
|
745
|
+
if (size > largestFile.size) {
|
|
746
|
+
largestFile = { name, size };
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
return {
|
|
750
|
+
totalSize,
|
|
751
|
+
fileCount,
|
|
752
|
+
largestFile
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// src/skills/tools/listSkills.ts
|
|
757
|
+
import { z as z3 } from "zod";
|
|
758
|
+
var ListSkillsInputSchema = z3.object({
|
|
759
|
+
filter: z3.string().optional().describe("Optional filter string to match against skill names and descriptions")
|
|
760
|
+
});
|
|
761
|
+
var ListSkillsOutputSchema = z3.object({
|
|
762
|
+
skills: z3.array(
|
|
763
|
+
z3.object({
|
|
764
|
+
name: z3.string(),
|
|
765
|
+
description: z3.string(),
|
|
766
|
+
source: z3.enum(["personal", "project", "plugin"])
|
|
767
|
+
})
|
|
768
|
+
),
|
|
769
|
+
total: z3.number()
|
|
770
|
+
});
|
|
771
|
+
async function listSkills(input, context) {
|
|
772
|
+
const { filter } = input;
|
|
773
|
+
let skills = context.availableSkills;
|
|
774
|
+
if (filter) {
|
|
775
|
+
const filterLower = filter.toLowerCase();
|
|
776
|
+
skills = skills.filter((s) => s.metadata.name.includes(filterLower) || s.metadata.description.toLowerCase().includes(filterLower));
|
|
777
|
+
}
|
|
778
|
+
return {
|
|
779
|
+
skills: skills.map((s) => ({
|
|
780
|
+
name: s.metadata.name,
|
|
781
|
+
description: s.metadata.description,
|
|
782
|
+
source: s.source
|
|
783
|
+
})),
|
|
784
|
+
total: skills.length
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
var listSkillsToolInfo = {
|
|
788
|
+
name: "listSkills",
|
|
789
|
+
description: "List all available skills with their descriptions. Use this to discover what specialized capabilities are available.",
|
|
790
|
+
parameters: ListSkillsInputSchema,
|
|
791
|
+
returns: ListSkillsOutputSchema
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
// src/skills/tools/loadSkill.ts
|
|
795
|
+
import { z as z4 } from "zod";
|
|
796
|
+
var LoadSkillInputSchema = z4.object({
|
|
797
|
+
skillName: z4.string().describe("The name of the skill to load")
|
|
798
|
+
});
|
|
799
|
+
var LoadSkillOutputSchema = z4.object({
|
|
800
|
+
success: z4.boolean(),
|
|
801
|
+
skill: z4.object({
|
|
802
|
+
name: z4.string(),
|
|
803
|
+
description: z4.string(),
|
|
804
|
+
content: z4.string(),
|
|
805
|
+
availableFiles: z4.array(z4.string())
|
|
806
|
+
}).optional(),
|
|
807
|
+
error: z4.string().optional(),
|
|
808
|
+
warnings: z4.array(z4.string()).optional()
|
|
809
|
+
});
|
|
810
|
+
async function loadSkill(input, context) {
|
|
811
|
+
const { skillName } = input;
|
|
812
|
+
const skillRef = context.availableSkills.find((s) => s.metadata.name === skillName);
|
|
813
|
+
if (!skillRef) {
|
|
814
|
+
return {
|
|
815
|
+
success: false,
|
|
816
|
+
error: `Skill '${skillName}' not found`
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
try {
|
|
820
|
+
const skill = await context.loadSkill(skillName);
|
|
821
|
+
if (!skill) {
|
|
822
|
+
return {
|
|
823
|
+
success: false,
|
|
824
|
+
error: `Failed to load skill '${skillName}'`
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
validateSkillSecurity(skill);
|
|
828
|
+
const warnings = validateSkillReferences(skill);
|
|
829
|
+
context.activeSkill = skill;
|
|
830
|
+
context.skillLoadingHistory.push(skillName);
|
|
831
|
+
return {
|
|
832
|
+
success: true,
|
|
833
|
+
skill: {
|
|
834
|
+
name: skill.metadata.name,
|
|
835
|
+
description: skill.metadata.description,
|
|
836
|
+
content: skill.content,
|
|
837
|
+
availableFiles: Array.from(skill.files.keys())
|
|
838
|
+
},
|
|
839
|
+
warnings: warnings.length > 0 ? warnings : void 0
|
|
840
|
+
};
|
|
841
|
+
} catch (error) {
|
|
842
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
843
|
+
return {
|
|
844
|
+
success: false,
|
|
845
|
+
error: `Failed to load skill '${skillName}': ${message}`
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
var loadSkillToolInfo = {
|
|
850
|
+
name: "loadSkill",
|
|
851
|
+
description: "Load a skill by name to access its instructions and resources. Use this when you need specialized knowledge or capabilities for a specific task.",
|
|
852
|
+
parameters: LoadSkillInputSchema,
|
|
853
|
+
returns: LoadSkillOutputSchema
|
|
854
|
+
};
|
|
855
|
+
|
|
856
|
+
// src/skills/tools/readSkillFile.ts
|
|
857
|
+
import { z as z5 } from "zod";
|
|
858
|
+
var ReadSkillFileInputSchema = z5.object({
|
|
859
|
+
skillName: z5.string().describe("The name of the skill"),
|
|
860
|
+
filename: z5.string().describe('The name of the file to read (e.g., "reference.md", "scripts/helper.py")')
|
|
861
|
+
});
|
|
862
|
+
var ReadSkillFileOutputSchema = z5.object({
|
|
863
|
+
success: z5.boolean(),
|
|
864
|
+
content: z5.string().optional(),
|
|
865
|
+
error: z5.string().optional()
|
|
866
|
+
});
|
|
867
|
+
async function readSkillFile(input, context) {
|
|
868
|
+
const { skillName, filename } = input;
|
|
869
|
+
let skill = context.activeSkill && context.activeSkill.metadata.name === skillName ? context.activeSkill : null;
|
|
870
|
+
if (!skill) {
|
|
871
|
+
try {
|
|
872
|
+
skill = await context.loadSkill(skillName);
|
|
873
|
+
} catch (_error) {
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
if (!skill) {
|
|
877
|
+
return {
|
|
878
|
+
success: false,
|
|
879
|
+
error: `Skill '${skillName}' not found or could not be loaded. Use loadSkill first.`
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
if (!skill.files.has(filename)) {
|
|
883
|
+
const availableFiles = Array.from(skill.files.keys()).sort();
|
|
884
|
+
return {
|
|
885
|
+
success: false,
|
|
886
|
+
error: `File '${filename}' not found in skill '${skillName}'. Available files: ${availableFiles.join(", ") || "none"}`
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
const content = skill.files.get(filename);
|
|
890
|
+
return {
|
|
891
|
+
success: true,
|
|
892
|
+
content
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
var readSkillFileToolInfo = {
|
|
896
|
+
name: "readSkillFile",
|
|
897
|
+
description: "Read a supporting file from a skill. Use this to access reference documentation, examples, scripts, or templates bundled with a skill. First use loadSkill to see available files, then use this tool to read specific files.",
|
|
898
|
+
parameters: ReadSkillFileInputSchema,
|
|
899
|
+
returns: ReadSkillFileOutputSchema
|
|
900
|
+
};
|
|
901
|
+
|
|
902
|
+
// src/tools/askFollowupQuestion.ts
|
|
903
|
+
import { z as z6 } from "zod";
|
|
904
|
+
var questionObject = z6.object({
|
|
905
|
+
prompt: z6.string().describe("The text of the question.").meta({ usageValue: "question text here" }),
|
|
906
|
+
options: z6.array(z6.string()).default([]).describe("Ordered list of suggested answers (omit if none).").meta({ usageValue: "suggested answer here" })
|
|
212
907
|
});
|
|
213
908
|
var toolInfo = {
|
|
214
909
|
name: "askFollowupQuestion",
|
|
215
910
|
description: "Call this when vital details are missing. Pose each follow-up as one direct, unambiguous question. If it speeds the reply, add up to five short, mutually-exclusive answer options. Group any related questions in the same call to avoid a back-and-forth chain.",
|
|
216
|
-
parameters:
|
|
217
|
-
questions:
|
|
911
|
+
parameters: z6.object({
|
|
912
|
+
questions: z6.array(questionObject).describe("One or more follow-up questions you need answered before you can continue.").meta({ usageValue: "questions here" })
|
|
218
913
|
}).meta({
|
|
219
914
|
examples: [
|
|
220
915
|
{
|
|
@@ -263,7 +958,7 @@ var toolInfo = {
|
|
|
263
958
|
var handler = async (provider, args) => {
|
|
264
959
|
if (!provider.askFollowupQuestion) {
|
|
265
960
|
return {
|
|
266
|
-
|
|
961
|
+
success: false,
|
|
267
962
|
message: {
|
|
268
963
|
type: "error-text",
|
|
269
964
|
value: "Not possible to ask followup question."
|
|
@@ -273,7 +968,7 @@ var handler = async (provider, args) => {
|
|
|
273
968
|
const { questions } = toolInfo.parameters.parse(args);
|
|
274
969
|
if (questions.length === 0) {
|
|
275
970
|
return {
|
|
276
|
-
|
|
971
|
+
success: false,
|
|
277
972
|
message: {
|
|
278
973
|
type: "error-text",
|
|
279
974
|
value: "No questions provided"
|
|
@@ -289,7 +984,7 @@ ${answer}
|
|
|
289
984
|
</ask_followup_question_answer>`);
|
|
290
985
|
}
|
|
291
986
|
return {
|
|
292
|
-
|
|
987
|
+
success: true,
|
|
293
988
|
message: {
|
|
294
989
|
type: "text",
|
|
295
990
|
value: answers.join("\n")
|
|
@@ -302,20 +997,41 @@ var askFollowupQuestion_default = {
|
|
|
302
997
|
};
|
|
303
998
|
|
|
304
999
|
// src/tools/executeCommand.ts
|
|
305
|
-
import { z as
|
|
1000
|
+
import { z as z7 } from "zod";
|
|
1001
|
+
|
|
1002
|
+
// src/tools/utils.ts
|
|
1003
|
+
function createProviderError(action) {
|
|
1004
|
+
return {
|
|
1005
|
+
success: false,
|
|
1006
|
+
message: {
|
|
1007
|
+
type: "error-text",
|
|
1008
|
+
value: `Not possible to ${action}.`
|
|
1009
|
+
}
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
function preprocessBoolean(val) {
|
|
1013
|
+
return typeof val === "string" ? val.toLowerCase() === "true" : val;
|
|
1014
|
+
}
|
|
1015
|
+
function createFileElement(tagName, path, content, attrs) {
|
|
1016
|
+
const allAttrs = { path, ...attrs };
|
|
1017
|
+
const attrStr = Object.entries(allAttrs).map(([k, v]) => ` ${k}="${v}"`).join("");
|
|
1018
|
+
if (content === void 0) {
|
|
1019
|
+
return `<${tagName}${attrStr} />`;
|
|
1020
|
+
}
|
|
1021
|
+
const isEmpty = content.trim().length === 0;
|
|
1022
|
+
if (isEmpty) {
|
|
1023
|
+
return `<${tagName}${attrStr} is_empty="true" />`;
|
|
1024
|
+
}
|
|
1025
|
+
return `<${tagName}${attrStr}>${content}</${tagName}>`;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
// src/tools/executeCommand.ts
|
|
306
1029
|
var toolInfo2 = {
|
|
307
1030
|
name: "executeCommand",
|
|
308
1031
|
description: "Run a single CLI command. The command is always executed in the project-root working directory (regardless of earlier commands). Prefer one-off shell commands over wrapper scripts for flexibility. **IMPORTANT**: After an `execute_command` call, you MUST stop and NOT allowed to make further tool calls in the same message.",
|
|
309
|
-
parameters:
|
|
310
|
-
command:
|
|
311
|
-
requiresApproval:
|
|
312
|
-
if (typeof val === "string") {
|
|
313
|
-
const lower = val.toLowerCase();
|
|
314
|
-
if (lower === "false") return false;
|
|
315
|
-
if (lower === "true") return true;
|
|
316
|
-
}
|
|
317
|
-
return val;
|
|
318
|
-
}, z3.boolean().optional().default(false)).describe(
|
|
1032
|
+
parameters: z7.object({
|
|
1033
|
+
command: z7.string().describe("The exact command to run (valid for the current OS). It must be correctly formatted and free of harmful instructions.").meta({ usageValue: "your-command-here" }),
|
|
1034
|
+
requiresApproval: z7.preprocess(preprocessBoolean, z7.boolean().optional().default(false)).describe(
|
|
319
1035
|
"Set to `true` for commands that install/uninstall software, modify or delete files, change system settings, perform network operations, or have other side effects. Use `false` for safe, read-only, or purely local development actions (e.g., listing files, make a build, running tests)."
|
|
320
1036
|
).meta({ usageValue: "true | false" })
|
|
321
1037
|
}).meta({
|
|
@@ -332,13 +1048,7 @@ var toolInfo2 = {
|
|
|
332
1048
|
};
|
|
333
1049
|
var handler2 = async (provider, args) => {
|
|
334
1050
|
if (!provider.executeCommand) {
|
|
335
|
-
return
|
|
336
|
-
type: "Error" /* Error */,
|
|
337
|
-
message: {
|
|
338
|
-
type: "error-text",
|
|
339
|
-
value: "Not possible to execute command. Abort."
|
|
340
|
-
}
|
|
341
|
-
};
|
|
1051
|
+
return createProviderError("execute command. Abort");
|
|
342
1052
|
}
|
|
343
1053
|
const { command, requiresApproval } = toolInfo2.parameters.parse(args);
|
|
344
1054
|
try {
|
|
@@ -362,7 +1072,7 @@ ${result.stderr}
|
|
|
362
1072
|
}
|
|
363
1073
|
if (result.exitCode === 0) {
|
|
364
1074
|
return {
|
|
365
|
-
|
|
1075
|
+
success: true,
|
|
366
1076
|
message: {
|
|
367
1077
|
type: "text",
|
|
368
1078
|
value: message
|
|
@@ -370,7 +1080,7 @@ ${result.stderr}
|
|
|
370
1080
|
};
|
|
371
1081
|
}
|
|
372
1082
|
return {
|
|
373
|
-
|
|
1083
|
+
success: false,
|
|
374
1084
|
message: {
|
|
375
1085
|
type: "error-text",
|
|
376
1086
|
value: message
|
|
@@ -378,7 +1088,7 @@ ${result.stderr}
|
|
|
378
1088
|
};
|
|
379
1089
|
} catch (error) {
|
|
380
1090
|
return {
|
|
381
|
-
|
|
1091
|
+
success: false,
|
|
382
1092
|
message: {
|
|
383
1093
|
type: "error-text",
|
|
384
1094
|
value: error instanceof Error ? error.message : String(error)
|
|
@@ -392,16 +1102,16 @@ var executeCommand_default = {
|
|
|
392
1102
|
};
|
|
393
1103
|
|
|
394
1104
|
// src/tools/fetchUrl.ts
|
|
395
|
-
import { z as
|
|
1105
|
+
import { z as z8 } from "zod";
|
|
396
1106
|
var toolInfo3 = {
|
|
397
1107
|
name: "fetchUrl",
|
|
398
1108
|
description: "Fetch the content located at one or more HTTP(S) URLs and return it in Markdown format. This works for standard web pages as well as raw files (e.g. README.md, source code) hosted on platforms like GitHub.",
|
|
399
|
-
parameters:
|
|
400
|
-
url:
|
|
1109
|
+
parameters: z8.object({
|
|
1110
|
+
url: z8.preprocess((val) => {
|
|
401
1111
|
if (!val) return [];
|
|
402
1112
|
const values = Array.isArray(val) ? val : [val];
|
|
403
1113
|
return values.flatMap((i) => typeof i === "string" ? i.split(",") : []).filter((s) => s.length > 0);
|
|
404
|
-
},
|
|
1114
|
+
}, z8.array(z8.string())).describe("One or more URLs to fetch, separated by commas if multiple.").meta({ usageValue: "url" })
|
|
405
1115
|
}).meta({
|
|
406
1116
|
examples: [
|
|
407
1117
|
{
|
|
@@ -428,7 +1138,7 @@ var toolInfo3 = {
|
|
|
428
1138
|
var handler3 = async (provider, args) => {
|
|
429
1139
|
if (!provider.fetchUrl) {
|
|
430
1140
|
return {
|
|
431
|
-
|
|
1141
|
+
success: false,
|
|
432
1142
|
message: {
|
|
433
1143
|
type: "error-text",
|
|
434
1144
|
value: "Not possible to fetch url."
|
|
@@ -438,7 +1148,7 @@ var handler3 = async (provider, args) => {
|
|
|
438
1148
|
const { url: urls } = toolInfo3.parameters.parse(args);
|
|
439
1149
|
if (urls.length === 0) {
|
|
440
1150
|
return {
|
|
441
|
-
|
|
1151
|
+
success: false,
|
|
442
1152
|
message: {
|
|
443
1153
|
type: "error-text",
|
|
444
1154
|
value: "No URLs provided. Please provide at least one URL to fetch."
|
|
@@ -457,7 +1167,7 @@ var handler3 = async (provider, args) => {
|
|
|
457
1167
|
}
|
|
458
1168
|
const resolvedResults = await Promise.all(results);
|
|
459
1169
|
return {
|
|
460
|
-
|
|
1170
|
+
success: true,
|
|
461
1171
|
message: {
|
|
462
1172
|
type: "text",
|
|
463
1173
|
value: resolvedResults.join("\n")
|
|
@@ -469,64 +1179,16 @@ var fetchUrl_default = {
|
|
|
469
1179
|
handler: handler3
|
|
470
1180
|
};
|
|
471
1181
|
|
|
472
|
-
// src/tools/getTodoItem.ts
|
|
473
|
-
import { z as z5 } from "zod";
|
|
474
|
-
var toolInfo4 = {
|
|
475
|
-
name: "getTodoItem",
|
|
476
|
-
description: "Get a to-do item by its ID.",
|
|
477
|
-
parameters: z5.object({
|
|
478
|
-
id: z5.string().describe("The ID of the to-do item.")
|
|
479
|
-
})
|
|
480
|
-
};
|
|
481
|
-
var handler4 = async (provider, args) => {
|
|
482
|
-
if (!provider.getTodoItem) {
|
|
483
|
-
return {
|
|
484
|
-
type: "Error" /* Error */,
|
|
485
|
-
message: {
|
|
486
|
-
type: "error-text",
|
|
487
|
-
value: "Not possible to get a to-do item."
|
|
488
|
-
}
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
const { id } = toolInfo4.parameters.parse(args);
|
|
492
|
-
const item = await provider.getTodoItem(id);
|
|
493
|
-
return {
|
|
494
|
-
type: "Reply" /* Reply */,
|
|
495
|
-
message: {
|
|
496
|
-
type: "json",
|
|
497
|
-
value: item ?? null
|
|
498
|
-
}
|
|
499
|
-
};
|
|
500
|
-
};
|
|
501
|
-
var getTodoItem_default = {
|
|
502
|
-
...toolInfo4,
|
|
503
|
-
handler: handler4
|
|
504
|
-
};
|
|
505
|
-
|
|
506
1182
|
// src/tools/listFiles.ts
|
|
507
|
-
import { z as
|
|
508
|
-
var
|
|
1183
|
+
import { z as z9 } from "zod";
|
|
1184
|
+
var toolInfo4 = {
|
|
509
1185
|
name: "listFiles",
|
|
510
1186
|
description: "Request to list files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. Do not use this tool to confirm the existence of files you may have created, as the user will let you know if the files were created successfully or not.",
|
|
511
|
-
parameters:
|
|
512
|
-
path:
|
|
513
|
-
maxCount:
|
|
514
|
-
recursive:
|
|
515
|
-
|
|
516
|
-
const lower = val.toLowerCase();
|
|
517
|
-
if (lower === "false") return false;
|
|
518
|
-
if (lower === "true") return true;
|
|
519
|
-
}
|
|
520
|
-
return val;
|
|
521
|
-
}, z6.boolean().optional().default(true)).describe("Whether to list files recursively. Use true for recursive listing, false or omit for top-level only.").meta({ usageValue: "true or false (optional)" }),
|
|
522
|
-
includeIgnored: z6.preprocess((val) => {
|
|
523
|
-
if (typeof val === "string") {
|
|
524
|
-
const lower = val.toLowerCase();
|
|
525
|
-
if (lower === "false") return false;
|
|
526
|
-
if (lower === "true") return true;
|
|
527
|
-
}
|
|
528
|
-
return val;
|
|
529
|
-
}, z6.boolean().optional().default(false)).describe("Whether to include ignored files. Use true to include files ignored by .gitignore.").meta({ usageValue: "true or false (optional)" })
|
|
1187
|
+
parameters: z9.object({
|
|
1188
|
+
path: z9.string().describe("The path of the directory to list contents for (relative to the current working directory)").meta({ usageValue: "Directory path here" }),
|
|
1189
|
+
maxCount: z9.coerce.number().optional().default(2e3).describe("The maximum number of files to list. Default to 2000").meta({ usageValue: "Maximum number of files to list (optional)" }),
|
|
1190
|
+
recursive: z9.preprocess(preprocessBoolean, z9.boolean().optional().default(true)).describe("Whether to list files recursively. Use true for recursive listing, false or omit for top-level only.").meta({ usageValue: "true or false (optional)" }),
|
|
1191
|
+
includeIgnored: z9.preprocess(preprocessBoolean, z9.boolean().optional().default(false)).describe("Whether to include ignored files. Use true to include files ignored by .gitignore.").meta({ usageValue: "true or false (optional)" })
|
|
530
1192
|
}).meta({
|
|
531
1193
|
examples: [
|
|
532
1194
|
{
|
|
@@ -539,20 +1201,14 @@ var toolInfo5 = {
|
|
|
539
1201
|
]
|
|
540
1202
|
})
|
|
541
1203
|
};
|
|
542
|
-
var
|
|
1204
|
+
var handler4 = async (provider, args) => {
|
|
543
1205
|
if (!provider.listFiles) {
|
|
544
|
-
return
|
|
545
|
-
type: "Error" /* Error */,
|
|
546
|
-
message: {
|
|
547
|
-
type: "error-text",
|
|
548
|
-
value: "Not possible to list files."
|
|
549
|
-
}
|
|
550
|
-
};
|
|
1206
|
+
return createProviderError("list files");
|
|
551
1207
|
}
|
|
552
|
-
const { path, maxCount, recursive, includeIgnored } =
|
|
1208
|
+
const { path, maxCount, recursive, includeIgnored } = toolInfo4.parameters.parse(args);
|
|
553
1209
|
const [files, limitReached] = await provider.listFiles(path, recursive, maxCount, includeIgnored);
|
|
554
1210
|
return {
|
|
555
|
-
|
|
1211
|
+
success: true,
|
|
556
1212
|
message: {
|
|
557
1213
|
type: "text",
|
|
558
1214
|
value: `<list_files_path>${path}</list_files_path>
|
|
@@ -564,110 +1220,8 @@ ${files.join("\n")}
|
|
|
564
1220
|
};
|
|
565
1221
|
};
|
|
566
1222
|
var listFiles_default = {
|
|
567
|
-
...
|
|
568
|
-
handler:
|
|
569
|
-
};
|
|
570
|
-
|
|
571
|
-
// src/tools/listMemoryTopics.ts
|
|
572
|
-
import { z as z7 } from "zod";
|
|
573
|
-
var toolInfo6 = {
|
|
574
|
-
name: "listMemoryTopics",
|
|
575
|
-
description: "Lists all topics in memory. Use this to see what information has been stored and which topics are available to read from.",
|
|
576
|
-
parameters: z7.object({})
|
|
577
|
-
};
|
|
578
|
-
var handler6 = async (provider, _args) => {
|
|
579
|
-
const topics = await provider.listMemoryTopics();
|
|
580
|
-
if (!topics.length) {
|
|
581
|
-
return { type: "Reply" /* Reply */, message: { type: "text", value: "No topics found." } };
|
|
582
|
-
}
|
|
583
|
-
return {
|
|
584
|
-
type: "Reply" /* Reply */,
|
|
585
|
-
message: {
|
|
586
|
-
type: "text",
|
|
587
|
-
value: `Memory topics:
|
|
588
|
-
${topics.join("\n")}`
|
|
589
|
-
}
|
|
590
|
-
};
|
|
591
|
-
};
|
|
592
|
-
var listMemoryTopics_default = {
|
|
593
|
-
...toolInfo6,
|
|
594
|
-
handler: handler6
|
|
595
|
-
};
|
|
596
|
-
|
|
597
|
-
// src/tools/listTodoItems.ts
|
|
598
|
-
import { z as z9 } from "zod";
|
|
599
|
-
|
|
600
|
-
// src/tools/todo.ts
|
|
601
|
-
import { z as z8 } from "zod";
|
|
602
|
-
var TodoStatus = z8.enum(["open", "completed", "closed"]);
|
|
603
|
-
var TodoItemSchema = z8.object({
|
|
604
|
-
id: z8.string(),
|
|
605
|
-
title: z8.string(),
|
|
606
|
-
description: z8.string(),
|
|
607
|
-
status: TodoStatus
|
|
608
|
-
});
|
|
609
|
-
var UpdateTodoItemInputSchema = z8.object({
|
|
610
|
-
operation: z8.enum(["add", "update"]),
|
|
611
|
-
id: z8.string().nullish(),
|
|
612
|
-
parentId: z8.string().nullish(),
|
|
613
|
-
title: z8.string().nullish(),
|
|
614
|
-
description: z8.string().nullish(),
|
|
615
|
-
status: TodoStatus.nullish()
|
|
616
|
-
}).superRefine((data, ctx) => {
|
|
617
|
-
if (data.operation === "add") {
|
|
618
|
-
if (!data.title) {
|
|
619
|
-
ctx.addIssue({
|
|
620
|
-
code: "custom",
|
|
621
|
-
message: 'Title is required for "add" operation',
|
|
622
|
-
path: ["title"]
|
|
623
|
-
});
|
|
624
|
-
}
|
|
625
|
-
} else if (data.operation === "update") {
|
|
626
|
-
if (!data.id) {
|
|
627
|
-
ctx.addIssue({
|
|
628
|
-
code: "custom",
|
|
629
|
-
message: 'ID is required for "update" operation',
|
|
630
|
-
path: ["id"]
|
|
631
|
-
});
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
});
|
|
635
|
-
var UpdateTodoItemOutputSchema = z8.object({
|
|
636
|
-
id: z8.string()
|
|
637
|
-
});
|
|
638
|
-
|
|
639
|
-
// src/tools/listTodoItems.ts
|
|
640
|
-
var toolInfo7 = {
|
|
641
|
-
name: "listTodoItems",
|
|
642
|
-
description: "List all to-do items, sorted by id. If an id is provided, it lists all sub-items for that id. Can be filtered by status.",
|
|
643
|
-
parameters: z9.object({
|
|
644
|
-
id: z9.string().nullish(),
|
|
645
|
-
status: TodoStatus.nullish()
|
|
646
|
-
})
|
|
647
|
-
};
|
|
648
|
-
var handler7 = async (provider, args) => {
|
|
649
|
-
if (!provider.listTodoItems) {
|
|
650
|
-
return {
|
|
651
|
-
type: "Error" /* Error */,
|
|
652
|
-
message: {
|
|
653
|
-
type: "error-text",
|
|
654
|
-
value: "Not possible to list to-do items."
|
|
655
|
-
}
|
|
656
|
-
};
|
|
657
|
-
}
|
|
658
|
-
const { id, status } = toolInfo7.parameters.parse(args);
|
|
659
|
-
const items = await provider.listTodoItems(id, status);
|
|
660
|
-
return {
|
|
661
|
-
type: "Reply" /* Reply */,
|
|
662
|
-
message: {
|
|
663
|
-
type: "json",
|
|
664
|
-
value: items
|
|
665
|
-
}
|
|
666
|
-
};
|
|
667
|
-
};
|
|
668
|
-
var listTodoItems_default = {
|
|
669
|
-
...toolInfo7,
|
|
670
|
-
handler: handler7
|
|
1223
|
+
...toolInfo4,
|
|
1224
|
+
handler: handler4
|
|
671
1225
|
};
|
|
672
1226
|
|
|
673
1227
|
// src/tools/provider.ts
|
|
@@ -733,28 +1287,28 @@ var MockProvider = class {
|
|
|
733
1287
|
|
|
734
1288
|
// src/tools/readBinaryFile.ts
|
|
735
1289
|
import { z as z10 } from "zod";
|
|
736
|
-
var
|
|
1290
|
+
var toolInfo5 = {
|
|
737
1291
|
name: "readBinaryFile",
|
|
738
1292
|
description: "Read a binary file from a URL or local path. Use file:// prefix to access local files. This can be used to access non-text files such as PDFs or images.",
|
|
739
1293
|
parameters: z10.object({
|
|
740
1294
|
url: z10.string().describe("The URL or local path of the file to read.")
|
|
741
1295
|
})
|
|
742
1296
|
};
|
|
743
|
-
var
|
|
1297
|
+
var handler5 = async (provider, args) => {
|
|
744
1298
|
if (!provider.readBinaryFile) {
|
|
745
1299
|
return {
|
|
746
|
-
|
|
1300
|
+
success: false,
|
|
747
1301
|
message: {
|
|
748
1302
|
type: "error-text",
|
|
749
1303
|
value: "Not possible to fetch files. Abort."
|
|
750
1304
|
}
|
|
751
1305
|
};
|
|
752
1306
|
}
|
|
753
|
-
const { url } =
|
|
1307
|
+
const { url } = toolInfo5.parameters.parse(args);
|
|
754
1308
|
try {
|
|
755
1309
|
const filePart = await provider.readBinaryFile(url);
|
|
756
1310
|
return {
|
|
757
|
-
|
|
1311
|
+
success: true,
|
|
758
1312
|
message: {
|
|
759
1313
|
type: "content",
|
|
760
1314
|
value: [
|
|
@@ -770,7 +1324,7 @@ var handler8 = async (provider, args) => {
|
|
|
770
1324
|
} catch (error) {
|
|
771
1325
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
772
1326
|
return {
|
|
773
|
-
|
|
1327
|
+
success: false,
|
|
774
1328
|
message: {
|
|
775
1329
|
type: "error-text",
|
|
776
1330
|
value: `Error fetching file from ${url}: ${errorMessage}`
|
|
@@ -779,13 +1333,13 @@ var handler8 = async (provider, args) => {
|
|
|
779
1333
|
}
|
|
780
1334
|
};
|
|
781
1335
|
var readBinaryFile_default = {
|
|
782
|
-
...
|
|
783
|
-
handler:
|
|
1336
|
+
...toolInfo5,
|
|
1337
|
+
handler: handler5
|
|
784
1338
|
};
|
|
785
1339
|
|
|
786
1340
|
// src/tools/readFile.ts
|
|
787
1341
|
import { z as z11 } from "zod";
|
|
788
|
-
var
|
|
1342
|
+
var toolInfo6 = {
|
|
789
1343
|
name: "readFile",
|
|
790
1344
|
description: "Request to read the contents of one or multiple files at the specified paths. Use comma separated paths to read multiple files. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. May not be suitable for other types of binary files, as it returns the raw content as a string. Try to list all the potential files are relevent to the task, and then use this tool to read all the relevant files.",
|
|
791
1345
|
parameters: z11.object({
|
|
@@ -794,14 +1348,7 @@ var toolInfo9 = {
|
|
|
794
1348
|
const values = Array.isArray(val) ? val : [val];
|
|
795
1349
|
return values.flatMap((i) => typeof i === "string" ? i.split(",") : []).filter((s) => s.length > 0);
|
|
796
1350
|
}, z11.array(z11.string())).describe("The path of the file to read").meta({ usageValue: "Comma separated paths here" }),
|
|
797
|
-
includeIgnored: z11.preprocess((
|
|
798
|
-
if (typeof val === "string") {
|
|
799
|
-
const lower = val.toLowerCase();
|
|
800
|
-
if (lower === "false") return false;
|
|
801
|
-
if (lower === "true") return true;
|
|
802
|
-
}
|
|
803
|
-
return val;
|
|
804
|
-
}, z11.boolean().nullish().default(false)).describe("Whether to include ignored files. Use true to include files ignored by .gitignore.").meta({ usageValue: "true or false (optional)" })
|
|
1351
|
+
includeIgnored: z11.preprocess(preprocessBoolean, z11.boolean().nullish().default(false)).describe("Whether to include ignored files. Use true to include files ignored by .gitignore.").meta({ usageValue: "true or false (optional)" })
|
|
805
1352
|
}).meta({
|
|
806
1353
|
examples: [
|
|
807
1354
|
{
|
|
@@ -819,33 +1366,22 @@ var toolInfo9 = {
|
|
|
819
1366
|
]
|
|
820
1367
|
})
|
|
821
1368
|
};
|
|
822
|
-
var
|
|
1369
|
+
var handler6 = async (provider, args) => {
|
|
823
1370
|
if (!provider.readFile) {
|
|
824
|
-
return
|
|
825
|
-
type: "Error" /* Error */,
|
|
826
|
-
message: {
|
|
827
|
-
type: "error-text",
|
|
828
|
-
value: "Not possible to read file."
|
|
829
|
-
}
|
|
830
|
-
};
|
|
1371
|
+
return createProviderError("read file");
|
|
831
1372
|
}
|
|
832
|
-
const { path: paths, includeIgnored } =
|
|
1373
|
+
const { path: paths, includeIgnored } = toolInfo6.parameters.parse(args);
|
|
833
1374
|
const resp = [];
|
|
834
1375
|
for (const path of paths) {
|
|
835
1376
|
const fileContent = await provider.readFile(path, includeIgnored ?? false);
|
|
836
1377
|
if (!fileContent) {
|
|
837
|
-
resp.push(
|
|
1378
|
+
resp.push(createFileElement("read_file_file_content", path, void 0, { file_not_found: "true" }));
|
|
838
1379
|
} else {
|
|
839
|
-
|
|
840
|
-
if (isEmpty) {
|
|
841
|
-
resp.push(`<read_file_file_content path="${path}" is_empty="true" />`);
|
|
842
|
-
} else {
|
|
843
|
-
resp.push(`<read_file_file_content path="${path}">${fileContent}</read_file_file_content>`);
|
|
844
|
-
}
|
|
1380
|
+
resp.push(createFileElement("read_file_file_content", path, fileContent));
|
|
845
1381
|
}
|
|
846
1382
|
}
|
|
847
1383
|
return {
|
|
848
|
-
|
|
1384
|
+
success: true,
|
|
849
1385
|
message: {
|
|
850
1386
|
type: "text",
|
|
851
1387
|
value: resp.join("\n")
|
|
@@ -853,53 +1389,17 @@ var handler9 = async (provider, args) => {
|
|
|
853
1389
|
};
|
|
854
1390
|
};
|
|
855
1391
|
var readFile_default = {
|
|
856
|
-
...
|
|
857
|
-
handler:
|
|
858
|
-
};
|
|
859
|
-
|
|
860
|
-
// src/tools/readMemory.ts
|
|
861
|
-
import { z as z12 } from "zod";
|
|
862
|
-
var toolInfo10 = {
|
|
863
|
-
name: "readMemory",
|
|
864
|
-
description: "Reads content from a memory topic. Use this to retrieve information stored in previous steps. If no topic is specified, reads from the default topic.",
|
|
865
|
-
parameters: z12.object({
|
|
866
|
-
topic: z12.string().nullish().describe('The topic to read from memory. Defaults to ":default:".')
|
|
867
|
-
})
|
|
868
|
-
};
|
|
869
|
-
var handler10 = async (provider, args) => {
|
|
870
|
-
const { topic } = toolInfo10.parameters.parse(args);
|
|
871
|
-
const content = await provider.readMemory(topic ?? void 0);
|
|
872
|
-
if (content) {
|
|
873
|
-
return {
|
|
874
|
-
type: "Reply" /* Reply */,
|
|
875
|
-
message: {
|
|
876
|
-
type: "text",
|
|
877
|
-
value: `<memory${topic ? ` topic="${topic}"` : ""}>
|
|
878
|
-
${content}
|
|
879
|
-
</memory>`
|
|
880
|
-
}
|
|
881
|
-
};
|
|
882
|
-
}
|
|
883
|
-
return {
|
|
884
|
-
type: "Reply" /* Reply */,
|
|
885
|
-
message: {
|
|
886
|
-
type: "text",
|
|
887
|
-
value: `<memory ${topic ? `topic="${topic}"` : ""} isEmpty="true" />`
|
|
888
|
-
}
|
|
889
|
-
};
|
|
890
|
-
};
|
|
891
|
-
var readMemory_default = {
|
|
892
|
-
...toolInfo10,
|
|
893
|
-
handler: handler10
|
|
1392
|
+
...toolInfo6,
|
|
1393
|
+
handler: handler6
|
|
894
1394
|
};
|
|
895
1395
|
|
|
896
1396
|
// src/tools/removeFile.ts
|
|
897
|
-
import { z as
|
|
898
|
-
var
|
|
1397
|
+
import { z as z12 } from "zod";
|
|
1398
|
+
var toolInfo7 = {
|
|
899
1399
|
name: "removeFile",
|
|
900
1400
|
description: "Request to remove a file at the specified path.",
|
|
901
|
-
parameters:
|
|
902
|
-
path:
|
|
1401
|
+
parameters: z12.object({
|
|
1402
|
+
path: z12.string().describe("The path of the file to remove").meta({ usageValue: "File path here" })
|
|
903
1403
|
}).meta({
|
|
904
1404
|
examples: [
|
|
905
1405
|
{
|
|
@@ -911,20 +1411,14 @@ var toolInfo11 = {
|
|
|
911
1411
|
]
|
|
912
1412
|
})
|
|
913
1413
|
};
|
|
914
|
-
var
|
|
1414
|
+
var handler7 = async (provider, args) => {
|
|
915
1415
|
if (!provider.removeFile) {
|
|
916
|
-
return
|
|
917
|
-
type: "Error" /* Error */,
|
|
918
|
-
message: {
|
|
919
|
-
type: "error-text",
|
|
920
|
-
value: "Not possible to remove file."
|
|
921
|
-
}
|
|
922
|
-
};
|
|
1416
|
+
return createProviderError("remove file");
|
|
923
1417
|
}
|
|
924
|
-
const parsed =
|
|
1418
|
+
const parsed = toolInfo7.parameters.safeParse(args);
|
|
925
1419
|
if (!parsed.success) {
|
|
926
1420
|
return {
|
|
927
|
-
|
|
1421
|
+
success: false,
|
|
928
1422
|
message: {
|
|
929
1423
|
type: "error-text",
|
|
930
1424
|
value: `Invalid arguments for removeFile: ${parsed.error.message}`
|
|
@@ -934,7 +1428,7 @@ var handler11 = async (provider, args) => {
|
|
|
934
1428
|
const { path } = parsed.data;
|
|
935
1429
|
await provider.removeFile(path);
|
|
936
1430
|
return {
|
|
937
|
-
|
|
1431
|
+
success: true,
|
|
938
1432
|
message: {
|
|
939
1433
|
type: "text",
|
|
940
1434
|
value: `<remove_file_path>${path}</remove_file_path><status>Success</status>`
|
|
@@ -942,18 +1436,18 @@ var handler11 = async (provider, args) => {
|
|
|
942
1436
|
};
|
|
943
1437
|
};
|
|
944
1438
|
var removeFile_default = {
|
|
945
|
-
...
|
|
946
|
-
handler:
|
|
1439
|
+
...toolInfo7,
|
|
1440
|
+
handler: handler7
|
|
947
1441
|
};
|
|
948
1442
|
|
|
949
1443
|
// src/tools/renameFile.ts
|
|
950
|
-
import { z as
|
|
951
|
-
var
|
|
1444
|
+
import { z as z13 } from "zod";
|
|
1445
|
+
var toolInfo8 = {
|
|
952
1446
|
name: "renameFile",
|
|
953
1447
|
description: "Request to rename a file from source path to target path.",
|
|
954
|
-
parameters:
|
|
955
|
-
source_path:
|
|
956
|
-
target_path:
|
|
1448
|
+
parameters: z13.object({
|
|
1449
|
+
source_path: z13.string().describe("The current path of the file").meta({ usageValue: "Source file path here" }),
|
|
1450
|
+
target_path: z13.string().describe("The new path for the file").meta({ usageValue: "Target file path here" })
|
|
957
1451
|
}).meta({
|
|
958
1452
|
examples: [
|
|
959
1453
|
{
|
|
@@ -966,20 +1460,20 @@ var toolInfo12 = {
|
|
|
966
1460
|
]
|
|
967
1461
|
})
|
|
968
1462
|
};
|
|
969
|
-
var
|
|
1463
|
+
var handler8 = async (provider, args) => {
|
|
970
1464
|
if (!provider.renameFile) {
|
|
971
1465
|
return {
|
|
972
|
-
|
|
1466
|
+
success: false,
|
|
973
1467
|
message: {
|
|
974
1468
|
type: "error-text",
|
|
975
1469
|
value: "Not possible to rename file."
|
|
976
1470
|
}
|
|
977
1471
|
};
|
|
978
1472
|
}
|
|
979
|
-
const { source_path, target_path } =
|
|
1473
|
+
const { source_path, target_path } = toolInfo8.parameters.parse(args);
|
|
980
1474
|
await provider.renameFile(source_path, target_path);
|
|
981
1475
|
return {
|
|
982
|
-
|
|
1476
|
+
success: true,
|
|
983
1477
|
message: {
|
|
984
1478
|
type: "text",
|
|
985
1479
|
value: `<rename_file_path>${target_path}</rename_file_path><status>Success</status>`
|
|
@@ -987,12 +1481,12 @@ var handler12 = async (provider, args) => {
|
|
|
987
1481
|
};
|
|
988
1482
|
};
|
|
989
1483
|
var renameFile_default = {
|
|
990
|
-
...
|
|
991
|
-
handler:
|
|
1484
|
+
...toolInfo8,
|
|
1485
|
+
handler: handler8
|
|
992
1486
|
};
|
|
993
1487
|
|
|
994
1488
|
// src/tools/replaceInFile.ts
|
|
995
|
-
import { z as
|
|
1489
|
+
import { z as z14 } from "zod";
|
|
996
1490
|
|
|
997
1491
|
// src/tools/utils/replaceInFile.ts
|
|
998
1492
|
var replaceInFile = (fileContent, diff) => {
|
|
@@ -1035,10 +1529,27 @@ var replaceInFile = (fileContent, diff) => {
|
|
|
1035
1529
|
}
|
|
1036
1530
|
runningIndex++;
|
|
1037
1531
|
}
|
|
1038
|
-
const
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1532
|
+
const searchWords = trimmedSearch.replace(/\s+/g, " ").split(" ").filter((w) => w.length > 0);
|
|
1533
|
+
let matchStartPos = -1;
|
|
1534
|
+
let matchEndPos = -1;
|
|
1535
|
+
if (searchWords.length > 0) {
|
|
1536
|
+
const firstWordPos = content.indexOf(searchWords[0], offset);
|
|
1537
|
+
if (firstWordPos !== -1) {
|
|
1538
|
+
matchStartPos = firstWordPos;
|
|
1539
|
+
matchEndPos = firstWordPos + searchWords[0].length;
|
|
1540
|
+
for (let i = 1; i < searchWords.length; i++) {
|
|
1541
|
+
const nextWordPos = content.indexOf(searchWords[i], matchEndPos);
|
|
1542
|
+
if (nextWordPos === -1 || nextWordPos > matchEndPos + 100) {
|
|
1543
|
+
matchStartPos = -1;
|
|
1544
|
+
break;
|
|
1545
|
+
}
|
|
1546
|
+
matchEndPos = nextWordPos + searchWords[i].length;
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
if (matchStartPos !== -1 && matchEndPos !== -1) {
|
|
1551
|
+
return content.slice(0, matchStartPos) + replace + content.slice(matchEndPos);
|
|
1552
|
+
}
|
|
1042
1553
|
}
|
|
1043
1554
|
return null;
|
|
1044
1555
|
};
|
|
@@ -1069,12 +1580,12 @@ var replaceInFile = (fileContent, diff) => {
|
|
|
1069
1580
|
};
|
|
1070
1581
|
|
|
1071
1582
|
// src/tools/replaceInFile.ts
|
|
1072
|
-
var
|
|
1583
|
+
var toolInfo9 = {
|
|
1073
1584
|
name: "replaceInFile",
|
|
1074
1585
|
description: "Request to replace sections of content in an existing file using SEARCH/REPLACE blocks that define exact changes to specific parts of the file. This tool should be used when you need to make targeted changes to specific parts of a file.",
|
|
1075
|
-
parameters:
|
|
1076
|
-
path:
|
|
1077
|
-
diff:
|
|
1586
|
+
parameters: z14.object({
|
|
1587
|
+
path: z14.string().describe("The path of the file to modify").meta({ usageValue: "File path here" }),
|
|
1588
|
+
diff: z14.string().describe(
|
|
1078
1589
|
`One or more SEARCH/REPLACE blocks following this exact format:
|
|
1079
1590
|
\`\`\`
|
|
1080
1591
|
<<<<<<< SEARCH
|
|
@@ -1181,20 +1692,20 @@ function oldFeature() {
|
|
|
1181
1692
|
]
|
|
1182
1693
|
})
|
|
1183
1694
|
};
|
|
1184
|
-
var
|
|
1695
|
+
var handler9 = async (provider, args) => {
|
|
1185
1696
|
if (!provider.readFile || !provider.writeFile) {
|
|
1186
1697
|
return {
|
|
1187
|
-
|
|
1698
|
+
success: false,
|
|
1188
1699
|
message: {
|
|
1189
1700
|
type: "error-text",
|
|
1190
1701
|
value: "Not possible to replace in file."
|
|
1191
1702
|
}
|
|
1192
1703
|
};
|
|
1193
1704
|
}
|
|
1194
|
-
const parsed =
|
|
1705
|
+
const parsed = toolInfo9.parameters.safeParse(args);
|
|
1195
1706
|
if (!parsed.success) {
|
|
1196
1707
|
return {
|
|
1197
|
-
|
|
1708
|
+
success: false,
|
|
1198
1709
|
message: {
|
|
1199
1710
|
type: "error-text",
|
|
1200
1711
|
value: `Invalid arguments for replaceInFile: ${parsed.error.message}`
|
|
@@ -1206,7 +1717,7 @@ var handler13 = async (provider, args) => {
|
|
|
1206
1717
|
const fileContent = await provider.readFile(path, false);
|
|
1207
1718
|
if (fileContent == null) {
|
|
1208
1719
|
return {
|
|
1209
|
-
|
|
1720
|
+
success: false,
|
|
1210
1721
|
message: {
|
|
1211
1722
|
type: "error-text",
|
|
1212
1723
|
value: `<replace_in_file_result path="${path}" status="failed" message="File not found" />`
|
|
@@ -1216,7 +1727,7 @@ var handler13 = async (provider, args) => {
|
|
|
1216
1727
|
const result = replaceInFile(fileContent, diff);
|
|
1217
1728
|
if (result.status === "no_diff_applied") {
|
|
1218
1729
|
return {
|
|
1219
|
-
|
|
1730
|
+
success: false,
|
|
1220
1731
|
message: {
|
|
1221
1732
|
type: "error-text",
|
|
1222
1733
|
value: `<replace_in_file_result path="${path}" status="failed" message="Unable to apply changes">
|
|
@@ -1228,7 +1739,7 @@ var handler13 = async (provider, args) => {
|
|
|
1228
1739
|
await provider.writeFile(path, result.content);
|
|
1229
1740
|
if (result.status === "some_diff_applied") {
|
|
1230
1741
|
return {
|
|
1231
|
-
|
|
1742
|
+
success: true,
|
|
1232
1743
|
message: {
|
|
1233
1744
|
type: "text",
|
|
1234
1745
|
value: `<replace_in_file_result path="${path}" status="some_diff_applied" applied_count="${result.appliedCount}" total_count="${result.totalCount}">
|
|
@@ -1238,7 +1749,7 @@ var handler13 = async (provider, args) => {
|
|
|
1238
1749
|
};
|
|
1239
1750
|
}
|
|
1240
1751
|
return {
|
|
1241
|
-
|
|
1752
|
+
success: true,
|
|
1242
1753
|
message: {
|
|
1243
1754
|
type: "text",
|
|
1244
1755
|
value: `<replace_in_file_result path="${path}" status="all_diff_applied" />`
|
|
@@ -1246,7 +1757,7 @@ var handler13 = async (provider, args) => {
|
|
|
1246
1757
|
};
|
|
1247
1758
|
} catch (error) {
|
|
1248
1759
|
return {
|
|
1249
|
-
|
|
1760
|
+
success: false,
|
|
1250
1761
|
message: {
|
|
1251
1762
|
type: "error-text",
|
|
1252
1763
|
value: `Invalid arguments for replaceInFile: ${error}`
|
|
@@ -1255,17 +1766,17 @@ var handler13 = async (provider, args) => {
|
|
|
1255
1766
|
}
|
|
1256
1767
|
};
|
|
1257
1768
|
var replaceInFile_default = {
|
|
1258
|
-
...
|
|
1259
|
-
handler:
|
|
1769
|
+
...toolInfo9,
|
|
1770
|
+
handler: handler9
|
|
1260
1771
|
};
|
|
1261
1772
|
|
|
1262
1773
|
// src/tools/search.ts
|
|
1263
|
-
import { z as
|
|
1264
|
-
var
|
|
1774
|
+
import { z as z15 } from "zod";
|
|
1775
|
+
var toolInfo10 = {
|
|
1265
1776
|
name: "search",
|
|
1266
1777
|
description: "Search the web for information using Google Search. Use this tool to find current information, facts, news, documentation, or research that is not available in your training data. Returns comprehensive search results with relevant content extracted from the web.",
|
|
1267
|
-
parameters:
|
|
1268
|
-
query:
|
|
1778
|
+
parameters: z15.object({
|
|
1779
|
+
query: z15.string().describe("The query to search for").meta({ usageValue: "Your search query here" })
|
|
1269
1780
|
}).meta({
|
|
1270
1781
|
examples: [
|
|
1271
1782
|
{
|
|
@@ -1289,11 +1800,11 @@ var toolInfo14 = {
|
|
|
1289
1800
|
]
|
|
1290
1801
|
})
|
|
1291
1802
|
};
|
|
1292
|
-
var
|
|
1293
|
-
const { query } =
|
|
1803
|
+
var handler10 = async (provider, args) => {
|
|
1804
|
+
const { query } = toolInfo10.parameters.parse(args);
|
|
1294
1805
|
if (!provider.search) {
|
|
1295
1806
|
return {
|
|
1296
|
-
|
|
1807
|
+
success: false,
|
|
1297
1808
|
message: {
|
|
1298
1809
|
type: "text",
|
|
1299
1810
|
value: "This tool requires a web provider to be installed."
|
|
@@ -1302,7 +1813,7 @@ var handler14 = async (provider, args) => {
|
|
|
1302
1813
|
}
|
|
1303
1814
|
const result = await provider.search(query);
|
|
1304
1815
|
return {
|
|
1305
|
-
|
|
1816
|
+
success: true,
|
|
1306
1817
|
message: {
|
|
1307
1818
|
type: "text",
|
|
1308
1819
|
value: result
|
|
@@ -1310,23 +1821,23 @@ var handler14 = async (provider, args) => {
|
|
|
1310
1821
|
};
|
|
1311
1822
|
};
|
|
1312
1823
|
var search_default = {
|
|
1313
|
-
...
|
|
1314
|
-
handler:
|
|
1824
|
+
...toolInfo10,
|
|
1825
|
+
handler: handler10
|
|
1315
1826
|
};
|
|
1316
1827
|
|
|
1317
1828
|
// src/tools/searchFiles.ts
|
|
1318
|
-
import { z as
|
|
1319
|
-
var
|
|
1829
|
+
import { z as z16 } from "zod";
|
|
1830
|
+
var toolInfo11 = {
|
|
1320
1831
|
name: "searchFiles",
|
|
1321
1832
|
description: "Request to perform a regex search across files in a specified directory, outputting context-rich results that include surrounding lines. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.",
|
|
1322
|
-
parameters:
|
|
1323
|
-
path:
|
|
1833
|
+
parameters: z16.object({
|
|
1834
|
+
path: z16.string().describe(
|
|
1324
1835
|
"The path of the directory to search in (relative to the current working directory). This directory will be recursively searched."
|
|
1325
1836
|
).meta({ usageValue: "Directory path here" }),
|
|
1326
|
-
regex:
|
|
1837
|
+
regex: z16.string().describe("The regular expression pattern to search for. Uses Rust regex syntax.").meta({
|
|
1327
1838
|
usageValue: "Your regex pattern here"
|
|
1328
1839
|
}),
|
|
1329
|
-
filePattern:
|
|
1840
|
+
filePattern: z16.string().optional().describe(
|
|
1330
1841
|
'Comma-separated glob pattern to filter files (e.g., "*.ts" for TypeScript files or "*.ts,*.js" for both TypeScript and JavaScript files). If not provided, it will search all files (*).'
|
|
1331
1842
|
).meta({
|
|
1332
1843
|
usageValue: "file pattern here (optional)"
|
|
@@ -1344,20 +1855,20 @@ var toolInfo15 = {
|
|
|
1344
1855
|
]
|
|
1345
1856
|
})
|
|
1346
1857
|
};
|
|
1347
|
-
var
|
|
1858
|
+
var handler11 = async (provider, args) => {
|
|
1348
1859
|
if (!provider.searchFiles) {
|
|
1349
1860
|
return {
|
|
1350
|
-
|
|
1861
|
+
success: false,
|
|
1351
1862
|
message: {
|
|
1352
1863
|
type: "error-text",
|
|
1353
1864
|
value: "Not possible to search files."
|
|
1354
1865
|
}
|
|
1355
1866
|
};
|
|
1356
1867
|
}
|
|
1357
|
-
const parsed =
|
|
1868
|
+
const parsed = toolInfo11.parameters.safeParse(args);
|
|
1358
1869
|
if (!parsed.success) {
|
|
1359
1870
|
return {
|
|
1360
|
-
|
|
1871
|
+
success: false,
|
|
1361
1872
|
message: {
|
|
1362
1873
|
type: "error-text",
|
|
1363
1874
|
value: `Invalid arguments for searchFiles: ${parsed.error.message}`
|
|
@@ -1368,7 +1879,7 @@ var handler15 = async (provider, args) => {
|
|
|
1368
1879
|
try {
|
|
1369
1880
|
const files = await provider.searchFiles(path, regex, filePattern ?? "*");
|
|
1370
1881
|
return {
|
|
1371
|
-
|
|
1882
|
+
success: true,
|
|
1372
1883
|
message: {
|
|
1373
1884
|
type: "text",
|
|
1374
1885
|
value: `<search_files_path>${path}</search_files_path>
|
|
@@ -1382,7 +1893,7 @@ ${files.join("\n")}
|
|
|
1382
1893
|
};
|
|
1383
1894
|
} catch (error) {
|
|
1384
1895
|
return {
|
|
1385
|
-
|
|
1896
|
+
success: false,
|
|
1386
1897
|
message: {
|
|
1387
1898
|
type: "error-text",
|
|
1388
1899
|
value: `Error searching files: ${error}`
|
|
@@ -1391,122 +1902,57 @@ ${files.join("\n")}
|
|
|
1391
1902
|
}
|
|
1392
1903
|
};
|
|
1393
1904
|
var searchFiles_default = {
|
|
1394
|
-
...
|
|
1395
|
-
handler:
|
|
1905
|
+
...toolInfo11,
|
|
1906
|
+
handler: handler11
|
|
1396
1907
|
};
|
|
1397
1908
|
|
|
1398
|
-
// src/tools/
|
|
1399
|
-
import { z as
|
|
1400
|
-
var
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
});
|
|
1423
|
-
}
|
|
1909
|
+
// src/tools/todo.ts
|
|
1910
|
+
import { z as z17 } from "zod";
|
|
1911
|
+
var TodoStatus = z17.enum(["open", "completed", "closed"]);
|
|
1912
|
+
var TodoItemSchema = z17.object({
|
|
1913
|
+
id: z17.string(),
|
|
1914
|
+
title: z17.string(),
|
|
1915
|
+
description: z17.string(),
|
|
1916
|
+
status: TodoStatus
|
|
1917
|
+
});
|
|
1918
|
+
var UpdateTodoItemInputSchema = z17.object({
|
|
1919
|
+
operation: z17.enum(["add", "update"]),
|
|
1920
|
+
id: z17.string().nullish(),
|
|
1921
|
+
parentId: z17.string().nullish(),
|
|
1922
|
+
title: z17.string().nullish(),
|
|
1923
|
+
description: z17.string().nullish(),
|
|
1924
|
+
status: TodoStatus.nullish()
|
|
1925
|
+
}).superRefine((data, ctx) => {
|
|
1926
|
+
if (data.operation === "add") {
|
|
1927
|
+
if (!data.title) {
|
|
1928
|
+
ctx.addIssue({
|
|
1929
|
+
code: "custom",
|
|
1930
|
+
message: 'Title is required for "add" operation',
|
|
1931
|
+
path: ["title"]
|
|
1932
|
+
});
|
|
1424
1933
|
}
|
|
1425
|
-
})
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
type: "error-text",
|
|
1433
|
-
value: "Memory operations are not supported by the current provider."
|
|
1434
|
-
}
|
|
1435
|
-
};
|
|
1436
|
-
}
|
|
1437
|
-
const params = toolInfo16.parameters.parse(args);
|
|
1438
|
-
await provider.updateMemory(params.operation, params.topic ?? void 0, params.content ?? void 0);
|
|
1439
|
-
switch (params.operation) {
|
|
1440
|
-
case "append":
|
|
1441
|
-
return {
|
|
1442
|
-
type: "Reply" /* Reply */,
|
|
1443
|
-
message: {
|
|
1444
|
-
type: "text",
|
|
1445
|
-
value: `Content appended to memory topic '${params.topic || ":default:"}'.`
|
|
1446
|
-
}
|
|
1447
|
-
};
|
|
1448
|
-
case "replace":
|
|
1449
|
-
return {
|
|
1450
|
-
type: "Reply" /* Reply */,
|
|
1451
|
-
message: {
|
|
1452
|
-
type: "text",
|
|
1453
|
-
value: `Memory topic '${params.topic || ":default:"}' replaced.`
|
|
1454
|
-
}
|
|
1455
|
-
};
|
|
1456
|
-
case "remove":
|
|
1457
|
-
return {
|
|
1458
|
-
type: "Reply" /* Reply */,
|
|
1459
|
-
message: {
|
|
1460
|
-
type: "text",
|
|
1461
|
-
value: `Memory topic '${params.topic || ":default:"}' removed.`
|
|
1462
|
-
}
|
|
1463
|
-
};
|
|
1464
|
-
}
|
|
1465
|
-
};
|
|
1466
|
-
var updateMemory_default = {
|
|
1467
|
-
...toolInfo16,
|
|
1468
|
-
handler: handler16
|
|
1469
|
-
};
|
|
1470
|
-
|
|
1471
|
-
// src/tools/updateTodoItem.ts
|
|
1472
|
-
var toolInfo17 = {
|
|
1473
|
-
name: "updateTodoItem",
|
|
1474
|
-
description: "Add or update a to-do item.",
|
|
1475
|
-
parameters: UpdateTodoItemInputSchema
|
|
1476
|
-
};
|
|
1477
|
-
var handler17 = async (provider, args) => {
|
|
1478
|
-
if (!provider.updateTodoItem) {
|
|
1479
|
-
return {
|
|
1480
|
-
type: "Error" /* Error */,
|
|
1481
|
-
message: {
|
|
1482
|
-
type: "error-text",
|
|
1483
|
-
value: "Not possible to update a to-do item."
|
|
1484
|
-
}
|
|
1485
|
-
};
|
|
1486
|
-
}
|
|
1487
|
-
const input = toolInfo17.parameters.parse(args);
|
|
1488
|
-
const result = await provider.updateTodoItem(input);
|
|
1489
|
-
return {
|
|
1490
|
-
type: "Reply" /* Reply */,
|
|
1491
|
-
message: {
|
|
1492
|
-
type: "json",
|
|
1493
|
-
value: result
|
|
1934
|
+
} else if (data.operation === "update") {
|
|
1935
|
+
if (!data.id) {
|
|
1936
|
+
ctx.addIssue({
|
|
1937
|
+
code: "custom",
|
|
1938
|
+
message: 'ID is required for "update" operation',
|
|
1939
|
+
path: ["id"]
|
|
1940
|
+
});
|
|
1494
1941
|
}
|
|
1495
|
-
}
|
|
1496
|
-
};
|
|
1497
|
-
var
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
};
|
|
1942
|
+
}
|
|
1943
|
+
});
|
|
1944
|
+
var UpdateTodoItemOutputSchema = z17.object({
|
|
1945
|
+
id: z17.string()
|
|
1946
|
+
});
|
|
1501
1947
|
|
|
1502
1948
|
// src/tools/writeToFile.ts
|
|
1503
|
-
import { z as
|
|
1504
|
-
var
|
|
1949
|
+
import { z as z18 } from "zod";
|
|
1950
|
+
var toolInfo12 = {
|
|
1505
1951
|
name: "writeToFile",
|
|
1506
1952
|
description: "Request to write content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Ensure that the output content does not include incorrect escaped character patterns such as `<`, `>`, or `&`. Also ensure there is no unwanted CDATA tags in the content.",
|
|
1507
|
-
parameters:
|
|
1508
|
-
path:
|
|
1509
|
-
content:
|
|
1953
|
+
parameters: z18.object({
|
|
1954
|
+
path: z18.string().describe("The path of the file to write to").meta({ usageValue: "File path here" }),
|
|
1955
|
+
content: z18.string().describe(
|
|
1510
1956
|
"The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified."
|
|
1511
1957
|
).meta({ usageValue: "Your file content here" })
|
|
1512
1958
|
}).meta({
|
|
@@ -1532,20 +1978,14 @@ export default App;
|
|
|
1532
1978
|
]
|
|
1533
1979
|
})
|
|
1534
1980
|
};
|
|
1535
|
-
var
|
|
1981
|
+
var handler12 = async (provider, args) => {
|
|
1536
1982
|
if (!provider.writeFile) {
|
|
1537
|
-
return
|
|
1538
|
-
type: "Error" /* Error */,
|
|
1539
|
-
message: {
|
|
1540
|
-
type: "error-text",
|
|
1541
|
-
value: "Not possible to write file."
|
|
1542
|
-
}
|
|
1543
|
-
};
|
|
1983
|
+
return createProviderError("write file");
|
|
1544
1984
|
}
|
|
1545
|
-
const parsed =
|
|
1985
|
+
const parsed = toolInfo12.parameters.safeParse(args);
|
|
1546
1986
|
if (!parsed.success) {
|
|
1547
1987
|
return {
|
|
1548
|
-
|
|
1988
|
+
success: false,
|
|
1549
1989
|
message: {
|
|
1550
1990
|
type: "error-text",
|
|
1551
1991
|
value: `Invalid arguments for writeToFile: ${parsed.error.message}`
|
|
@@ -1554,10 +1994,10 @@ var handler18 = async (provider, args) => {
|
|
|
1554
1994
|
}
|
|
1555
1995
|
let { path, content } = parsed.data;
|
|
1556
1996
|
const trimmedContent = content.trim();
|
|
1557
|
-
if (trimmedContent.startsWith("<![CDATA[") &&
|
|
1997
|
+
if (trimmedContent.startsWith("<![CDATA[") && trimmedContent.endsWith("]]>")) content = trimmedContent.slice(9, -3);
|
|
1558
1998
|
await provider.writeFile(path, content);
|
|
1559
1999
|
return {
|
|
1560
|
-
|
|
2000
|
+
success: true,
|
|
1561
2001
|
message: {
|
|
1562
2002
|
type: "text",
|
|
1563
2003
|
value: `<write_to_file_path>${path}</write_to_file_path><status>Success</status>`
|
|
@@ -1565,8 +2005,8 @@ var handler18 = async (provider, args) => {
|
|
|
1565
2005
|
};
|
|
1566
2006
|
};
|
|
1567
2007
|
var writeToFile_default = {
|
|
1568
|
-
...
|
|
1569
|
-
handler:
|
|
2008
|
+
...toolInfo12,
|
|
2009
|
+
handler: handler12
|
|
1570
2010
|
};
|
|
1571
2011
|
|
|
1572
2012
|
// src/UsageMeter.ts
|
|
@@ -1591,7 +2031,7 @@ var UsageMeter = class {
|
|
|
1591
2031
|
this.#maxMessages = opts.maxMessages ?? 1e3;
|
|
1592
2032
|
this.#maxCost = opts.maxCost ?? 100;
|
|
1593
2033
|
}
|
|
1594
|
-
#
|
|
2034
|
+
#calculateUsage(usage, providerMetadata, modelInfo) {
|
|
1595
2035
|
const providerMetadataKey = Object.keys(providerMetadata ?? {})[0];
|
|
1596
2036
|
const metadata = providerMetadata?.[providerMetadataKey] ?? {};
|
|
1597
2037
|
switch (providerMetadataKey) {
|
|
@@ -1649,7 +2089,7 @@ var UsageMeter = class {
|
|
|
1649
2089
|
cacheReadsPrice: 0
|
|
1650
2090
|
};
|
|
1651
2091
|
const usage = "totalUsage" in resp ? resp.totalUsage : resp.usage;
|
|
1652
|
-
const result = this.#
|
|
2092
|
+
const result = this.#calculateUsage(usage, resp.providerMetadata, modelInfo);
|
|
1653
2093
|
this.#totals.input += result.input || 0;
|
|
1654
2094
|
this.#totals.output += result.output || 0;
|
|
1655
2095
|
this.#totals.cachedRead += result.cachedRead || 0;
|
|
@@ -1719,7 +2159,7 @@ var UsageMeter = class {
|
|
|
1719
2159
|
|
|
1720
2160
|
// src/workflow/agent.workflow.ts
|
|
1721
2161
|
import { jsonSchema } from "ai";
|
|
1722
|
-
import { toJSONSchema, z as
|
|
2162
|
+
import { toJSONSchema, z as z19 } from "zod";
|
|
1723
2163
|
|
|
1724
2164
|
// src/workflow/types.ts
|
|
1725
2165
|
var TaskEventKind = /* @__PURE__ */ ((TaskEventKind2) => {
|
|
@@ -1739,11 +2179,11 @@ var TaskEventKind = /* @__PURE__ */ ((TaskEventKind2) => {
|
|
|
1739
2179
|
// src/workflow/agent.workflow.ts
|
|
1740
2180
|
var agentWorkflow = async (input, { step, tools, logger }) => {
|
|
1741
2181
|
const event = (name, event2) => step(name, () => tools.taskEvent(event2));
|
|
1742
|
-
const { tools:
|
|
2182
|
+
const { tools: toolInfo13, maxToolRoundTrips = 200 } = input;
|
|
1743
2183
|
const messages = "systemPrompt" in input ? [{ role: "system", content: input.systemPrompt }] : input.messages;
|
|
1744
2184
|
await event("start-task", { kind: "StartTask" /* StartTask */, systemPrompt: "systemPrompt" in input ? input.systemPrompt : "" });
|
|
1745
2185
|
const toolSet = {};
|
|
1746
|
-
for (const tool of
|
|
2186
|
+
for (const tool of toolInfo13) {
|
|
1747
2187
|
toolSet[tool.name] = {
|
|
1748
2188
|
description: tool.description,
|
|
1749
2189
|
inputSchema: jsonSchema(toJSONSchema(tool.parameters))
|
|
@@ -1791,9 +2231,9 @@ var agentWorkflow = async (input, { step, tools, logger }) => {
|
|
|
1791
2231
|
await event(`end-round-${i}`, { kind: "EndRequest" /* EndRequest */, message: textContent });
|
|
1792
2232
|
if (toolCalls.length === 0) {
|
|
1793
2233
|
if (!input.outputSchema) {
|
|
1794
|
-
const
|
|
1795
|
-
await event("end-task", { kind: "EndTask" /* EndTask */, exitReason:
|
|
1796
|
-
return
|
|
2234
|
+
const exitReason3 = { type: "Exit", message: textContent, messages };
|
|
2235
|
+
await event("end-task", { kind: "EndTask" /* EndTask */, exitReason: exitReason3 });
|
|
2236
|
+
return exitReason3;
|
|
1797
2237
|
}
|
|
1798
2238
|
const parsed = parseJsonFromMarkdown(textContent);
|
|
1799
2239
|
if (!parsed.success) {
|
|
@@ -1803,13 +2243,13 @@ var agentWorkflow = async (input, { step, tools, logger }) => {
|
|
|
1803
2243
|
}
|
|
1804
2244
|
const validated = input.outputSchema.safeParse(parsed.data);
|
|
1805
2245
|
if (!validated.success) {
|
|
1806
|
-
const errorMessage = `Output validation failed. Error: ${
|
|
2246
|
+
const errorMessage = `Output validation failed. Error: ${z19.prettifyError(validated.error)}. Please correct the output.`;
|
|
1807
2247
|
nextMessage = [{ role: "user", content: errorMessage }];
|
|
1808
2248
|
continue;
|
|
1809
2249
|
}
|
|
1810
|
-
const
|
|
1811
|
-
await event("end-task", { kind: "EndTask" /* EndTask */, exitReason });
|
|
1812
|
-
return
|
|
2250
|
+
const exitReason2 = { type: "Exit", message: textContent, object: validated.data, messages };
|
|
2251
|
+
await event("end-task", { kind: "EndTask" /* EndTask */, exitReason: exitReason2 });
|
|
2252
|
+
return exitReason2;
|
|
1813
2253
|
}
|
|
1814
2254
|
const toolResults = [];
|
|
1815
2255
|
for (const toolCall of toolCalls) {
|
|
@@ -1844,128 +2284,277 @@ var agentWorkflow = async (input, { step, tools, logger }) => {
|
|
|
1844
2284
|
input: toolCall.input
|
|
1845
2285
|
});
|
|
1846
2286
|
});
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
toolName: toolCall.toolName,
|
|
1877
|
-
output: {
|
|
1878
|
-
type: "error-text",
|
|
1879
|
-
value: `Error: The tool '${toolCall.toolName}' must be called alone, but it was called with other tools.`
|
|
1880
|
-
}
|
|
1881
|
-
});
|
|
1882
|
-
break;
|
|
1883
|
-
}
|
|
1884
|
-
if (toolResults.length > 0) {
|
|
1885
|
-
break;
|
|
1886
|
-
}
|
|
1887
|
-
const exitReason = { ...toolResponse, messages };
|
|
1888
|
-
await event("end-task", { kind: "EndTask" /* EndTask */, exitReason });
|
|
1889
|
-
return exitReason;
|
|
1890
|
-
}
|
|
1891
|
-
}
|
|
1892
|
-
}
|
|
1893
|
-
nextMessage = [
|
|
1894
|
-
{
|
|
1895
|
-
role: "tool",
|
|
1896
|
-
content: toolResults.map((r) => ({
|
|
1897
|
-
type: "tool-result",
|
|
2287
|
+
if (toolResponse.success) {
|
|
2288
|
+
await event(`event-tool-reply-${toolCall.toolName}-${toolCall.toolCallId}`, {
|
|
2289
|
+
kind: "ToolReply" /* ToolReply */,
|
|
2290
|
+
tool: toolCall.toolName,
|
|
2291
|
+
content: toolResponse.message
|
|
2292
|
+
});
|
|
2293
|
+
toolResults.push({
|
|
2294
|
+
toolCallId: toolCall.toolCallId,
|
|
2295
|
+
toolName: toolCall.toolName,
|
|
2296
|
+
output: toolResponse.message
|
|
2297
|
+
});
|
|
2298
|
+
} else {
|
|
2299
|
+
await event(`event-tool-error-${toolCall.toolName}-${toolCall.toolCallId}`, {
|
|
2300
|
+
kind: "ToolError" /* ToolError */,
|
|
2301
|
+
tool: toolCall.toolName,
|
|
2302
|
+
error: toolResponse.message ?? "Unknown error"
|
|
2303
|
+
});
|
|
2304
|
+
toolResults.push({
|
|
2305
|
+
toolCallId: toolCall.toolCallId,
|
|
2306
|
+
toolName: toolCall.toolName,
|
|
2307
|
+
output: toolResponse.message
|
|
2308
|
+
});
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
nextMessage = [
|
|
2312
|
+
{
|
|
2313
|
+
role: "tool",
|
|
2314
|
+
content: toolResults.map((r) => ({
|
|
2315
|
+
type: "tool-result",
|
|
1898
2316
|
...r
|
|
1899
2317
|
}))
|
|
1900
2318
|
}
|
|
1901
2319
|
];
|
|
1902
2320
|
}
|
|
1903
|
-
|
|
1904
|
-
|
|
2321
|
+
const exitReason = { type: "UsageExceeded", messages };
|
|
2322
|
+
await event("end-task", { kind: "EndTask" /* EndTask */, exitReason });
|
|
2323
|
+
return exitReason;
|
|
1905
2324
|
};
|
|
1906
2325
|
|
|
1907
2326
|
// src/workflow/dynamic.ts
|
|
1908
|
-
import { parse } from "yaml";
|
|
1909
|
-
import { z as
|
|
2327
|
+
import { parse as parse2 } from "yaml";
|
|
2328
|
+
import { z as z21 } from "zod";
|
|
1910
2329
|
|
|
1911
2330
|
// src/workflow/dynamic-types.ts
|
|
1912
|
-
import { z as
|
|
1913
|
-
var WorkflowInputDefinitionSchema =
|
|
1914
|
-
id:
|
|
1915
|
-
description:
|
|
1916
|
-
default:
|
|
2331
|
+
import { z as z20 } from "zod";
|
|
2332
|
+
var WorkflowInputDefinitionSchema = z20.object({
|
|
2333
|
+
id: z20.string(),
|
|
2334
|
+
description: z20.string().nullish(),
|
|
2335
|
+
default: z20.any().nullish()
|
|
1917
2336
|
});
|
|
1918
|
-
var WorkflowStepDefinitionSchema =
|
|
1919
|
-
id:
|
|
1920
|
-
tools:
|
|
1921
|
-
task:
|
|
1922
|
-
output:
|
|
1923
|
-
expected_outcome:
|
|
1924
|
-
/**
|
|
1925
|
-
* Persisted JavaScript/TypeScript (JS-compatible) async function body.
|
|
1926
|
-
* The code is wrapped as: `async (ctx) => { <code> }`.
|
|
1927
|
-
*/
|
|
1928
|
-
code: z21.string().nullish(),
|
|
2337
|
+
var WorkflowStepDefinitionSchema = z20.object({
|
|
2338
|
+
id: z20.string(),
|
|
2339
|
+
tools: z20.array(z20.string()).nullish(),
|
|
2340
|
+
task: z20.string(),
|
|
2341
|
+
output: z20.string().nullish(),
|
|
2342
|
+
expected_outcome: z20.string().nullish(),
|
|
1929
2343
|
/**
|
|
1930
2344
|
* Optional JSON schema or other metadata for future structured outputs.
|
|
1931
2345
|
* Not interpreted by core today.
|
|
1932
2346
|
*/
|
|
1933
|
-
outputSchema:
|
|
2347
|
+
outputSchema: z20.any().nullish(),
|
|
1934
2348
|
/**
|
|
1935
2349
|
* Optional timeout in milliseconds. Step execution will be aborted if it exceeds this duration.
|
|
1936
2350
|
*/
|
|
1937
|
-
timeout:
|
|
2351
|
+
timeout: z20.number().positive().nullish()
|
|
1938
2352
|
});
|
|
1939
|
-
var
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
2353
|
+
var WhileLoopStepSchema = z20.object({
|
|
2354
|
+
id: z20.string(),
|
|
2355
|
+
while: z20.object({
|
|
2356
|
+
condition: z20.string().describe("JavaScript expression that evaluates to true/false"),
|
|
2357
|
+
steps: z20.array(z20.lazy(() => WorkflowControlFlowStepSchema))
|
|
2358
|
+
}),
|
|
2359
|
+
output: z20.string().nullish()
|
|
1944
2360
|
});
|
|
1945
|
-
var
|
|
1946
|
-
|
|
2361
|
+
var IfElseStepSchema = z20.object({
|
|
2362
|
+
id: z20.string(),
|
|
2363
|
+
if: z20.object({
|
|
2364
|
+
condition: z20.string().describe("JavaScript expression that evaluates to true/false"),
|
|
2365
|
+
thenBranch: z20.array(z20.lazy(() => WorkflowControlFlowStepSchema)),
|
|
2366
|
+
elseBranch: z20.array(z20.lazy(() => WorkflowControlFlowStepSchema)).optional()
|
|
2367
|
+
}),
|
|
2368
|
+
output: z20.string().nullish()
|
|
2369
|
+
});
|
|
2370
|
+
var BreakStepSchema = z20.object({
|
|
2371
|
+
break: z20.literal(true)
|
|
2372
|
+
});
|
|
2373
|
+
var ContinueStepSchema = z20.object({
|
|
2374
|
+
continue: z20.literal(true)
|
|
2375
|
+
});
|
|
2376
|
+
var TryCatchStepSchema = z20.object({
|
|
2377
|
+
id: z20.string(),
|
|
2378
|
+
try: z20.object({
|
|
2379
|
+
trySteps: z20.array(z20.lazy(() => WorkflowControlFlowStepSchema)),
|
|
2380
|
+
catchSteps: z20.array(z20.lazy(() => WorkflowControlFlowStepSchema))
|
|
2381
|
+
}),
|
|
2382
|
+
output: z20.string().nullish()
|
|
2383
|
+
});
|
|
2384
|
+
var WorkflowControlFlowStepSchema = z20.union([
|
|
2385
|
+
WorkflowStepDefinitionSchema,
|
|
2386
|
+
WhileLoopStepSchema,
|
|
2387
|
+
IfElseStepSchema,
|
|
2388
|
+
BreakStepSchema,
|
|
2389
|
+
ContinueStepSchema,
|
|
2390
|
+
TryCatchStepSchema
|
|
2391
|
+
]);
|
|
2392
|
+
var WorkflowDefinitionSchema = z20.object({
|
|
2393
|
+
task: z20.string(),
|
|
2394
|
+
inputs: z20.array(WorkflowInputDefinitionSchema).nullish(),
|
|
2395
|
+
steps: z20.array(WorkflowControlFlowStepSchema),
|
|
2396
|
+
output: z20.string().nullish()
|
|
2397
|
+
});
|
|
2398
|
+
var WorkflowFileSchema = z20.object({
|
|
2399
|
+
workflows: z20.record(z20.string(), WorkflowDefinitionSchema)
|
|
1947
2400
|
});
|
|
1948
2401
|
|
|
1949
2402
|
// src/workflow/dynamic.ts
|
|
2403
|
+
var MAX_WHILE_LOOP_ITERATIONS = 1e3;
|
|
2404
|
+
function convertJsonSchemaToZod(schema) {
|
|
2405
|
+
if (schema.enum) {
|
|
2406
|
+
return z21.enum(schema.enum.map((v) => String(v)));
|
|
2407
|
+
}
|
|
2408
|
+
if (Array.isArray(schema.type)) {
|
|
2409
|
+
const types = schema.type;
|
|
2410
|
+
if (types.includes("null") && types.length === 2) {
|
|
2411
|
+
const nonNullType = types.find((t) => t !== "null");
|
|
2412
|
+
if (nonNullType === "string") return z21.string().nullable();
|
|
2413
|
+
if (nonNullType === "number") return z21.number().nullable();
|
|
2414
|
+
if (nonNullType === "integer")
|
|
2415
|
+
return z21.number().refine((val) => Number.isInteger(val)).nullable();
|
|
2416
|
+
if (nonNullType === "boolean") return z21.boolean().nullable();
|
|
2417
|
+
if (nonNullType === "object") {
|
|
2418
|
+
const shape = {};
|
|
2419
|
+
if (schema.properties) {
|
|
2420
|
+
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
2421
|
+
const propZod = convertJsonSchemaToZod(propSchema);
|
|
2422
|
+
const isRequired = schema.required?.includes(propName);
|
|
2423
|
+
shape[propName] = isRequired ? propZod : propZod.optional();
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2426
|
+
return z21.object(shape).nullable();
|
|
2427
|
+
}
|
|
2428
|
+
if (nonNullType === "array") return z21.array(z21.any()).nullable();
|
|
2429
|
+
}
|
|
2430
|
+
return z21.any();
|
|
2431
|
+
}
|
|
2432
|
+
const type = schema.type;
|
|
2433
|
+
switch (type) {
|
|
2434
|
+
case "string":
|
|
2435
|
+
return z21.string();
|
|
2436
|
+
case "number":
|
|
2437
|
+
return z21.number();
|
|
2438
|
+
case "integer":
|
|
2439
|
+
return z21.number().refine((val) => Number.isInteger(val), { message: "Expected an integer" });
|
|
2440
|
+
case "boolean":
|
|
2441
|
+
return z21.boolean();
|
|
2442
|
+
case "null":
|
|
2443
|
+
return z21.null();
|
|
2444
|
+
case "object": {
|
|
2445
|
+
const shape = {};
|
|
2446
|
+
if (schema.properties) {
|
|
2447
|
+
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
2448
|
+
const propZod = convertJsonSchemaToZod(propSchema);
|
|
2449
|
+
const isRequired = schema.required?.includes(propName);
|
|
2450
|
+
shape[propName] = isRequired ? propZod : propZod.optional();
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
let objectSchema = z21.object(shape);
|
|
2454
|
+
if (schema.additionalProperties === true) {
|
|
2455
|
+
objectSchema = objectSchema.and(z21.any());
|
|
2456
|
+
} else if (typeof schema.additionalProperties === "object") {
|
|
2457
|
+
const additionalSchema = convertJsonSchemaToZod(schema.additionalProperties);
|
|
2458
|
+
objectSchema = objectSchema.and(z21.record(z21.string(), additionalSchema));
|
|
2459
|
+
}
|
|
2460
|
+
return objectSchema;
|
|
2461
|
+
}
|
|
2462
|
+
case "array": {
|
|
2463
|
+
if (!schema.items) {
|
|
2464
|
+
return z21.array(z21.any());
|
|
2465
|
+
}
|
|
2466
|
+
const itemSchema = convertJsonSchemaToZod(schema.items);
|
|
2467
|
+
return z21.array(itemSchema);
|
|
2468
|
+
}
|
|
2469
|
+
default:
|
|
2470
|
+
return z21.any();
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
var TOOL_GROUPS = {
|
|
2474
|
+
readonly: ["readFile", "readBinaryFile", "listFiles", "searchFiles"],
|
|
2475
|
+
readwrite: ["readFile", "readBinaryFile", "listFiles", "searchFiles", "writeToFile", "replaceInFile", "removeFile", "renameFile"],
|
|
2476
|
+
internet: ["fetchUrl", "search"]
|
|
2477
|
+
};
|
|
2478
|
+
function validateWorkflowFile(definition) {
|
|
2479
|
+
const errors = [];
|
|
2480
|
+
for (const [workflowId, workflow] of Object.entries(definition.workflows)) {
|
|
2481
|
+
if (!workflow.steps || workflow.steps.length === 0) {
|
|
2482
|
+
errors.push(`Workflow '${workflowId}' has no steps`);
|
|
2483
|
+
continue;
|
|
2484
|
+
}
|
|
2485
|
+
const checkBreakOutsideLoop = (steps, inLoop, path) => {
|
|
2486
|
+
for (const step of steps) {
|
|
2487
|
+
if (isBreakStep(step) || isContinueStep(step)) {
|
|
2488
|
+
if (!inLoop) {
|
|
2489
|
+
errors.push(`${path} has break/continue outside of a loop`);
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
if (isWhileLoopStep(step)) {
|
|
2493
|
+
checkBreakOutsideLoop(step.while.steps, true, `${path}/${step.id}`);
|
|
2494
|
+
}
|
|
2495
|
+
if (isIfElseStep(step)) {
|
|
2496
|
+
if (step.if.thenBranch) {
|
|
2497
|
+
checkBreakOutsideLoop(step.if.thenBranch, inLoop, `${path}/${step.id}/then`);
|
|
2498
|
+
}
|
|
2499
|
+
if (step.if.elseBranch) {
|
|
2500
|
+
checkBreakOutsideLoop(step.if.elseBranch, inLoop, `${path}/${step.id}/else`);
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
if (isTryCatchStep(step)) {
|
|
2504
|
+
checkBreakOutsideLoop(step.try.trySteps, inLoop, `${path}/${step.id}/try`);
|
|
2505
|
+
checkBreakOutsideLoop(step.try.catchSteps, inLoop, `${path}/${step.id}/catch`);
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
};
|
|
2509
|
+
checkBreakOutsideLoop(workflow.steps, false, workflowId);
|
|
2510
|
+
const findRunWorkflowCalls = (steps, path) => {
|
|
2511
|
+
for (const step of steps) {
|
|
2512
|
+
if (isWhileLoopStep(step)) {
|
|
2513
|
+
findRunWorkflowCalls(step.while.steps, `${path}/${step.id}`);
|
|
2514
|
+
}
|
|
2515
|
+
if (isIfElseStep(step)) {
|
|
2516
|
+
if (step.if.thenBranch) {
|
|
2517
|
+
findRunWorkflowCalls(step.if.thenBranch, `${path}/${step.id}/then`);
|
|
2518
|
+
}
|
|
2519
|
+
if (step.if.elseBranch) {
|
|
2520
|
+
findRunWorkflowCalls(step.if.elseBranch, `${path}/${step.id}/else`);
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
if (isTryCatchStep(step)) {
|
|
2524
|
+
findRunWorkflowCalls(step.try.trySteps, `${path}/${step.id}/try`);
|
|
2525
|
+
findRunWorkflowCalls(step.try.catchSteps, `${path}/${step.id}/catch`);
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
};
|
|
2529
|
+
findRunWorkflowCalls(workflow.steps, workflowId);
|
|
2530
|
+
}
|
|
2531
|
+
if (errors.length > 0) {
|
|
2532
|
+
return { success: false, errors };
|
|
2533
|
+
}
|
|
2534
|
+
return { success: true };
|
|
2535
|
+
}
|
|
1950
2536
|
function parseDynamicWorkflowDefinition(source) {
|
|
1951
2537
|
try {
|
|
1952
|
-
const raw =
|
|
2538
|
+
const raw = parse2(source);
|
|
1953
2539
|
const validated = WorkflowFileSchema.safeParse(raw);
|
|
1954
2540
|
if (!validated.success) {
|
|
1955
|
-
return { success: false, error:
|
|
2541
|
+
return { success: false, error: z21.prettifyError(validated.error) };
|
|
2542
|
+
}
|
|
2543
|
+
const validation = validateWorkflowFile(validated.data);
|
|
2544
|
+
if (!validation.success) {
|
|
2545
|
+
return { success: false, error: `Workflow validation failed:
|
|
2546
|
+
${validation.errors.map((e) => ` - ${e}`).join("\n")}` };
|
|
1956
2547
|
}
|
|
1957
2548
|
return { success: true, definition: validated.data };
|
|
1958
2549
|
} catch (error) {
|
|
1959
2550
|
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
1960
2551
|
}
|
|
1961
2552
|
}
|
|
1962
|
-
var AsyncFunction = Object.getPrototypeOf(async () => {
|
|
1963
|
-
}).constructor;
|
|
1964
2553
|
function validateAndApplyDefaults(workflowId, workflow, input) {
|
|
1965
2554
|
if (!workflow.inputs || workflow.inputs.length === 0) {
|
|
1966
2555
|
return input;
|
|
1967
2556
|
}
|
|
1968
|
-
const validatedInput = {};
|
|
2557
|
+
const validatedInput = { ...input };
|
|
1969
2558
|
const errors = [];
|
|
1970
2559
|
for (const inputDef of workflow.inputs) {
|
|
1971
2560
|
const providedValue = input[inputDef.id];
|
|
@@ -1983,36 +2572,217 @@ ${errors.map((e) => ` - ${e}`).join("\n")}`);
|
|
|
1983
2572
|
}
|
|
1984
2573
|
return validatedInput;
|
|
1985
2574
|
}
|
|
2575
|
+
function evaluateCondition(condition, input, state, allowUnsafeCodeExecution = false) {
|
|
2576
|
+
if (allowUnsafeCodeExecution) {
|
|
2577
|
+
const functionBody = `
|
|
2578
|
+
try {
|
|
2579
|
+
return ${condition};
|
|
2580
|
+
} catch (error) {
|
|
2581
|
+
throw new Error('Condition evaluation failed: ' + (error instanceof Error ? error.message : String(error)));
|
|
2582
|
+
}
|
|
2583
|
+
`;
|
|
2584
|
+
try {
|
|
2585
|
+
const fn = new Function("input", "state", functionBody);
|
|
2586
|
+
const result = fn(input, state);
|
|
2587
|
+
return Boolean(result);
|
|
2588
|
+
} catch (error) {
|
|
2589
|
+
throw new Error(`Failed to evaluate condition: ${condition}. Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
2590
|
+
}
|
|
2591
|
+
} else {
|
|
2592
|
+
return evaluateConditionSafe(condition, input, state);
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
function evaluateConditionSafe(condition, input, state) {
|
|
2596
|
+
condition = condition.trim();
|
|
2597
|
+
if (condition === "true") return true;
|
|
2598
|
+
if (condition === "false") return false;
|
|
2599
|
+
const orIndex = findTopLevelOperator(condition, "||");
|
|
2600
|
+
if (orIndex !== -1) {
|
|
2601
|
+
const left = condition.slice(0, orIndex).trim();
|
|
2602
|
+
const right = condition.slice(orIndex + 2).trim();
|
|
2603
|
+
return evaluateConditionSafe(left, input, state) || evaluateConditionSafe(right, input, state);
|
|
2604
|
+
}
|
|
2605
|
+
const andIndex = findTopLevelOperator(condition, "&&");
|
|
2606
|
+
if (andIndex !== -1) {
|
|
2607
|
+
const left = condition.slice(0, andIndex).trim();
|
|
2608
|
+
const right = condition.slice(andIndex + 2).trim();
|
|
2609
|
+
return evaluateConditionSafe(left, input, state) && evaluateConditionSafe(right, input, state);
|
|
2610
|
+
}
|
|
2611
|
+
const comparisonOps = ["===", "!==", "==", "!=", ">=", "<=", ">", "<"];
|
|
2612
|
+
for (const op of comparisonOps) {
|
|
2613
|
+
const opIndex = findTopLevelOperator(condition, op);
|
|
2614
|
+
if (opIndex !== -1) {
|
|
2615
|
+
const left = evaluateValue(condition.slice(0, opIndex).trim(), input, state);
|
|
2616
|
+
const right = evaluateValue(condition.slice(opIndex + op.length).trim(), input, state);
|
|
2617
|
+
return compareValues(left, right, op);
|
|
2618
|
+
}
|
|
2619
|
+
}
|
|
2620
|
+
if (condition.startsWith("!")) {
|
|
2621
|
+
return !evaluateConditionSafe(condition.slice(1).trim(), input, state);
|
|
2622
|
+
}
|
|
2623
|
+
if (hasEnclosingParens(condition)) {
|
|
2624
|
+
const inner = condition.slice(1, -1);
|
|
2625
|
+
return evaluateConditionSafe(inner, input, state);
|
|
2626
|
+
}
|
|
2627
|
+
const value = evaluateValue(condition, input, state);
|
|
2628
|
+
return Boolean(value);
|
|
2629
|
+
}
|
|
2630
|
+
function findTopLevelOperator(expr, op) {
|
|
2631
|
+
let parenDepth = 0;
|
|
2632
|
+
let inString = false;
|
|
2633
|
+
let stringChar = "";
|
|
2634
|
+
let escapeNext = false;
|
|
2635
|
+
for (let i = 0; i <= expr.length - op.length; i++) {
|
|
2636
|
+
const char = expr[i];
|
|
2637
|
+
if (escapeNext) {
|
|
2638
|
+
escapeNext = false;
|
|
2639
|
+
continue;
|
|
2640
|
+
}
|
|
2641
|
+
if (char === "\\") {
|
|
2642
|
+
escapeNext = true;
|
|
2643
|
+
continue;
|
|
2644
|
+
}
|
|
2645
|
+
if (!inString && (char === '"' || char === "'")) {
|
|
2646
|
+
inString = true;
|
|
2647
|
+
stringChar = char;
|
|
2648
|
+
continue;
|
|
2649
|
+
}
|
|
2650
|
+
if (inString && char === stringChar) {
|
|
2651
|
+
inString = false;
|
|
2652
|
+
stringChar = "";
|
|
2653
|
+
continue;
|
|
2654
|
+
}
|
|
2655
|
+
if (inString) continue;
|
|
2656
|
+
if (char === "(") parenDepth++;
|
|
2657
|
+
if (char === ")") parenDepth--;
|
|
2658
|
+
if (parenDepth === 0 && expr.slice(i, i + op.length) === op) {
|
|
2659
|
+
return i;
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
return -1;
|
|
2663
|
+
}
|
|
2664
|
+
function hasEnclosingParens(expr) {
|
|
2665
|
+
expr = expr.trim();
|
|
2666
|
+
if (!expr.startsWith("(") || !expr.endsWith(")")) {
|
|
2667
|
+
return false;
|
|
2668
|
+
}
|
|
2669
|
+
let depth = 0;
|
|
2670
|
+
let inString = false;
|
|
2671
|
+
let stringChar = "";
|
|
2672
|
+
let escapeNext = false;
|
|
2673
|
+
for (let i = 0; i < expr.length; i++) {
|
|
2674
|
+
const char = expr[i];
|
|
2675
|
+
if (escapeNext) {
|
|
2676
|
+
escapeNext = false;
|
|
2677
|
+
continue;
|
|
2678
|
+
}
|
|
2679
|
+
if (char === "\\") {
|
|
2680
|
+
escapeNext = true;
|
|
2681
|
+
continue;
|
|
2682
|
+
}
|
|
2683
|
+
if (!inString && (char === '"' || char === "'")) {
|
|
2684
|
+
inString = true;
|
|
2685
|
+
stringChar = char;
|
|
2686
|
+
continue;
|
|
2687
|
+
}
|
|
2688
|
+
if (inString && char === stringChar) {
|
|
2689
|
+
inString = false;
|
|
2690
|
+
stringChar = "";
|
|
2691
|
+
continue;
|
|
2692
|
+
}
|
|
2693
|
+
if (inString) continue;
|
|
2694
|
+
if (char === "(") {
|
|
2695
|
+
depth++;
|
|
2696
|
+
if (i === 0) depth = 1;
|
|
2697
|
+
}
|
|
2698
|
+
if (char === ")") {
|
|
2699
|
+
depth--;
|
|
2700
|
+
if (depth === 0 && i === expr.length - 1) {
|
|
2701
|
+
return true;
|
|
2702
|
+
}
|
|
2703
|
+
if (depth === 0 && i < expr.length - 1) {
|
|
2704
|
+
return false;
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
return false;
|
|
2709
|
+
}
|
|
2710
|
+
function evaluateValue(expr, input, state) {
|
|
2711
|
+
expr = expr.trim();
|
|
2712
|
+
const stringMatch = expr.match(/^(["'])(?:(?=(\\?))\2.)*?\1$/);
|
|
2713
|
+
if (stringMatch) {
|
|
2714
|
+
const quote = stringMatch[1];
|
|
2715
|
+
if (quote === '"') {
|
|
2716
|
+
try {
|
|
2717
|
+
return JSON.parse(expr);
|
|
2718
|
+
} catch (error) {
|
|
2719
|
+
throw new Error(`Invalid string literal: "${expr}". Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
2720
|
+
}
|
|
2721
|
+
} else {
|
|
2722
|
+
let inner = expr.slice(1, -1);
|
|
2723
|
+
inner = inner.replace(/\\'/g, "'");
|
|
2724
|
+
inner = inner.replace(/\\"/g, '"');
|
|
2725
|
+
const converted = `"${inner.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
2726
|
+
try {
|
|
2727
|
+
return JSON.parse(converted);
|
|
2728
|
+
} catch (error) {
|
|
2729
|
+
throw new Error(`Invalid string literal: "${expr}". Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
if (/^-?\d*\.?\d+(?:[eE][+-]?\d+)?$/.test(expr)) {
|
|
2734
|
+
return Number.parseFloat(expr);
|
|
2735
|
+
}
|
|
2736
|
+
if (expr === "true") return true;
|
|
2737
|
+
if (expr === "false") return false;
|
|
2738
|
+
if (expr === "null") return null;
|
|
2739
|
+
if (expr.startsWith("input.")) {
|
|
2740
|
+
return getNestedProperty(input, expr.slice(6));
|
|
2741
|
+
}
|
|
2742
|
+
if (expr.startsWith("state.")) {
|
|
2743
|
+
return getNestedProperty(state, expr.slice(6));
|
|
2744
|
+
}
|
|
2745
|
+
throw new Error(
|
|
2746
|
+
`Unrecognized expression in condition: "${expr}". Valid expressions are: string literals, numbers, boolean literals, null, or property access like "input.foo" or "state.bar"`
|
|
2747
|
+
);
|
|
2748
|
+
}
|
|
2749
|
+
function getNestedProperty(obj, path) {
|
|
2750
|
+
const parts = path.split(".");
|
|
2751
|
+
let current = obj;
|
|
2752
|
+
for (const part of parts) {
|
|
2753
|
+
if (current == null) return void 0;
|
|
2754
|
+
current = current[part];
|
|
2755
|
+
}
|
|
2756
|
+
return current;
|
|
2757
|
+
}
|
|
2758
|
+
function compareValues(left, right, op) {
|
|
2759
|
+
switch (op) {
|
|
2760
|
+
case "===":
|
|
2761
|
+
return left === right;
|
|
2762
|
+
case "!==":
|
|
2763
|
+
return left !== right;
|
|
2764
|
+
case "==":
|
|
2765
|
+
return Object.is(left, right);
|
|
2766
|
+
case "!=":
|
|
2767
|
+
return !Object.is(left, right);
|
|
2768
|
+
case ">=":
|
|
2769
|
+
return left >= right;
|
|
2770
|
+
case "<=":
|
|
2771
|
+
return left <= right;
|
|
2772
|
+
case ">":
|
|
2773
|
+
return left > right;
|
|
2774
|
+
case "<":
|
|
2775
|
+
return left < right;
|
|
2776
|
+
default:
|
|
2777
|
+
throw new Error(`Unknown comparison operator: ${op}`);
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
1986
2780
|
function createRunWorkflowFn(args) {
|
|
1987
2781
|
return async (subWorkflowId, subInput) => {
|
|
1988
2782
|
const mergedInput = { ...args.input, ...args.state, ...subInput ?? {} };
|
|
1989
2783
|
return await args.runInternal(subWorkflowId, mergedInput, args.context, args.state);
|
|
1990
2784
|
};
|
|
1991
2785
|
}
|
|
1992
|
-
function compileStep(stepDef, workflowId, compiledSteps) {
|
|
1993
|
-
const key = `${workflowId}.${stepDef.id}`;
|
|
1994
|
-
const existing = compiledSteps.get(key);
|
|
1995
|
-
if (existing) {
|
|
1996
|
-
return existing;
|
|
1997
|
-
}
|
|
1998
|
-
if (!stepDef.code) {
|
|
1999
|
-
throw new Error(`Step '${stepDef.id}' in workflow '${workflowId}' has no code`);
|
|
2000
|
-
}
|
|
2001
|
-
try {
|
|
2002
|
-
const fn = new AsyncFunction("ctx", stepDef.code);
|
|
2003
|
-
compiledSteps.set(key, fn);
|
|
2004
|
-
return fn;
|
|
2005
|
-
} catch (error) {
|
|
2006
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
2007
|
-
const codePreview = stepDef.code.length > 200 ? `${stepDef.code.substring(0, 200)}...` : stepDef.code;
|
|
2008
|
-
throw new Error(
|
|
2009
|
-
`Failed to compile code for step '${stepDef.id}' in workflow '${workflowId}':
|
|
2010
|
-
Error: ${errorMsg}
|
|
2011
|
-
Code:
|
|
2012
|
-
${codePreview.split("\n").map((line) => ` ${line}`).join("\n")}`
|
|
2013
|
-
);
|
|
2014
|
-
}
|
|
2015
|
-
}
|
|
2016
2786
|
async function executeStepWithAgent(stepDef, workflowId, input, state, context, options, runInternal) {
|
|
2017
2787
|
const tools = context.tools;
|
|
2018
2788
|
if (typeof tools.generateText !== "function" || typeof tools.invokeTool !== "function" || typeof tools.taskEvent !== "function") {
|
|
@@ -2025,18 +2795,42 @@ async function executeStepWithAgent(stepDef, workflowId, input, state, context,
|
|
|
2025
2795
|
`Step '${stepDef.id}' in workflow '${workflowId}' requires agent execution, but no toolInfo was provided to DynamicWorkflowRunner.`
|
|
2026
2796
|
);
|
|
2027
2797
|
}
|
|
2028
|
-
const
|
|
2029
|
-
|
|
2030
|
-
if (
|
|
2798
|
+
const rawAllowedToolNames = stepDef.tools;
|
|
2799
|
+
let toolsForAgent;
|
|
2800
|
+
if (rawAllowedToolNames) {
|
|
2801
|
+
const expandedToolNames = /* @__PURE__ */ new Set();
|
|
2802
|
+
let includeAll = false;
|
|
2803
|
+
for (const name of rawAllowedToolNames) {
|
|
2804
|
+
if (name === "all") {
|
|
2805
|
+
includeAll = true;
|
|
2806
|
+
break;
|
|
2807
|
+
}
|
|
2808
|
+
if (Object.hasOwn(TOOL_GROUPS, name)) {
|
|
2809
|
+
for (const tool of TOOL_GROUPS[name]) {
|
|
2810
|
+
expandedToolNames.add(tool);
|
|
2811
|
+
}
|
|
2812
|
+
} else {
|
|
2813
|
+
expandedToolNames.add(name);
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
if (includeAll) {
|
|
2817
|
+
toolsForAgent = [...options.toolInfo];
|
|
2818
|
+
} else {
|
|
2819
|
+
toolsForAgent = options.toolInfo.filter((t) => expandedToolNames.has(t.name));
|
|
2820
|
+
}
|
|
2821
|
+
} else {
|
|
2822
|
+
toolsForAgent = [...options.toolInfo];
|
|
2823
|
+
}
|
|
2824
|
+
if (!rawAllowedToolNames || rawAllowedToolNames.includes("all") || rawAllowedToolNames.includes("runWorkflow")) {
|
|
2031
2825
|
toolsForAgent.push({
|
|
2032
2826
|
name: "runWorkflow",
|
|
2033
2827
|
description: "Run a named sub-workflow defined in the current workflow file.",
|
|
2034
|
-
parameters:
|
|
2035
|
-
workflowId:
|
|
2036
|
-
input:
|
|
2828
|
+
parameters: z21.object({
|
|
2829
|
+
workflowId: z21.string().describe("Sub-workflow id to run"),
|
|
2830
|
+
input: z21.any().nullish().describe("Optional input object for the sub-workflow")
|
|
2037
2831
|
}),
|
|
2038
2832
|
handler: async () => {
|
|
2039
|
-
return {
|
|
2833
|
+
return { success: false, message: { type: "error-text", value: "runWorkflow is virtual." } };
|
|
2040
2834
|
}
|
|
2041
2835
|
});
|
|
2042
2836
|
}
|
|
@@ -2066,7 +2860,7 @@ async function executeStepWithAgent(stepDef, workflowId, input, state, context,
|
|
|
2066
2860
|
invokeTool: async ({ toolName, input: toolInput }) => {
|
|
2067
2861
|
if (!allowedToolNameSet.has(toolName)) {
|
|
2068
2862
|
return {
|
|
2069
|
-
|
|
2863
|
+
success: false,
|
|
2070
2864
|
message: { type: "error-text", value: `Tool '${toolName}' is not allowed in this step.` }
|
|
2071
2865
|
};
|
|
2072
2866
|
}
|
|
@@ -2075,17 +2869,17 @@ async function executeStepWithAgent(stepDef, workflowId, input, state, context,
|
|
|
2075
2869
|
const subInput = toolInput?.input;
|
|
2076
2870
|
if (typeof subWorkflowId !== "string") {
|
|
2077
2871
|
return {
|
|
2078
|
-
|
|
2872
|
+
success: false,
|
|
2079
2873
|
message: { type: "error-text", value: "runWorkflow.workflowId must be a string." }
|
|
2080
2874
|
};
|
|
2081
2875
|
}
|
|
2082
2876
|
try {
|
|
2083
2877
|
const output = await runWorkflow(subWorkflowId, subInput);
|
|
2084
2878
|
const jsonResult = { type: "json", value: output };
|
|
2085
|
-
return {
|
|
2879
|
+
return { success: true, message: jsonResult };
|
|
2086
2880
|
} catch (error) {
|
|
2087
2881
|
return {
|
|
2088
|
-
|
|
2882
|
+
success: false,
|
|
2089
2883
|
message: { type: "error-text", value: error instanceof Error ? error.message : String(error) }
|
|
2090
2884
|
};
|
|
2091
2885
|
}
|
|
@@ -2123,29 +2917,11 @@ async function executeStepWithAgent(stepDef, workflowId, input, state, context,
|
|
|
2123
2917
|
if (result.type === "UsageExceeded") {
|
|
2124
2918
|
throw new Error(`Agent step '${stepDef.id}' in workflow '${workflowId}' exceeded usage limits (tokens or rounds)`);
|
|
2125
2919
|
}
|
|
2126
|
-
|
|
2920
|
+
const _exhaustiveCheck = result;
|
|
2921
|
+
throw new Error(`Agent step '${stepDef.id}' in workflow '${workflowId}' exited unexpectedly with unhandled type`);
|
|
2127
2922
|
}
|
|
2128
|
-
async function executeStepWithTimeout(stepDef, workflowId, input, state, context, options,
|
|
2923
|
+
async function executeStepWithTimeout(stepDef, workflowId, input, state, context, options, runInternal) {
|
|
2129
2924
|
const executeStepLogic = async () => {
|
|
2130
|
-
if (stepDef.code && options.allowUnsafeCodeExecution) {
|
|
2131
|
-
context.logger.debug(`[Step] Executing step '${stepDef.id}' with compiled code`);
|
|
2132
|
-
const fn = compileStep(stepDef, workflowId, compiledSteps);
|
|
2133
|
-
const runWorkflow = createRunWorkflowFn({ input, state, context, runInternal });
|
|
2134
|
-
const runtimeCtx = {
|
|
2135
|
-
workflowId,
|
|
2136
|
-
stepId: stepDef.id,
|
|
2137
|
-
input,
|
|
2138
|
-
state,
|
|
2139
|
-
tools: context.tools,
|
|
2140
|
-
logger: context.logger,
|
|
2141
|
-
step: context.step,
|
|
2142
|
-
runWorkflow,
|
|
2143
|
-
toolInfo: options.toolInfo
|
|
2144
|
-
};
|
|
2145
|
-
const result2 = await fn(runtimeCtx);
|
|
2146
|
-
context.logger.debug(`[Step] Compiled code execution completed for step '${stepDef.id}'`);
|
|
2147
|
-
return result2;
|
|
2148
|
-
}
|
|
2149
2925
|
context.logger.debug(`[Step] Executing step '${stepDef.id}' with agent`);
|
|
2150
2926
|
const result = await executeStepWithAgent(stepDef, workflowId, input, state, context, options, runInternal);
|
|
2151
2927
|
context.logger.debug(`[Step] Agent execution completed for step '${stepDef.id}'`);
|
|
@@ -2168,22 +2944,19 @@ async function executeStepWithTimeout(stepDef, workflowId, input, state, context
|
|
|
2168
2944
|
}
|
|
2169
2945
|
return await executeStepLogic();
|
|
2170
2946
|
}
|
|
2171
|
-
async function executeStep(stepDef, workflowId, input, state, context, options,
|
|
2172
|
-
const result = await executeStepWithTimeout(stepDef, workflowId, input, state, context, options,
|
|
2947
|
+
async function executeStep(stepDef, workflowId, input, state, context, options, runInternal) {
|
|
2948
|
+
const result = await executeStepWithTimeout(stepDef, workflowId, input, state, context, options, runInternal);
|
|
2173
2949
|
if (stepDef.outputSchema) {
|
|
2174
2950
|
try {
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
throw new Error(`Expected array output, got ${typeof result}`);
|
|
2185
|
-
}
|
|
2186
|
-
}
|
|
2951
|
+
context.logger.debug(`[Step] Validating output for step '${stepDef.id}' against schema`);
|
|
2952
|
+
const zodSchema = convertJsonSchemaToZod(stepDef.outputSchema);
|
|
2953
|
+
const validationResult = zodSchema.safeParse(result);
|
|
2954
|
+
if (!validationResult.success) {
|
|
2955
|
+
const errorDetails = validationResult.error.issues.map((e) => ` - ${e.path.join(".") || "root"}: ${e.message}`).join("\n");
|
|
2956
|
+
throw new Error(`Output does not match expected schema:
|
|
2957
|
+
${errorDetails}`);
|
|
2958
|
+
}
|
|
2959
|
+
context.logger.debug(`[Step] Output validation successful for step '${stepDef.id}'`);
|
|
2187
2960
|
} catch (error) {
|
|
2188
2961
|
throw new Error(
|
|
2189
2962
|
`Step '${stepDef.id}' in workflow '${workflowId}' output validation failed: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -2192,6 +2965,204 @@ async function executeStep(stepDef, workflowId, input, state, context, options,
|
|
|
2192
2965
|
}
|
|
2193
2966
|
return result;
|
|
2194
2967
|
}
|
|
2968
|
+
function isBreakStep(step) {
|
|
2969
|
+
return typeof step === "object" && step !== null && "break" in step && step.break === true;
|
|
2970
|
+
}
|
|
2971
|
+
function isContinueStep(step) {
|
|
2972
|
+
return typeof step === "object" && step !== null && "continue" in step && step.continue === true;
|
|
2973
|
+
}
|
|
2974
|
+
function isWhileLoopStep(step) {
|
|
2975
|
+
return typeof step === "object" && step !== null && "while" in step;
|
|
2976
|
+
}
|
|
2977
|
+
function isIfElseStep(step) {
|
|
2978
|
+
return typeof step === "object" && step !== null && "if" in step;
|
|
2979
|
+
}
|
|
2980
|
+
function isTryCatchStep(step) {
|
|
2981
|
+
return typeof step === "object" && step !== null && "try" in step;
|
|
2982
|
+
}
|
|
2983
|
+
function storeStepOutput(step, result, state) {
|
|
2984
|
+
if ("id" in step && step.output) {
|
|
2985
|
+
const outputKey = step.output;
|
|
2986
|
+
state[outputKey] = result;
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2989
|
+
function getStepId(step) {
|
|
2990
|
+
if ("id" in step && step.id) {
|
|
2991
|
+
return step.id;
|
|
2992
|
+
}
|
|
2993
|
+
if (isWhileLoopStep(step)) {
|
|
2994
|
+
return "while";
|
|
2995
|
+
}
|
|
2996
|
+
if (isIfElseStep(step)) {
|
|
2997
|
+
return "if";
|
|
2998
|
+
}
|
|
2999
|
+
if (isTryCatchStep(step)) {
|
|
3000
|
+
return "try";
|
|
3001
|
+
}
|
|
3002
|
+
return "control";
|
|
3003
|
+
}
|
|
3004
|
+
async function executeControlFlowStep(step, workflowId, input, state, context, options, runInternal, loopDepth, breakFlag, continueFlag) {
|
|
3005
|
+
if (isBreakStep(step)) {
|
|
3006
|
+
if (loopDepth === 0) {
|
|
3007
|
+
throw new Error(`'break' statement found outside of a loop in workflow '${workflowId}'`);
|
|
3008
|
+
}
|
|
3009
|
+
context.logger.debug(`[ControlFlow] Executing break statement (loop depth: ${loopDepth})`);
|
|
3010
|
+
return { result: void 0, shouldBreak: true, shouldContinue: false };
|
|
3011
|
+
}
|
|
3012
|
+
if (isContinueStep(step)) {
|
|
3013
|
+
if (loopDepth === 0) {
|
|
3014
|
+
throw new Error(`'continue' statement found outside of a loop in workflow '${workflowId}'`);
|
|
3015
|
+
}
|
|
3016
|
+
context.logger.debug(`[ControlFlow] Executing continue statement (loop depth: ${loopDepth})`);
|
|
3017
|
+
return { result: void 0, shouldBreak: false, shouldContinue: true };
|
|
3018
|
+
}
|
|
3019
|
+
if (isWhileLoopStep(step)) {
|
|
3020
|
+
context.logger.info(`[ControlFlow] Executing while loop '${step.id}'`);
|
|
3021
|
+
context.logger.debug(`[ControlFlow] Condition: ${step.while.condition}`);
|
|
3022
|
+
context.logger.debug(`[ControlFlow] Loop body has ${step.while.steps.length} step(s)`);
|
|
3023
|
+
let iterationCount = 0;
|
|
3024
|
+
let loopResult;
|
|
3025
|
+
while (true) {
|
|
3026
|
+
iterationCount++;
|
|
3027
|
+
if (iterationCount > MAX_WHILE_LOOP_ITERATIONS) {
|
|
3028
|
+
throw new Error(
|
|
3029
|
+
`While loop '${step.id}' in workflow '${workflowId}' exceeded maximum iteration limit of ${MAX_WHILE_LOOP_ITERATIONS}`
|
|
3030
|
+
);
|
|
3031
|
+
}
|
|
3032
|
+
const conditionResult = evaluateCondition(step.while.condition, input, state, options.allowUnsafeCodeExecution);
|
|
3033
|
+
context.logger.debug(`[ControlFlow] While loop '${step.id}' iteration ${iterationCount}: condition = ${conditionResult}`);
|
|
3034
|
+
if (!conditionResult) {
|
|
3035
|
+
context.logger.info(`[ControlFlow] While loop '${step.id}' terminated after ${iterationCount - 1} iteration(s)`);
|
|
3036
|
+
break;
|
|
3037
|
+
}
|
|
3038
|
+
for (const bodyStep of step.while.steps) {
|
|
3039
|
+
const { result, shouldBreak, shouldContinue } = await executeControlFlowStep(
|
|
3040
|
+
bodyStep,
|
|
3041
|
+
workflowId,
|
|
3042
|
+
input,
|
|
3043
|
+
state,
|
|
3044
|
+
context,
|
|
3045
|
+
options,
|
|
3046
|
+
runInternal,
|
|
3047
|
+
loopDepth + 1,
|
|
3048
|
+
breakFlag,
|
|
3049
|
+
continueFlag
|
|
3050
|
+
);
|
|
3051
|
+
if (shouldBreak) {
|
|
3052
|
+
context.logger.debug(`[ControlFlow] Breaking from while loop '${step.id}'`);
|
|
3053
|
+
breakFlag.value = false;
|
|
3054
|
+
return { result: loopResult, shouldBreak: false, shouldContinue: false };
|
|
3055
|
+
}
|
|
3056
|
+
if (shouldContinue) {
|
|
3057
|
+
context.logger.debug(`[ControlFlow] Continuing to next iteration of while loop '${step.id}'`);
|
|
3058
|
+
continueFlag.value = false;
|
|
3059
|
+
break;
|
|
3060
|
+
}
|
|
3061
|
+
storeStepOutput(bodyStep, result, state);
|
|
3062
|
+
loopResult = result;
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
3065
|
+
const outputKey = step.output ?? step.id;
|
|
3066
|
+
state[outputKey] = loopResult;
|
|
3067
|
+
context.logger.debug(`[ControlFlow] While loop '${step.id}' stored output as '${outputKey}'`);
|
|
3068
|
+
return { result: loopResult, shouldBreak: false, shouldContinue: false };
|
|
3069
|
+
}
|
|
3070
|
+
if (isIfElseStep(step)) {
|
|
3071
|
+
const ifStep = step;
|
|
3072
|
+
context.logger.info(`[ControlFlow] Executing if/else branch '${ifStep.id}'`);
|
|
3073
|
+
context.logger.debug(`[ControlFlow] Condition: ${ifStep.if.condition}`);
|
|
3074
|
+
context.logger.debug(`[ControlFlow] Then branch has ${ifStep.if.thenBranch.length} step(s)`);
|
|
3075
|
+
if (ifStep.if.elseBranch) {
|
|
3076
|
+
context.logger.debug(`[ControlFlow] Else branch has ${ifStep.if.elseBranch.length} step(s)`);
|
|
3077
|
+
}
|
|
3078
|
+
const conditionResult = evaluateCondition(ifStep.if.condition, input, state, options.allowUnsafeCodeExecution);
|
|
3079
|
+
context.logger.debug(`[ControlFlow] If/else '${ifStep.id}' condition = ${conditionResult}`);
|
|
3080
|
+
const branchSteps = conditionResult ? ifStep.if.thenBranch : ifStep.if.elseBranch ?? [];
|
|
3081
|
+
const branchName = conditionResult ? "then" : ifStep.if.elseBranch ? "else" : "else (empty)";
|
|
3082
|
+
context.logger.info(`[ControlFlow] Taking '${branchName}' branch of '${ifStep.id}'`);
|
|
3083
|
+
let branchResult;
|
|
3084
|
+
for (const branchStep of branchSteps) {
|
|
3085
|
+
const { result, shouldBreak, shouldContinue } = await executeControlFlowStep(
|
|
3086
|
+
branchStep,
|
|
3087
|
+
workflowId,
|
|
3088
|
+
input,
|
|
3089
|
+
state,
|
|
3090
|
+
context,
|
|
3091
|
+
options,
|
|
3092
|
+
runInternal,
|
|
3093
|
+
loopDepth,
|
|
3094
|
+
breakFlag,
|
|
3095
|
+
continueFlag
|
|
3096
|
+
);
|
|
3097
|
+
if (shouldBreak || shouldContinue) {
|
|
3098
|
+
return { result, shouldBreak, shouldContinue };
|
|
3099
|
+
}
|
|
3100
|
+
storeStepOutput(branchStep, result, state);
|
|
3101
|
+
branchResult = result;
|
|
3102
|
+
}
|
|
3103
|
+
const outputKey = ifStep.output ?? ifStep.id;
|
|
3104
|
+
state[outputKey] = branchResult;
|
|
3105
|
+
context.logger.debug(`[ControlFlow] If/else '${ifStep.id}' stored output as '${outputKey}'`);
|
|
3106
|
+
return { result: branchResult, shouldBreak: false, shouldContinue: false };
|
|
3107
|
+
}
|
|
3108
|
+
if (isTryCatchStep(step)) {
|
|
3109
|
+
const tryStep = step;
|
|
3110
|
+
context.logger.info(`[ControlFlow] Executing try/catch block '${tryStep.id}'`);
|
|
3111
|
+
context.logger.debug(`[ControlFlow] Try block has ${tryStep.try.trySteps.length} step(s)`);
|
|
3112
|
+
context.logger.debug(`[ControlFlow] Catch block has ${tryStep.try.catchSteps.length} step(s)`);
|
|
3113
|
+
let tryResult;
|
|
3114
|
+
let caughtError;
|
|
3115
|
+
try {
|
|
3116
|
+
for (const tryStepItem of tryStep.try.trySteps) {
|
|
3117
|
+
const { result } = await executeControlFlowStep(
|
|
3118
|
+
tryStepItem,
|
|
3119
|
+
workflowId,
|
|
3120
|
+
input,
|
|
3121
|
+
state,
|
|
3122
|
+
context,
|
|
3123
|
+
options,
|
|
3124
|
+
runInternal,
|
|
3125
|
+
loopDepth,
|
|
3126
|
+
breakFlag,
|
|
3127
|
+
continueFlag
|
|
3128
|
+
);
|
|
3129
|
+
storeStepOutput(tryStepItem, result, state);
|
|
3130
|
+
tryResult = result;
|
|
3131
|
+
}
|
|
3132
|
+
const outputKey = tryStep.output ?? tryStep.id;
|
|
3133
|
+
state[outputKey] = tryResult;
|
|
3134
|
+
context.logger.debug(`[ControlFlow] Try/catch '${tryStep.id}' completed successfully`);
|
|
3135
|
+
return { result: tryResult, shouldBreak: false, shouldContinue: false };
|
|
3136
|
+
} catch (error) {
|
|
3137
|
+
caughtError = error instanceof Error ? error : new Error(String(error));
|
|
3138
|
+
context.logger.warn(`[ControlFlow] Try/catch '${tryStep.id}' caught error: ${caughtError.message}`);
|
|
3139
|
+
let catchResult;
|
|
3140
|
+
for (const catchStepItem of tryStep.try.catchSteps) {
|
|
3141
|
+
const { result } = await executeControlFlowStep(
|
|
3142
|
+
catchStepItem,
|
|
3143
|
+
workflowId,
|
|
3144
|
+
input,
|
|
3145
|
+
state,
|
|
3146
|
+
context,
|
|
3147
|
+
options,
|
|
3148
|
+
runInternal,
|
|
3149
|
+
loopDepth,
|
|
3150
|
+
breakFlag,
|
|
3151
|
+
continueFlag
|
|
3152
|
+
);
|
|
3153
|
+
storeStepOutput(catchStepItem, result, state);
|
|
3154
|
+
catchResult = result;
|
|
3155
|
+
}
|
|
3156
|
+
const outputKey = tryStep.output ?? tryStep.id;
|
|
3157
|
+
state[outputKey] = catchResult;
|
|
3158
|
+
context.logger.debug(`[ControlFlow] Try/catch '${tryStep.id}' caught error and executed catch block`);
|
|
3159
|
+
return { result: catchResult, shouldBreak: false, shouldContinue: false };
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
const stepDef = step;
|
|
3163
|
+
const stepResult = await executeStep(stepDef, workflowId, input, state, context, options, runInternal);
|
|
3164
|
+
return { result: stepResult, shouldBreak: false, shouldContinue: false };
|
|
3165
|
+
}
|
|
2195
3166
|
function createDynamicWorkflow(definition, options = {}) {
|
|
2196
3167
|
if (typeof definition === "string") {
|
|
2197
3168
|
const res = parseDynamicWorkflowDefinition(definition);
|
|
@@ -2200,36 +3171,49 @@ function createDynamicWorkflow(definition, options = {}) {
|
|
|
2200
3171
|
}
|
|
2201
3172
|
definition = res.definition;
|
|
2202
3173
|
}
|
|
2203
|
-
const compiledSteps = /* @__PURE__ */ new Map();
|
|
2204
3174
|
const runInternal = async (workflowId, input, context, inheritedState) => {
|
|
2205
3175
|
const workflow = definition.workflows[workflowId];
|
|
2206
3176
|
if (!workflow) {
|
|
3177
|
+
const builtIn = options.builtInWorkflows?.[workflowId];
|
|
3178
|
+
if (builtIn) {
|
|
3179
|
+
context.logger.info(`[Workflow] Delegating to built-in workflow '${workflowId}'`);
|
|
3180
|
+
return await builtIn(input, context);
|
|
3181
|
+
}
|
|
2207
3182
|
throw new Error(`Workflow '${workflowId}' not found`);
|
|
2208
3183
|
}
|
|
2209
3184
|
const validatedInput = validateAndApplyDefaults(workflowId, workflow, input);
|
|
2210
3185
|
context.logger.info(`[Workflow] Starting workflow '${workflowId}'`);
|
|
2211
3186
|
context.logger.debug(`[Workflow] Input: ${JSON.stringify(validatedInput)}`);
|
|
2212
3187
|
context.logger.debug(`[Workflow] Inherited state: ${JSON.stringify(inheritedState)}`);
|
|
2213
|
-
context.logger.debug(`[Workflow] Steps: ${workflow.steps.map((s) => s.id).join(", ")}`);
|
|
3188
|
+
context.logger.debug(`[Workflow] Steps: ${workflow.steps.map((s) => "id" in s ? s.id : "<control flow>").join(", ")}`);
|
|
2214
3189
|
const state = { ...inheritedState };
|
|
2215
3190
|
let lastOutput;
|
|
3191
|
+
const breakFlag = { value: false };
|
|
3192
|
+
const continueFlag = { value: false };
|
|
2216
3193
|
for (let i = 0; i < workflow.steps.length; i++) {
|
|
2217
3194
|
const stepDef = workflow.steps[i];
|
|
2218
|
-
const
|
|
2219
|
-
context.logger.info(`[Workflow] Step ${i + 1}/${workflow.steps.length}: ${
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
3195
|
+
const stepId = getStepId(stepDef);
|
|
3196
|
+
context.logger.info(`[Workflow] Step ${i + 1}/${workflow.steps.length}: ${stepId}`);
|
|
3197
|
+
const { result } = await executeControlFlowStep(
|
|
3198
|
+
stepDef,
|
|
3199
|
+
workflowId,
|
|
3200
|
+
validatedInput,
|
|
3201
|
+
state,
|
|
3202
|
+
context,
|
|
3203
|
+
options,
|
|
3204
|
+
runInternal,
|
|
3205
|
+
0,
|
|
3206
|
+
// loop depth
|
|
3207
|
+
breakFlag,
|
|
3208
|
+
continueFlag
|
|
2232
3209
|
);
|
|
3210
|
+
lastOutput = result;
|
|
3211
|
+
storeStepOutput(stepDef, result, state);
|
|
3212
|
+
if ("id" in stepDef && stepDef.output) {
|
|
3213
|
+
context.logger.debug(
|
|
3214
|
+
`[Workflow] Step output stored as '${stepDef.output}': ${typeof lastOutput === "object" ? JSON.stringify(lastOutput).substring(0, 200) : lastOutput}`
|
|
3215
|
+
);
|
|
3216
|
+
}
|
|
2233
3217
|
}
|
|
2234
3218
|
context.logger.info(`[Workflow] Completed workflow '${workflowId}'`);
|
|
2235
3219
|
if (workflow.output) {
|
|
@@ -2244,595 +3228,6 @@ function createDynamicWorkflow(definition, options = {}) {
|
|
|
2244
3228
|
};
|
|
2245
3229
|
}
|
|
2246
3230
|
|
|
2247
|
-
// src/workflow/dynamic-generator.workflow.ts
|
|
2248
|
-
import { z as z23 } from "zod";
|
|
2249
|
-
var GenerateWorkflowDefinitionInputSchema = z23.object({
|
|
2250
|
-
prompt: z23.string(),
|
|
2251
|
-
availableTools: z23.array(
|
|
2252
|
-
z23.object({
|
|
2253
|
-
name: z23.string(),
|
|
2254
|
-
description: z23.string()
|
|
2255
|
-
})
|
|
2256
|
-
).optional()
|
|
2257
|
-
});
|
|
2258
|
-
var GenerateWorkflowCodeInputSchema = z23.object({
|
|
2259
|
-
workflow: WorkflowFileSchema
|
|
2260
|
-
});
|
|
2261
|
-
var WORKFLOW_DEFINITION_SYSTEM_PROMPT = `You are an expert workflow architect.
|
|
2262
|
-
Your task is to create a JSON workflow definition based on the user's request.
|
|
2263
|
-
|
|
2264
|
-
The workflow definition must follow this structure:
|
|
2265
|
-
{
|
|
2266
|
-
"workflows": {
|
|
2267
|
-
"workflowName": {
|
|
2268
|
-
"task": "Description of the workflow",
|
|
2269
|
-
"inputs": [
|
|
2270
|
-
{ "id": "inputName", "description": "Description", "default": "optionalDefault" }
|
|
2271
|
-
],
|
|
2272
|
-
"steps": [
|
|
2273
|
-
{
|
|
2274
|
-
"id": "stepId",
|
|
2275
|
-
"task": "Description of the step",
|
|
2276
|
-
"tools": ["toolName1", "toolName2"], // Optional: restrict which tools can be used
|
|
2277
|
-
"output": "outputVariableName", // Optional: defaults to step id
|
|
2278
|
-
"timeout": 30000, // Optional: timeout in milliseconds
|
|
2279
|
-
"expected_outcome": "What this step produces", // Optional: documentation
|
|
2280
|
-
"outputSchema": { "type": "object" } // Optional: validation schema
|
|
2281
|
-
}
|
|
2282
|
-
],
|
|
2283
|
-
"output": "outputVariableName" // Optional
|
|
2284
|
-
}
|
|
2285
|
-
}
|
|
2286
|
-
}
|
|
2287
|
-
|
|
2288
|
-
Constraints:
|
|
2289
|
-
- You MUST always include a workflow named 'main'. This is the entry point.
|
|
2290
|
-
- The 'main' workflow input must be either empty (no input) or a single string input.
|
|
2291
|
-
- Break down complex tasks into logical steps.
|
|
2292
|
-
- Define clear inputs and outputs.
|
|
2293
|
-
|
|
2294
|
-
Quality Guidelines:
|
|
2295
|
-
- Add "timeout" field (in milliseconds) for steps that might take long (file I/O, API calls, searches)
|
|
2296
|
-
- Use "expected_outcome" field to document what each step should produce
|
|
2297
|
-
- Use descriptive step IDs (e.g., "validateInput", "fetchUserData", not "step1", "step2")
|
|
2298
|
-
- Design steps to be focused - one responsibility per step
|
|
2299
|
-
- For steps that process multiple items, consider creating a sub-workflow
|
|
2300
|
-
- Add "outputSchema" with type information for validation-critical steps
|
|
2301
|
-
- Order steps logically with clear data flow
|
|
2302
|
-
|
|
2303
|
-
Example 1:
|
|
2304
|
-
User: "Research a topic and summarize it."
|
|
2305
|
-
Output:
|
|
2306
|
-
\`\`\`json
|
|
2307
|
-
{
|
|
2308
|
-
"workflows": {
|
|
2309
|
-
"main": {
|
|
2310
|
-
"task": "Research a topic and provide a summary",
|
|
2311
|
-
"inputs": [
|
|
2312
|
-
{ "id": "topic", "description": "The topic to research" }
|
|
2313
|
-
],
|
|
2314
|
-
"steps": [
|
|
2315
|
-
{
|
|
2316
|
-
"id": "search",
|
|
2317
|
-
"task": "Search for information about the topic",
|
|
2318
|
-
"tools": ["search"],
|
|
2319
|
-
"output": "searchResults"
|
|
2320
|
-
},
|
|
2321
|
-
{
|
|
2322
|
-
"id": "summarize",
|
|
2323
|
-
"task": "Summarize the search results",
|
|
2324
|
-
"tools": ["generateText"],
|
|
2325
|
-
"output": "summary"
|
|
2326
|
-
}
|
|
2327
|
-
],
|
|
2328
|
-
"output": "summary"
|
|
2329
|
-
}
|
|
2330
|
-
}
|
|
2331
|
-
}
|
|
2332
|
-
\`\`\`
|
|
2333
|
-
|
|
2334
|
-
Example 2:
|
|
2335
|
-
User: "Review urgent PRs. For each PR, run the review workflow."
|
|
2336
|
-
Output:
|
|
2337
|
-
\`\`\`json
|
|
2338
|
-
{
|
|
2339
|
-
"workflows": {
|
|
2340
|
-
"main": {
|
|
2341
|
-
"task": "Fetch urgent PRs and review them",
|
|
2342
|
-
"inputs": [],
|
|
2343
|
-
"steps": [
|
|
2344
|
-
{
|
|
2345
|
-
"id": "fetchPRs",
|
|
2346
|
-
"task": "Fetch list of urgent PRs",
|
|
2347
|
-
"tools": ["github_list_prs"],
|
|
2348
|
-
"output": "prs"
|
|
2349
|
-
},
|
|
2350
|
-
{
|
|
2351
|
-
"id": "reviewEachPR",
|
|
2352
|
-
"task": "Run review workflow for each PR",
|
|
2353
|
-
"tools": [],
|
|
2354
|
-
"output": "reviews"
|
|
2355
|
-
}
|
|
2356
|
-
],
|
|
2357
|
-
"output": "reviews"
|
|
2358
|
-
},
|
|
2359
|
-
"reviewPR": {
|
|
2360
|
-
"task": "Review a single PR",
|
|
2361
|
-
"inputs": [
|
|
2362
|
-
{ "id": "prId", "description": "ID of the PR to review" }
|
|
2363
|
-
],
|
|
2364
|
-
"steps": [
|
|
2365
|
-
{
|
|
2366
|
-
"id": "getDiff",
|
|
2367
|
-
"task": "Get PR diff",
|
|
2368
|
-
"tools": ["github_get_diff"],
|
|
2369
|
-
"output": "diff"
|
|
2370
|
-
},
|
|
2371
|
-
{
|
|
2372
|
-
"id": "analyze",
|
|
2373
|
-
"task": "Analyze the diff",
|
|
2374
|
-
"tools": ["generateText"],
|
|
2375
|
-
"output": "analysis"
|
|
2376
|
-
}
|
|
2377
|
-
],
|
|
2378
|
-
"output": "analysis"
|
|
2379
|
-
}
|
|
2380
|
-
}
|
|
2381
|
-
}
|
|
2382
|
-
\`\`\`
|
|
2383
|
-
`;
|
|
2384
|
-
var WORKFLOW_CODE_SYSTEM_PROMPT = `You are an expert TypeScript developer.
|
|
2385
|
-
Your task is to implement the TypeScript code for the steps in the provided workflow definition.
|
|
2386
|
-
|
|
2387
|
-
You will receive a JSON workflow definition where the "code" field is null.
|
|
2388
|
-
You must fill in the "code" field for each step with valid TypeScript code.
|
|
2389
|
-
|
|
2390
|
-
CRITICAL: Each step "code" field must contain ONLY the function body statements (the code inside the curly braces).
|
|
2391
|
-
DO NOT include function declaration, arrow function syntax, async keyword, parameter list, or outer curly braces.
|
|
2392
|
-
|
|
2393
|
-
The code will be wrapped automatically in: \`async (ctx) => { YOUR_CODE_HERE }\`
|
|
2394
|
-
|
|
2395
|
-
Example of CORRECT code field:
|
|
2396
|
-
\`\`\`ts
|
|
2397
|
-
const result = await ctx.tools.readFile({ path: 'README.md' })
|
|
2398
|
-
if (!result) throw new Error('File not found')
|
|
2399
|
-
return result
|
|
2400
|
-
\`\`\`
|
|
2401
|
-
|
|
2402
|
-
Example of INCORRECT code field (DO NOT DO THIS):
|
|
2403
|
-
\`\`\`ts
|
|
2404
|
-
async (ctx) => {
|
|
2405
|
-
const result = await ctx.tools.readFile({ path: 'README.md' })
|
|
2406
|
-
return result
|
|
2407
|
-
}
|
|
2408
|
-
\`\`\`
|
|
2409
|
-
|
|
2410
|
-
Example of INCORRECT code field (DO NOT DO THIS):
|
|
2411
|
-
\`\`\`ts
|
|
2412
|
-
(ctx) => {
|
|
2413
|
-
return 'hello'
|
|
2414
|
-
}
|
|
2415
|
-
\`\`\`
|
|
2416
|
-
|
|
2417
|
-
## Runtime context (ctx)
|
|
2418
|
-
\`\`\`ts
|
|
2419
|
-
// Runtime types (for reference)
|
|
2420
|
-
type Logger = {
|
|
2421
|
-
debug: (...args: any[]) => void
|
|
2422
|
-
info: (...args: any[]) => void
|
|
2423
|
-
warn: (...args: any[]) => void
|
|
2424
|
-
error: (...args: any[]) => void
|
|
2425
|
-
}
|
|
2426
|
-
|
|
2427
|
-
type StepFn = {
|
|
2428
|
-
<T>(name: string, fn: () => Promise<T>): Promise<T>
|
|
2429
|
-
<T>(name: string, options: { retry?: number }, fn: () => Promise<T>): Promise<T>
|
|
2430
|
-
}
|
|
2431
|
-
|
|
2432
|
-
type JsonModelMessage = { role: 'system' | 'user' | 'assistant' | 'tool'; content: any }
|
|
2433
|
-
type JsonResponseMessage = { role: 'assistant' | 'tool'; content: any }
|
|
2434
|
-
type ToolSet = Record<string, any>
|
|
2435
|
-
|
|
2436
|
-
type ToolResponseResult =
|
|
2437
|
-
| { type: 'text'; value: string }
|
|
2438
|
-
| { type: 'json'; value: any }
|
|
2439
|
-
| { type: 'error-text'; value: string }
|
|
2440
|
-
| { type: 'error-json'; value: any }
|
|
2441
|
-
| { type: 'content'; value: any[] }
|
|
2442
|
-
|
|
2443
|
-
type AgentToolResponse =
|
|
2444
|
-
| { type: 'Reply'; message: ToolResponseResult }
|
|
2445
|
-
| { type: 'Exit'; message: string; object?: any }
|
|
2446
|
-
| { type: 'Error'; message: ToolResponseResult }
|
|
2447
|
-
|
|
2448
|
-
type ExitReason =
|
|
2449
|
-
| { type: 'UsageExceeded' }
|
|
2450
|
-
| { type: 'Exit'; message: string; object?: any }
|
|
2451
|
-
| { type: 'Error'; error: { message: string; stack?: string } }
|
|
2452
|
-
|
|
2453
|
-
type FullAgentToolInfo = { name: string; description: string; parameters: any; handler: any }
|
|
2454
|
-
|
|
2455
|
-
// Tools available on ctx.tools in dynamic steps
|
|
2456
|
-
type DynamicWorkflowTools = {
|
|
2457
|
-
// LLM + agent helpers
|
|
2458
|
-
generateText: (input: { messages: JsonModelMessage[]; tools: ToolSet }) => Promise<JsonResponseMessage[]>
|
|
2459
|
-
runAgent: (input: {
|
|
2460
|
-
tools: Readonly<FullAgentToolInfo[]>
|
|
2461
|
-
maxToolRoundTrips?: number
|
|
2462
|
-
userMessage: readonly JsonModelMessage[]
|
|
2463
|
-
} & ({ messages: JsonModelMessage[] } | { systemPrompt: string })) => Promise<ExitReason>
|
|
2464
|
-
|
|
2465
|
-
// Generic bridge to "agent tools" by name
|
|
2466
|
-
invokeTool: (input: { toolName: string; input: any }) => Promise<AgentToolResponse>
|
|
2467
|
-
|
|
2468
|
-
// File + command helpers (direct)
|
|
2469
|
-
readFile: (input: { path: string }) => Promise<string | null>
|
|
2470
|
-
writeToFile: (input: { path: string; content: string }) => Promise<void>
|
|
2471
|
-
executeCommand: (input: { command: string; pipe?: boolean } & ({ args: string[]; shell?: false } | { shell: true })) => Promise<{
|
|
2472
|
-
exitCode: number
|
|
2473
|
-
stdout: string
|
|
2474
|
-
stderr: string
|
|
2475
|
-
}>
|
|
2476
|
-
|
|
2477
|
-
// CLI UX helpers
|
|
2478
|
-
confirm: (input: { message: string }) => Promise<boolean>
|
|
2479
|
-
input: (input: { message: string; default?: string }) => Promise<string>
|
|
2480
|
-
select: (input: { message: string; choices: { name: string; value: string }[] }) => Promise<string>
|
|
2481
|
-
}
|
|
2482
|
-
|
|
2483
|
-
type DynamicStepRuntimeContext = {
|
|
2484
|
-
workflowId: string
|
|
2485
|
-
stepId: string
|
|
2486
|
-
input: Record<string, any>
|
|
2487
|
-
state: Record<string, any>
|
|
2488
|
-
tools: DynamicWorkflowTools
|
|
2489
|
-
logger: Logger
|
|
2490
|
-
step: StepFn
|
|
2491
|
-
runWorkflow: (workflowId: string, input?: Record<string, any>) => Promise<any>
|
|
2492
|
-
toolInfo?: ReadonlyArray<FullAgentToolInfo>
|
|
2493
|
-
}
|
|
2494
|
-
\`\`\`
|
|
2495
|
-
|
|
2496
|
-
- \`ctx.input\`: workflow inputs (read-only).
|
|
2497
|
-
- \`ctx.state\`: shared state between steps (previous step outputs are stored here).
|
|
2498
|
-
- \`ctx.tools\`: async tool functions. Call tools as \`await ctx.tools.someTool({ ... })\`.
|
|
2499
|
-
- \`ctx.runWorkflow\`: run a sub-workflow by id.
|
|
2500
|
-
|
|
2501
|
-
## Guidelines
|
|
2502
|
-
- Use \`await\` for all async operations.
|
|
2503
|
-
- Return the output value for the step (this becomes the step output).
|
|
2504
|
-
- Access inputs via \`ctx.input.<inputId>\`.
|
|
2505
|
-
- Access previous step outputs via \`ctx.state.<stepOutputKey>\` (defaults to the step \`output\` or \`id\`).
|
|
2506
|
-
|
|
2507
|
-
## Quality Guidelines for Code Implementation
|
|
2508
|
-
|
|
2509
|
-
### Error Handling
|
|
2510
|
-
- ALWAYS validate inputs at the start of steps
|
|
2511
|
-
- Use try-catch for operations that might fail (file I/O, parsing, API calls)
|
|
2512
|
-
- Preserve stack traces: re-throw original errors rather than creating new ones
|
|
2513
|
-
- Use error type guards: \`const err = error instanceof Error ? error : new Error(String(error))\`
|
|
2514
|
-
- Check for null/undefined before using values
|
|
2515
|
-
- Handle edge cases (empty arrays, missing files, invalid data)
|
|
2516
|
-
|
|
2517
|
-
### Logging
|
|
2518
|
-
- Use \`ctx.logger.info()\` for important progress updates
|
|
2519
|
-
- Use \`ctx.logger.debug()\` for detailed information
|
|
2520
|
-
- Use \`ctx.logger.warn()\` for recoverable issues
|
|
2521
|
-
- Use \`ctx.logger.error()\` before throwing errors
|
|
2522
|
-
- Log when starting and completing significant operations
|
|
2523
|
-
- Use template literals for readability: \`ctx.logger.info(\\\`Processing \${items.length} items...\\\`)\`
|
|
2524
|
-
|
|
2525
|
-
### User Experience
|
|
2526
|
-
- Provide progress feedback for long operations
|
|
2527
|
-
- Return structured data (objects/arrays), not strings when possible
|
|
2528
|
-
- Include helpful metadata in results (counts, timestamps, status)
|
|
2529
|
-
- For batch operations, report progress: \`Processed 5/10 items\`
|
|
2530
|
-
|
|
2531
|
-
### Data Validation
|
|
2532
|
-
- Validate required fields exist before accessing
|
|
2533
|
-
- Check data types match expectations
|
|
2534
|
-
- Validate array lengths before iteration
|
|
2535
|
-
- Example: \`if (!data?.users || !Array.isArray(data.users)) throw new Error('Invalid data format')\`
|
|
2536
|
-
|
|
2537
|
-
### Best Practices
|
|
2538
|
-
- Use meaningful variable names
|
|
2539
|
-
- Avoid nested callbacks - use async/await
|
|
2540
|
-
- Clean up resources (close files, clear timeouts)
|
|
2541
|
-
- Return consistent data structures across similar steps
|
|
2542
|
-
- For iteration, consider batching or rate limiting
|
|
2543
|
-
|
|
2544
|
-
### When to Simplify
|
|
2545
|
-
- Simple transformation steps (e.g., formatting strings) need only basic error handling
|
|
2546
|
-
- Internal sub-workflow steps with validated inputs from parent can skip redundant validation
|
|
2547
|
-
- Minimal logging is fine for fast steps (<100ms) that don't perform I/O or external calls
|
|
2548
|
-
- Use judgment: match error handling complexity to the step's failure risk and impact
|
|
2549
|
-
|
|
2550
|
-
## Tool calling examples (every tool)
|
|
2551
|
-
|
|
2552
|
-
### Direct ctx.tools methods
|
|
2553
|
-
\`\`\`ts
|
|
2554
|
-
// readFile
|
|
2555
|
-
const readme = await ctx.tools.readFile({ path: 'README.md' })
|
|
2556
|
-
if (readme == null) throw new Error('README.md not found')
|
|
2557
|
-
|
|
2558
|
-
// writeToFile
|
|
2559
|
-
await ctx.tools.writeToFile({ path: 'notes.txt', content: 'hello\\n' })
|
|
2560
|
-
|
|
2561
|
-
// executeCommand (args form)
|
|
2562
|
-
const rg = await ctx.tools.executeCommand({ command: 'rg', args: ['-n', 'TODO', '.'] })
|
|
2563
|
-
if (rg.exitCode !== 0) throw new Error(rg.stderr)
|
|
2564
|
-
|
|
2565
|
-
// executeCommand (shell form)
|
|
2566
|
-
await ctx.tools.executeCommand({ command: 'ls -la', shell: true, pipe: true })
|
|
2567
|
-
|
|
2568
|
-
// generateText (LLM call; pass tools: {})
|
|
2569
|
-
const msgs = await ctx.tools.generateText({
|
|
2570
|
-
messages: [
|
|
2571
|
-
{ role: 'system', content: 'Summarize the following text.' },
|
|
2572
|
-
{ role: 'user', content: readme },
|
|
2573
|
-
],
|
|
2574
|
-
tools: {},
|
|
2575
|
-
})
|
|
2576
|
-
const last = msgs[msgs.length - 1]
|
|
2577
|
-
const lastText = typeof last?.content === 'string' ? last.content : JSON.stringify(last?.content)
|
|
2578
|
-
|
|
2579
|
-
// runAgent (nested agent; use ctx.toolInfo as the tool list)
|
|
2580
|
-
const agentRes = await ctx.tools.runAgent({
|
|
2581
|
-
systemPrompt: 'You are a helpful assistant.',
|
|
2582
|
-
userMessage: [{ role: 'user', content: 'Summarize README.md in 3 bullets.' }],
|
|
2583
|
-
tools: (ctx.toolInfo ?? []) as any,
|
|
2584
|
-
})
|
|
2585
|
-
if (agentRes.type !== 'Exit') throw new Error('runAgent failed')
|
|
2586
|
-
|
|
2587
|
-
// confirm / input / select (interactive)
|
|
2588
|
-
const ok = await ctx.tools.confirm({ message: 'Proceed?' })
|
|
2589
|
-
const name = await ctx.tools.input({ message: 'Name?', default: 'main' })
|
|
2590
|
-
const flavor = await ctx.tools.select({
|
|
2591
|
-
message: 'Pick one',
|
|
2592
|
-
choices: [
|
|
2593
|
-
{ name: 'A', value: 'a' },
|
|
2594
|
-
{ name: 'B', value: 'b' },
|
|
2595
|
-
],
|
|
2596
|
-
})
|
|
2597
|
-
|
|
2598
|
-
\`\`\`
|
|
2599
|
-
|
|
2600
|
-
### Agent tools via ctx.tools.invokeTool (toolName examples)
|
|
2601
|
-
\`\`\`ts
|
|
2602
|
-
// Helper to unwrap a successful tool reply
|
|
2603
|
-
function unwrapToolValue(resp: any) {
|
|
2604
|
-
if (!resp || resp.type !== 'Reply') {
|
|
2605
|
-
const msg = resp?.message?.value
|
|
2606
|
-
throw new Error(typeof msg === 'string' ? msg : JSON.stringify(resp))
|
|
2607
|
-
}
|
|
2608
|
-
return resp.message.value
|
|
2609
|
-
}
|
|
2610
|
-
|
|
2611
|
-
// askFollowupQuestion
|
|
2612
|
-
const answersText = unwrapToolValue(
|
|
2613
|
-
await ctx.tools.invokeTool({
|
|
2614
|
-
toolName: 'askFollowupQuestion',
|
|
2615
|
-
input: { questions: [{ prompt: 'Which directory?', options: ['src', 'packages'] }] },
|
|
2616
|
-
}),
|
|
2617
|
-
)
|
|
2618
|
-
|
|
2619
|
-
// listFiles
|
|
2620
|
-
const filesText = unwrapToolValue(
|
|
2621
|
-
await ctx.tools.invokeTool({
|
|
2622
|
-
toolName: 'listFiles',
|
|
2623
|
-
input: { path: 'src', recursive: true, maxCount: 2000, includeIgnored: false },
|
|
2624
|
-
}),
|
|
2625
|
-
)
|
|
2626
|
-
|
|
2627
|
-
// searchFiles
|
|
2628
|
-
const hitsText = unwrapToolValue(
|
|
2629
|
-
await ctx.tools.invokeTool({
|
|
2630
|
-
toolName: 'searchFiles',
|
|
2631
|
-
input: { path: '.', regex: 'generateWorkflowCodeWorkflow', filePattern: '*.ts' },
|
|
2632
|
-
}),
|
|
2633
|
-
)
|
|
2634
|
-
|
|
2635
|
-
// fetchUrl
|
|
2636
|
-
const pageText = unwrapToolValue(await ctx.tools.invokeTool({ toolName: 'fetchUrl', input: { url: 'https://example.com' } }))
|
|
2637
|
-
|
|
2638
|
-
// search (web search)
|
|
2639
|
-
const webResults = unwrapToolValue(
|
|
2640
|
-
await ctx.tools.invokeTool({ toolName: 'search', input: { query: 'TypeScript zod schema examples' } }),
|
|
2641
|
-
)
|
|
2642
|
-
|
|
2643
|
-
// executeCommand (provider-backed; may require approval in some environments)
|
|
2644
|
-
const cmdText = unwrapToolValue(
|
|
2645
|
-
await ctx.tools.invokeTool({ toolName: 'executeCommand', input: { command: 'bun test', requiresApproval: false } }),
|
|
2646
|
-
)
|
|
2647
|
-
|
|
2648
|
-
// readFile / writeToFile (provider-backed)
|
|
2649
|
-
const fileText = unwrapToolValue(
|
|
2650
|
-
await ctx.tools.invokeTool({ toolName: 'readFile', input: { path: 'README.md', includeIgnored: false } }),
|
|
2651
|
-
)
|
|
2652
|
-
const writeText = unwrapToolValue(await ctx.tools.invokeTool({ toolName: 'writeToFile', input: { path: 'out.txt', content: 'hi' } }))
|
|
2653
|
-
|
|
2654
|
-
// replaceInFile
|
|
2655
|
-
const diff = ['<<<<<<< SEARCH', 'old', '=======', 'new', '>>>>>>> REPLACE'].join('\\n')
|
|
2656
|
-
const replaceText = unwrapToolValue(await ctx.tools.invokeTool({ toolName: 'replaceInFile', input: { path: 'out.txt', diff } }))
|
|
2657
|
-
|
|
2658
|
-
// removeFile / renameFile
|
|
2659
|
-
const rmText = unwrapToolValue(await ctx.tools.invokeTool({ toolName: 'removeFile', input: { path: 'out.txt' } }))
|
|
2660
|
-
const mvText = unwrapToolValue(
|
|
2661
|
-
await ctx.tools.invokeTool({ toolName: 'renameFile', input: { source_path: 'a.txt', target_path: 'b.txt' } }),
|
|
2662
|
-
)
|
|
2663
|
-
|
|
2664
|
-
// readBinaryFile (returns { type: 'content', value: [...] } in resp.message)
|
|
2665
|
-
const binResp = await ctx.tools.invokeTool({ toolName: 'readBinaryFile', input: { url: 'file://path/to/image.png' } })
|
|
2666
|
-
\`\`\`
|
|
2667
|
-
|
|
2668
|
-
### Sub-workflow example (ctx.runWorkflow)
|
|
2669
|
-
\`\`\`ts
|
|
2670
|
-
const results: any[] = []
|
|
2671
|
-
for (const pr of ctx.state.prs ?? []) {
|
|
2672
|
-
results.push(await ctx.runWorkflow('reviewPR', { prId: pr.id }))
|
|
2673
|
-
}
|
|
2674
|
-
return results
|
|
2675
|
-
\`\`\`
|
|
2676
|
-
|
|
2677
|
-
## Complete Example: High-Quality Step Implementation
|
|
2678
|
-
|
|
2679
|
-
This example demonstrates all quality guidelines in a single step:
|
|
2680
|
-
|
|
2681
|
-
\`\`\`ts
|
|
2682
|
-
// Step: processUserData
|
|
2683
|
-
// Task: Read, validate, and process user data from a file
|
|
2684
|
-
|
|
2685
|
-
// Input validation
|
|
2686
|
-
if (!ctx.input.dataFile) {
|
|
2687
|
-
throw new Error('Missing required input: dataFile')
|
|
2688
|
-
}
|
|
2689
|
-
|
|
2690
|
-
ctx.logger.info(\`Starting user data processing for: \${ctx.input.dataFile}\`)
|
|
2691
|
-
|
|
2692
|
-
// Read file with error handling
|
|
2693
|
-
let rawData
|
|
2694
|
-
try {
|
|
2695
|
-
ctx.logger.debug(\`Reading file: \${ctx.input.dataFile}\`)
|
|
2696
|
-
rawData = await ctx.tools.readFile({ path: ctx.input.dataFile })
|
|
2697
|
-
|
|
2698
|
-
if (!rawData) {
|
|
2699
|
-
throw new Error(\`File not found or empty: \${ctx.input.dataFile}\`)
|
|
2700
|
-
}
|
|
2701
|
-
} catch (error) {
|
|
2702
|
-
const err = error instanceof Error ? error : new Error(String(error))
|
|
2703
|
-
ctx.logger.error(\`Failed to read file: \${err.message}\`)
|
|
2704
|
-
throw err // Preserve original stack trace
|
|
2705
|
-
}
|
|
2706
|
-
|
|
2707
|
-
// Parse and validate data
|
|
2708
|
-
let users
|
|
2709
|
-
try {
|
|
2710
|
-
ctx.logger.debug('Parsing JSON data')
|
|
2711
|
-
const parsed = JSON.parse(rawData)
|
|
2712
|
-
|
|
2713
|
-
if (!parsed?.users || !Array.isArray(parsed.users)) {
|
|
2714
|
-
throw new Error('Invalid data format: expected {users: [...]}')
|
|
2715
|
-
}
|
|
2716
|
-
|
|
2717
|
-
users = parsed.users
|
|
2718
|
-
ctx.logger.info(\`Found \${users.length} users to process\`)
|
|
2719
|
-
} catch (error) {
|
|
2720
|
-
const err = error instanceof Error ? error : new Error(String(error))
|
|
2721
|
-
ctx.logger.error(\`Data parsing failed: \${err.message}\`)
|
|
2722
|
-
throw err // Preserve original stack trace
|
|
2723
|
-
}
|
|
2724
|
-
|
|
2725
|
-
// Process each user with progress reporting
|
|
2726
|
-
const results = []
|
|
2727
|
-
for (let i = 0; i < users.length; i++) {
|
|
2728
|
-
const user = users[i]
|
|
2729
|
-
|
|
2730
|
-
// Validate each user object
|
|
2731
|
-
if (!user?.id || !user?.email) {
|
|
2732
|
-
ctx.logger.warn(\`Skipping invalid user at index \${i}: missing id or email\`)
|
|
2733
|
-
continue
|
|
2734
|
-
}
|
|
2735
|
-
|
|
2736
|
-
// Process user
|
|
2737
|
-
const processed = {
|
|
2738
|
-
id: user.id,
|
|
2739
|
-
email: user.email.toLowerCase().trim(),
|
|
2740
|
-
name: user.name?.trim() || 'Unknown',
|
|
2741
|
-
processedAt: new Date().toISOString(),
|
|
2742
|
-
status: 'active'
|
|
2743
|
-
}
|
|
2744
|
-
|
|
2745
|
-
results.push(processed)
|
|
2746
|
-
|
|
2747
|
-
// Progress feedback every 10 items
|
|
2748
|
-
if ((i + 1) % 10 === 0) {
|
|
2749
|
-
ctx.logger.info(\`Processed \${i + 1}/\${users.length} users\`)
|
|
2750
|
-
}
|
|
2751
|
-
}
|
|
2752
|
-
|
|
2753
|
-
ctx.logger.info(\`Successfully processed \${results.length}/\${users.length} users\`)
|
|
2754
|
-
|
|
2755
|
-
// Return structured result with metadata
|
|
2756
|
-
return {
|
|
2757
|
-
users: results,
|
|
2758
|
-
metadata: {
|
|
2759
|
-
totalInput: users.length,
|
|
2760
|
-
totalProcessed: results.length,
|
|
2761
|
-
skipped: users.length - results.length,
|
|
2762
|
-
processedAt: new Date().toISOString()
|
|
2763
|
-
}
|
|
2764
|
-
}
|
|
2765
|
-
\`\`\`
|
|
2766
|
-
|
|
2767
|
-
Key features demonstrated:
|
|
2768
|
-
- Input validation at start
|
|
2769
|
-
- Comprehensive error handling with try-catch that preserves stack traces
|
|
2770
|
-
- Logging at info, debug, warn, and error levels
|
|
2771
|
-
- Progress reporting for long operations (every 10 items)
|
|
2772
|
-
- Data validation throughout (null checks, type checks, array validation)
|
|
2773
|
-
- Structured return value with metadata for observability
|
|
2774
|
-
- Descriptive error messages with context
|
|
2775
|
-
- Meaningful variable names (rawData, users, processed)
|
|
2776
|
-
- Clean async/await usage
|
|
2777
|
-
- Template literals for readable string interpolation
|
|
2778
|
-
- Proper error type guards (error instanceof Error)
|
|
2779
|
-
|
|
2780
|
-
## Final Instructions
|
|
2781
|
-
|
|
2782
|
-
REMEMBER: The "code" field must be ONLY the function body statements.
|
|
2783
|
-
- DO NOT wrap code in arrow functions: \`(ctx) => { ... }\`
|
|
2784
|
-
- DO NOT wrap code in async functions: \`async (ctx) => { ... }\`
|
|
2785
|
-
- DO NOT include outer curly braces
|
|
2786
|
-
- DO include a return statement if the step should produce output
|
|
2787
|
-
- Each "code" field should be a string containing multiple statements separated by newlines
|
|
2788
|
-
|
|
2789
|
-
Return the complete workflow JSON with the "code" fields populated.
|
|
2790
|
-
`;
|
|
2791
|
-
var generateWorkflowDefinitionWorkflow = async (input, ctx) => {
|
|
2792
|
-
let systemPrompt = WORKFLOW_DEFINITION_SYSTEM_PROMPT;
|
|
2793
|
-
if (input.availableTools && input.availableTools.length > 0) {
|
|
2794
|
-
const toolsList = input.availableTools.map((t) => `- ${t.name}: ${t.description}`).join("\n");
|
|
2795
|
-
systemPrompt += `
|
|
2796
|
-
|
|
2797
|
-
Available Tools:
|
|
2798
|
-
${toolsList}
|
|
2799
|
-
|
|
2800
|
-
Use these tools when appropriate.`;
|
|
2801
|
-
}
|
|
2802
|
-
const result = await ctx.step("generate-workflow-definition", async () => {
|
|
2803
|
-
return agentWorkflow(
|
|
2804
|
-
{
|
|
2805
|
-
systemPrompt,
|
|
2806
|
-
userMessage: [{ role: "user", content: input.prompt }],
|
|
2807
|
-
tools: [],
|
|
2808
|
-
outputSchema: WorkflowFileSchema
|
|
2809
|
-
},
|
|
2810
|
-
ctx
|
|
2811
|
-
);
|
|
2812
|
-
});
|
|
2813
|
-
if (result.type === "Exit" && result.object) {
|
|
2814
|
-
return result.object;
|
|
2815
|
-
}
|
|
2816
|
-
throw new Error("Failed to generate workflow definition");
|
|
2817
|
-
};
|
|
2818
|
-
var generateWorkflowCodeWorkflow = async (input, ctx) => {
|
|
2819
|
-
const result = await ctx.step("generate-workflow-code", async () => {
|
|
2820
|
-
return agentWorkflow(
|
|
2821
|
-
{
|
|
2822
|
-
systemPrompt: WORKFLOW_CODE_SYSTEM_PROMPT,
|
|
2823
|
-
userMessage: [{ role: "user", content: JSON.stringify(input.workflow, null, 2) }],
|
|
2824
|
-
tools: [],
|
|
2825
|
-
outputSchema: WorkflowFileSchema
|
|
2826
|
-
},
|
|
2827
|
-
ctx
|
|
2828
|
-
);
|
|
2829
|
-
});
|
|
2830
|
-
if (result.type === "Exit" && result.object) {
|
|
2831
|
-
return result.object;
|
|
2832
|
-
}
|
|
2833
|
-
throw new Error("Failed to generate workflow code");
|
|
2834
|
-
};
|
|
2835
|
-
|
|
2836
3231
|
// src/workflow/json-ai-types.ts
|
|
2837
3232
|
var toJsonDataContent = (data) => {
|
|
2838
3233
|
if (data instanceof URL) {
|
|
@@ -3031,16 +3426,35 @@ var makeStepFn = () => {
|
|
|
3031
3426
|
};
|
|
3032
3427
|
};
|
|
3033
3428
|
export {
|
|
3034
|
-
|
|
3035
|
-
|
|
3429
|
+
BreakStepSchema,
|
|
3430
|
+
ContinueStepSchema,
|
|
3431
|
+
IGNORED_DIRECTORIES,
|
|
3432
|
+
IfElseStepSchema,
|
|
3433
|
+
ListSkillsInputSchema,
|
|
3434
|
+
ListSkillsOutputSchema,
|
|
3435
|
+
LoadSkillInputSchema,
|
|
3436
|
+
LoadSkillOutputSchema,
|
|
3036
3437
|
MockProvider,
|
|
3438
|
+
NodeFileSystemProvider,
|
|
3439
|
+
ReadSkillFileInputSchema,
|
|
3440
|
+
ReadSkillFileOutputSchema,
|
|
3441
|
+
SKILL_ERROR_MESSAGES,
|
|
3442
|
+
SKILL_LIMITS,
|
|
3443
|
+
SOURCE_ICONS,
|
|
3444
|
+
SUSPICIOUS_PATTERNS,
|
|
3445
|
+
SkillDiscoveryError,
|
|
3446
|
+
SkillDiscoveryService,
|
|
3447
|
+
SkillValidationError,
|
|
3448
|
+
TOOL_GROUPS,
|
|
3037
3449
|
TaskEventKind,
|
|
3038
3450
|
TodoItemSchema,
|
|
3039
3451
|
TodoStatus,
|
|
3040
|
-
|
|
3452
|
+
TryCatchStepSchema,
|
|
3041
3453
|
UpdateTodoItemInputSchema,
|
|
3042
3454
|
UpdateTodoItemOutputSchema,
|
|
3043
3455
|
UsageMeter,
|
|
3456
|
+
WhileLoopStepSchema,
|
|
3457
|
+
WorkflowControlFlowStepSchema,
|
|
3044
3458
|
WorkflowDefinitionSchema,
|
|
3045
3459
|
WorkflowFileSchema,
|
|
3046
3460
|
WorkflowInputDefinitionSchema,
|
|
@@ -3054,29 +3468,36 @@ export {
|
|
|
3054
3468
|
executeCommand_default as executeCommand,
|
|
3055
3469
|
fetchUrl_default as fetchUrl,
|
|
3056
3470
|
fromJsonModelMessage,
|
|
3057
|
-
|
|
3058
|
-
generateWorkflowDefinitionWorkflow,
|
|
3059
|
-
getTodoItem_default as getTodoItem,
|
|
3471
|
+
getSkillStats,
|
|
3060
3472
|
listFiles_default as listFiles,
|
|
3061
|
-
|
|
3062
|
-
|
|
3473
|
+
listSkills,
|
|
3474
|
+
listSkillsToolInfo,
|
|
3475
|
+
loadSkill,
|
|
3476
|
+
loadSkillToolInfo,
|
|
3063
3477
|
makeStepFn,
|
|
3478
|
+
mcpServerConfigSchema,
|
|
3064
3479
|
parseDynamicWorkflowDefinition,
|
|
3065
3480
|
parseJsonFromMarkdown,
|
|
3481
|
+
providerConfigSchema,
|
|
3066
3482
|
providerModelSchema,
|
|
3067
3483
|
readBinaryFile_default as readBinaryFile,
|
|
3068
3484
|
readFile_default as readFile,
|
|
3069
|
-
|
|
3485
|
+
readSkillFile,
|
|
3486
|
+
readSkillFileToolInfo,
|
|
3070
3487
|
removeFile_default as removeFile,
|
|
3071
3488
|
renameFile_default as renameFile,
|
|
3072
3489
|
replaceInFile_default as replaceInFile,
|
|
3073
3490
|
replaceInFile as replaceInFileHelper,
|
|
3074
3491
|
responsePrompts,
|
|
3075
3492
|
ruleSchema,
|
|
3493
|
+
scriptSchema,
|
|
3076
3494
|
search_default as search,
|
|
3077
3495
|
searchFiles_default as searchFiles,
|
|
3496
|
+
skillMetadataSchema,
|
|
3078
3497
|
toJsonModelMessage,
|
|
3079
|
-
|
|
3080
|
-
|
|
3498
|
+
validateSkillMetadata,
|
|
3499
|
+
validateSkillReferences,
|
|
3500
|
+
validateSkillSecurity,
|
|
3501
|
+
validateWorkflowFile,
|
|
3081
3502
|
writeToFile_default as writeToFile
|
|
3082
3503
|
};
|