@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/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/tool.ts
200
- var ToolResponseType = /* @__PURE__ */ ((ToolResponseType2) => {
201
- ToolResponseType2["Reply"] = "Reply";
202
- ToolResponseType2["Exit"] = "Exit";
203
- ToolResponseType2["Error"] = "Error";
204
- return ToolResponseType2;
205
- })(ToolResponseType || {});
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/tools/askFollowupQuestion.ts
338
+ // src/skills/types.ts
208
339
  import { z as z2 } from "zod";
209
- var questionObject = z2.object({
210
- prompt: z2.string().describe("The text of the question.").meta({ usageValue: "question text here" }),
211
- options: z2.array(z2.string()).default([]).describe("Ordered list of suggested answers (omit if none).").meta({ usageValue: "suggested answer here" })
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: z2.object({
217
- questions: z2.array(questionObject).describe("One or more follow-up questions you need answered before you can continue.").meta({ usageValue: "questions here" })
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
- type: "Error" /* Error */,
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
- type: "Error" /* Error */,
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
- type: "Reply" /* Reply */,
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 z3 } from "zod";
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: z3.object({
310
- command: z3.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" }),
311
- requiresApproval: z3.preprocess((val) => {
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
- type: "Reply" /* Reply */,
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
- type: "Error" /* Error */,
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
- type: "Error" /* Error */,
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 z4 } from "zod";
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: z4.object({
400
- url: z4.preprocess((val) => {
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
- }, z4.array(z4.string())).describe("One or more URLs to fetch, separated by commas if multiple.").meta({ usageValue: "url" })
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
- type: "Error" /* Error */,
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
- type: "Error" /* Error */,
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
- type: "Reply" /* Reply */,
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 z6 } from "zod";
508
- var toolInfo5 = {
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: z6.object({
512
- path: z6.string().describe("The path of the directory to list contents for (relative to the current working directory)").meta({ usageValue: "Directory path here" }),
513
- maxCount: z6.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)" }),
514
- recursive: z6.preprocess((val) => {
515
- if (typeof val === "string") {
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 handler5 = async (provider, args) => {
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 } = toolInfo5.parameters.parse(args);
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
- type: "Reply" /* Reply */,
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
- ...toolInfo5,
568
- handler: handler5
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 toolInfo8 = {
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 handler8 = async (provider, args) => {
1297
+ var handler5 = async (provider, args) => {
744
1298
  if (!provider.readBinaryFile) {
745
1299
  return {
746
- type: "Error" /* Error */,
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 } = toolInfo8.parameters.parse(args);
1307
+ const { url } = toolInfo5.parameters.parse(args);
754
1308
  try {
755
1309
  const filePart = await provider.readBinaryFile(url);
756
1310
  return {
757
- type: "Reply" /* Reply */,
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
- type: "Error" /* Error */,
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
- ...toolInfo8,
783
- handler: handler8
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 toolInfo9 = {
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((val) => {
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 handler9 = async (provider, args) => {
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 } = toolInfo9.parameters.parse(args);
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(`<read_file_file_content path="${path}" file_not_found="true" />`);
1378
+ resp.push(createFileElement("read_file_file_content", path, void 0, { file_not_found: "true" }));
838
1379
  } else {
839
- const isEmpty = fileContent.trim().length === 0;
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
- type: "Reply" /* Reply */,
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
- ...toolInfo9,
857
- handler: handler9
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 z13 } from "zod";
898
- var toolInfo11 = {
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: z13.object({
902
- path: z13.string().describe("The path of the file to remove").meta({ usageValue: "File path here" })
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 handler11 = async (provider, args) => {
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 = toolInfo11.parameters.safeParse(args);
1418
+ const parsed = toolInfo7.parameters.safeParse(args);
925
1419
  if (!parsed.success) {
926
1420
  return {
927
- type: "Error" /* Error */,
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
- type: "Reply" /* Reply */,
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
- ...toolInfo11,
946
- handler: handler11
1439
+ ...toolInfo7,
1440
+ handler: handler7
947
1441
  };
948
1442
 
949
1443
  // src/tools/renameFile.ts
950
- import { z as z14 } from "zod";
951
- var toolInfo12 = {
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: z14.object({
955
- source_path: z14.string().describe("The current path of the file").meta({ usageValue: "Source file path here" }),
956
- target_path: z14.string().describe("The new path for the file").meta({ usageValue: "Target file path here" })
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 handler12 = async (provider, args) => {
1463
+ var handler8 = async (provider, args) => {
970
1464
  if (!provider.renameFile) {
971
1465
  return {
972
- type: "Error" /* Error */,
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 } = toolInfo12.parameters.parse(args);
1473
+ const { source_path, target_path } = toolInfo8.parameters.parse(args);
980
1474
  await provider.renameFile(source_path, target_path);
981
1475
  return {
982
- type: "Reply" /* Reply */,
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
- ...toolInfo12,
991
- handler: handler12
1484
+ ...toolInfo8,
1485
+ handler: handler8
992
1486
  };
993
1487
 
994
1488
  // src/tools/replaceInFile.ts
995
- import { z as z15 } from "zod";
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 strippedSearch = trimmedSearch.replace(/\s+/g, "");
1039
- const endPos = actualPos;
1040
- const startPos = endPos - strippedSearch.length;
1041
- return content.slice(0, startPos) + replace + content.slice(endPos);
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 toolInfo13 = {
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: z15.object({
1076
- path: z15.string().describe("The path of the file to modify").meta({ usageValue: "File path here" }),
1077
- diff: z15.string().describe(
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 handler13 = async (provider, args) => {
1695
+ var handler9 = async (provider, args) => {
1185
1696
  if (!provider.readFile || !provider.writeFile) {
1186
1697
  return {
1187
- type: "Error" /* Error */,
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 = toolInfo13.parameters.safeParse(args);
1705
+ const parsed = toolInfo9.parameters.safeParse(args);
1195
1706
  if (!parsed.success) {
1196
1707
  return {
1197
- type: "Error" /* Error */,
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
- type: "Error" /* Error */,
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
- type: "Error" /* Error */,
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
- type: "Reply" /* Reply */,
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
- type: "Reply" /* Reply */,
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
- type: "Error" /* Error */,
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
- ...toolInfo13,
1259
- handler: handler13
1769
+ ...toolInfo9,
1770
+ handler: handler9
1260
1771
  };
1261
1772
 
1262
1773
  // src/tools/search.ts
1263
- import { z as z16 } from "zod";
1264
- var toolInfo14 = {
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: z16.object({
1268
- query: z16.string().describe("The query to search for").meta({ usageValue: "Your search query here" })
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 handler14 = async (provider, args) => {
1293
- const { query } = toolInfo14.parameters.parse(args);
1803
+ var handler10 = async (provider, args) => {
1804
+ const { query } = toolInfo10.parameters.parse(args);
1294
1805
  if (!provider.search) {
1295
1806
  return {
1296
- type: "Error" /* Error */,
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
- type: "Reply" /* Reply */,
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
- ...toolInfo14,
1314
- handler: handler14
1824
+ ...toolInfo10,
1825
+ handler: handler10
1315
1826
  };
1316
1827
 
1317
1828
  // src/tools/searchFiles.ts
1318
- import { z as z17 } from "zod";
1319
- var toolInfo15 = {
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: z17.object({
1323
- path: z17.string().describe(
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: z17.string().describe("The regular expression pattern to search for. Uses Rust regex syntax.").meta({
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: z17.string().optional().describe(
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 handler15 = async (provider, args) => {
1858
+ var handler11 = async (provider, args) => {
1348
1859
  if (!provider.searchFiles) {
1349
1860
  return {
1350
- type: "Error" /* Error */,
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 = toolInfo15.parameters.safeParse(args);
1868
+ const parsed = toolInfo11.parameters.safeParse(args);
1358
1869
  if (!parsed.success) {
1359
1870
  return {
1360
- type: "Error" /* Error */,
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
- type: "Reply" /* Reply */,
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
- type: "Error" /* Error */,
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
- ...toolInfo15,
1395
- handler: handler15
1905
+ ...toolInfo11,
1906
+ handler: handler11
1396
1907
  };
1397
1908
 
1398
- // src/tools/updateMemory.ts
1399
- import { z as z18 } from "zod";
1400
- var toolInfo16 = {
1401
- name: "updateMemory",
1402
- description: 'Appends, replaces, or removes content from a memory topic. Use "append" to add to existing content, "replace" to overwrite entirely, or "remove" to delete a topic. Memory persists across tool calls within a workflow.',
1403
- parameters: z18.object({
1404
- operation: z18.enum(["append", "replace", "remove"]).describe("The operation to perform."),
1405
- topic: z18.string().nullish().describe('The topic to update in memory. Defaults to ":default:".'),
1406
- content: z18.string().nullish().describe("The content for append or replace operations. Must be omitted for remove operation.")
1407
- }).superRefine((data, ctx) => {
1408
- if (data.operation === "append" || data.operation === "replace") {
1409
- if (data.content === void 0) {
1410
- ctx.addIssue({
1411
- code: "custom",
1412
- message: 'Content is required for "append" and "replace" operations.',
1413
- path: ["content"]
1414
- });
1415
- }
1416
- } else if (data.operation === "remove") {
1417
- if (data.content !== void 0) {
1418
- ctx.addIssue({
1419
- code: "custom",
1420
- message: 'Content must not be provided for "remove" operation.',
1421
- path: ["content"]
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
- var handler16 = async (provider, args) => {
1428
- if (!provider.updateMemory) {
1429
- return {
1430
- type: "Error" /* Error */,
1431
- message: {
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 updateTodoItem_default = {
1498
- ...toolInfo17,
1499
- handler: handler17
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 z19 } from "zod";
1504
- var toolInfo18 = {
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 `&lt;`, `&gt;`, or `&amp;`. Also ensure there is no unwanted CDATA tags in the content.",
1507
- parameters: z19.object({
1508
- path: z19.string().describe("The path of the file to write to").meta({ usageValue: "File path here" }),
1509
- content: z19.string().describe(
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 handler18 = async (provider, args) => {
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 = toolInfo18.parameters.safeParse(args);
1985
+ const parsed = toolInfo12.parameters.safeParse(args);
1546
1986
  if (!parsed.success) {
1547
1987
  return {
1548
- type: "Error" /* Error */,
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[") && content.endsWith("]]>")) content = trimmedContent.slice(9, -3);
1997
+ if (trimmedContent.startsWith("<![CDATA[") && trimmedContent.endsWith("]]>")) content = trimmedContent.slice(9, -3);
1558
1998
  await provider.writeFile(path, content);
1559
1999
  return {
1560
- type: "Reply" /* Reply */,
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
- ...toolInfo18,
1569
- handler: handler18
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
- #calculageUsage(usage, providerMetadata, modelInfo) {
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.#calculageUsage(usage, resp.providerMetadata, modelInfo);
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 z20 } from "zod";
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: toolInfo19, maxToolRoundTrips = 200 } = input;
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 toolInfo19) {
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 exitReason2 = { type: "Exit", message: textContent, messages };
1795
- await event("end-task", { kind: "EndTask" /* EndTask */, exitReason: exitReason2 });
1796
- return exitReason2;
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: ${z20.prettifyError(validated.error)}. Please correct the output.`;
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 exitReason = { type: "Exit", message: textContent, object: validated.data, messages };
1811
- await event("end-task", { kind: "EndTask" /* EndTask */, exitReason });
1812
- return exitReason;
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
- switch (toolResponse.type) {
1848
- case "Reply" /* Reply */:
1849
- await event(`event-tool-reply-${toolCall.toolName}-${toolCall.toolCallId}`, {
1850
- kind: "ToolReply" /* ToolReply */,
1851
- tool: toolCall.toolName,
1852
- content: toolResponse.message
1853
- });
1854
- toolResults.push({
1855
- toolCallId: toolCall.toolCallId,
1856
- toolName: toolCall.toolName,
1857
- output: toolResponse.message
1858
- });
1859
- break;
1860
- case "Error" /* Error */:
1861
- await event(`event-tool-error-${toolCall.toolName}-${toolCall.toolCallId}`, {
1862
- kind: "ToolError" /* ToolError */,
1863
- tool: toolCall.toolName,
1864
- error: toolResponse.message ?? "Unknown error"
1865
- });
1866
- toolResults.push({
1867
- toolCallId: toolCall.toolCallId,
1868
- toolName: toolCall.toolName,
1869
- output: toolResponse.message
1870
- });
1871
- break;
1872
- case "Exit": {
1873
- if (toolCalls.length > 1) {
1874
- toolResults.push({
1875
- toolCallId: toolCall.toolCallId,
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
- await event("end-task", { kind: "EndTask" /* EndTask */, exitReason: { type: "UsageExceeded", messages } });
1904
- throw new Error("Maximum number of tool round trips reached.");
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 z22 } from "zod";
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 z21 } from "zod";
1913
- var WorkflowInputDefinitionSchema = z21.object({
1914
- id: z21.string(),
1915
- description: z21.string().nullish(),
1916
- default: z21.any().nullish()
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 = z21.object({
1919
- id: z21.string(),
1920
- tools: z21.array(z21.string()).nullish(),
1921
- task: z21.string(),
1922
- output: z21.string().nullish(),
1923
- expected_outcome: z21.string().nullish(),
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: z21.any().nullish(),
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: z21.number().positive().nullish()
2351
+ timeout: z20.number().positive().nullish()
1938
2352
  });
1939
- var WorkflowDefinitionSchema = z21.object({
1940
- task: z21.string(),
1941
- inputs: z21.array(WorkflowInputDefinitionSchema).nullish(),
1942
- steps: z21.array(WorkflowStepDefinitionSchema),
1943
- output: z21.string().nullish()
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 WorkflowFileSchema = z21.object({
1946
- workflows: z21.record(z21.string(), WorkflowDefinitionSchema)
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 = parse(source);
2538
+ const raw = parse2(source);
1953
2539
  const validated = WorkflowFileSchema.safeParse(raw);
1954
2540
  if (!validated.success) {
1955
- return { success: false, error: z22.prettifyError(validated.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 allowedToolNames = stepDef.tools;
2029
- const toolsForAgent = allowedToolNames ? options.toolInfo.filter((t) => allowedToolNames.includes(t.name)) : [...options.toolInfo];
2030
- if (!allowedToolNames || allowedToolNames.includes("runWorkflow")) {
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: z22.object({
2035
- workflowId: z22.string().describe("Sub-workflow id to run"),
2036
- input: z22.any().nullish().describe("Optional input object for the sub-workflow")
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 { type: "Error" /* Error */, message: { type: "error-text", value: "runWorkflow is virtual." } };
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
- type: "Error" /* Error */,
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
- type: "Error" /* Error */,
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 { type: "Reply" /* Reply */, message: jsonResult };
2879
+ return { success: true, message: jsonResult };
2086
2880
  } catch (error) {
2087
2881
  return {
2088
- type: "Error" /* Error */,
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
- throw new Error(`Agent step '${stepDef.id}' in workflow '${workflowId}' exited unexpectedly with type: ${result.type}`);
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, compiledSteps, runInternal) {
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, compiledSteps, runInternal) {
2172
- const result = await executeStepWithTimeout(stepDef, workflowId, input, state, context, options, compiledSteps, runInternal);
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
- const _schema = z22.any();
2176
- if (typeof stepDef.outputSchema === "object") {
2177
- context.logger.debug(`[Step] Validating output for step '${stepDef.id}' against schema`);
2178
- if (stepDef.outputSchema.type === "object") {
2179
- if (typeof result !== "object" || result === null || Array.isArray(result)) {
2180
- throw new Error(`Expected object output, got ${Array.isArray(result) ? "array" : result === null ? "null" : typeof result}`);
2181
- }
2182
- }
2183
- if (stepDef.outputSchema.type === "array" && !Array.isArray(result)) {
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 stepName = `${workflowId}.${stepDef.id}`;
2219
- context.logger.info(`[Workflow] Step ${i + 1}/${workflow.steps.length}: ${stepDef.id}`);
2220
- context.logger.debug(`[Workflow] Step task: ${stepDef.task}`);
2221
- if (stepDef.expected_outcome) {
2222
- context.logger.debug(`[Workflow] Expected outcome: ${stepDef.expected_outcome}`);
2223
- }
2224
- context.logger.debug(`[Workflow] Current state keys: ${Object.keys(state).join(", ")}`);
2225
- lastOutput = await context.step(stepName, async () => {
2226
- return await executeStep(stepDef, workflowId, validatedInput, state, context, options, compiledSteps, runInternal);
2227
- });
2228
- const outputKey = stepDef.output ?? stepDef.id;
2229
- state[outputKey] = lastOutput;
2230
- context.logger.debug(
2231
- `[Workflow] Step output stored as '${outputKey}': ${typeof lastOutput === "object" ? JSON.stringify(lastOutput).substring(0, 200) : lastOutput}`
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
- GenerateWorkflowCodeInputSchema,
3035
- GenerateWorkflowDefinitionInputSchema,
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
- ToolResponseType,
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
- generateWorkflowCodeWorkflow,
3058
- generateWorkflowDefinitionWorkflow,
3059
- getTodoItem_default as getTodoItem,
3471
+ getSkillStats,
3060
3472
  listFiles_default as listFiles,
3061
- listMemoryTopics_default as listMemoryTopics,
3062
- listTodoItems_default as listTodoItems,
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
- readMemory_default as readMemory,
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
- updateMemory_default as updateMemory,
3080
- updateTodoItem_default as updateTodoItem,
3498
+ validateSkillMetadata,
3499
+ validateSkillReferences,
3500
+ validateSkillSecurity,
3501
+ validateWorkflowFile,
3081
3502
  writeToFile_default as writeToFile
3082
3503
  };