agent-worker 0.9.0 → 0.11.0

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.mjs CHANGED
@@ -1,7 +1,14 @@
1
- import { D as SUPPORTED_PROVIDERS, E as FRONTIER_MODELS, O as createModel, a as createMockBackend, c as CodexBackend, i as MockAIBackend, k as createModelAsync, l as ClaudeCodeBackend, n as createBackend, o as SdkBackend, r as listBackends, s as CursorBackend, t as checkBackends } from "./backends-BOAkfYyL.mjs";
2
- import { a as createSkillsTool, c as createFeedbackTool, i as parseImportSpec, l as AgentSession, n as buildGitUrl, o as SkillsProvider, r as getSpecDisplayName, s as FEEDBACK_PROMPT, t as SkillImporter } from "./skills-xNmQZf8e.mjs";
1
+ import { A as createModelAsync, D as FRONTIER_MODELS, O as SUPPORTED_PROVIDERS, a as createMockBackend, c as CodexBackend, i as MockAIBackend, k as createModel, l as ClaudeCodeBackend, n as createBackend, o as SdkBackend, r as listBackends, s as CursorBackend, t as checkBackends } from "./backends-DLaP0rMW.mjs";
2
+ import { t as AgentWorker } from "./worker-CJ5_b2_q.mjs";
3
3
  import { jsonSchema, tool } from "ai";
4
4
  import { createBashTool } from "bash-tool";
5
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
6
+ import { join, normalize } from "node:path";
7
+ import { parse } from "yaml";
8
+ import { mkdir, readFile, readdir, rm, stat } from "node:fs/promises";
9
+ import { z } from "zod";
10
+ import { tmpdir } from "node:os";
11
+ import { spawn } from "node:child_process";
5
12
 
6
13
  //#region src/agent/tools/bash.ts
7
14
  /**
@@ -10,7 +17,7 @@ import { createBashTool } from "bash-tool";
10
17
  * Provides bash, readFile, writeFile tools for AI agents in a sandboxed environment
11
18
  */
12
19
  /**
13
- * Create bash tools as AI SDK tool() objects for use with AgentSession
20
+ * Create bash tools as AI SDK tool() objects for use with AgentWorker
14
21
  *
15
22
  * @example
16
23
  * ```typescript
@@ -18,7 +25,7 @@ import { createBashTool } from "bash-tool";
18
25
  * files: { 'src/index.ts': 'console.log("hello")' }
19
26
  * })
20
27
  *
21
- * const session = new AgentSession({
28
+ * const session = new AgentWorker({
22
29
  * model: 'anthropic/claude-sonnet-4-5',
23
30
  * system: 'You are a coding assistant.',
24
31
  * tools
@@ -108,4 +115,553 @@ async function createBashToolsFromFiles(files, options = {}) {
108
115
  }
109
116
 
110
117
  //#endregion
111
- export { AgentSession, ClaudeCodeBackend, CodexBackend, CursorBackend, FEEDBACK_PROMPT, FRONTIER_MODELS, MockAIBackend, SUPPORTED_PROVIDERS, SdkBackend, SkillImporter, SkillsProvider, buildGitUrl, checkBackends, createBackend, createBashTool, createBashTools, createBashToolsFromDirectory, createBashToolsFromFiles, createFeedbackTool, createMockBackend, createModel, createModelAsync, createSkillsTool, getSpecDisplayName, listBackends, parseImportSpec };
118
+ //#region src/agent/tools/feedback.ts
119
+ /**
120
+ * Feedback tool — lets agents surface workflow improvement needs
121
+ *
122
+ * When enabled, agents can report what's missing or inconvenient during work:
123
+ * a tool they wished they had, a step that felt unnecessarily slow, or a
124
+ * capability gap. The purpose is workflow improvement, not bug reporting.
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * const { tool, getFeedback } = createFeedbackTool();
129
+ *
130
+ * const session = new AgentWorker({
131
+ * model: 'anthropic/claude-sonnet-4-5',
132
+ * system: FEEDBACK_PROMPT + '\nYou are a coding assistant.',
133
+ * tools: { feedback: tool, bash: bashTool },
134
+ * });
135
+ *
136
+ * await session.send('...');
137
+ * console.log(getFeedback()); // review what the agent reported
138
+ * ```
139
+ */
140
+ /**
141
+ * Append this to the system prompt when the feedback tool is enabled.
142
+ * Tells the agent the tool exists and when to use it.
143
+ */
144
+ const FEEDBACK_PROMPT = `
145
+ ## Feedback
146
+
147
+ You have a \`feedback\` tool. If you run into something inconvenient during your work — a tool you wish you had, a workflow step that feels unnecessarily slow, a capability gap — use it to report what you needed.
148
+
149
+ The purpose is to improve the workflow for future runs. Don't force feedback; only call it when you genuinely hit a pain point.
150
+ `.trim();
151
+ function createFeedbackTool(options = {}) {
152
+ const { onFeedback, maxEntries = 50 } = options;
153
+ const entries = [];
154
+ return {
155
+ tool: tool({
156
+ description: "Report a workflow improvement need. Use when you hit something inconvenient — a missing tool, an awkward step, or a capability you wished you had.",
157
+ inputSchema: jsonSchema({
158
+ type: "object",
159
+ properties: {
160
+ target: {
161
+ type: "string",
162
+ description: "The area this is about — a tool name (e.g. bash, readFile), a workflow step, or a general area (e.g. file search, code review)."
163
+ },
164
+ type: {
165
+ type: "string",
166
+ enum: [
167
+ "missing",
168
+ "friction",
169
+ "suggestion"
170
+ ],
171
+ description: "missing: a tool or capability you needed but didn't have. friction: something that works but is awkward or slow. suggestion: a concrete improvement idea."
172
+ },
173
+ description: {
174
+ type: "string",
175
+ description: "What you needed or what could be improved. Be specific."
176
+ },
177
+ context: {
178
+ type: "string",
179
+ description: "Optional: what you were trying to do when you hit this."
180
+ }
181
+ },
182
+ required: [
183
+ "target",
184
+ "type",
185
+ "description"
186
+ ]
187
+ }),
188
+ execute: async (args) => {
189
+ const validTypes = [
190
+ "missing",
191
+ "friction",
192
+ "suggestion"
193
+ ];
194
+ const rawType = args.type;
195
+ const type = validTypes.includes(rawType) ? rawType : "suggestion";
196
+ const entry = {
197
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
198
+ target: args.target,
199
+ type,
200
+ description: args.description,
201
+ ...args.context ? { context: args.context } : {}
202
+ };
203
+ if (entries.length >= maxEntries) entries.shift();
204
+ entries.push(entry);
205
+ onFeedback?.(entry);
206
+ return { recorded: true };
207
+ }
208
+ }),
209
+ getFeedback: () => [...entries],
210
+ clearFeedback: () => {
211
+ entries.length = 0;
212
+ }
213
+ };
214
+ }
215
+
216
+ //#endregion
217
+ //#region src/agent/skills/provider.ts
218
+ const frontmatterSchema = z.object({
219
+ name: z.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/).max(64),
220
+ description: z.string().min(1).max(1024),
221
+ license: z.string().optional(),
222
+ compatibility: z.string().max(500).optional(),
223
+ metadata: z.record(z.string()).optional(),
224
+ "allowed-tools": z.string().optional()
225
+ });
226
+ var SkillsProvider = class {
227
+ skills = /* @__PURE__ */ new Map();
228
+ /**
229
+ * Add a single skill directory
230
+ */
231
+ async addSkill(skillPath) {
232
+ const skillMdPath = join(skillPath, "SKILL.md");
233
+ try {
234
+ await stat(skillMdPath);
235
+ } catch {
236
+ throw new Error(`SKILL.md not found in ${skillPath}`);
237
+ }
238
+ const frontmatter = await this.parseFrontmatter(skillMdPath);
239
+ this.skills.set(frontmatter.name, {
240
+ name: frontmatter.name,
241
+ description: frontmatter.description,
242
+ path: skillPath
243
+ });
244
+ }
245
+ /**
246
+ * Scan a directory and add all valid skills found
247
+ */
248
+ async scanDirectory(dir) {
249
+ const resolved = this.resolvePath(dir);
250
+ try {
251
+ const entries = await readdir(resolved, { withFileTypes: true });
252
+ for (const entry of entries) if (entry.isDirectory()) {
253
+ const skillPath = join(resolved, entry.name);
254
+ try {
255
+ await this.addSkill(skillPath);
256
+ } catch {}
257
+ }
258
+ } catch {}
259
+ }
260
+ /**
261
+ * List all available skills (metadata only)
262
+ */
263
+ list() {
264
+ return Array.from(this.skills.values()).map(({ name, description }) => ({
265
+ name,
266
+ description
267
+ }));
268
+ }
269
+ /**
270
+ * View the full SKILL.md content
271
+ */
272
+ async view(skillName) {
273
+ const skill = this.skills.get(skillName);
274
+ if (!skill) throw new Error(`Skill "${skillName}" not found`);
275
+ return await readFile(join(skill.path, "SKILL.md"), "utf-8");
276
+ }
277
+ /**
278
+ * Read a file within a skill directory (relative path)
279
+ */
280
+ async readFile(skillName, relativePath) {
281
+ const skill = this.skills.get(skillName);
282
+ if (!skill) throw new Error(`Skill "${skillName}" not found`);
283
+ const normalized = normalize(relativePath);
284
+ if (normalized.startsWith("..")) throw new Error("Path traversal not allowed");
285
+ return await readFile(join(skill.path, normalized), "utf-8");
286
+ }
287
+ /**
288
+ * Parse YAML frontmatter from SKILL.md
289
+ */
290
+ async parseFrontmatter(skillMdPath) {
291
+ const match = (await readFile(skillMdPath, "utf-8")).match(/^---\n([\s\S]+?)\n---/);
292
+ if (!match || !match[1]) throw new Error(`Invalid SKILL.md: missing frontmatter in ${skillMdPath}`);
293
+ try {
294
+ return frontmatterSchema.parse(parse(match[1]));
295
+ } catch (error) {
296
+ throw new Error(`Invalid frontmatter in ${skillMdPath}: ${error instanceof Error ? error.message : String(error)}`);
297
+ }
298
+ }
299
+ /**
300
+ * Resolve tilde and relative paths
301
+ */
302
+ resolvePath(path) {
303
+ if (path.startsWith("~/")) {
304
+ const home = process.env.HOME || process.env.USERPROFILE;
305
+ if (!home) throw new Error("Cannot resolve ~/ without HOME environment variable");
306
+ return join(home, path.slice(2));
307
+ }
308
+ return path;
309
+ }
310
+ /**
311
+ * Synchronous version: Add a single skill directory
312
+ */
313
+ addSkillSync(skillPath) {
314
+ const skillMdPath = join(skillPath, "SKILL.md");
315
+ try {
316
+ statSync(skillMdPath);
317
+ } catch {
318
+ throw new Error(`SKILL.md not found in ${skillPath}`);
319
+ }
320
+ const frontmatter = this.parseFrontmatterSync(skillMdPath);
321
+ this.skills.set(frontmatter.name, {
322
+ name: frontmatter.name,
323
+ description: frontmatter.description,
324
+ path: skillPath
325
+ });
326
+ }
327
+ /**
328
+ * Synchronous version: Scan a directory and add all valid skills found
329
+ */
330
+ scanDirectorySync(dir) {
331
+ const resolved = this.resolvePath(dir);
332
+ try {
333
+ const entries = readdirSync(resolved, { withFileTypes: true });
334
+ for (const entry of entries) if (entry.isDirectory()) {
335
+ const skillPath = join(resolved, entry.name);
336
+ try {
337
+ this.addSkillSync(skillPath);
338
+ } catch {}
339
+ }
340
+ } catch {}
341
+ }
342
+ /**
343
+ * Synchronous version: Parse YAML frontmatter from SKILL.md
344
+ */
345
+ parseFrontmatterSync(skillMdPath) {
346
+ const match = readFileSync(skillMdPath, "utf-8").match(/^---\n([\s\S]+?)\n---/);
347
+ if (!match || !match[1]) throw new Error(`Invalid SKILL.md: missing frontmatter in ${skillMdPath}`);
348
+ try {
349
+ return frontmatterSchema.parse(parse(match[1]));
350
+ } catch (error) {
351
+ throw new Error(`Invalid frontmatter in ${skillMdPath}: ${error instanceof Error ? error.message : String(error)}`);
352
+ }
353
+ }
354
+ /**
355
+ * Add skills from a SkillImporter (async)
356
+ * Used for temporary imported skills during session lifecycle
357
+ */
358
+ async addImportedSkills(importer) {
359
+ const skillPaths = importer.getAllImportedSkillPaths();
360
+ for (const skillPath of skillPaths) await this.addSkill(skillPath);
361
+ }
362
+ /**
363
+ * Add skills from a SkillImporter (sync)
364
+ */
365
+ addImportedSkillsSync(importer) {
366
+ const skillPaths = importer.getAllImportedSkillPaths();
367
+ for (const skillPath of skillPaths) this.addSkillSync(skillPath);
368
+ }
369
+ };
370
+
371
+ //#endregion
372
+ //#region src/agent/tools/skills.ts
373
+ /**
374
+ * Create a Skills tool as an AI SDK tool() object
375
+ */
376
+ function createSkillsTool(provider) {
377
+ return tool({
378
+ description: "Interact with available agent skills. Use \"list\" to see all skills with their descriptions, \"view\" to read a complete SKILL.md file, \"readFile\" to read files within a skill directory (e.g., references/, scripts/, assets/).",
379
+ inputSchema: jsonSchema({
380
+ type: "object",
381
+ properties: {
382
+ operation: {
383
+ type: "string",
384
+ enum: [
385
+ "list",
386
+ "view",
387
+ "readFile"
388
+ ],
389
+ description: "Operation to perform"
390
+ },
391
+ skillName: {
392
+ type: "string",
393
+ description: "Skill name (required for view and readFile operations)"
394
+ },
395
+ filePath: {
396
+ type: "string",
397
+ description: "Relative file path within the skill directory (required for readFile operation, e.g., \"references/search-strategies.md\")"
398
+ }
399
+ },
400
+ required: ["operation"]
401
+ }),
402
+ execute: async (args) => {
403
+ const operation = args.operation;
404
+ const skillName = args.skillName;
405
+ const filePath = args.filePath;
406
+ switch (operation) {
407
+ case "list": {
408
+ const skills = provider.list();
409
+ if (skills.length === 0) return { message: "No skills available" };
410
+ return { skills: skills.map((s) => ({
411
+ name: s.name,
412
+ description: s.description
413
+ })) };
414
+ }
415
+ case "view":
416
+ if (!skillName) throw new Error("skillName is required for view operation");
417
+ return { content: await provider.view(skillName) };
418
+ case "readFile":
419
+ if (!skillName || !filePath) throw new Error("skillName and filePath are required for readFile operation");
420
+ return { content: await provider.readFile(skillName, filePath) };
421
+ default: throw new Error(`Unknown operation: ${operation}`);
422
+ }
423
+ }
424
+ });
425
+ }
426
+
427
+ //#endregion
428
+ //#region src/agent/skills/import-spec.ts
429
+ const providerUrls = {
430
+ github: "https://github.com",
431
+ gitlab: "https://gitlab.com",
432
+ gitee: "https://gitee.com"
433
+ };
434
+ /**
435
+ * Parse import spec: [provider:]owner/repo[@ref]:{skill1,skill2,...}
436
+ *
437
+ * Examples:
438
+ * vercel-labs/agent-skills:react-best-practices
439
+ * vercel-labs/agent-skills:{react,web}
440
+ * vercel-labs/agent-skills@v1.0.0:react
441
+ * github:vercel-labs/agent-skills@main:react
442
+ * vercel-labs/agent-skills (imports all)
443
+ */
444
+ function parseImportSpec(spec) {
445
+ const match = spec.match(/^(?:([a-z]+):)?([^/@:]+)\/([^/@:]+)(?:@([^:]+))?(?::(.+))?$/);
446
+ if (!match) throw new Error(`Invalid import spec: ${spec}\nFormat: [provider:]owner/repo[@ref]:{skill1,skill2,...}`);
447
+ const [, providerMatch = "github", ownerMatch, repoMatch, refMatch = "main", skillsStr] = match;
448
+ if (!ownerMatch || !repoMatch) throw new Error(`Invalid import spec: ${spec}\nFormat: [provider:]owner/repo[@ref]:{skill1,skill2,...}`);
449
+ const provider = providerMatch;
450
+ const owner = ownerMatch;
451
+ const repo = repoMatch;
452
+ const ref = refMatch;
453
+ if (![
454
+ "github",
455
+ "gitlab",
456
+ "gitee"
457
+ ].includes(provider)) throw new Error(`Unsupported provider: ${provider}. Supported: github, gitlab, gitee`);
458
+ const safeNamePattern = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
459
+ if (!safeNamePattern.test(owner)) throw new Error(`Invalid owner: "${owner}". Must start with alphanumeric and only contain alphanumeric, hyphen, underscore, or dot`);
460
+ if (!safeNamePattern.test(repo)) throw new Error(`Invalid repo: "${repo}". Must start with alphanumeric and only contain alphanumeric, hyphen, underscore, or dot`);
461
+ if (!safeNamePattern.test(ref)) throw new Error(`Invalid ref: "${ref}". Must start with alphanumeric and only contain alphanumeric, hyphen, underscore, or dot`);
462
+ let skills = "all";
463
+ if (skillsStr) if (skillsStr.startsWith("{") && skillsStr.endsWith("}")) {
464
+ const skillList = skillsStr.slice(1, -1).split(",").map((s) => s.trim()).filter((s) => s.length > 0);
465
+ if (skillList.length === 0) throw new Error("Empty skill list in braces");
466
+ skills = skillList;
467
+ } else skills = [skillsStr.trim()];
468
+ return {
469
+ provider,
470
+ owner,
471
+ repo,
472
+ ref,
473
+ skills,
474
+ rawSpec: spec
475
+ };
476
+ }
477
+ /**
478
+ * Build Git URL from import spec
479
+ */
480
+ function buildGitUrl(spec) {
481
+ return `${providerUrls[spec.provider]}/${spec.owner}/${spec.repo}.git`;
482
+ }
483
+ /**
484
+ * Get display name for import spec
485
+ */
486
+ function getSpecDisplayName(spec) {
487
+ const skillsDisplay = spec.skills === "all" ? "all skills" : spec.skills.length === 1 ? spec.skills[0] : `${spec.skills.length} skills`;
488
+ return `${spec.owner}/${spec.repo}@${spec.ref} (${skillsDisplay})`;
489
+ }
490
+
491
+ //#endregion
492
+ //#region src/agent/skills/importer.ts
493
+ /**
494
+ * Temporary skill importer for session lifecycle
495
+ * Clones Git repos to temp directory and manages imported skills
496
+ */
497
+ var SkillImporter = class {
498
+ tempDir;
499
+ imported = /* @__PURE__ */ new Map();
500
+ constructor(sessionId) {
501
+ this.tempDir = join(tmpdir(), `agent-worker-skills-${sessionId}`);
502
+ }
503
+ /**
504
+ * Import skills from a Git repository
505
+ */
506
+ async import(spec) {
507
+ const parsed = parseImportSpec(spec);
508
+ console.log(`Importing: ${getSpecDisplayName(parsed)}`);
509
+ const repoDir = await this.cloneRepo(parsed);
510
+ const skillNames = await this.extractSkills(repoDir, parsed);
511
+ console.log(`✓ Imported ${skillNames.length} skill(s): ${skillNames.join(", ")}`);
512
+ return skillNames;
513
+ }
514
+ /**
515
+ * Import skills from multiple specs
516
+ */
517
+ async importMultiple(specs) {
518
+ const allSkillNames = [];
519
+ for (const spec of specs) try {
520
+ const skillNames = await this.import(spec);
521
+ allSkillNames.push(...skillNames);
522
+ } catch (error) {
523
+ console.error(`Failed to import ${spec}:`, error);
524
+ }
525
+ return allSkillNames;
526
+ }
527
+ /**
528
+ * Get path for an imported skill
529
+ */
530
+ getImportedSkillPath(skillName) {
531
+ return this.imported.get(skillName)?.tempPath || null;
532
+ }
533
+ /**
534
+ * Get all imported skill paths
535
+ */
536
+ getAllImportedSkillPaths() {
537
+ return Array.from(this.imported.values()).map((s) => s.tempPath);
538
+ }
539
+ /**
540
+ * Get all imported skills metadata
541
+ */
542
+ getImportedSkills() {
543
+ return Array.from(this.imported.values());
544
+ }
545
+ /**
546
+ * Get temporary directory path
547
+ */
548
+ getTempDir() {
549
+ return this.tempDir;
550
+ }
551
+ /**
552
+ * Cleanup temporary directory
553
+ */
554
+ async cleanup() {
555
+ if (existsSync(this.tempDir)) await rm(this.tempDir, {
556
+ recursive: true,
557
+ force: true
558
+ });
559
+ }
560
+ /**
561
+ * Clone Git repository (shallow clone)
562
+ */
563
+ async cloneRepo(spec) {
564
+ await mkdir(this.tempDir, { recursive: true });
565
+ const repoDir = join(this.tempDir, `${spec.owner}-${spec.repo}`);
566
+ if (existsSync(repoDir)) return repoDir;
567
+ const gitUrl = buildGitUrl(spec);
568
+ await this.execGit([
569
+ "clone",
570
+ "--depth",
571
+ "1",
572
+ "--branch",
573
+ spec.ref,
574
+ "--single-branch",
575
+ gitUrl,
576
+ repoDir
577
+ ]);
578
+ return repoDir;
579
+ }
580
+ /**
581
+ * Extract skills from cloned repository
582
+ */
583
+ async extractSkills(repoDir, spec) {
584
+ const skillsDir = await this.findSkillsDirectory(repoDir);
585
+ const skillsToImport = spec.skills === "all" ? await this.findAllSkills(skillsDir) : spec.skills;
586
+ const importedSkills = [];
587
+ for (const skillName of skillsToImport) {
588
+ const skillPath = join(skillsDir, skillName);
589
+ const skillMdPath = join(skillPath, "SKILL.md");
590
+ try {
591
+ await stat(skillMdPath);
592
+ this.imported.set(skillName, {
593
+ name: skillName,
594
+ source: spec.rawSpec,
595
+ tempPath: skillPath
596
+ });
597
+ importedSkills.push(skillName);
598
+ } catch {
599
+ console.warn(`Skipping ${skillName}: SKILL.md not found`);
600
+ }
601
+ }
602
+ if (importedSkills.length === 0) throw new Error(`No valid skills found in ${spec.owner}/${spec.repo}`);
603
+ return importedSkills;
604
+ }
605
+ /**
606
+ * Find skills directory in repository
607
+ * Tries: skills/, agent-skills/, . (root)
608
+ */
609
+ async findSkillsDirectory(repoDir) {
610
+ for (const candidate of [
611
+ "skills",
612
+ "agent-skills",
613
+ "."
614
+ ]) {
615
+ const dir = join(repoDir, candidate);
616
+ try {
617
+ if ((await stat(dir)).isDirectory()) {
618
+ if ((await readdir(dir, { withFileTypes: true })).some((entry) => entry.isDirectory() && existsSync(join(dir, entry.name, "SKILL.md")))) return dir;
619
+ }
620
+ } catch {
621
+ continue;
622
+ }
623
+ }
624
+ throw new Error(`No skills directory found in ${repoDir}`);
625
+ }
626
+ /**
627
+ * Find all skills in a directory
628
+ */
629
+ async findAllSkills(skillsDir) {
630
+ const entries = await readdir(skillsDir, { withFileTypes: true });
631
+ const skills = [];
632
+ for (const entry of entries) if (entry.isDirectory()) {
633
+ if (existsSync(join(skillsDir, entry.name, "SKILL.md"))) skills.push(entry.name);
634
+ }
635
+ return skills;
636
+ }
637
+ /**
638
+ * Execute git command
639
+ */
640
+ async execGit(args) {
641
+ return new Promise((resolve, reject) => {
642
+ const git = spawn("git", args, { stdio: [
643
+ "ignore",
644
+ "pipe",
645
+ "pipe"
646
+ ] });
647
+ let stdout = "";
648
+ let stderr = "";
649
+ git.stdout?.on("data", (data) => {
650
+ stdout += data.toString();
651
+ });
652
+ git.stderr?.on("data", (data) => {
653
+ stderr += data.toString();
654
+ });
655
+ git.on("close", (code) => {
656
+ if (code === 0) resolve();
657
+ else reject(/* @__PURE__ */ new Error(`Git command failed (exit ${code}): ${stderr || stdout}`));
658
+ });
659
+ git.on("error", (error) => {
660
+ reject(/* @__PURE__ */ new Error(`Failed to spawn git: ${error.message}`));
661
+ });
662
+ });
663
+ }
664
+ };
665
+
666
+ //#endregion
667
+ export { AgentWorker as AgentSession, AgentWorker, ClaudeCodeBackend, CodexBackend, CursorBackend, FEEDBACK_PROMPT, FRONTIER_MODELS, MockAIBackend, SUPPORTED_PROVIDERS, SdkBackend, SkillImporter, SkillsProvider, buildGitUrl, checkBackends, createBackend, createBashTool, createBashTools, createBashToolsFromDirectory, createBashToolsFromFiles, createFeedbackTool, createMockBackend, createModel, createModelAsync, createSkillsTool, getSpecDisplayName, listBackends, parseImportSpec };
@@ -16,7 +16,7 @@ function createSilentLogger() {
16
16
  /**
17
17
  * Create a logger that writes to the channel.
18
18
  *
19
- * - info/warn/error → channel entry with kind="log" (always shown to user)
19
+ * - info/warn/error → channel entry with kind="system" (always shown to user)
20
20
  * - debug → channel entry with kind="debug" (only shown with --debug)
21
21
  *
22
22
  * The display layer handles formatting and filtering.
@@ -31,7 +31,7 @@ function createChannelLogger(config) {
31
31
  };
32
32
  const write = (level, message, args) => {
33
33
  const content = formatContent(level, message, args);
34
- const kind = level === "debug" ? "debug" : "log";
34
+ const kind = level === "debug" ? "debug" : "system";
35
35
  provider.appendChannel(from, content, { kind }).catch(() => {});
36
36
  };
37
37
  return {
@@ -0,0 +1,70 @@
1
+ import { o as MemoryStorage, s as ContextProviderImpl } from "./cli/index.mjs";
2
+
3
+ //#region src/workflow/context/memory-provider.ts
4
+ /**
5
+ * In-memory ContextProvider for testing.
6
+ * All domain logic is inherited from ContextProviderImpl;
7
+ * this class adds test helpers for inspection and cleanup.
8
+ */
9
+ var MemoryContextProvider = class extends ContextProviderImpl {
10
+ memoryStorage;
11
+ constructor(validAgents) {
12
+ const storage = new MemoryStorage();
13
+ super(storage, validAgents);
14
+ this.memoryStorage = storage;
15
+ }
16
+ /** Get underlying MemoryStorage (for testing) */
17
+ getStorage() {
18
+ return this.memoryStorage;
19
+ }
20
+ /** Get all channel messages (for testing, unfiltered) */
21
+ async getMessages() {
22
+ return this.readChannel();
23
+ }
24
+ /** Clear all data (for testing) */
25
+ clear() {
26
+ this.memoryStorage.clear();
27
+ }
28
+ /** Get all resources (for testing) */
29
+ async getResources() {
30
+ const keys = await this.memoryStorage.list("resources/");
31
+ const map = /* @__PURE__ */ new Map();
32
+ for (const key of keys) {
33
+ const content = await this.memoryStorage.read(`resources/${key}`);
34
+ if (content !== null) {
35
+ const id = key.replace(/\.[^.]+$/, "");
36
+ map.set(id, content);
37
+ }
38
+ }
39
+ return map;
40
+ }
41
+ /** Get inbox state for an agent (for testing) */
42
+ async getInboxState(agent) {
43
+ const raw = await this.memoryStorage.read("_state/inbox.json");
44
+ if (!raw) return void 0;
45
+ try {
46
+ return JSON.parse(raw).readCursors?.[agent];
47
+ } catch {
48
+ return;
49
+ }
50
+ }
51
+ /** Get all documents (for testing) */
52
+ async getDocuments() {
53
+ const files = await this.memoryStorage.list("documents/");
54
+ const map = /* @__PURE__ */ new Map();
55
+ for (const file of files) {
56
+ const content = await this.memoryStorage.read(`documents/${file}`);
57
+ if (content !== null) map.set(file, content);
58
+ }
59
+ return map;
60
+ }
61
+ };
62
+ /**
63
+ * Create a memory context provider
64
+ */
65
+ function createMemoryContextProvider(validAgents) {
66
+ return new MemoryContextProvider(validAgents);
67
+ }
68
+
69
+ //#endregion
70
+ export { createMemoryContextProvider as n, MemoryContextProvider as t };