@swifty.js/swifty 0.0.30 → 0.0.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +14 -0
  2. package/dist/agent-O6C4FCQY.js +4 -0
  3. package/dist/anthropic-CFM2DXMB.js +4 -0
  4. package/dist/checker-A6PGACKZ.js +4 -0
  5. package/dist/chunk-4ABFUBO4.js +601 -0
  6. package/dist/{chunk-K5ZK27BX.js → chunk-AGJYBJ5M.js} +1 -1
  7. package/dist/chunk-MVVSONAE.js +4 -0
  8. package/dist/chunk-OQCEP2F5.js +4 -0
  9. package/dist/{chunk-QFSCPD63.js → chunk-TCQ4EB4G.js} +1 -1
  10. package/dist/chunk-Y2UUXOQA.js +4 -0
  11. package/dist/chunk-YAXZ2OUD.js +150 -0
  12. package/dist/lib/agent-YRTROHXU.js +10 -0
  13. package/dist/lib/{anthropic-CL5IK7KN.js → anthropic-R6DMXE7U.js} +3 -4
  14. package/dist/lib/{checker-BS4MK3O6.js → checker-M7RKMKJQ.js} +2 -3
  15. package/dist/lib/{chunk-IYPTOY3Y.js → chunk-4MDCRPVV.js} +35 -26
  16. package/dist/lib/{chunk-RMB3J46W.js → chunk-A4MXZA5Q.js} +197 -36
  17. package/dist/lib/{chunk-7QPDFSQE.js → chunk-EWE5IWZE.js} +20 -10
  18. package/dist/lib/{chunk-KG4MJGKQ.js → chunk-FZ4Y6MBN.js} +1 -4
  19. package/dist/lib/{chunk-OOSNCCI7.js → chunk-J6AV7SIF.js} +143 -148
  20. package/dist/lib/{chunk-I7OPU4K2.js → chunk-OEPCNATU.js} +192 -32
  21. package/dist/lib/{chunk-PIL7M52F.js → chunk-XT67KGWE.js} +16 -3
  22. package/dist/lib/index.d.ts +856 -778
  23. package/dist/lib/index.js +2261 -749
  24. package/dist/lib/{openai-LH4E7ZQS.js → openai-5N42ZYIJ.js} +1 -1
  25. package/dist/lib/{tool-filter-CS6W2GMU.js → tool-filter-XG2GXFVN.js} +3 -2
  26. package/dist/main.js +140 -140
  27. package/dist/openai-NQYARTT6.js +34 -0
  28. package/dist/{server-IVIRQTO7.js → server-BNT2IIKB.js} +17 -17
  29. package/dist/tool-filter-5NDJMSTD.js +4 -0
  30. package/package.json +1 -1
  31. package/dist/agent-TCNIRVHD.js +0 -4
  32. package/dist/anthropic-7RIVSEDF.js +0 -4
  33. package/dist/checker-3LHDOATB.js +0 -4
  34. package/dist/chunk-2IBMB3ZM.js +0 -150
  35. package/dist/chunk-E55KH72M.js +0 -4
  36. package/dist/chunk-FA26MQ7K.js +0 -257
  37. package/dist/chunk-QCKJLO6X.js +0 -4
  38. package/dist/chunk-TKADLB2Q.js +0 -4
  39. package/dist/chunk-VA64DJNN.js +0 -4
  40. package/dist/lib/agent-IRY7KR5D.js +0 -11
  41. package/dist/lib/chunk-GHF2PSEW.js +0 -8
  42. package/dist/openai-RZLCY55V.js +0 -34
  43. package/dist/tool-filter-CG3KGQQW.js +0 -4
package/dist/lib/index.js CHANGED
@@ -38,7 +38,7 @@ import {
38
38
  toDisplayPreview,
39
39
  toolResultsToRecords,
40
40
  toolUsesToRecords
41
- } from "./chunk-OOSNCCI7.js";
41
+ } from "./chunk-J6AV7SIF.js";
42
42
  import {
43
43
  AnthropicClient,
44
44
  CHARS_PER_TOKEN,
@@ -55,18 +55,18 @@ import {
55
55
  markToolsForCache,
56
56
  measureSchemaChars,
57
57
  needsToolSearchBeta
58
- } from "./chunk-IYPTOY3Y.js";
58
+ } from "./chunk-4MDCRPVV.js";
59
59
  import {
60
60
  SKIP_DIRS,
61
61
  isToolResultContentBlock,
62
62
  normalizeToolResultContentBlock
63
- } from "./chunk-PIL7M52F.js";
63
+ } from "./chunk-XT67KGWE.js";
64
64
  import {
65
65
  OpenAIClient,
66
66
  OpenAICompatClient,
67
67
  buildChatCompletionMessages,
68
68
  buildOpenAIInput
69
- } from "./chunk-I7OPU4K2.js";
69
+ } from "./chunk-OEPCNATU.js";
70
70
  import {
71
71
  AuthenticationError,
72
72
  ConfigError,
@@ -110,7 +110,7 @@ import {
110
110
  extractContent,
111
111
  isPathWithin,
112
112
  isSafeCommand
113
- } from "./chunk-RMB3J46W.js";
113
+ } from "./chunk-A4MXZA5Q.js";
114
114
  import {
115
115
  MCPToolWrapper,
116
116
  MCP_NAME_SEP,
@@ -122,7 +122,7 @@ import {
122
122
  mcpCallPermissionContent,
123
123
  mcpToolNamePrefix,
124
124
  sanitizeSegment
125
- } from "./chunk-KG4MJGKQ.js";
125
+ } from "./chunk-FZ4Y6MBN.js";
126
126
  import {
127
127
  asError,
128
128
  asErrorString,
@@ -148,16 +148,13 @@ import {
148
148
  ASYNC_AGENT_ALLOWED_TOOLS,
149
149
  CUSTOM_AGENT_DISALLOWED_TOOLS,
150
150
  FORK_QUERY_SOURCE,
151
+ MAIN_AGENT_ONLY_TOOLS,
151
152
  SUBAGENT_DISALLOWED_TOOLS,
152
153
  TEAMMATE_DISALLOWED_TOOLS,
153
154
  ToolRegistry,
154
155
  cloneRegistryForFork,
155
156
  filterToolsForAgent
156
- } from "./chunk-7QPDFSQE.js";
157
- import {
158
- MCP_CALL_TOOL_NAME,
159
- TOOL_SEARCH_TOOL_NAME
160
- } from "./chunk-GHF2PSEW.js";
157
+ } from "./chunk-EWE5IWZE.js";
161
158
  import {
162
159
  BwrapSandbox
163
160
  } from "./chunk-ORSYNBMM.js";
@@ -1265,7 +1262,7 @@ function createDefaultRegistry() {
1265
1262
  }
1266
1263
  return detail;
1267
1264
  }
1268
- const cmds = registry.listCommands();
1265
+ const cmds = registry.listCommands().filter((c) => !c.isSkill);
1269
1266
  let output = "Available commands:\n\n";
1270
1267
  output += cmds.map((c) => {
1271
1268
  const aliases = c.aliases.length > 0 ? `, /${c.aliases.join(", /")}` : "";
@@ -2645,15 +2642,15 @@ async function saveClipboardImage(workDir, sessionId) {
2645
2642
  async function createClient(config, systemPrompt) {
2646
2643
  switch (config.protocol) {
2647
2644
  case "anthropic": {
2648
- const { AnthropicClient: AnthropicClient2 } = await import("./anthropic-CL5IK7KN.js");
2645
+ const { AnthropicClient: AnthropicClient2 } = await import("./anthropic-R6DMXE7U.js");
2649
2646
  return new AnthropicClient2(config, systemPrompt);
2650
2647
  }
2651
2648
  case "openai": {
2652
- const { OpenAIClient: OpenAIClient2 } = await import("./openai-LH4E7ZQS.js");
2649
+ const { OpenAIClient: OpenAIClient2 } = await import("./openai-5N42ZYIJ.js");
2653
2650
  return new OpenAIClient2(config, systemPrompt);
2654
2651
  }
2655
2652
  case "openai-compat": {
2656
- const { OpenAICompatClient: OpenAICompatClient2 } = await import("./openai-LH4E7ZQS.js");
2653
+ const { OpenAICompatClient: OpenAICompatClient2 } = await import("./openai-5N42ZYIJ.js");
2657
2654
  return new OpenAICompatClient2(config, systemPrompt);
2658
2655
  }
2659
2656
  default:
@@ -2702,7 +2699,7 @@ var PackageJsonSchema = z5.object({
2702
2699
  });
2703
2700
  function resolveVersion() {
2704
2701
  if (true) {
2705
- return "0.0.30";
2702
+ return "0.0.31";
2706
2703
  }
2707
2704
  const here = path.dirname(fileURLToPath(import.meta.url));
2708
2705
  for (const levels of ["..", "../.."]) {
@@ -2816,7 +2813,10 @@ var MCPClient = class {
2816
2813
  const opts = {
2817
2814
  requestInit: { headers }
2818
2815
  };
2819
- this.transport = this.config.transport === "sse" ? new SSEClientTransport(url, opts) : new StreamableHTTPClientTransport(url, opts);
2816
+ this.transport = this.config.transport === "sse" ? (
2817
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
2818
+ new SSEClientTransport(url, opts)
2819
+ ) : new StreamableHTTPClientTransport(url, opts);
2820
2820
  } else {
2821
2821
  throw new Error(
2822
2822
  `MCP server '${this.name}': needs either 'command' (stdio) or 'url' (http/sse)`
@@ -2945,9 +2945,63 @@ import {
2945
2945
  import { homedir as homedir3 } from "os";
2946
2946
  import { join as join12, basename as basename3 } from "path";
2947
2947
 
2948
+ // src/memory/permissions.ts
2949
+ import { homedir as homedir2 } from "os";
2950
+ import { extname as extname2, join as join8, resolve as resolve2 } from "path";
2951
+ var MemoryPermissionChecker = class extends PermissionChecker {
2952
+ constructor(memoryWorkDir, allowProjectReads = false) {
2953
+ super(memoryWorkDir, "default");
2954
+ this.memoryWorkDir = memoryWorkDir;
2955
+ this.allowProjectReads = allowProjectReads;
2956
+ this.memoryRoots = [
2957
+ join8(memoryWorkDir, ".swifty", "memory"),
2958
+ join8(homedir2(), ".swifty", "memory")
2959
+ ];
2960
+ }
2961
+ memoryWorkDir;
2962
+ allowProjectReads;
2963
+ memoryRoots;
2964
+ check(_name, category, args) {
2965
+ const requested = args.file_path ?? args.path ?? this.memoryWorkDir;
2966
+ if (category === "command" || typeof requested !== "string") {
2967
+ return {
2968
+ effect: "deny",
2969
+ reason: "Background memory tasks only support scoped file operations"
2970
+ };
2971
+ }
2972
+ const path2 = canonicalPath(resolve2(this.memoryWorkDir, requested));
2973
+ const insideMemory = this.memoryRoots.some((root) => isPathWithin(canonicalPath(root), path2));
2974
+ if (category === "write") {
2975
+ return insideMemory && extname2(path2) === ".md" ? { effect: "allow", reason: "Memory file update" } : {
2976
+ effect: "deny",
2977
+ reason: "Background memory writes must stay in the memory directories and use .md files"
2978
+ };
2979
+ }
2980
+ return insideMemory || this.allowProjectReads && isPathWithin(canonicalPath(this.memoryWorkDir), path2) ? { effect: "allow", reason: "Memory task read" } : { effect: "deny", reason: "Read outside the memory task's scope" };
2981
+ }
2982
+ };
2983
+
2984
+ // src/memory/written-paths.ts
2985
+ function extractWrittenPaths(messages) {
2986
+ const successful = new Set(
2987
+ messages.flatMap(
2988
+ (message) => (message.toolResults ?? []).filter((result) => !result.isError).map((result) => result.toolUseId)
2989
+ )
2990
+ );
2991
+ const paths = /* @__PURE__ */ new Set();
2992
+ for (const message of messages) {
2993
+ for (const tool of message.toolUses ?? []) {
2994
+ if ((tool.toolName === "WriteFile" || tool.toolName === "EditFile") && successful.has(tool.toolUseId) && typeof tool.arguments.file_path === "string") {
2995
+ paths.add(tool.arguments.file_path);
2996
+ }
2997
+ }
2998
+ }
2999
+ return [...paths];
3000
+ }
3001
+
2948
3002
  // src/tools/edit-file.ts
2949
3003
  import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
2950
- import { resolve as resolve3 } from "path";
3004
+ import { resolve as resolve4 } from "path";
2951
3005
 
2952
3006
  // src/tools/descriptions.ts
2953
3007
  var GIT_GUIDANCE = `Git: commit or push only when requested. Destructive operations (push --force, reset --hard, checkout ., clean -f, branch -D), amending, or skipping hooks/signing require explicit authorization; never bypass permission/hook denials. Prefer new commits. When committing, include Co-Authored-By: Swifty <usr161043261@outlook.com>.`;
@@ -3044,15 +3098,15 @@ function buildDiff(oldContent, newContent) {
3044
3098
 
3045
3099
  // src/tools/file-mutation-queue.ts
3046
3100
  import { realpath } from "fs/promises";
3047
- import { basename, dirname as dirname2, join as join8, resolve as resolve2 } from "path";
3101
+ import { basename, dirname as dirname2, join as join9, resolve as resolve3 } from "path";
3048
3102
  var queues = /* @__PURE__ */ new Map();
3049
3103
  async function canonicalPath2(filePath) {
3050
- const absolutePath = resolve2(filePath);
3104
+ const absolutePath = resolve3(filePath);
3051
3105
  try {
3052
3106
  return await realpath(absolutePath);
3053
3107
  } catch {
3054
3108
  try {
3055
- return join8(await realpath(dirname2(absolutePath)), basename(absolutePath));
3109
+ return join9(await realpath(dirname2(absolutePath)), basename(absolutePath));
3056
3110
  } catch {
3057
3111
  return absolutePath;
3058
3112
  }
@@ -3125,7 +3179,7 @@ var EditFileTool = class {
3125
3179
  isError: true
3126
3180
  };
3127
3181
  }
3128
- const filePath = resolve3(ctx.workDir, requestedPath);
3182
+ const filePath = resolve4(ctx.workDir, requestedPath);
3129
3183
  if (!oldString) {
3130
3184
  return {
3131
3185
  output: "Error: old_string is required",
@@ -3259,7 +3313,7 @@ var FileStateCache = class {
3259
3313
 
3260
3314
  // src/tools/glob.ts
3261
3315
  import { statSync as statSync5 } from "fs";
3262
- import { join as join9, resolve as resolve4 } from "path";
3316
+ import { join as join10, resolve as resolve5 } from "path";
3263
3317
  import { globIterate } from "glob";
3264
3318
  var log15 = createChildLogger({ module: "tools" });
3265
3319
  var IGNORE = [...SKIP_DIRS].map((dir) => `**/${dir}/**`);
@@ -3300,7 +3354,7 @@ var GlobTool = class {
3300
3354
  isError: true
3301
3355
  };
3302
3356
  }
3303
- const basePath = resolve4(ctx.workDir, strArg(args, "path", ctx.workDir));
3357
+ const basePath = resolve5(ctx.workDir, strArg(args, "path", ctx.workDir));
3304
3358
  if (!statSync5(basePath, { throwIfNoEntry: false })?.isDirectory()) {
3305
3359
  return {
3306
3360
  output: `Error: not a directory, scan '${basePath}'`,
@@ -3334,7 +3388,7 @@ var GlobTool = class {
3334
3388
  for (const match of matches) {
3335
3389
  let mtime = 0;
3336
3390
  try {
3337
- mtime = statSync5(join9(basePath, match)).mtimeMs;
3391
+ mtime = statSync5(join10(basePath, match)).mtimeMs;
3338
3392
  } catch {
3339
3393
  }
3340
3394
  mtimes.set(match, mtime);
@@ -3361,7 +3415,7 @@ var GlobTool = class {
3361
3415
 
3362
3416
  // src/tools/grep.ts
3363
3417
  import { lstat, readdir, readFile as readFile3, stat } from "fs/promises";
3364
- import { join as join10, relative, resolve as resolve5, sep } from "path";
3418
+ import { join as join11, relative, resolve as resolve6, sep } from "path";
3365
3419
  import { Minimatch } from "minimatch";
3366
3420
  var log16 = createChildLogger({ module: "tools" });
3367
3421
  var MAX_RESULTS = 500;
@@ -3448,7 +3502,7 @@ var GrepTool = class {
3448
3502
  isError: true
3449
3503
  };
3450
3504
  }
3451
- const searchPath = resolve5(ctx.workDir, strArg(args, "path", ctx.workDir));
3505
+ const searchPath = resolve6(ctx.workDir, strArg(args, "path", ctx.workDir));
3452
3506
  const include = strArg(args, "include");
3453
3507
  let regex;
3454
3508
  try {
@@ -3485,7 +3539,7 @@ var GrepTool = class {
3485
3539
  if (SKIP_DIRS.has(entry)) {
3486
3540
  continue;
3487
3541
  }
3488
- const fullPath = join10(dir, entry);
3542
+ const fullPath = join11(dir, entry);
3489
3543
  let fileStat;
3490
3544
  try {
3491
3545
  fileStat = await lstat(fullPath);
@@ -3555,7 +3609,7 @@ var GrepTool = class {
3555
3609
 
3556
3610
  // src/tools/read-file.ts
3557
3611
  import { existsSync as existsSync6, readFileSync as readFileSync9, statSync as statSync6 } from "fs";
3558
- import { basename as basename2, resolve as resolve6 } from "path";
3612
+ import { basename as basename2, resolve as resolve7 } from "path";
3559
3613
 
3560
3614
  // src/tools/shell-output.ts
3561
3615
  var MAX_SHELL_OUTPUT_BYTES = 10 * 1024 * 1024;
@@ -3644,7 +3698,7 @@ var ReadFileTool = class {
3644
3698
  isError: true
3645
3699
  });
3646
3700
  }
3647
- const filePath = resolve6(ctx.workDir, requestedPath);
3701
+ const filePath = resolve7(ctx.workDir, requestedPath);
3648
3702
  if (!existsSync6(filePath)) {
3649
3703
  return Promise.resolve({
3650
3704
  output: `Error: file not found: ${filePath}`,
@@ -3767,7 +3821,7 @@ var ReadFileTool = class {
3767
3821
 
3768
3822
  // src/tools/write-file.ts
3769
3823
  import { existsSync as existsSync7, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
3770
- import { dirname as dirname3, resolve as resolve7 } from "path";
3824
+ import { dirname as dirname3, resolve as resolve8 } from "path";
3771
3825
  var log18 = createChildLogger({ module: "tools" });
3772
3826
  var WriteFileTool = class {
3773
3827
  // Use a hardcoded string instead of WriteFileTool.name.replace("Tool", "")
@@ -3812,10 +3866,13 @@ var WriteFileTool = class {
3812
3866
  });
3813
3867
  }
3814
3868
  const content = args.content;
3815
- const filePath = resolve7(ctx.workDir, requestedPath);
3869
+ const filePath = resolve8(ctx.workDir, requestedPath);
3816
3870
  return withFileMutationQueue(filePath, () => {
3817
3871
  if (ctx.abortSignal?.aborted) {
3818
- return Promise.resolve({ output: "Error: operation interrupted", isError: true });
3872
+ return Promise.resolve({
3873
+ output: "Error: operation interrupted",
3874
+ isError: true
3875
+ });
3819
3876
  }
3820
3877
  if (ctx.fileStateCache && (existsSync7(filePath) || ctx.fileStateCache.has(filePath))) {
3821
3878
  const gate = ctx.fileStateCache.check(filePath);
@@ -3844,60 +3901,6 @@ var WriteFileTool = class {
3844
3901
  }
3845
3902
  };
3846
3903
 
3847
- // src/memory/permissions.ts
3848
- import { homedir as homedir2 } from "os";
3849
- import { extname as extname2, join as join11, resolve as resolve8 } from "path";
3850
- var MemoryPermissionChecker = class extends PermissionChecker {
3851
- constructor(memoryWorkDir, allowProjectReads = false) {
3852
- super(memoryWorkDir, "default");
3853
- this.memoryWorkDir = memoryWorkDir;
3854
- this.allowProjectReads = allowProjectReads;
3855
- this.memoryRoots = [
3856
- join11(memoryWorkDir, ".swifty", "memory"),
3857
- join11(homedir2(), ".swifty", "memory")
3858
- ];
3859
- }
3860
- memoryWorkDir;
3861
- allowProjectReads;
3862
- memoryRoots;
3863
- check(_name, category, args) {
3864
- const requested = args.file_path ?? args.path ?? this.memoryWorkDir;
3865
- if (category === "command" || typeof requested !== "string") {
3866
- return {
3867
- effect: "deny",
3868
- reason: "Background memory tasks only support scoped file operations"
3869
- };
3870
- }
3871
- const path2 = canonicalPath(resolve8(this.memoryWorkDir, requested));
3872
- const insideMemory = this.memoryRoots.some((root) => isPathWithin(canonicalPath(root), path2));
3873
- if (category === "write") {
3874
- return insideMemory && extname2(path2) === ".md" ? { effect: "allow", reason: "Memory file update" } : {
3875
- effect: "deny",
3876
- reason: "Background memory writes must stay in the memory directories and use .md files"
3877
- };
3878
- }
3879
- return insideMemory || this.allowProjectReads && isPathWithin(canonicalPath(this.memoryWorkDir), path2) ? { effect: "allow", reason: "Memory task read" } : { effect: "deny", reason: "Read outside the memory task's scope" };
3880
- }
3881
- };
3882
-
3883
- // src/memory/written-paths.ts
3884
- function extractWrittenPaths(messages) {
3885
- const successful = new Set(
3886
- messages.flatMap(
3887
- (message) => (message.toolResults ?? []).filter((result) => !result.isError).map((result) => result.toolUseId)
3888
- )
3889
- );
3890
- const paths = /* @__PURE__ */ new Set();
3891
- for (const message of messages) {
3892
- for (const tool of message.toolUses ?? []) {
3893
- if ((tool.toolName === "WriteFile" || tool.toolName === "EditFile") && successful.has(tool.toolUseId) && typeof tool.arguments.file_path === "string") {
3894
- paths.add(tool.arguments.file_path);
3895
- }
3896
- }
3897
- }
3898
- return [...paths];
3899
- }
3900
-
3901
3904
  // src/memory/consolidation.ts
3902
3905
  var log19 = createChildLogger({ module: "memory" });
3903
3906
  var DEFAULT_MIN_HOURS = 24;
@@ -5117,6 +5120,113 @@ function buildSystemPrompt(env) {
5117
5120
  // src/subagent/agent-tool.ts
5118
5121
  import { randomBytes as randomBytes2 } from "crypto";
5119
5122
 
5123
+ // src/subagent/loader.ts
5124
+ import { readdirSync as readdirSync4, readFileSync as readFileSync14, existsSync as existsSync12 } from "fs";
5125
+ import { homedir as homedir7 } from "os";
5126
+ import { join as join16 } from "path";
5127
+ import yaml4 from "js-yaml";
5128
+ import z7, { parse as parse5 } from "zod";
5129
+
5130
+ // src/subagent/definition.ts
5131
+ var BUILTIN_AGENTS = [
5132
+ {
5133
+ name: "general-purpose",
5134
+ description: "General-purpose agent for researching complex questions, exploring codebase, and executing multi-step tasks."
5135
+ },
5136
+ {
5137
+ name: "plan",
5138
+ description: "Investigate the existing architecture and propose a concrete implementation plan with relevant files, constraints, and verification steps. Read-only: do not create, edit, or delete files. Return unresolved questions to the parent agent.",
5139
+ disallowedTools: ["EditFile", "WriteFile"],
5140
+ permissionMode: "plan"
5141
+ },
5142
+ {
5143
+ name: "explore",
5144
+ description: "Find code, trace relevant call paths, and report evidence with file paths and line numbers. Use Glob, Grep, ReadFile, and read-only shell commands. Do not modify files; return missing context or blockers to the parent agent.",
5145
+ disallowedTools: ["EditFile", "WriteFile"],
5146
+ permissionMode: "plan",
5147
+ model: "haiku"
5148
+ }
5149
+ ];
5150
+
5151
+ // src/subagent/loader.ts
5152
+ var log23 = createChildLogger({ module: "subagent" });
5153
+ function loadAgentDefinitions(workDir) {
5154
+ const definitions = [...BUILTIN_AGENTS];
5155
+ const home = homedir7();
5156
+ if (home) {
5157
+ loadDir(join16(home, ".swifty", "agents"), definitions);
5158
+ }
5159
+ const dirs = [join16(workDir, ".swifty", "agents")];
5160
+ for (const dir of dirs) {
5161
+ loadDir(dir, definitions);
5162
+ }
5163
+ return definitions;
5164
+ }
5165
+ function loadDir(dir, definitions) {
5166
+ if (!existsSync12(dir)) {
5167
+ return;
5168
+ }
5169
+ const files = readdirSync4(dir).filter((f) => f.endsWith(".md"));
5170
+ for (const file of files) {
5171
+ try {
5172
+ const content = readFileSync14(join16(dir, file), "utf-8");
5173
+ const def = parseAgentDefinition(content);
5174
+ if (def) {
5175
+ const existing = definitions.findIndex((d) => d.name === def.name);
5176
+ if (existing >= 0) {
5177
+ definitions[existing] = def;
5178
+ } else {
5179
+ definitions.push(def);
5180
+ }
5181
+ }
5182
+ } catch (err) {
5183
+ log23.error({ err }, "subagent operation failed");
5184
+ continue;
5185
+ }
5186
+ }
5187
+ }
5188
+ var YamlFrontmatterSchema2 = z7.looseObject({
5189
+ name: z7.string(),
5190
+ description: z7.string().optional(),
5191
+ tools: z7.array(z7.string()).optional(),
5192
+ disallowed_tools: z7.array(z7.string()).optional(),
5193
+ system_prompt: z7.string().optional(),
5194
+ max_turns: z7.number().optional(),
5195
+ model: z7.string().optional(),
5196
+ background: z7.boolean().optional(),
5197
+ isolation: z7.literal("worktree").optional()
5198
+ });
5199
+ function parseAgentDefinition(content) {
5200
+ if (!content.startsWith("---")) {
5201
+ return null;
5202
+ }
5203
+ const endIdx = content.indexOf("---", 3);
5204
+ if (endIdx === -1) {
5205
+ return null;
5206
+ }
5207
+ const frontmatter = content.slice(3, endIdx).trim();
5208
+ const body = content.slice(endIdx + 3).trim();
5209
+ try {
5210
+ const raw = yaml4.load(frontmatter);
5211
+ const parsed = parse5(YamlFrontmatterSchema2, raw);
5212
+ return {
5213
+ name: parsed.name,
5214
+ description: parsed.description ?? body.slice(0, 200),
5215
+ tools: parsed.tools,
5216
+ disallowedTools: parsed.disallowed_tools,
5217
+ systemPromptOverride: parsed.system_prompt,
5218
+ maxTurns: parsed.max_turns,
5219
+ model: parsed.model,
5220
+ background: parsed.background,
5221
+ isolation: parsed.isolation,
5222
+ initialPrompt: body
5223
+ };
5224
+ } catch (err) {
5225
+ log23.error({ err }, "subagent operation failed");
5226
+ return null;
5227
+ }
5228
+ }
5229
+
5120
5230
  // src/teams/task-tools.ts
5121
5231
  var VALID_STATUSES = /* @__PURE__ */ new Set(["pending", "in_progress", "completed", "blocked"]);
5122
5232
  var TaskCreateTool = class {
@@ -5503,7 +5613,7 @@ function getNameRegistry() {
5503
5613
  }
5504
5614
 
5505
5615
  // src/teams/tools.ts
5506
- var log23 = createChildLogger({ module: "teams" });
5616
+ var log24 = createChildLogger({ module: "teams" });
5507
5617
  var TeamCreateTool = class {
5508
5618
  constructor(mgr) {
5509
5619
  this.mgr = mgr;
@@ -5728,7 +5838,7 @@ var SendMessageTool = class {
5728
5838
  try {
5729
5839
  await t.sendMessage(this.senderName, recipient, message);
5730
5840
  } catch (err) {
5731
- log23.error({ err }, "teams operation failed");
5841
+ log24.error({ err }, "teams operation failed");
5732
5842
  return {
5733
5843
  output: `Error: ${asErrorString(err)}`,
5734
5844
  isError: true
@@ -5808,9 +5918,9 @@ var TeamDeleteTool = class {
5808
5918
  // src/worktree/worktree.ts
5809
5919
  import { execFile } from "child_process";
5810
5920
  import { access, cp, mkdir as mkdir2, readFile as readFile4, realpath as realpath2, stat as stat2, symlink } from "fs/promises";
5811
- import { dirname as dirname5, isAbsolute as isAbsolute3, join as join16 } from "path";
5921
+ import { dirname as dirname5, isAbsolute as isAbsolute3, join as join17 } from "path";
5812
5922
  import { promisify } from "util";
5813
- var log24 = createChildLogger({ module: "worktree" });
5923
+ var log25 = createChildLogger({ module: "worktree" });
5814
5924
  var execFileAsync = promisify(execFile);
5815
5925
  var SAFE_REF_RE = /^[a-zA-Z0-9/._+@-]+$/;
5816
5926
  var SHA_RE = /^[0-9a-f]{40}([0-9a-f]{24})?$/;
@@ -5834,12 +5944,12 @@ async function pathExists(path2) {
5834
5944
  await access(path2);
5835
5945
  return true;
5836
5946
  } catch (err) {
5837
- log24.error({ err }, "worktree operation failed");
5947
+ log25.error({ err }, "worktree operation failed");
5838
5948
  return false;
5839
5949
  }
5840
5950
  }
5841
5951
  async function resolveGitDir(root) {
5842
- const gitPath = join16(root, ".git");
5952
+ const gitPath = join17(root, ".git");
5843
5953
  if (!await pathExists(gitPath)) {
5844
5954
  return "";
5845
5955
  }
@@ -5852,24 +5962,24 @@ async function resolveGitDir(root) {
5852
5962
  return "";
5853
5963
  }
5854
5964
  const rel = raw.slice("gitdir:".length).trim();
5855
- return isAbsolute3(rel) ? rel : join16(root, rel);
5965
+ return isAbsolute3(rel) ? rel : join17(root, rel);
5856
5966
  }
5857
5967
  async function getCommonDir(gitDir) {
5858
5968
  try {
5859
- const commonDir = join16(gitDir, "commondir");
5969
+ const commonDir = join17(gitDir, "commondir");
5860
5970
  const raw = (await readFile4(commonDir, "utf-8")).trim();
5861
- return isAbsolute3(raw) ? raw : join16(gitDir, raw);
5971
+ return isAbsolute3(raw) ? raw : join17(gitDir, raw);
5862
5972
  } catch (err) {
5863
- log24.error({ err }, "worktree operation failed");
5973
+ log25.error({ err }, "worktree operation failed");
5864
5974
  return "";
5865
5975
  }
5866
5976
  }
5867
5977
  async function readGitHead(gitDir) {
5868
5978
  let raw;
5869
5979
  try {
5870
- raw = (await readFile4(join16(gitDir, "HEAD"), "utf-8")).trim();
5980
+ raw = (await readFile4(join17(gitDir, "HEAD"), "utf-8")).trim();
5871
5981
  } catch (err) {
5872
- log24.error({ err }, "worktree operation failed");
5982
+ log25.error({ err }, "worktree operation failed");
5873
5983
  return null;
5874
5984
  }
5875
5985
  if (raw.startsWith("ref:")) {
@@ -5894,7 +6004,7 @@ async function readGitHead(gitDir) {
5894
6004
  }
5895
6005
  async function resolveRefInDir(dir, ref) {
5896
6006
  try {
5897
- const content = (await readFile4(join16(dir, ref), "utf-8")).trim();
6007
+ const content = (await readFile4(join17(dir, ref), "utf-8")).trim();
5898
6008
  if (content.startsWith("ref:")) {
5899
6009
  const target = content.slice("ref:".length).trim();
5900
6010
  if (!isSafeRefName(target)) {
@@ -5907,10 +6017,10 @@ async function resolveRefInDir(dir, ref) {
5907
6017
  }
5908
6018
  return "";
5909
6019
  } catch (err) {
5910
- log24.error({ err }, "worktree operation failed");
6020
+ log25.error({ err }, "worktree operation failed");
5911
6021
  }
5912
6022
  try {
5913
- const packed = await readFile4(join16(dir, "packed-refs"), "utf-8");
6023
+ const packed = await readFile4(join17(dir, "packed-refs"), "utf-8");
5914
6024
  for (const line of packed.split("\n")) {
5915
6025
  if (!line || line.startsWith("#") || line.startsWith("^")) {
5916
6026
  continue;
@@ -5928,7 +6038,7 @@ async function resolveRefInDir(dir, ref) {
5928
6038
  }
5929
6039
  }
5930
6040
  } catch (err) {
5931
- log24.error({ err }, "worktree operation failed");
6041
+ log25.error({ err }, "worktree operation failed");
5932
6042
  }
5933
6043
  return "";
5934
6044
  }
@@ -5946,16 +6056,16 @@ async function resolveRef(gitDir, ref) {
5946
6056
  async function readWorktreeHeadSha(worktreePath) {
5947
6057
  let raw;
5948
6058
  try {
5949
- raw = (await readFile4(join16(worktreePath, ".git"), "utf-8")).trim();
6059
+ raw = (await readFile4(join17(worktreePath, ".git"), "utf-8")).trim();
5950
6060
  } catch (err) {
5951
- log24.error({ err }, "worktree operation failed");
6061
+ log25.error({ err }, "worktree operation failed");
5952
6062
  return "";
5953
6063
  }
5954
6064
  if (!raw.startsWith("gitdir:")) {
5955
6065
  return "";
5956
6066
  }
5957
6067
  const rel = raw.slice("gitdir:".length).trim();
5958
- const gitDir = isAbsolute3(rel) ? rel : join16(worktreePath, rel);
6068
+ const gitDir = isAbsolute3(rel) ? rel : join17(worktreePath, rel);
5959
6069
  const head = await readGitHead(gitDir);
5960
6070
  if (!head) {
5961
6071
  return "";
@@ -5981,7 +6091,7 @@ async function createAgentWorktree(slug, gitRoot) {
5981
6091
  throw new Error("Invalid worktree slug: use only alphanumeric, hyphen, underscore");
5982
6092
  }
5983
6093
  const root = gitRoot ?? (await execFileAsync("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
5984
- const worktreeDir = join16(root, ".swifty", "worktrees", slug);
6094
+ const worktreeDir = join17(root, ".swifty", "worktrees", slug);
5985
6095
  const branch = `worktree-${slug}`;
5986
6096
  if (await pathExists(worktreeDir)) {
5987
6097
  const { stdout: topLevel } = await execFileAsync("git", ["rev-parse", "--show-toplevel"], {
@@ -6029,7 +6139,9 @@ async function createAgentWorktree(slug, gitRoot) {
6029
6139
  };
6030
6140
  }
6031
6141
  async function removeAgentWorktree(path2, branch, gitRoot) {
6032
- await execFileAsync("git", ["worktree", "remove", "--", path2], { cwd: gitRoot });
6142
+ await execFileAsync("git", ["worktree", "remove", "--", path2], {
6143
+ cwd: gitRoot
6144
+ });
6033
6145
  await execFileAsync("git", ["branch", "-d", "--", branch], { cwd: gitRoot });
6034
6146
  }
6035
6147
  async function hasWorktreeChanges(path2, headCommit) {
@@ -6043,7 +6155,7 @@ async function hasWorktreeChanges(path2, headCommit) {
6043
6155
  const currentHead = await readWorktreeHeadSha(path2) || (await execFileAsync("git", ["rev-parse", "HEAD"], { cwd: path2 })).stdout.trim();
6044
6156
  return currentHead !== headCommit;
6045
6157
  } catch (err) {
6046
- log24.error({ err }, "worktree operation failed");
6158
+ log25.error({ err }, "worktree operation failed");
6047
6159
  return true;
6048
6160
  }
6049
6161
  }
@@ -6062,56 +6174,56 @@ async function performPostCreationSetup(repoRoot, wtPath) {
6062
6174
  var SHARED_SWIFTY_ENTRIES = ["permissions.yaml", "agents", "memory"];
6063
6175
  var SHARED_AGENTS_ENTRIES = ["AGENTS.md", "skills"];
6064
6176
  async function copySwiftySettings(repoRoot, wtPath) {
6065
- const swiftyDir = join16(repoRoot, ".swifty");
6177
+ const swiftyDir = join17(repoRoot, ".swifty");
6066
6178
  if (!await pathExists(swiftyDir)) {
6067
6179
  return;
6068
6180
  }
6069
- const dstRoot = join16(wtPath, ".swifty");
6181
+ const dstRoot = join17(wtPath, ".swifty");
6070
6182
  try {
6071
6183
  await mkdir2(dstRoot, { recursive: true });
6072
6184
  } catch (err) {
6073
- log24.error({ err }, "failed to create .swifty/ in worktree");
6185
+ log25.error({ err }, "failed to create .swifty/ in worktree");
6074
6186
  return;
6075
6187
  }
6076
6188
  for (const entry of SHARED_SWIFTY_ENTRIES) {
6077
- const src = join16(swiftyDir, entry);
6189
+ const src = join17(swiftyDir, entry);
6078
6190
  if (!await pathExists(src)) {
6079
6191
  continue;
6080
6192
  }
6081
6193
  try {
6082
- await cp(src, join16(dstRoot, entry), { recursive: true });
6194
+ await cp(src, join17(dstRoot, entry), { recursive: true });
6083
6195
  } catch (err) {
6084
- log24.error({ err, entry }, "failed to copy .swifty/ entry to worktree");
6196
+ log25.error({ err, entry }, "failed to copy .swifty/ entry to worktree");
6085
6197
  }
6086
6198
  }
6087
6199
  }
6088
6200
  async function copyAgentsSettings(repoRoot, wtPath) {
6089
- const agentsDir = join16(repoRoot, ".agents");
6201
+ const agentsDir = join17(repoRoot, ".agents");
6090
6202
  if (!await pathExists(agentsDir)) {
6091
6203
  return;
6092
6204
  }
6093
- const dstRoot = join16(wtPath, ".agents");
6205
+ const dstRoot = join17(wtPath, ".agents");
6094
6206
  try {
6095
6207
  await mkdir2(dstRoot, { recursive: true });
6096
6208
  } catch (err) {
6097
- log24.error({ err }, "failed to create .agents in worktree");
6209
+ log25.error({ err }, "failed to create .agents in worktree");
6098
6210
  return;
6099
6211
  }
6100
6212
  for (const entry of SHARED_AGENTS_ENTRIES) {
6101
- const src = join16(agentsDir, entry);
6213
+ const src = join17(agentsDir, entry);
6102
6214
  if (!await pathExists(src)) {
6103
6215
  continue;
6104
6216
  }
6105
6217
  try {
6106
- await cp(src, join16(dstRoot, entry), { recursive: true });
6218
+ await cp(src, join17(dstRoot, entry), { recursive: true });
6107
6219
  } catch (err) {
6108
- log24.error({ err, entry }, "failed to copy .agents/ entry to worktree");
6220
+ log25.error({ err, entry }, "failed to copy .agents/ entry to worktree");
6109
6221
  }
6110
6222
  }
6111
6223
  }
6112
6224
  async function configureHooksPath(repoRoot, worktreePath) {
6113
6225
  try {
6114
- const candidates = [join16(repoRoot, ".husky"), join16(repoRoot, ".git", "hooks")];
6226
+ const candidates = [join17(repoRoot, ".husky"), join17(repoRoot, ".git", "hooks")];
6115
6227
  let hooksPath;
6116
6228
  for (const c of candidates) {
6117
6229
  try {
@@ -6121,7 +6233,7 @@ async function configureHooksPath(repoRoot, worktreePath) {
6121
6233
  break;
6122
6234
  }
6123
6235
  } catch (err) {
6124
- log24.error({ err }, "worktree operation failed");
6236
+ log25.error({ err }, "worktree operation failed");
6125
6237
  }
6126
6238
  }
6127
6239
  if (!hooksPath) {
@@ -6131,27 +6243,27 @@ async function configureHooksPath(repoRoot, worktreePath) {
6131
6243
  cwd: worktreePath
6132
6244
  });
6133
6245
  } catch (err) {
6134
- log24.error({ err }, "failed to configure hooks path in worktree");
6246
+ log25.error({ err }, "failed to configure hooks path in worktree");
6135
6247
  }
6136
6248
  }
6137
6249
  async function symlinkNodeModules(repoRoot, worktreePath) {
6138
6250
  try {
6139
- const src = join16(repoRoot, "node_modules");
6251
+ const src = join17(repoRoot, "node_modules");
6140
6252
  if (!await pathExists(src)) {
6141
6253
  return;
6142
6254
  }
6143
- const dst = join16(worktreePath, "node_modules");
6255
+ const dst = join17(worktreePath, "node_modules");
6144
6256
  if (await pathExists(dst)) {
6145
6257
  return;
6146
6258
  }
6147
6259
  await symlink(src, dst);
6148
6260
  } catch (err) {
6149
- log24.warn({ err }, "failed to symlink node_modules in worktree");
6261
+ log25.warn({ err }, "failed to symlink node_modules in worktree");
6150
6262
  }
6151
6263
  }
6152
6264
  async function copyWorktreeIncludeFiles(repoRoot, worktreePath) {
6153
6265
  try {
6154
- const includeFile = join16(repoRoot, ".worktreeinclude");
6266
+ const includeFile = join17(repoRoot, ".worktreeinclude");
6155
6267
  if (!await pathExists(includeFile)) {
6156
6268
  return;
6157
6269
  }
@@ -6162,11 +6274,11 @@ async function copyWorktreeIncludeFiles(repoRoot, worktreePath) {
6162
6274
  continue;
6163
6275
  }
6164
6276
  try {
6165
- const src = join16(repoRoot, relPath);
6277
+ const src = join17(repoRoot, relPath);
6166
6278
  if (!await pathExists(src)) {
6167
6279
  continue;
6168
6280
  }
6169
- const dst = join16(worktreePath, relPath);
6281
+ const dst = join17(worktreePath, relPath);
6170
6282
  await mkdir2(dirname5(dst), { recursive: true });
6171
6283
  const info = await stat2(src);
6172
6284
  if (info.isDirectory()) {
@@ -6175,126 +6287,19 @@ async function copyWorktreeIncludeFiles(repoRoot, worktreePath) {
6175
6287
  await cp(src, dst);
6176
6288
  }
6177
6289
  } catch (err) {
6178
- log24.error({ err }, "worktree operation failed");
6290
+ log25.error({ err }, "worktree operation failed");
6179
6291
  }
6180
6292
  }
6181
6293
  } catch (err) {
6182
- log24.error({ err }, "failed to process .worktreeinclude");
6294
+ log25.error({ err }, "failed to process .worktreeinclude");
6183
6295
  }
6184
6296
  }
6185
6297
 
6186
- // src/subagent/loader.ts
6187
- import { readdirSync as readdirSync4, readFileSync as readFileSync14, existsSync as existsSync12 } from "fs";
6188
- import { homedir as homedir7 } from "os";
6189
- import { join as join17 } from "path";
6190
- import yaml4 from "js-yaml";
6191
- import z7, { parse as parse5 } from "zod";
6192
-
6193
- // src/subagent/definition.ts
6194
- var BUILTIN_AGENTS = [
6195
- {
6196
- name: "general-purpose",
6197
- description: "General-purpose agent for researching complex questions, exploring codebase, and executing multi-step tasks."
6198
- },
6199
- {
6200
- name: "plan",
6201
- description: "Investigate the existing architecture and propose a concrete implementation plan with relevant files, constraints, and verification steps. Read-only: do not create, edit, or delete files. Return unresolved questions to the parent agent.",
6202
- disallowedTools: ["EditFile", "WriteFile"],
6203
- permissionMode: "plan"
6204
- },
6205
- {
6206
- name: "explore",
6207
- description: "Find code, trace relevant call paths, and report evidence with file paths and line numbers. Use Glob, Grep, ReadFile, and read-only shell commands. Do not modify files; return missing context or blockers to the parent agent.",
6208
- disallowedTools: ["EditFile", "WriteFile"],
6209
- permissionMode: "plan",
6210
- model: "haiku"
6211
- }
6212
- ];
6213
-
6214
- // src/subagent/loader.ts
6215
- var log25 = createChildLogger({ module: "subagent" });
6216
- function loadAgentDefinitions(workDir) {
6217
- const definitions = [...BUILTIN_AGENTS];
6218
- const home = homedir7();
6219
- if (home) {
6220
- loadDir(join17(home, ".swifty", "agents"), definitions);
6221
- }
6222
- const dirs = [join17(workDir, ".swifty", "agents")];
6223
- for (const dir of dirs) {
6224
- loadDir(dir, definitions);
6225
- }
6226
- return definitions;
6227
- }
6228
- function loadDir(dir, definitions) {
6229
- if (!existsSync12(dir)) {
6230
- return;
6231
- }
6232
- const files = readdirSync4(dir).filter((f) => f.endsWith(".md"));
6233
- for (const file of files) {
6234
- try {
6235
- const content = readFileSync14(join17(dir, file), "utf-8");
6236
- const def = parseAgentDefinition(content);
6237
- if (def) {
6238
- const existing = definitions.findIndex((d) => d.name === def.name);
6239
- if (existing >= 0) {
6240
- definitions[existing] = def;
6241
- } else {
6242
- definitions.push(def);
6243
- }
6244
- }
6245
- } catch (err) {
6246
- log25.error({ err }, "subagent operation failed");
6247
- continue;
6248
- }
6249
- }
6250
- }
6251
- var YamlFrontmatterSchema2 = z7.looseObject({
6252
- name: z7.string(),
6253
- description: z7.string().optional(),
6254
- tools: z7.array(z7.string()).optional(),
6255
- disallowed_tools: z7.array(z7.string()).optional(),
6256
- system_prompt: z7.string().optional(),
6257
- max_turns: z7.number().optional(),
6258
- model: z7.string().optional(),
6259
- background: z7.boolean().optional(),
6260
- isolation: z7.literal("worktree").optional()
6261
- });
6262
- function parseAgentDefinition(content) {
6263
- if (!content.startsWith("---")) {
6264
- return null;
6265
- }
6266
- const endIdx = content.indexOf("---", 3);
6267
- if (endIdx === -1) {
6268
- return null;
6269
- }
6270
- const frontmatter = content.slice(3, endIdx).trim();
6271
- const body = content.slice(endIdx + 3).trim();
6272
- try {
6273
- const raw = yaml4.load(frontmatter);
6274
- const parsed = parse5(YamlFrontmatterSchema2, raw);
6275
- return {
6276
- name: parsed.name,
6277
- description: parsed.description ?? body.slice(0, 200),
6278
- tools: parsed.tools,
6279
- disallowedTools: parsed.disallowed_tools,
6280
- systemPromptOverride: parsed.system_prompt,
6281
- maxTurns: parsed.max_turns,
6282
- model: parsed.model,
6283
- background: parsed.background,
6284
- isolation: parsed.isolation,
6285
- initialPrompt: body
6286
- };
6287
- } catch (err) {
6288
- log25.error({ err }, "subagent operation failed");
6289
- return null;
6290
- }
6291
- }
6292
-
6293
- // src/subagent/agent-tool.ts
6294
- var log26 = createChildLogger({ module: "subagent" });
6295
- var GENERAL_PURPOSE_AGENT_TYPE = "general-purpose";
6296
- function newAgentSlug() {
6297
- return `agent-a${randomBytes2(4).toString("hex").slice(0, 7)}`;
6298
+ // src/subagent/agent-tool.ts
6299
+ var log26 = createChildLogger({ module: "subagent" });
6300
+ var GENERAL_PURPOSE_AGENT_TYPE = "general-purpose";
6301
+ function newAgentSlug() {
6302
+ return `agent-a${randomBytes2(4).toString("hex").slice(0, 7)}`;
6298
6303
  }
6299
6304
  var FORK_BOILERPLATE_TAG = "<fork_boilerplate>";
6300
6305
  var FORK_QUERY_SOURCE2 = "agent:builtin:fork";
@@ -6588,7 +6593,7 @@ ${prompt}`;
6588
6593
  permissionChecker: ctx.permissionChecker?.forWorkDir(worktree.path)
6589
6594
  };
6590
6595
  }
6591
- const { cloneRegistryForFork: cloneRegistryForFork2 } = await import("./tool-filter-CS6W2GMU.js");
6596
+ const { cloneRegistryForFork: cloneRegistryForFork2 } = await import("./tool-filter-XG2GXFVN.js");
6592
6597
  const forkedRegistry = cloneRegistryForFork2(this.registry);
6593
6598
  const snapshot = this.conversation.fork();
6594
6599
  const output = await this.forkHandler(
@@ -6808,142 +6813,6 @@ var TaskStopTool = class {
6808
6813
  import { mkdirSync as mkdirSync13, readFileSync as readFileSync19, rmSync } from "fs";
6809
6814
  import { join as join21 } from "path";
6810
6815
 
6811
- // src/utils/verbs.ts
6812
- var spinnerVerbs = [
6813
- "Accomplishing",
6814
- "Architecting",
6815
- "Baking",
6816
- "Barking",
6817
- "Woofing",
6818
- "Howling",
6819
- "Be-bopping'",
6820
- "Befuddling",
6821
- "Boogieing",
6822
- "Boondoggling",
6823
- "Bootstrapping",
6824
- "Brewing",
6825
- "Calculating",
6826
- "Canoodling",
6827
- "Caramelizing",
6828
- "Cascading",
6829
- "Cerebrating",
6830
- "Choreographing",
6831
- "Churning",
6832
- "Coalescing",
6833
- "Cogitating",
6834
- "Combobulating",
6835
- "Composing",
6836
- "Computing",
6837
- "Concocting",
6838
- "Considering",
6839
- "Contemplating",
6840
- "Cooking",
6841
- "Crafting",
6842
- "Creating",
6843
- "Crunching",
6844
- "Crystallizing",
6845
- "Cultivating",
6846
- "Deciphering",
6847
- "Deliberating",
6848
- "Dilly-dallying",
6849
- "Discombobulating",
6850
- "Doodling",
6851
- "Elucidating",
6852
- "Enchanting",
6853
- "Envisioning",
6854
- "Fermenting",
6855
- "Finagling",
6856
- "Flamb\xE9ing",
6857
- "Flummoxing",
6858
- "Forging",
6859
- "Frolicking",
6860
- "Gallivanting",
6861
- "Garnishing",
6862
- "Generating",
6863
- "Germinating",
6864
- "Grooving",
6865
- "Harmonizing",
6866
- "Hatching",
6867
- "Honking",
6868
- "Ideating",
6869
- "Imagining",
6870
- "Improvising",
6871
- "Incubating",
6872
- "Inferring",
6873
- "Infusing",
6874
- "Kneading",
6875
- "Lollygagging",
6876
- "Manifesting",
6877
- "Marinating",
6878
- "Meandering",
6879
- "Metamorphosing",
6880
- "Mewing",
6881
- "Moonwalking",
6882
- "Moseying",
6883
- "Mulling",
6884
- "Musing",
6885
- "Noodling",
6886
- "Orbiting",
6887
- "Orchestrating",
6888
- "Percolating",
6889
- "Philosophizing",
6890
- "Pondering",
6891
- "Pontificating",
6892
- "Pouncing",
6893
- "Purring",
6894
- "Puzzling",
6895
- "Razzle-dazzling",
6896
- "Ruminating",
6897
- "Scampering",
6898
- "Simmering",
6899
- "Sketching",
6900
- "Spelunking",
6901
- "Spinning",
6902
- "Sprouting",
6903
- "Synthesizing",
6904
- "Thinking",
6905
- "Tinkering",
6906
- "Transfiguring",
6907
- "Transmuting",
6908
- "Undulating",
6909
- "Unfurling",
6910
- "Unravelling",
6911
- "Vibing",
6912
- "Wandering",
6913
- "Whisking",
6914
- "Working",
6915
- "Wrangling",
6916
- "Zigzagging"
6917
- ];
6918
- function randomVerb() {
6919
- return spinnerVerbs[Math.floor(Math.random() * spinnerVerbs.length)];
6920
- }
6921
- var completionVerbs = [
6922
- "Accomplished",
6923
- "Baked",
6924
- "Brewed",
6925
- "Cooked",
6926
- "Crafted",
6927
- "Crunched",
6928
- "Forged",
6929
- "Hatched",
6930
- "Pondered",
6931
- "Synthesized",
6932
- "Tinkered",
6933
- "Worked",
6934
- "Wrangled",
6935
- "Computed",
6936
- "Created",
6937
- "Composed",
6938
- "Conjured",
6939
- "Concocted",
6940
- "Cultivated",
6941
- "Deciphered"
6942
- ];
6943
- function randomCompletionVerb() {
6944
- return completionVerbs[Math.floor(Math.random() * completionVerbs.length)];
6945
- }
6946
-
6947
6816
  // src/teams/file-mailbox.ts
6948
6817
  import {
6949
6818
  readFileSync as readFileSync15,
@@ -7493,65 +7362,201 @@ function loadTranscript(workDir, teamName, agentId) {
7493
7362
  }
7494
7363
  }
7495
7364
 
7496
- // src/teams/team.ts
7497
- var log29 = createChildLogger({ module: "teams" });
7498
- var Team = class _Team {
7499
- name;
7500
- mode;
7501
- members = /* @__PURE__ */ new Map();
7502
- leadMailbox;
7503
- mailboxDir;
7504
- workDir;
7505
- // Team-level metadata for persistence
7506
- leadAgentId = "";
7507
- description;
7508
- createdAt = Math.floor(Date.now() / 1e3);
7509
- constructor(name, mode, workDir) {
7510
- this.name = name;
7511
- this.mode = mode;
7512
- this.workDir = workDir;
7513
- this.mailboxDir = join21(teamDir(name), "inboxes");
7514
- mkdirSync13(this.mailboxDir, { recursive: true });
7515
- this.leadMailbox = new FileMailbox(this.mailboxDir, "lead");
7516
- }
7517
- addMember(name) {
7518
- const mailbox = new FileMailbox(this.mailboxDir, name);
7519
- const member = {
7520
- name,
7521
- active: false,
7522
- mailbox,
7523
- agentId: name,
7524
- joinedAt: Math.floor(Date.now() / 1e3)
7525
- };
7526
- this.members.set(name, member);
7527
- this.persist();
7528
- return member;
7529
- }
7530
- /**
7531
- * Backfills member metadata (agent type, model, worktree path) and persists.
7532
- * The spawn flow obtains this information later than addMember, hence the two-step write.
7533
- */
7534
- setMemberMeta(name, meta) {
7535
- const member = this.members.get(name);
7536
- if (!member) {
7537
- return;
7538
- }
7539
- member.agentType = meta.agentType;
7540
- member.model = meta.model;
7541
- member.worktreePath = meta.worktreePath;
7542
- this.persist();
7543
- }
7544
- /** Exports the current state into a persistable structure. */
7545
- snapshot() {
7546
- return {
7547
- name: this.name,
7548
- description: this.description,
7549
- createdAt: this.createdAt,
7550
- leadAgentId: this.leadAgentId,
7551
- members: [...this.members.values()].map((m) => ({
7552
- agentId: m.agentId ?? m.name,
7553
- name: m.name,
7554
- agentType: m.agentType,
7365
+ // src/utils/verbs.ts
7366
+ var spinnerVerbs = [
7367
+ "Accomplishing",
7368
+ "Architecting",
7369
+ "Baking",
7370
+ "Barking",
7371
+ "Woofing",
7372
+ "Howling",
7373
+ "Be-bopping'",
7374
+ "Befuddling",
7375
+ "Boogieing",
7376
+ "Boondoggling",
7377
+ "Bootstrapping",
7378
+ "Brewing",
7379
+ "Calculating",
7380
+ "Canoodling",
7381
+ "Caramelizing",
7382
+ "Cascading",
7383
+ "Cerebrating",
7384
+ "Choreographing",
7385
+ "Churning",
7386
+ "Coalescing",
7387
+ "Cogitating",
7388
+ "Combobulating",
7389
+ "Composing",
7390
+ "Computing",
7391
+ "Concocting",
7392
+ "Considering",
7393
+ "Contemplating",
7394
+ "Cooking",
7395
+ "Crafting",
7396
+ "Creating",
7397
+ "Crunching",
7398
+ "Crystallizing",
7399
+ "Cultivating",
7400
+ "Deciphering",
7401
+ "Deliberating",
7402
+ "Dilly-dallying",
7403
+ "Discombobulating",
7404
+ "Doodling",
7405
+ "Elucidating",
7406
+ "Enchanting",
7407
+ "Envisioning",
7408
+ "Fermenting",
7409
+ "Finagling",
7410
+ "Flamb\xE9ing",
7411
+ "Flummoxing",
7412
+ "Forging",
7413
+ "Frolicking",
7414
+ "Gallivanting",
7415
+ "Garnishing",
7416
+ "Generating",
7417
+ "Germinating",
7418
+ "Grooving",
7419
+ "Harmonizing",
7420
+ "Hatching",
7421
+ "Honking",
7422
+ "Ideating",
7423
+ "Imagining",
7424
+ "Improvising",
7425
+ "Incubating",
7426
+ "Inferring",
7427
+ "Infusing",
7428
+ "Kneading",
7429
+ "Lollygagging",
7430
+ "Manifesting",
7431
+ "Marinating",
7432
+ "Meandering",
7433
+ "Metamorphosing",
7434
+ "Mewing",
7435
+ "Moonwalking",
7436
+ "Moseying",
7437
+ "Mulling",
7438
+ "Musing",
7439
+ "Noodling",
7440
+ "Orbiting",
7441
+ "Orchestrating",
7442
+ "Percolating",
7443
+ "Philosophizing",
7444
+ "Pondering",
7445
+ "Pontificating",
7446
+ "Pouncing",
7447
+ "Purring",
7448
+ "Puzzling",
7449
+ "Razzle-dazzling",
7450
+ "Ruminating",
7451
+ "Scampering",
7452
+ "Simmering",
7453
+ "Sketching",
7454
+ "Spelunking",
7455
+ "Spinning",
7456
+ "Sprouting",
7457
+ "Synthesizing",
7458
+ "Thinking",
7459
+ "Tinkering",
7460
+ "Transfiguring",
7461
+ "Transmuting",
7462
+ "Undulating",
7463
+ "Unfurling",
7464
+ "Unravelling",
7465
+ "Vibing",
7466
+ "Wandering",
7467
+ "Whisking",
7468
+ "Working",
7469
+ "Wrangling",
7470
+ "Zigzagging"
7471
+ ];
7472
+ function randomVerb() {
7473
+ return spinnerVerbs[Math.floor(Math.random() * spinnerVerbs.length)];
7474
+ }
7475
+ var completionVerbs = [
7476
+ "Accomplished",
7477
+ "Baked",
7478
+ "Brewed",
7479
+ "Cooked",
7480
+ "Crafted",
7481
+ "Crunched",
7482
+ "Forged",
7483
+ "Hatched",
7484
+ "Pondered",
7485
+ "Synthesized",
7486
+ "Tinkered",
7487
+ "Worked",
7488
+ "Wrangled",
7489
+ "Computed",
7490
+ "Created",
7491
+ "Composed",
7492
+ "Conjured",
7493
+ "Concocted",
7494
+ "Cultivated",
7495
+ "Deciphered"
7496
+ ];
7497
+ function randomCompletionVerb() {
7498
+ return completionVerbs[Math.floor(Math.random() * completionVerbs.length)];
7499
+ }
7500
+
7501
+ // src/teams/team.ts
7502
+ var log29 = createChildLogger({ module: "teams" });
7503
+ var Team = class _Team {
7504
+ name;
7505
+ mode;
7506
+ members = /* @__PURE__ */ new Map();
7507
+ leadMailbox;
7508
+ mailboxDir;
7509
+ workDir;
7510
+ // Team-level metadata for persistence
7511
+ leadAgentId = "";
7512
+ description;
7513
+ createdAt = Math.floor(Date.now() / 1e3);
7514
+ constructor(name, mode, workDir) {
7515
+ this.name = name;
7516
+ this.mode = mode;
7517
+ this.workDir = workDir;
7518
+ this.mailboxDir = join21(teamDir(name), "inboxes");
7519
+ mkdirSync13(this.mailboxDir, { recursive: true });
7520
+ this.leadMailbox = new FileMailbox(this.mailboxDir, "lead");
7521
+ }
7522
+ addMember(name) {
7523
+ const mailbox = new FileMailbox(this.mailboxDir, name);
7524
+ const member = {
7525
+ name,
7526
+ active: false,
7527
+ mailbox,
7528
+ agentId: name,
7529
+ joinedAt: Math.floor(Date.now() / 1e3)
7530
+ };
7531
+ this.members.set(name, member);
7532
+ this.persist();
7533
+ return member;
7534
+ }
7535
+ /**
7536
+ * Backfills member metadata (agent type, model, worktree path) and persists.
7537
+ * The spawn flow obtains this information later than addMember, hence the two-step write.
7538
+ */
7539
+ setMemberMeta(name, meta) {
7540
+ const member = this.members.get(name);
7541
+ if (!member) {
7542
+ return;
7543
+ }
7544
+ member.agentType = meta.agentType;
7545
+ member.model = meta.model;
7546
+ member.worktreePath = meta.worktreePath;
7547
+ this.persist();
7548
+ }
7549
+ /** Exports the current state into a persistable structure. */
7550
+ snapshot() {
7551
+ return {
7552
+ name: this.name,
7553
+ description: this.description,
7554
+ createdAt: this.createdAt,
7555
+ leadAgentId: this.leadAgentId,
7556
+ members: [...this.members.values()].map((m) => ({
7557
+ agentId: m.agentId ?? m.name,
7558
+ name: m.name,
7559
+ agentType: m.agentType,
7555
7560
  model: m.model,
7556
7561
  joinedAt: m.joinedAt ?? 0,
7557
7562
  worktreePath: m.worktreePath,
@@ -7645,7 +7650,9 @@ var Team = class _Team {
7645
7650
  member.active = true;
7646
7651
  member.checker = checker;
7647
7652
  const abortController = new AbortController();
7648
- member.cancel = () => abortController.abort();
7653
+ member.cancel = () => {
7654
+ abortController.abort();
7655
+ };
7649
7656
  getNameRegistry().register(name, name);
7650
7657
  const uiState = {
7651
7658
  name,
@@ -7966,8 +7973,8 @@ function isTeamMode(mode) {
7966
7973
 
7967
7974
  // src/tools/bash.ts
7968
7975
  import { spawn as spawn3 } from "child_process";
7969
- var MAX_TIMEOUT = 600;
7970
- var KILL_GRACE_MS = 3e3;
7976
+
7977
+ // src/tools/exit-code-hints.ts
7971
7978
  function extractBaseCmd(command) {
7972
7979
  const lastSegment = command.split("|").pop()?.trim() ?? command;
7973
7980
  const tokens = lastSegment.split(/\s+/);
@@ -7975,25 +7982,233 @@ function extractBaseCmd(command) {
7975
7982
  if (token.includes("=") && !token.startsWith("-")) {
7976
7983
  continue;
7977
7984
  }
7978
- return token.split("/").pop() ?? token;
7985
+ const base = token.split(/[\\/]/).pop() ?? token;
7986
+ return base.replace(/\.exe$/i, "").toLowerCase();
7979
7987
  }
7980
7988
  return "";
7981
7989
  }
7982
- var exitCodeHints = /* @__PURE__ */ new Map([
7983
- ["grep", /* @__PURE__ */ new Map([[1, "no matches found"]])],
7984
- ["egrep", /* @__PURE__ */ new Map([[1, "no matches found"]])],
7985
- ["fgrep", /* @__PURE__ */ new Map([[1, "no matches found"]])],
7986
- ["rg", /* @__PURE__ */ new Map([[1, "no matches found"]])],
7987
- ["diff", /* @__PURE__ */ new Map([[1, "files differ"]])],
7988
- ["test", /* @__PURE__ */ new Map([[1, "condition is false"]])],
7989
- ["[", /* @__PURE__ */ new Map([[1, "condition is false"]])],
7990
- ["find", /* @__PURE__ */ new Map([[1, "partial success"]])]
7990
+ var EXIT_CODE_HINTS = /* @__PURE__ */ new Map([
7991
+ // Search tools: exit 1 means "no match", not failure
7992
+ [
7993
+ "grep",
7994
+ /* @__PURE__ */ new Map([
7995
+ [1, "no matches found"],
7996
+ [2, "error while searching"]
7997
+ ])
7998
+ ],
7999
+ [
8000
+ "egrep",
8001
+ /* @__PURE__ */ new Map([
8002
+ [1, "no matches found"],
8003
+ [2, "error while searching"]
8004
+ ])
8005
+ ],
8006
+ [
8007
+ "fgrep",
8008
+ /* @__PURE__ */ new Map([
8009
+ [1, "no matches found"],
8010
+ [2, "error while searching"]
8011
+ ])
8012
+ ],
8013
+ [
8014
+ "zgrep",
8015
+ /* @__PURE__ */ new Map([
8016
+ [1, "no matches found"],
8017
+ [2, "error while searching"]
8018
+ ])
8019
+ ],
8020
+ [
8021
+ "rg",
8022
+ /* @__PURE__ */ new Map([
8023
+ [1, "no matches found"],
8024
+ [2, "error while searching"]
8025
+ ])
8026
+ ],
8027
+ ["ack", /* @__PURE__ */ new Map([[1, "no matches found"]])],
8028
+ ["ag", /* @__PURE__ */ new Map([[1, "no matches found"]])],
8029
+ [
8030
+ "findstr",
8031
+ /* @__PURE__ */ new Map([
8032
+ [1, "no matches found"],
8033
+ [2, "error while searching"]
8034
+ ])
8035
+ ],
8036
+ ["where", /* @__PURE__ */ new Map([[1, "not found"]])],
8037
+ // Comparison / condition tools: non-zero exit is informational
8038
+ [
8039
+ "diff",
8040
+ /* @__PURE__ */ new Map([
8041
+ [1, "files differ"],
8042
+ [2, "trouble reading files"]
8043
+ ])
8044
+ ],
8045
+ [
8046
+ "diff3",
8047
+ /* @__PURE__ */ new Map([
8048
+ [1, "files differ"],
8049
+ [2, "trouble reading files"]
8050
+ ])
8051
+ ],
8052
+ [
8053
+ "sdiff",
8054
+ /* @__PURE__ */ new Map([
8055
+ [1, "files differ"],
8056
+ [2, "trouble reading files"]
8057
+ ])
8058
+ ],
8059
+ [
8060
+ "cmp",
8061
+ /* @__PURE__ */ new Map([
8062
+ [1, "files differ"],
8063
+ [2, "trouble reading files"]
8064
+ ])
8065
+ ],
8066
+ [
8067
+ "test",
8068
+ /* @__PURE__ */ new Map([
8069
+ [1, "condition is false"],
8070
+ [2, "expression error"]
8071
+ ])
8072
+ ],
8073
+ [
8074
+ "[",
8075
+ /* @__PURE__ */ new Map([
8076
+ [1, "condition is false"],
8077
+ [2, "expression error"]
8078
+ ])
8079
+ ],
8080
+ [
8081
+ "expr",
8082
+ /* @__PURE__ */ new Map([
8083
+ [1, "expression is null or zero"],
8084
+ [2, "syntax error"]
8085
+ ])
8086
+ ],
8087
+ ["find", /* @__PURE__ */ new Map([[1, "partial success \u2014 some inputs could not be processed"]])],
8088
+ // Process / batch helpers
8089
+ [
8090
+ "xargs",
8091
+ /* @__PURE__ */ new Map([
8092
+ [123, "some sub-command invocations exited with 1-125"],
8093
+ [124, "a sub-command exited with 255"]
8094
+ ])
8095
+ ],
8096
+ ["timeout", /* @__PURE__ */ new Map([[124, "command timed out"]])],
8097
+ // File / build tools
8098
+ [
8099
+ "ls",
8100
+ /* @__PURE__ */ new Map([
8101
+ [1, "minor problem (e.g. unreadable subdirectory)"],
8102
+ [2, "serious problem (e.g. inaccessible path)"]
8103
+ ])
8104
+ ],
8105
+ [
8106
+ "tar",
8107
+ /* @__PURE__ */ new Map([
8108
+ [1, "some files changed or differed while archiving"],
8109
+ [2, "fatal error"]
8110
+ ])
8111
+ ],
8112
+ [
8113
+ "make",
8114
+ /* @__PURE__ */ new Map([
8115
+ [1, "targets not up to date (with -q)"],
8116
+ [2, "build failed"]
8117
+ ])
8118
+ ],
8119
+ ["gcc", /* @__PURE__ */ new Map([[1, "compilation failed"]])],
8120
+ ["clang", /* @__PURE__ */ new Map([[1, "compilation failed"]])],
8121
+ ["tsc", /* @__PURE__ */ new Map([[1, "type errors found"]])],
8122
+ [
8123
+ "eslint",
8124
+ /* @__PURE__ */ new Map([
8125
+ [1, "lint errors found"],
8126
+ [2, "fatal lint problem (bad config or rule crash)"]
8127
+ ])
8128
+ ],
8129
+ // Test runners
8130
+ ["jest", /* @__PURE__ */ new Map([[1, "tests failed"]])],
8131
+ ["vitest", /* @__PURE__ */ new Map([[1, "tests failed"]])],
8132
+ ["go", /* @__PURE__ */ new Map([[1, "build/test/vet reported problems"]])],
8133
+ // Interpreters
8134
+ ["node", /* @__PURE__ */ new Map([[1, "uncaught exception"]])],
8135
+ ["python", /* @__PURE__ */ new Map([[1, "unhandled exception"]])],
8136
+ ["python3", /* @__PURE__ */ new Map([[1, "unhandled exception"]])],
8137
+ // Network / remote tools
8138
+ ["git", /* @__PURE__ */ new Map([[128, "fatal error (e.g. invalid ref or not a git repository)"]])],
8139
+ ["ssh", /* @__PURE__ */ new Map([[255, "ssh error"]])],
8140
+ [
8141
+ "wget",
8142
+ /* @__PURE__ */ new Map([
8143
+ [1, "generic error"],
8144
+ [4, "network failure"],
8145
+ [5, "SSL verification failed"],
8146
+ [6, "authentication failure"],
8147
+ [7, "protocol error"],
8148
+ [8, "server issued an error response"]
8149
+ ])
8150
+ ],
8151
+ [
8152
+ "curl",
8153
+ /* @__PURE__ */ new Map([
8154
+ [6, "couldn't resolve host"],
8155
+ [7, "failed to connect to host"],
8156
+ [22, "HTTP error >= 400 (with -f)"],
8157
+ [28, "operation timed out"]
8158
+ ])
8159
+ ],
8160
+ [
8161
+ "jq",
8162
+ /* @__PURE__ */ new Map([
8163
+ [2, "usage or system error"],
8164
+ [3, "jq program failed to compile"],
8165
+ [5, "no valid result was produced"]
8166
+ ])
8167
+ ],
8168
+ ["ping", /* @__PURE__ */ new Map([[1, "no response received"]])],
8169
+ [
8170
+ "rsync",
8171
+ /* @__PURE__ */ new Map([
8172
+ [23, "partial transfer due to error"],
8173
+ [24, "source files vanished before transfer"]
8174
+ ])
8175
+ ],
8176
+ // Windows
8177
+ [
8178
+ "robocopy",
8179
+ /* @__PURE__ */ new Map([
8180
+ [1, "success \u2014 files copied"],
8181
+ [2, "success \u2014 extra files or directories detected"],
8182
+ [4, "success \u2014 mismatched files detected"],
8183
+ [8, "some files failed to copy"],
8184
+ [16, "serious error \u2014 no files copied"]
8185
+ ])
8186
+ ]
8187
+ ]);
8188
+ var GENERIC_HINTS = /* @__PURE__ */ new Map([
8189
+ [126, "command found but not executable"],
8190
+ [127, "command not found"],
8191
+ [129, "terminated by SIGHUP"],
8192
+ [130, "terminated by SIGINT (Ctrl+C)"],
8193
+ [137, "killed by SIGKILL (often OOM)"],
8194
+ [141, "downstream consumer exited early (SIGPIPE)"],
8195
+ [143, "terminated by SIGTERM"]
7991
8196
  ]);
7992
8197
  function exitCodeHint(command, exitCode) {
7993
8198
  const baseCmd = extractBaseCmd(command);
7994
- const hints = exitCodeHints.get(baseCmd);
7995
- return hints?.get(exitCode) ?? "";
8199
+ const hint = EXIT_CODE_HINTS.get(baseCmd)?.get(exitCode) ?? GENERIC_HINTS.get(exitCode);
8200
+ if (hint) {
8201
+ return hint;
8202
+ }
8203
+ if (exitCode >= 129 && exitCode <= 159) {
8204
+ return `terminated by signal ${String(exitCode - 128)}`;
8205
+ }
8206
+ return "";
7996
8207
  }
8208
+
8209
+ // src/tools/bash.ts
8210
+ var MAX_TIMEOUT = 600;
8211
+ var KILL_GRACE_MS = 3e3;
7997
8212
  var BashTool = class {
7998
8213
  // Use a hardcoded string instead of BashTool.name.replace("Tool", "")
7999
8214
  // because class names are not stable after minification — bundlers like
@@ -8067,7 +8282,10 @@ var BashTool = class {
8067
8282
  actualCommand = this.sandbox.wrap(command, this.sandboxConfig);
8068
8283
  }
8069
8284
  if (ctx.abortSignal?.aborted) {
8070
- return Promise.resolve({ output: "Error: command interrupted", isError: true });
8285
+ return Promise.resolve({
8286
+ output: "Error: command interrupted",
8287
+ isError: true
8288
+ });
8071
8289
  }
8072
8290
  return new Promise((resolve12) => {
8073
8291
  let timedOut = false;
@@ -8083,161 +8301,1450 @@ var BashTool = class {
8083
8301
  let stderr = "";
8084
8302
  let total = 0;
8085
8303
  let outputTruncated = false;
8086
- const alreadyExited = () => child.exitCode !== null || child.signalCode !== null;
8087
8304
  const killTree = (signal) => {
8088
8305
  if (typeof child.pid !== "number") {
8089
8306
  return;
8090
8307
  }
8091
- try {
8092
- process.kill(-child.pid, signal);
8093
- } catch {
8308
+ try {
8309
+ process.kill(-child.pid, signal);
8310
+ } catch {
8311
+ try {
8312
+ child.kill(signal);
8313
+ } catch {
8314
+ }
8315
+ }
8316
+ };
8317
+ const terminate = () => {
8318
+ if (terminating) {
8319
+ return;
8320
+ }
8321
+ terminating = true;
8322
+ killTree("SIGTERM");
8323
+ escalateTimer = setTimeout(() => {
8324
+ killTree("SIGKILL");
8325
+ child.stdout.destroy();
8326
+ child.stderr.destroy();
8327
+ }, KILL_GRACE_MS);
8328
+ escalateTimer.unref();
8329
+ };
8330
+ const appendChunk = (chunk, target) => {
8331
+ if (outputTruncated) {
8332
+ return;
8333
+ }
8334
+ const remaining = MAX_SHELL_OUTPUT_BYTES - total;
8335
+ const piece = takeUtf8Prefix(chunk, remaining);
8336
+ total += utf8ByteLength(piece);
8337
+ if (piece.length < chunk.length) {
8338
+ outputTruncated = true;
8339
+ terminate();
8340
+ }
8341
+ if (target === "stdout") {
8342
+ stdout += piece;
8343
+ } else {
8344
+ stderr += piece;
8345
+ }
8346
+ };
8347
+ child.stdout.setEncoding("utf-8");
8348
+ child.stdout.on("data", (chunk) => {
8349
+ appendChunk(chunk, "stdout");
8350
+ });
8351
+ child.stderr.setEncoding("utf-8");
8352
+ child.stderr.on("data", (chunk) => {
8353
+ appendChunk(chunk, "stderr");
8354
+ });
8355
+ const onAbort = () => {
8356
+ aborted = true;
8357
+ terminate();
8358
+ };
8359
+ const timeoutTimer = setTimeout(() => {
8360
+ timedOut = true;
8361
+ terminate();
8362
+ }, timeout * 1e3);
8363
+ timeoutTimer.unref();
8364
+ ctx.abortSignal?.addEventListener("abort", onAbort, { once: true });
8365
+ if (ctx.abortSignal?.aborted) {
8366
+ onAbort();
8367
+ }
8368
+ const cleanup = () => {
8369
+ clearTimeout(timeoutTimer);
8370
+ if (escalateTimer) {
8371
+ clearTimeout(escalateTimer);
8372
+ }
8373
+ ctx.abortSignal?.removeEventListener("abort", onAbort);
8374
+ };
8375
+ child.on("error", (err) => {
8376
+ cleanup();
8377
+ resolve12({
8378
+ output: `Error executing command: ${err.message}`,
8379
+ isError: true
8380
+ });
8381
+ });
8382
+ child.on("close", (code, signal) => {
8383
+ cleanup();
8384
+ if (aborted) {
8385
+ const captured = stdout || stderr || outputTruncated ? formatShellOutput("$ ", command, stdout, stderr, outputTruncated) : "";
8386
+ resolve12({
8387
+ output: captured ? `${captured}
8388
+ Error: command interrupted` : "Error: command interrupted",
8389
+ isError: true
8390
+ });
8391
+ return;
8392
+ }
8393
+ if (timedOut) {
8394
+ const captured = stdout || stderr || outputTruncated ? formatShellOutput("$ ", command, stdout, stderr, outputTruncated) : "";
8395
+ resolve12({
8396
+ output: captured ? `${captured}
8397
+ Error: command timed out after ${String(timeout)}s` : `Error: command timed out after ${String(timeout)}s`,
8398
+ isError: true
8399
+ });
8400
+ return;
8401
+ }
8402
+ const exitCode = code ?? 0;
8403
+ let output = formatShellOutput("$ ", command, stdout, stderr, outputTruncated);
8404
+ if (outputTruncated) {
8405
+ resolve12({ output, isError: true });
8406
+ return;
8407
+ }
8408
+ if (exitCode !== 0) {
8409
+ const hint = exitCodeHint(command, exitCode);
8410
+ output += hint ? `
8411
+ Exit code ${String(exitCode)} (${hint})` : `
8412
+ Exit code ${String(exitCode)}`;
8413
+ }
8414
+ if (code === null) {
8415
+ output += `
8416
+ Process terminated${signal ? ` by ${signal}` : " unexpectedly"}`;
8417
+ }
8418
+ resolve12({ output, isError: exitCode !== 0 || code === null });
8419
+ });
8420
+ });
8421
+ }
8422
+ };
8423
+
8424
+ // src/tools/computer-use.ts
8425
+ import { spawn as spawn4 } from "child_process";
8426
+ import { mkdtemp, readFile as readFile5, rm as rm2, writeFile as writeFile3 } from "fs/promises";
8427
+ import { tmpdir } from "os";
8428
+ import { join as join22 } from "path";
8429
+ import { setTimeout as delay } from "timers/promises";
8430
+ import sharp2 from "sharp";
8431
+ import { safeParse as safeParse3, z as z13 } from "zod";
8432
+
8433
+ // raw-import:/Users/user/github/swifty-code/apps/swifty/src/tools/snippets/macos.swift
8434
+ var macos_default = `import AppKit
8435
+ import ApplicationServices
8436
+ import Foundation
8437
+
8438
+ func fail(_ message: String) -> Never {
8439
+ FileHandle.standardError.write(Data(message.utf8))
8440
+ exit(2)
8441
+ }
8442
+
8443
+ guard let encoded = ProcessInfo.processInfo.environment["SWIFTY_COMPUTER_INPUT"],
8444
+ let data = Data(base64Encoded: encoded),
8445
+ let object = try? JSONSerialization.jsonObject(with: data),
8446
+ let input = object as? [String: Any],
8447
+ let action = input["action"] as? String
8448
+ else {
8449
+ fail("Invalid computer action payload.")
8450
+ }
8451
+
8452
+ let keyCodes: [String: CGKeyCode] = [
8453
+ "A": 0, "S": 1, "D": 2, "F": 3, "H": 4, "G": 5, "Z": 6, "X": 7,
8454
+ "C": 8, "V": 9, "B": 11, "Q": 12, "W": 13, "E": 14, "R": 15,
8455
+ "Y": 16, "T": 17, "1": 18, "2": 19, "3": 20, "4": 21, "6": 22,
8456
+ "5": 23, "=": 24, "9": 25, "7": 26, "-": 27, "8": 28, "0": 29,
8457
+ "]": 30, "O": 31, "U": 32, "[": 33, "I": 34, "P": 35, "RETURN": 36,
8458
+ "ENTER": 36, "L": 37, "J": 38, "'": 39, "K": 40, ";": 41, "\\\\": 42,
8459
+ ",": 43, "/": 44, "N": 45, "M": 46, ".": 47, "TAB": 48, "SPACE": 49,
8460
+ "GRAVE": 50, "BACKTICK": 50, "BACKSPACE": 51, "DELETE": 51, "ESC": 53, "ESCAPE": 53,
8461
+ "CMD": 55, "COMMAND": 55, "META": 55, "SHIFT": 56, "CAPSLOCK": 57,
8462
+ "ALT": 58, "OPTION": 58, "CTRL": 59, "CONTROL": 59, "LEFT": 123,
8463
+ "RIGHT": 124, "DOWN": 125, "UP": 126, "HOME": 115, "END": 119,
8464
+ "PAGEUP": 116, "PAGEDOWN": 121, "F1": 122, "F2": 120, "F3": 99,
8465
+ "F4": 118, "F5": 96, "F6": 97, "F7": 98, "F8": 100, "F9": 101,
8466
+ "F10": 109, "F11": 103, "F12": 111,
8467
+ ]
8468
+
8469
+ func number(_ name: String) -> CGFloat {
8470
+ guard let value = input[name] as? NSNumber else { fail("Missing " + name + ".") }
8471
+ return CGFloat(value.doubleValue)
8472
+ }
8473
+
8474
+ func keys() -> [String] {
8475
+ return (input["keys"] as? [String] ?? []).map { $0.uppercased() }
8476
+ }
8477
+
8478
+ func keyCode(_ name: String) -> CGKeyCode {
8479
+ guard let code = keyCodes[name.uppercased()] else { fail("Unsupported key: " + name) }
8480
+ return code
8481
+ }
8482
+
8483
+ func keyEvent(_ name: String, _ down: Bool) {
8484
+ guard let event = CGEvent(keyboardEventSource: nil, virtualKey: keyCode(name), keyDown: down)
8485
+ else {
8486
+ fail("Unable to create keyboard event.")
8487
+ }
8488
+ event.post(tap: .cghidEventTap)
8489
+ }
8490
+
8491
+ func withKeys(_ names: [String], _ body: () -> Void) {
8492
+ for name in names { keyEvent(name, true) }
8493
+ body()
8494
+ for name in names.reversed() { keyEvent(name, false) }
8495
+ }
8496
+
8497
+ func mouseEvent(_ type: CGEventType, _ point: CGPoint, _ button: CGMouseButton, clicks: Int64 = 1) {
8498
+ guard
8499
+ let event = CGEvent(
8500
+ mouseEventSource: nil, mouseType: type, mouseCursorPosition: point, mouseButton: button)
8501
+ else {
8502
+ fail("Unable to create mouse event.")
8503
+ }
8504
+ event.setIntegerValueField(.mouseEventClickState, value: clicks)
8505
+ event.post(tap: .cghidEventTap)
8506
+ }
8507
+
8508
+ if action == "screen_size" {
8509
+ let bounds = CGDisplayBounds(CGMainDisplayID())
8510
+ print("\\(Int(bounds.width)),\\(Int(bounds.height))")
8511
+ exit(0)
8512
+ }
8513
+
8514
+ if action == "cursor_position" {
8515
+ guard let event = CGEvent(source: nil) else { fail("Unable to read cursor position.") }
8516
+ print("\\(Int(event.location.x)),\\(Int(event.location.y))")
8517
+ exit(0)
8518
+ }
8519
+
8520
+ if !AXIsProcessTrusted() {
8521
+ fail(
8522
+ "Accessibility permission is required. Enable it for the terminal running Swifty in System Settings > Privacy & Security > Accessibility."
8523
+ )
8524
+ }
8525
+
8526
+ let point = CGPoint(
8527
+ x: (input["x"] as? NSNumber)?.doubleValue ?? 0,
8528
+ y: (input["y"] as? NSNumber)?.doubleValue ?? 0
8529
+ )
8530
+ let heldKeys = keys()
8531
+
8532
+ switch action {
8533
+ case "mouse_move":
8534
+ withKeys(heldKeys) { mouseEvent(.mouseMoved, point, .left) }
8535
+ case "left_mouse_down":
8536
+ mouseEvent(.leftMouseDown, CGEvent(source: nil)?.location ?? point, .left)
8537
+ case "left_mouse_up":
8538
+ mouseEvent(.leftMouseUp, CGEvent(source: nil)?.location ?? point, .left)
8539
+ case "mouse_click":
8540
+ let buttonName = input["button"] as? String ?? "left"
8541
+ let button: CGMouseButton =
8542
+ buttonName == "right" ? .right : buttonName == "middle" ? .center : .left
8543
+ let downType: CGEventType =
8544
+ button == .right ? .rightMouseDown : button == .center ? .otherMouseDown : .leftMouseDown
8545
+ let upType: CGEventType =
8546
+ button == .right ? .rightMouseUp : button == .center ? .otherMouseUp : .leftMouseUp
8547
+ let count = (input["clicks"] as? NSNumber)?.int64Value ?? 1
8548
+ withKeys(heldKeys) {
8549
+ mouseEvent(.mouseMoved, point, button)
8550
+ for click in 1...count {
8551
+ mouseEvent(downType, point, button, clicks: click)
8552
+ mouseEvent(upType, point, button, clicks: click)
8553
+ Thread.sleep(forTimeInterval: 0.08)
8554
+ }
8555
+ }
8556
+ case "left_click_drag":
8557
+ guard let path = input["path"] as? [[String: NSNumber]], let first = path.first else {
8558
+ fail("Drag path is missing.")
8559
+ }
8560
+ let start = CGPoint(x: first["x"]?.doubleValue ?? 0, y: first["y"]?.doubleValue ?? 0)
8561
+ withKeys(heldKeys) {
8562
+ mouseEvent(.mouseMoved, start, .left)
8563
+ mouseEvent(.leftMouseDown, start, .left)
8564
+ for item in path.dropFirst() {
8565
+ let next = CGPoint(x: item["x"]?.doubleValue ?? 0, y: item["y"]?.doubleValue ?? 0)
8566
+ mouseEvent(.leftMouseDragged, next, .left)
8567
+ Thread.sleep(forTimeInterval: 0.02)
8568
+ }
8569
+ let end = path.last ?? first
8570
+ mouseEvent(
8571
+ .leftMouseUp, CGPoint(x: end["x"]?.doubleValue ?? 0, y: end["y"]?.doubleValue ?? 0), .left)
8572
+ }
8573
+ case "scroll":
8574
+ if let x = input["x"] as? NSNumber, let y = input["y"] as? NSNumber {
8575
+ mouseEvent(.mouseMoved, CGPoint(x: x.doubleValue, y: y.doubleValue), .left)
8576
+ }
8577
+ let horizontal = Int32((input["scrollX"] as? NSNumber)?.intValue ?? 0)
8578
+ let vertical = Int32((input["scrollY"] as? NSNumber)?.intValue ?? 0)
8579
+ withKeys(heldKeys) {
8580
+ CGEvent(
8581
+ scrollWheelEvent2Source: nil, units: .line, wheelCount: 2, wheel1: -vertical,
8582
+ wheel2: -horizontal, wheel3: 0)?.post(tap: .cghidEventTap)
8583
+ }
8584
+ case "key", "hold_key":
8585
+ let names = keys()
8586
+ if names.isEmpty { fail("No keys supplied.") }
8587
+ for name in names { keyEvent(name, true) }
8588
+ if action == "hold_key" {
8589
+ Thread.sleep(forTimeInterval: (input["duration"] as? NSNumber)?.doubleValue ?? 0)
8590
+ }
8591
+ for name in names.reversed() { keyEvent(name, false) }
8592
+ case "type":
8593
+ let text = input["text"] as? String ?? ""
8594
+ let units = Array(text.utf16)
8595
+ units.withUnsafeBufferPointer { buffer in
8596
+ guard let down = CGEvent(keyboardEventSource: nil, virtualKey: 0, keyDown: true),
8597
+ let up = CGEvent(keyboardEventSource: nil, virtualKey: 0, keyDown: false)
8598
+ else {
8599
+ fail("Unable to create text input events.")
8600
+ }
8601
+ down.keyboardSetUnicodeString(stringLength: units.count, unicodeString: buffer.baseAddress)
8602
+ up.keyboardSetUnicodeString(stringLength: units.count, unicodeString: buffer.baseAddress)
8603
+ down.post(tap: .cghidEventTap)
8604
+ up.post(tap: .cghidEventTap)
8605
+ }
8606
+ default:
8607
+ fail("Unsupported action: " + action)
8608
+ }
8609
+ `;
8610
+
8611
+ // raw-import:/Users/user/github/swifty-code/apps/swifty/src/tools/snippets/win32-script.ps1
8612
+ var win32_script_default = `$ErrorActionPreference = 'Stop';
8613
+
8614
+ Add-Type -AssemblyName System.Drawing;
8615
+
8616
+ Add-Type -AssemblyName System.Windows.Forms;
8617
+
8618
+ $bounds = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds;
8619
+
8620
+ $bitmap = New-Object System.Drawing.Bitmap $bounds.Width, $bounds.Height;
8621
+
8622
+ $graphics = [System.Drawing.Graphics]::FromImage($bitmap);
8623
+
8624
+ $graphics.CopyFromScreen($bounds.X, $bounds.Y, 0, 0, $bounds.Size);
8625
+
8626
+ $bitmap.Save($env:SWIFTY_SCREENSHOT_PATH, [System.Drawing.Imaging.ImageFormat]::Png);
8627
+
8628
+ $graphics.Dispose(); $bitmap.Dispose();
8629
+
8630
+ Write-Output "$($bounds.Width),$($bounds.Height)"
8631
+ `;
8632
+
8633
+ // raw-import:/Users/user/github/swifty-code/apps/swifty/src/tools/snippets/windows.cs
8634
+ var windows_default = 'using System;\nusing System.Drawing;\nusing System.Runtime.InteropServices;\nusing System.Windows.Forms;\n\npublic static class SwiftyComputer\n{\n [StructLayout(LayoutKind.Sequential)]\n private struct INPUT { public uint type; public InputUnion value; }\n [StructLayout(LayoutKind.Explicit)]\n private struct InputUnion { [FieldOffset(0)] public KEYBDINPUT keyboard; }\n [StructLayout(LayoutKind.Sequential)]\n private struct KEYBDINPUT\n {\n public ushort virtualKey;\n public ushort scanCode;\n public uint flags;\n public uint time;\n public UIntPtr extraInfo;\n }\n\n [DllImport("user32.dll")]\n private static extern void mouse_event(uint flags, uint dx, uint dy, int data, UIntPtr extraInfo);\n [DllImport("user32.dll", SetLastError = true)]\n private static extern uint SendInput(uint count, INPUT[] inputs, int size);\n\n private static void SendKeyboard(ushort virtualKey, ushort scanCode, uint flags)\n {\n var inputs = new[] {\n new INPUT {\n type = 1,\n value = new InputUnion {\n keyboard = new KEYBDINPUT {\n virtualKey = virtualKey,\n scanCode = scanCode,\n flags = flags,\n extraInfo = UIntPtr.Zero\n }\n }\n }\n };\n if (SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT))) != 1)\n {\n throw new InvalidOperationException("SendInput failed.");\n }\n }\n\n public static void Move(int x, int y) { Cursor.Position = new Point(x, y); }\n public static Point Position() { return Cursor.Position; }\n public static void Mouse(uint flags, int data = 0) { mouse_event(flags, 0, 0, data, UIntPtr.Zero); }\n public static void Key(int virtualKey, bool down) { SendKeyboard((ushort)virtualKey, 0, down ? 0u : 2u); }\n public static void Text(string text)\n {\n foreach (char value in text)\n {\n SendKeyboard(0, value, 4u);\n SendKeyboard(0, value, 6u);\n }\n }\n}\n';
8635
+
8636
+ // raw-import:/Users/user/github/swifty-code/apps/swifty/src/tools/snippets/windows.ps1
8637
+ var windows_default2 = `$ErrorActionPreference = 'Stop'
8638
+ Add-Type -AssemblyName System.Drawing
8639
+ Add-Type -AssemblyName System.Windows.Forms
8640
+ Add-Type -TypeDefinition @'
8641
+ \${WINDOWS.CS}
8642
+ '@
8643
+
8644
+ $payload = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($env:SWIFTY_COMPUTER_INPUT)) | ConvertFrom-Json
8645
+ function Resolve-Key([string]$name) {
8646
+ switch ($name.ToUpperInvariant()) {
8647
+ 'CTRL' { return 0x11 }
8648
+ 'CONTROL' { return 0x11 }
8649
+ 'SHIFT' { return 0x10 }
8650
+ 'ALT' { return 0x12 }
8651
+ 'OPTION' { return 0x12 }
8652
+ 'CMD' { return 0x5B }
8653
+ 'COMMAND' { return 0x5B }
8654
+ 'META' { return 0x5B }
8655
+ 'WIN' { return 0x5B }
8656
+ 'WINDOWS' { return 0x5B }
8657
+ 'ENTER' { return 0x0D }
8658
+ 'RETURN' { return 0x0D }
8659
+ 'ESC' { return 0x1B }
8660
+ 'ESCAPE' { return 0x1B }
8661
+ 'BACKSPACE' { return 0x08 }
8662
+ 'DELETE' { return 0x2E }
8663
+ 'TAB' { return 0x09 }
8664
+ 'SPACE' { return 0x20 }
8665
+ 'LEFT' { return 0x25 }
8666
+ 'UP' { return 0x26 }
8667
+ 'RIGHT' { return 0x27 }
8668
+ 'DOWN' { return 0x28 }
8669
+ 'HOME' { return 0x24 }
8670
+ 'END' { return 0x23 }
8671
+ 'PAGEUP' { return 0x21 }
8672
+ 'PAGEDOWN' { return 0x22 }
8673
+ default {
8674
+ if ($name.Length -eq 1) { return [int][char]$name.ToUpperInvariant() }
8675
+ try { return [int]([System.Enum]::Parse([System.Windows.Forms.Keys], $name, $true)) }
8676
+ catch { throw "Unsupported key: $name" }
8677
+ }
8678
+ }
8679
+ }
8680
+ function Key-Down($keys) { foreach ($key in $keys) { [SwiftyComputer]::Key((Resolve-Key $key), $true) } }
8681
+ function Key-Up($keys) { for ($i = $keys.Count - 1; $i -ge 0; $i--) { [SwiftyComputer]::Key((Resolve-Key $keys[$i]), $false) } }
8682
+ function Move-To($value) {
8683
+ $bounds = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds
8684
+ [SwiftyComputer]::Move($bounds.X + [int]$value.x, $bounds.Y + [int]$value.y)
8685
+ }
8686
+
8687
+ $keys = @($payload.keys)
8688
+ switch ($payload.action) {
8689
+ 'cursor_position' {
8690
+ $bounds = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds
8691
+ $point = [SwiftyComputer]::Position()
8692
+ Write-Output "$(($point.X - $bounds.X)),$(($point.Y - $bounds.Y))"
8693
+ }
8694
+ 'mouse_move' { Key-Down $keys; Move-To $payload; Key-Up $keys }
8695
+ 'left_mouse_down' { [SwiftyComputer]::Mouse(0x0002) }
8696
+ 'left_mouse_up' { [SwiftyComputer]::Mouse(0x0004) }
8697
+ 'mouse_click' {
8698
+ Move-To $payload
8699
+ Key-Down $keys
8700
+ $down = switch ($payload.button) { 'right' { 0x0008 } 'middle' { 0x0020 } default { 0x0002 } }
8701
+ $up = switch ($payload.button) { 'right' { 0x0010 } 'middle' { 0x0040 } default { 0x0004 } }
8702
+ for ($i = 0; $i -lt [int]$payload.clicks; $i++) {
8703
+ [SwiftyComputer]::Mouse($down); [SwiftyComputer]::Mouse($up); Start-Sleep -Milliseconds 80
8704
+ }
8705
+ Key-Up $keys
8706
+ }
8707
+ 'left_click_drag' {
8708
+ Key-Down $keys
8709
+ Move-To $payload.path[0]
8710
+ [SwiftyComputer]::Mouse(0x0002)
8711
+ foreach ($point in $payload.path | Select-Object -Skip 1) { Move-To $point; Start-Sleep -Milliseconds 20 }
8712
+ [SwiftyComputer]::Mouse(0x0004)
8713
+ Key-Up $keys
8714
+ }
8715
+ 'scroll' {
8716
+ if ($null -ne $payload.x -and $null -ne $payload.y) { Move-To $payload }
8717
+ Key-Down $keys
8718
+ [SwiftyComputer]::Mouse(0x0800, - ([int]$payload.scrollY * 120))
8719
+ [SwiftyComputer]::Mouse(0x01000, [int]$payload.scrollX * 120)
8720
+ Key-Up $keys
8721
+ }
8722
+ 'key' { Key-Down $keys; Key-Up $keys }
8723
+ 'hold_key' { Key-Down $keys; Start-Sleep -Milliseconds ([int]([double]$payload.duration * 1000)); Key-Up $keys }
8724
+ 'type' { [SwiftyComputer]::Text([string]$payload.text) }
8725
+ default { throw "Unsupported action: $($payload.action)" }
8726
+ }
8727
+ `;
8728
+
8729
+ // src/tools/computer-use.ts
8730
+ var ACTIONS = [
8731
+ "key",
8732
+ "hold_key",
8733
+ "type",
8734
+ "cursor_position",
8735
+ "mouse_move",
8736
+ "left_mouse_down",
8737
+ "left_mouse_up",
8738
+ "left_click",
8739
+ "left_click_drag",
8740
+ "right_click",
8741
+ "middle_click",
8742
+ "double_click",
8743
+ "triple_click",
8744
+ "scroll",
8745
+ "wait",
8746
+ "screenshot",
8747
+ "zoom",
8748
+ "click",
8749
+ "drag",
8750
+ "keypress",
8751
+ "move"
8752
+ ];
8753
+ var CoordinateSchema = z13.tuple([z13.number().int().nonnegative(), z13.number().int().nonnegative()]);
8754
+ var PathPointSchema = z13.object({
8755
+ x: z13.number().int().nonnegative(),
8756
+ y: z13.number().int().nonnegative()
8757
+ });
8758
+ var OPENAI_ACTION_TYPES = [
8759
+ "click",
8760
+ "double_click",
8761
+ "drag",
8762
+ "keypress",
8763
+ "move",
8764
+ "screenshot",
8765
+ "scroll",
8766
+ "type",
8767
+ "wait"
8768
+ ];
8769
+ var MAX_BATCH_ACTIONS = 100;
8770
+ var OpenAIActionSchema = z13.object({
8771
+ type: z13.enum(OPENAI_ACTION_TYPES),
8772
+ button: z13.enum(["left", "right", "wheel", "back", "forward"]).optional(),
8773
+ x: z13.number().int().nonnegative().optional(),
8774
+ y: z13.number().int().nonnegative().optional(),
8775
+ keys: z13.array(z13.string().min(1)).max(8).optional(),
8776
+ path: z13.array(PathPointSchema).min(2).max(200).optional(),
8777
+ scrollX: z13.number().optional(),
8778
+ scrollY: z13.number().optional(),
8779
+ text: z13.string().max(1e4).optional()
8780
+ });
8781
+ var SafetyCheckSchema = z13.object({
8782
+ id: z13.string().min(1),
8783
+ code: z13.string().optional(),
8784
+ message: z13.string().optional()
8785
+ });
8786
+ var ComputerUseInputSchema = z13.object({
8787
+ action: z13.enum(ACTIONS).optional(),
8788
+ actions: z13.array(OpenAIActionSchema).min(1).max(MAX_BATCH_ACTIONS).optional(),
8789
+ pendingSafetyChecks: z13.array(SafetyCheckSchema).optional(),
8790
+ status: z13.enum(["in_progress", "completed", "incomplete"]).optional(),
8791
+ coordinate: CoordinateSchema.optional(),
8792
+ duration: z13.number().nonnegative().max(60).optional(),
8793
+ region: z13.tuple([
8794
+ z13.number().int().nonnegative(),
8795
+ z13.number().int().nonnegative(),
8796
+ z13.number().int().nonnegative(),
8797
+ z13.number().int().nonnegative()
8798
+ ]).optional(),
8799
+ scroll_amount: z13.number().optional(),
8800
+ scroll_direction: z13.enum(["up", "down", "left", "right"]).optional(),
8801
+ start_coordinate: CoordinateSchema.optional(),
8802
+ text: z13.string().max(1e4).optional(),
8803
+ x: z13.number().int().nonnegative().optional(),
8804
+ y: z13.number().int().nonnegative().optional(),
8805
+ button: z13.enum(["left", "right", "wheel", "middle", "back", "forward"]).optional(),
8806
+ keys: z13.array(z13.string().min(1)).max(8).optional(),
8807
+ path: z13.array(PathPointSchema).min(2).max(200).optional(),
8808
+ scroll_x: z13.number().optional(),
8809
+ scroll_y: z13.number().optional()
8810
+ });
8811
+ var COMMAND_TIMEOUT_MS = 15e3;
8812
+ var MAX_COMMAND_OUTPUT_BYTES = 32 * 1024 * 1024;
8813
+ var MAX_SCREENSHOT_WIDTH = 1366;
8814
+ var MAX_SCREENSHOT_HEIGHT = 900;
8815
+ function defaultEnvironment(platform2) {
8816
+ if (platform2 === "darwin") {
8817
+ return "mac";
8818
+ }
8819
+ if (platform2 === "win32") {
8820
+ return "windows";
8821
+ }
8822
+ return "linux";
8823
+ }
8824
+ function runCommand(command, args, options = {}) {
8825
+ const timeoutMs = options.timeoutMs ?? COMMAND_TIMEOUT_MS;
8826
+ const maxOutputBytes = options.maxOutputBytes ?? MAX_COMMAND_OUTPUT_BYTES;
8827
+ return new Promise((resolvePromise, rejectPromise) => {
8828
+ const child = spawn4(command, [...args], {
8829
+ env: options.env ?? process.env,
8830
+ shell: false,
8831
+ stdio: ["ignore", "pipe", "pipe"],
8832
+ windowsHide: true
8833
+ });
8834
+ let settled = false;
8835
+ let totalBytes = 0;
8836
+ const stdout = [];
8837
+ const stderr = [];
8838
+ const finish = (callback) => {
8839
+ if (settled) {
8840
+ return;
8841
+ }
8842
+ settled = true;
8843
+ clearTimeout(timer);
8844
+ options.signal?.removeEventListener("abort", onAbort);
8845
+ callback();
8846
+ };
8847
+ const fail = (message) => {
8848
+ child.kill();
8849
+ finish(() => {
8850
+ rejectPromise(new Error(message));
8851
+ });
8852
+ };
8853
+ const onAbort = () => {
8854
+ fail(`${command} was interrupted.`);
8855
+ };
8856
+ const append2 = (chunk, target) => {
8857
+ totalBytes += chunk.length;
8858
+ if (totalBytes > maxOutputBytes) {
8859
+ fail(`${command} exceeded the ${String(maxOutputBytes)} byte output limit.`);
8860
+ return;
8861
+ }
8862
+ target.push(chunk);
8863
+ };
8864
+ const timer = setTimeout(() => {
8865
+ fail(`${command} timed out after ${String(timeoutMs)}ms.`);
8866
+ }, timeoutMs);
8867
+ timer.unref();
8868
+ child.stdout.on("data", (chunk) => {
8869
+ append2(chunk, stdout);
8870
+ });
8871
+ child.stderr.on("data", (chunk) => {
8872
+ append2(chunk, stderr);
8873
+ });
8874
+ child.on("error", (err) => {
8875
+ finish(() => {
8876
+ rejectPromise(
8877
+ err.code === "ENOENT" ? new Error(`${command} is not installed or not on PATH.`) : err
8878
+ );
8879
+ });
8880
+ });
8881
+ child.on("close", (code) => {
8882
+ finish(() => {
8883
+ resolvePromise({
8884
+ code: code ?? 1,
8885
+ stdout: Buffer.concat(stdout),
8886
+ stderr: Buffer.concat(stderr).toString("utf8").trim()
8887
+ });
8888
+ });
8889
+ });
8890
+ options.signal?.addEventListener("abort", onAbort, { once: true });
8891
+ if (options.signal?.aborted) {
8892
+ onAbort();
8893
+ }
8894
+ });
8895
+ }
8896
+ function requiredPoint(input) {
8897
+ if (input.coordinate) {
8898
+ return { x: input.coordinate[0], y: input.coordinate[1] };
8899
+ }
8900
+ if (input.x !== void 0 && input.y !== void 0) {
8901
+ return { x: input.x, y: input.y };
8902
+ }
8903
+ throw new Error(`action=${String(input.action)} requires coordinate or x and y.`);
8904
+ }
8905
+ function keysFor(input) {
8906
+ if (input.keys?.length) {
8907
+ return input.keys;
8908
+ }
8909
+ if (input.text) {
8910
+ return input.text.split("+").map((key) => key.trim()).filter(Boolean);
8911
+ }
8912
+ return [];
8913
+ }
8914
+ function normalizeAction(input) {
8915
+ switch (input.action) {
8916
+ case "screenshot":
8917
+ return "screenshot";
8918
+ case "zoom": {
8919
+ if (!input.region || input.region[2] <= input.region[0] || input.region[3] <= input.region[1]) {
8920
+ throw new Error("action=zoom requires region [x1, y1, x2, y2] with positive area.");
8921
+ }
8922
+ return { region: input.region };
8923
+ }
8924
+ case "wait":
8925
+ return { action: "wait", duration: input.duration ?? 1 };
8926
+ case "cursor_position":
8927
+ return { action: "cursor_position" };
8928
+ case "type":
8929
+ if (input.text === void 0) {
8930
+ throw new Error("action=type requires text.");
8931
+ }
8932
+ return { action: "type", text: input.text };
8933
+ case "key":
8934
+ case "keypress": {
8935
+ const keys = keysFor(input);
8936
+ if (keys.length === 0) {
8937
+ throw new Error(`action=${input.action} requires text or keys.`);
8938
+ }
8939
+ return { action: "key", keys };
8940
+ }
8941
+ case "hold_key": {
8942
+ const keys = keysFor(input);
8943
+ if (keys.length === 0 || input.duration === void 0) {
8944
+ throw new Error("action=hold_key requires text or keys and duration.");
8945
+ }
8946
+ return { action: "hold_key", keys, duration: input.duration };
8947
+ }
8948
+ case "mouse_move":
8949
+ case "move": {
8950
+ const point = requiredPoint(input);
8951
+ return { action: "mouse_move", ...point, keys: input.keys };
8952
+ }
8953
+ case "left_mouse_down":
8954
+ case "left_mouse_up":
8955
+ return { action: input.action, keys: input.keys };
8956
+ case "left_click_drag": {
8957
+ if (!input.start_coordinate) {
8958
+ throw new Error("action=left_click_drag requires start_coordinate.");
8959
+ }
8960
+ const end = requiredPoint(input);
8961
+ return {
8962
+ action: "left_click_drag",
8963
+ path: [{ x: input.start_coordinate[0], y: input.start_coordinate[1] }, end],
8964
+ keys: input.keys
8965
+ };
8966
+ }
8967
+ case "drag": {
8968
+ if (!input.path) {
8969
+ throw new Error("action=drag requires a path with at least two points.");
8970
+ }
8971
+ return { action: "left_click_drag", path: input.path, keys: input.keys };
8972
+ }
8973
+ case "left_click":
8974
+ case "right_click":
8975
+ case "middle_click":
8976
+ case "double_click":
8977
+ case "triple_click":
8978
+ case "click": {
8979
+ const point = requiredPoint(input);
8980
+ const button = input.action === "right_click" ? "right" : input.action === "middle_click" ? "middle" : input.action === "click" ? input.button === "wheel" ? "middle" : input.button ?? "left" : "left";
8981
+ const clicks = input.action === "double_click" ? 2 : input.action === "triple_click" ? 3 : 1;
8982
+ return {
8983
+ action: "mouse_click",
8984
+ button,
8985
+ clicks,
8986
+ ...point,
8987
+ keys: keysFor(input)
8988
+ };
8989
+ }
8990
+ case "scroll": {
8991
+ const point = input.coordinate ? { x: input.coordinate[0], y: input.coordinate[1] } : input.x !== void 0 && input.y !== void 0 ? { x: input.x, y: input.y } : {};
8992
+ if (input.scroll_x !== void 0 || input.scroll_y !== void 0) {
8993
+ const toWheelClicks = (value) => value === 0 ? 0 : Math.sign(value) * Math.max(1, Math.round(Math.abs(value) / 100));
8994
+ return {
8995
+ action: "scroll",
8996
+ ...point,
8997
+ scrollX: toWheelClicks(input.scroll_x ?? 0),
8998
+ scrollY: toWheelClicks(input.scroll_y ?? 0),
8999
+ keys: keysFor(input)
9000
+ };
9001
+ }
9002
+ if (input.scroll_amount === void 0 || !input.scroll_direction) {
9003
+ throw new Error(
9004
+ "action=scroll requires scroll_amount and scroll_direction, or scroll_x and scroll_y."
9005
+ );
9006
+ }
9007
+ const amount = Math.max(1, Math.round(Math.abs(input.scroll_amount)));
9008
+ return {
9009
+ action: "scroll",
9010
+ ...point,
9011
+ scrollX: input.scroll_direction === "left" ? -amount : input.scroll_direction === "right" ? amount : 0,
9012
+ scrollY: input.scroll_direction === "up" ? -amount : input.scroll_direction === "down" ? amount : 0,
9013
+ keys: keysFor(input)
9014
+ };
9015
+ }
9016
+ default:
9017
+ throw new Error("action is required.");
9018
+ }
9019
+ }
9020
+ function openaiActionToFlat(item) {
9021
+ const point = {
9022
+ ...item.x !== void 0 ? { x: item.x } : {},
9023
+ ...item.y !== void 0 ? { y: item.y } : {}
9024
+ };
9025
+ switch (item.type) {
9026
+ case "click":
9027
+ return {
9028
+ action: "click",
9029
+ ...item.button ? { button: item.button } : {},
9030
+ ...point,
9031
+ keys: item.keys
9032
+ };
9033
+ case "double_click":
9034
+ return { action: "double_click", ...point, keys: item.keys };
9035
+ case "drag":
9036
+ return { action: "drag", path: item.path, keys: item.keys };
9037
+ case "keypress":
9038
+ return { action: "keypress", keys: item.keys };
9039
+ case "move":
9040
+ return { action: "move", ...point, keys: item.keys };
9041
+ case "screenshot":
9042
+ return { action: "screenshot" };
9043
+ case "scroll":
9044
+ return {
9045
+ action: "scroll",
9046
+ ...point,
9047
+ scroll_x: item.scrollX ?? 0,
9048
+ scroll_y: item.scrollY ?? 0,
9049
+ keys: item.keys
9050
+ };
9051
+ case "type":
9052
+ return { action: "type", text: item.text };
9053
+ case "wait":
9054
+ return { action: "wait" };
9055
+ }
9056
+ }
9057
+ var WINDOWS_POWERSHELL = windows_default2.replace("${WINDOWS.CS}", () => windows_default);
9058
+ function commandError(command, result) {
9059
+ return new Error(
9060
+ `${command} failed: ${result.stderr || result.stdout.toString("utf8").trim() || `exit ${String(result.code)}`}`
9061
+ );
9062
+ }
9063
+ var ComputerUseTool = class {
9064
+ name = "ComputerUse";
9065
+ description;
9066
+ category = "command";
9067
+ deferred = false;
9068
+ displayHeightPx;
9069
+ displayNumber;
9070
+ displayWidthPx;
9071
+ enableZoom;
9072
+ environment;
9073
+ platform;
9074
+ run;
9075
+ coordinateScaleX = 1;
9076
+ coordinateScaleY = 1;
9077
+ macHelperPromise;
9078
+ constructor(options = {}) {
9079
+ this.displayHeightPx = options.displayHeightPx ?? MAX_SCREENSHOT_HEIGHT;
9080
+ this.displayNumber = options.displayNumber;
9081
+ this.displayWidthPx = options.displayWidthPx ?? MAX_SCREENSHOT_WIDTH;
9082
+ this.enableZoom = options.enableZoom ?? true;
9083
+ this.platform = options.platform ?? process.platform;
9084
+ this.environment = options.environment ?? defaultEnvironment(this.platform);
9085
+ this.run = options.runCommand ?? runCommand;
9086
+ this.description = `Control the current ${this.environment} computer with screenshots, mouse, keyboard, scrolling, waiting, and zoom. Use screenshot before choosing coordinates and verify consequential actions with another screenshot. Two call styles are supported: Anthropic-style single actions (action + coordinate/scroll_amount/scroll_direction/start_coordinate/region/text), and OpenAI-style batches (actions[] of typed actions with x/y/button/keys/path/scrollX/scrollY/text, plus pendingSafetyChecks and status; a screenshot is returned after the batch). Flat OpenAI aliases (action=click/drag/keypress/move) are also accepted.`;
9087
+ }
9088
+ isConcurrencySafe(_args) {
9089
+ return false;
9090
+ }
9091
+ providerSchema(protocol) {
9092
+ if (protocol === "anthropic") {
9093
+ return {
9094
+ type: "computer_20251124",
9095
+ name: "computer",
9096
+ display_width_px: this.displayWidthPx,
9097
+ display_height_px: this.displayHeightPx,
9098
+ ...this.displayNumber !== void 0 ? { display_number: this.displayNumber } : {},
9099
+ ...this.enableZoom ? { enable_zoom: true } : {}
9100
+ };
9101
+ }
9102
+ if (protocol === "openai") {
9103
+ return { type: "computer" };
9104
+ }
9105
+ return void 0;
9106
+ }
9107
+ schema() {
9108
+ return {
9109
+ name: this.name,
9110
+ description: this.description,
9111
+ input_schema: {
9112
+ type: "object",
9113
+ properties: {
9114
+ action: {
9115
+ type: "string",
9116
+ enum: ACTIONS,
9117
+ description: "Anthropic-style single computer action. Send either action or actions, not both."
9118
+ },
9119
+ actions: {
9120
+ type: "array",
9121
+ items: {
9122
+ type: "object",
9123
+ properties: {
9124
+ type: {
9125
+ type: "string",
9126
+ enum: OPENAI_ACTION_TYPES,
9127
+ description: "OpenAI-style action type: click (button, x, y, keys), double_click (x, y, keys), drag (path, keys), keypress (keys), move (x, y, keys), screenshot, scroll (x, y, scrollX, scrollY, keys), type (text), wait."
9128
+ },
9129
+ button: {
9130
+ type: "string",
9131
+ enum: ["left", "right", "wheel", "back", "forward"],
9132
+ description: "Button for type=click."
9133
+ },
9134
+ x: { type: "integer", minimum: 0 },
9135
+ y: { type: "integer", minimum: 0 },
9136
+ keys: {
9137
+ type: "array",
9138
+ items: { type: "string" },
9139
+ maxItems: 8,
9140
+ description: "Keys held during the action, or pressed by keypress."
9141
+ },
9142
+ path: {
9143
+ type: "array",
9144
+ items: {
9145
+ type: "object",
9146
+ properties: {
9147
+ x: { type: "integer", minimum: 0 },
9148
+ y: { type: "integer", minimum: 0 }
9149
+ },
9150
+ required: ["x", "y"],
9151
+ additionalProperties: false
9152
+ },
9153
+ minItems: 2,
9154
+ maxItems: 200,
9155
+ description: "Drag path for type=drag."
9156
+ },
9157
+ scrollX: {
9158
+ type: "number",
9159
+ description: "Horizontal scroll delta for type=scroll."
9160
+ },
9161
+ scrollY: {
9162
+ type: "number",
9163
+ description: "Vertical scroll delta for type=scroll."
9164
+ },
9165
+ text: {
9166
+ type: "string",
9167
+ description: "Text to type for type=type."
9168
+ }
9169
+ },
9170
+ required: ["type"],
9171
+ additionalProperties: false
9172
+ },
9173
+ minItems: 1,
9174
+ maxItems: MAX_BATCH_ACTIONS,
9175
+ description: "OpenAI-style ordered batch of computer actions, executed in sequence; a screenshot is returned after the batch."
9176
+ },
9177
+ pendingSafetyChecks: {
9178
+ type: "array",
9179
+ items: {
9180
+ type: "object",
9181
+ properties: {
9182
+ id: { type: "string" },
9183
+ code: { type: "string" },
9184
+ message: { type: "string" }
9185
+ },
9186
+ required: ["id"],
9187
+ additionalProperties: false
9188
+ },
9189
+ description: "OpenAI-style safety checks raised with the batch; acknowledged in the tool result."
9190
+ },
9191
+ status: {
9192
+ type: "string",
9193
+ enum: ["in_progress", "completed", "incomplete"],
9194
+ description: "OpenAI-style status of the computer call; echoed in the tool result."
9195
+ },
9196
+ coordinate: {
9197
+ type: "array",
9198
+ items: { type: "integer", minimum: 0 },
9199
+ minItems: 2,
9200
+ maxItems: 2,
9201
+ description: "Anthropic-style [x, y] coordinate in the latest screenshot space."
9202
+ },
9203
+ duration: {
9204
+ type: "number",
9205
+ minimum: 0,
9206
+ maximum: 60,
9207
+ description: "Seconds for hold_key or wait."
9208
+ },
9209
+ region: {
9210
+ type: "array",
9211
+ items: { type: "integer", minimum: 0 },
9212
+ minItems: 4,
9213
+ maxItems: 4,
9214
+ description: "Zoom region [x1, y1, x2, y2] in the latest screenshot space."
9215
+ },
9216
+ scroll_amount: {
9217
+ type: "number",
9218
+ description: "Anthropic-style number of wheel clicks to scroll."
9219
+ },
9220
+ scroll_direction: {
9221
+ type: "string",
9222
+ enum: ["up", "down", "left", "right"]
9223
+ },
9224
+ start_coordinate: {
9225
+ type: "array",
9226
+ items: { type: "integer", minimum: 0 },
9227
+ minItems: 2,
9228
+ maxItems: 2,
9229
+ description: "Anthropic-style drag start coordinate."
9230
+ },
9231
+ text: {
9232
+ type: "string",
9233
+ description: "Text to type, or a '+'-separated key combination."
9234
+ },
9235
+ x: {
9236
+ type: "integer",
9237
+ minimum: 0,
9238
+ description: "OpenAI-style x coordinate."
9239
+ },
9240
+ y: {
9241
+ type: "integer",
9242
+ minimum: 0,
9243
+ description: "OpenAI-style y coordinate."
9244
+ },
9245
+ button: {
9246
+ type: "string",
9247
+ enum: ["left", "right", "wheel", "middle", "back", "forward"],
9248
+ description: "Button for action=click."
9249
+ },
9250
+ keys: {
9251
+ type: "array",
9252
+ items: { type: "string" },
9253
+ maxItems: 8,
9254
+ description: "OpenAI-style keys held during an action or pressed by keypress."
9255
+ },
9256
+ path: {
9257
+ type: "array",
9258
+ items: {
9259
+ type: "object",
9260
+ properties: {
9261
+ x: { type: "integer", minimum: 0 },
9262
+ y: { type: "integer", minimum: 0 }
9263
+ },
9264
+ required: ["x", "y"],
9265
+ additionalProperties: false
9266
+ },
9267
+ minItems: 2,
9268
+ maxItems: 200,
9269
+ description: "OpenAI-style drag path."
9270
+ },
9271
+ scroll_x: {
9272
+ type: "number",
9273
+ description: "OpenAI-style horizontal scroll delta."
9274
+ },
9275
+ scroll_y: {
9276
+ type: "number",
9277
+ description: "OpenAI-style vertical scroll delta."
9278
+ }
9279
+ },
9280
+ // Either action (Anthropic-style) or actions (OpenAI-style) is required;
9281
+ // execute() enforces the mutual exclusivity JSON Schema cannot express.
9282
+ required: [],
9283
+ additionalProperties: false
9284
+ }
9285
+ };
9286
+ }
9287
+ async execute(ctx, args) {
9288
+ const parsed = safeParse3(ComputerUseInputSchema, args);
9289
+ if (!parsed.success) {
9290
+ return {
9291
+ output: `Error: ${z13.prettifyError(parsed.error)}`,
9292
+ isError: true
9293
+ };
9294
+ }
9295
+ const input = parsed.data;
9296
+ const batch = input.actions ?? [];
9297
+ if (batch.length > 0 && input.action !== void 0) {
9298
+ return {
9299
+ output: "Error: send either action (Anthropic-style, one action per call) or actions (OpenAI-style batch), not both.",
9300
+ isError: true
9301
+ };
9302
+ }
9303
+ if (batch.length === 0 && input.action === void 0) {
9304
+ return {
9305
+ output: "Error: action (Anthropic-style) or actions (OpenAI-style batch) is required.",
9306
+ isError: true
9307
+ };
9308
+ }
9309
+ try {
9310
+ ctx.abortSignal?.throwIfAborted();
9311
+ if (batch.length > 0) {
9312
+ return await this.executeBatch(ctx, input);
9313
+ }
9314
+ return await this.runSingle(ctx, input);
9315
+ } catch (err) {
9316
+ return { output: `Error: ${asErrorString(err)}`, isError: true };
9317
+ }
9318
+ }
9319
+ /** Execute one Anthropic-style action (also the per-item path for batches). */
9320
+ async runSingle(ctx, input) {
9321
+ const action = normalizeAction(input);
9322
+ if (action === "screenshot") {
9323
+ return await this.screenshot(ctx.abortSignal);
9324
+ }
9325
+ if ("region" in action) {
9326
+ return await this.screenshot(ctx.abortSignal, action.region);
9327
+ }
9328
+ if (action.action === "wait") {
9329
+ await delay((action.duration ?? 1) * 1e3, void 0, {
9330
+ signal: ctx.abortSignal
9331
+ });
9332
+ return { output: "Wait completed.", isError: false };
9333
+ }
9334
+ const native = this.toNativeCoordinates(action);
9335
+ let output = await this.executeNative(native, ctx.abortSignal);
9336
+ if (native.action === "cursor_position" && output) {
9337
+ const [x, y] = output.split(",").map(Number);
9338
+ if (Number.isFinite(x) && Number.isFinite(y)) {
9339
+ output = `${String(Math.round(x / this.coordinateScaleX))},${String(Math.round(y / this.coordinateScaleY))}`;
9340
+ }
9341
+ }
9342
+ return {
9343
+ output: output || `Computer action ${input.action ?? "batch"} completed.`,
9344
+ isError: false
9345
+ };
9346
+ }
9347
+ /**
9348
+ * Execute an OpenAI-style ordered action batch. Per the OpenAI computer
9349
+ * output contract, the result always carries the screenshot taken after the
9350
+ * batch ran, and status / safety checks are echoed so the model can continue.
9351
+ */
9352
+ async executeBatch(ctx, input) {
9353
+ const actions = input.actions ?? [];
9354
+ const executed = [];
9355
+ let screenshotResult;
9356
+ for (const [index, item] of actions.entries()) {
9357
+ ctx.abortSignal?.throwIfAborted();
9358
+ let result;
9359
+ try {
9360
+ result = await this.runSingle(ctx, openaiActionToFlat(item));
9361
+ } catch (err) {
9362
+ return {
9363
+ output: `Error at actions[${String(index)}] (${item.type}): ${asErrorString(err)}`,
9364
+ isError: true
9365
+ };
9366
+ }
9367
+ if (result.isError) {
9368
+ return {
9369
+ output: `Error at actions[${String(index)}] (${item.type}): ${result.output}`,
9370
+ contentBlocks: result.contentBlocks,
9371
+ isError: true
9372
+ };
9373
+ }
9374
+ executed.push(item.type);
9375
+ if (item.type === "screenshot") {
9376
+ screenshotResult = result;
9377
+ }
9378
+ }
9379
+ screenshotResult ??= await this.screenshot(ctx.abortSignal).catch(
9380
+ (err) => ({
9381
+ output: `Actions completed, but the follow-up screenshot failed: ${asErrorString(err)}`,
9382
+ isError: false
9383
+ })
9384
+ );
9385
+ const notes = [
9386
+ input.status ? `Status: ${input.status}.` : "",
9387
+ input.pendingSafetyChecks?.length ? `Acknowledged safety checks: ${input.pendingSafetyChecks.map((check) => check.id).join(", ")}.` : ""
9388
+ ].filter(Boolean).join(" ");
9389
+ return {
9390
+ output: `${screenshotResult.output} Executed ${String(executed.length)} action(s): ${executed.join(", ")}.${notes ? ` ${notes}` : ""}`,
9391
+ contentBlocks: screenshotResult.contentBlocks,
9392
+ isError: false
9393
+ };
9394
+ }
9395
+ toNativeCoordinates(action) {
9396
+ const scalePoint = (point) => ({
9397
+ x: Math.round(point.x * this.coordinateScaleX),
9398
+ y: Math.round(point.y * this.coordinateScaleY)
9399
+ });
9400
+ return {
9401
+ ...action,
9402
+ ...action.x !== void 0 && action.y !== void 0 ? scalePoint({ x: action.x, y: action.y }) : {},
9403
+ ...action.path ? { path: action.path.map(scalePoint) } : {}
9404
+ };
9405
+ }
9406
+ async executeNative(action, signal) {
9407
+ switch (this.platform) {
9408
+ case "darwin":
9409
+ return this.executeMac(action, signal);
9410
+ case "win32":
9411
+ return this.executeWindows(action, signal);
9412
+ case "linux":
9413
+ return this.executeLinux(action, signal);
9414
+ default:
9415
+ throw new Error(`ComputerUse is not supported on ${this.platform}.`);
9416
+ }
9417
+ }
9418
+ async executeMac(action, signal) {
9419
+ return this.runMacPayload(
9420
+ action,
9421
+ signal,
9422
+ action.action === "hold_key" ? Math.max(COMMAND_TIMEOUT_MS, (action.duration ?? 0) * 1e3 + 5e3) : COMMAND_TIMEOUT_MS
9423
+ );
9424
+ }
9425
+ async runMacPayload(payload, signal, timeoutMs = COMMAND_TIMEOUT_MS) {
9426
+ const helper = await this.getMacHelper(signal);
9427
+ const result = await this.run(helper, [], {
9428
+ env: {
9429
+ ...process.env,
9430
+ SWIFTY_COMPUTER_INPUT: Buffer.from(JSON.stringify(payload)).toString("base64")
9431
+ },
9432
+ signal,
9433
+ timeoutMs
9434
+ });
9435
+ if (result.code !== 0) {
9436
+ throw commandError("macOS computer helper", result);
9437
+ }
9438
+ return result.stdout.toString("utf8").trim();
9439
+ }
9440
+ async getMacHelper(signal) {
9441
+ this.macHelperPromise ??= this.compileMacHelper(signal);
9442
+ try {
9443
+ return await this.macHelperPromise;
9444
+ } catch (err) {
9445
+ this.macHelperPromise = void 0;
9446
+ throw err;
9447
+ }
9448
+ }
9449
+ async compileMacHelper(signal) {
9450
+ const directory = await mkdtemp(join22(tmpdir(), "swifty-computer-helper-"));
9451
+ const sourcePath = join22(directory, "main.swift");
9452
+ const executablePath = join22(directory, "computer-helper");
9453
+ try {
9454
+ await writeFile3(sourcePath, macos_default, "utf8");
9455
+ const result = await this.run(
9456
+ "/usr/bin/xcrun",
9457
+ ["swiftc", "-O", sourcePath, "-o", executablePath],
9458
+ { signal, timeoutMs: 12e4 }
9459
+ );
9460
+ if (result.code !== 0) {
9461
+ throw commandError("swiftc", result);
9462
+ }
9463
+ return executablePath;
9464
+ } catch (err) {
9465
+ await rm2(directory, { recursive: true, force: true });
9466
+ throw err;
9467
+ }
9468
+ }
9469
+ async executeWindows(action, signal) {
9470
+ const result = await this.run(
9471
+ "powershell.exe",
9472
+ ["-NoProfile", "-NonInteractive", "-Sta", "-Command", WINDOWS_POWERSHELL],
9473
+ {
9474
+ env: {
9475
+ ...process.env,
9476
+ SWIFTY_COMPUTER_INPUT: Buffer.from(JSON.stringify(action)).toString("base64")
9477
+ },
9478
+ signal,
9479
+ timeoutMs: action.action === "hold_key" ? Math.max(COMMAND_TIMEOUT_MS, (action.duration ?? 0) * 1e3 + 5e3) : COMMAND_TIMEOUT_MS
9480
+ }
9481
+ );
9482
+ if (result.code !== 0) {
9483
+ throw commandError("powershell.exe", result);
9484
+ }
9485
+ return result.stdout.toString("utf8").trim();
9486
+ }
9487
+ async executeLinux(action, signal) {
9488
+ const runXdotool = async (args) => {
9489
+ const result = await this.run("xdotool", args, { signal });
9490
+ if (result.code !== 0) {
9491
+ throw commandError("xdotool", result);
9492
+ }
9493
+ return result.stdout.toString("utf8").trim();
9494
+ };
9495
+ const keys = action.keys ?? [];
9496
+ const keyDown = async () => {
9497
+ for (const key of keys) {
9498
+ await runXdotool(["keydown", key]);
9499
+ }
9500
+ };
9501
+ const keyUp = async () => {
9502
+ for (const key of [...keys].reverse()) {
9503
+ await runXdotool(["keyup", key]);
9504
+ }
9505
+ };
9506
+ const withKeys = async (operation) => {
9507
+ await keyDown();
9508
+ try {
9509
+ await operation();
9510
+ } finally {
9511
+ await keyUp();
9512
+ }
9513
+ };
9514
+ const move = async () => {
9515
+ if (action.x !== void 0 && action.y !== void 0) {
9516
+ await runXdotool(["mousemove", "--sync", String(action.x), String(action.y)]);
9517
+ }
9518
+ };
9519
+ switch (action.action) {
9520
+ case "cursor_position": {
9521
+ const output = await runXdotool(["getmouselocation", "--shell"]);
9522
+ const x = /(?:^|\n)X=(\d+)/.exec(output)?.[1];
9523
+ const y = /(?:^|\n)Y=(\d+)/.exec(output)?.[1];
9524
+ if (!x || !y) {
9525
+ throw new Error(`Unable to parse cursor position: ${output}`);
9526
+ }
9527
+ return `${x},${y}`;
9528
+ }
9529
+ case "mouse_move":
9530
+ await withKeys(move);
9531
+ return "";
9532
+ case "left_mouse_down":
9533
+ await runXdotool(["mousedown", "1"]);
9534
+ return "";
9535
+ case "left_mouse_up":
9536
+ await runXdotool(["mouseup", "1"]);
9537
+ return "";
9538
+ case "mouse_click": {
9539
+ await move();
9540
+ const button = action.button === "right" ? 3 : action.button === "middle" ? 2 : action.button === "back" ? 8 : action.button === "forward" ? 9 : 1;
9541
+ await withKeys(async () => {
9542
+ await runXdotool([
9543
+ "click",
9544
+ "--repeat",
9545
+ String(action.clicks ?? 1),
9546
+ "--delay",
9547
+ "80",
9548
+ String(button)
9549
+ ]);
9550
+ });
9551
+ return "";
9552
+ }
9553
+ case "left_click_drag": {
9554
+ const path2 = action.path ?? [];
9555
+ if (path2.length < 2) {
9556
+ throw new Error("Drag path must contain at least two points.");
9557
+ }
9558
+ await withKeys(async () => {
9559
+ await runXdotool(["mousemove", "--sync", String(path2[0].x), String(path2[0].y)]);
9560
+ await runXdotool(["mousedown", "1"]);
8094
9561
  try {
8095
- child.kill(signal);
8096
- } catch {
9562
+ for (const point of path2.slice(1)) {
9563
+ await runXdotool(["mousemove", "--sync", String(point.x), String(point.y)]);
9564
+ }
9565
+ } finally {
9566
+ await runXdotool(["mouseup", "1"]);
8097
9567
  }
8098
- }
8099
- };
8100
- const terminate = () => {
8101
- if (terminating) {
8102
- return;
8103
- }
8104
- terminating = true;
8105
- killTree("SIGTERM");
8106
- escalateTimer = setTimeout(() => {
8107
- killTree("SIGKILL");
8108
- child.stdout.destroy();
8109
- child.stderr.destroy();
8110
- }, KILL_GRACE_MS);
8111
- escalateTimer.unref();
8112
- };
8113
- const appendChunk = (chunk, target) => {
8114
- if (outputTruncated) {
8115
- return;
8116
- }
8117
- const remaining = MAX_SHELL_OUTPUT_BYTES - total;
8118
- const piece = takeUtf8Prefix(chunk, remaining);
8119
- total += utf8ByteLength(piece);
8120
- if (piece.length < chunk.length) {
8121
- outputTruncated = true;
8122
- terminate();
8123
- }
8124
- if (target === "stdout") {
8125
- stdout += piece;
8126
- } else {
8127
- stderr += piece;
8128
- }
8129
- };
8130
- child.stdout.setEncoding("utf-8");
8131
- child.stdout.on("data", (chunk) => {
8132
- appendChunk(chunk, "stdout");
8133
- });
8134
- child.stderr.setEncoding("utf-8");
8135
- child.stderr.on("data", (chunk) => {
8136
- appendChunk(chunk, "stderr");
8137
- });
8138
- const onAbort = () => {
8139
- if (alreadyExited()) {
8140
- return;
8141
- }
8142
- aborted = true;
8143
- terminate();
8144
- };
8145
- const timeoutTimer = setTimeout(() => {
8146
- if (alreadyExited()) {
8147
- return;
8148
- }
8149
- timedOut = true;
8150
- terminate();
8151
- }, timeout * 1e3);
8152
- timeoutTimer.unref();
8153
- ctx.abortSignal?.addEventListener("abort", onAbort, { once: true });
8154
- if (ctx.abortSignal?.aborted) {
8155
- onAbort();
9568
+ });
9569
+ return "";
8156
9570
  }
8157
- const cleanup = () => {
8158
- clearTimeout(timeoutTimer);
8159
- if (escalateTimer) {
8160
- clearTimeout(escalateTimer);
8161
- }
8162
- ctx.abortSignal?.removeEventListener("abort", onAbort);
8163
- };
8164
- child.on("error", (err) => {
8165
- cleanup();
8166
- resolve12({
8167
- output: `Error executing command: ${err.message}`,
8168
- isError: true
9571
+ case "scroll": {
9572
+ await move();
9573
+ await withKeys(async () => {
9574
+ const clicks = [
9575
+ [action.scrollY ?? 0, (action.scrollY ?? 0) < 0 ? 4 : 5],
9576
+ [action.scrollX ?? 0, (action.scrollX ?? 0) < 0 ? 6 : 7]
9577
+ ];
9578
+ for (const [amount, button] of clicks) {
9579
+ if (amount !== 0) {
9580
+ await runXdotool(["click", "--repeat", String(Math.abs(amount)), String(button)]);
9581
+ }
9582
+ }
8169
9583
  });
8170
- });
8171
- child.on("close", (code, signal) => {
8172
- cleanup();
8173
- if (aborted) {
8174
- const captured = stdout || stderr || outputTruncated ? formatShellOutput("$ ", command, stdout, stderr, outputTruncated) : "";
8175
- resolve12({
8176
- output: captured ? `${captured}
8177
- Error: command interrupted` : "Error: command interrupted",
8178
- isError: true
8179
- });
8180
- return;
8181
- }
8182
- if (timedOut) {
8183
- const captured = stdout || stderr || outputTruncated ? formatShellOutput("$ ", command, stdout, stderr, outputTruncated) : "";
8184
- resolve12({
8185
- output: captured ? `${captured}
8186
- Error: command timed out after ${String(timeout)}s` : `Error: command timed out after ${String(timeout)}s`,
8187
- isError: true
8188
- });
8189
- return;
8190
- }
8191
- const exitCode = code ?? 0;
8192
- let output = formatShellOutput("$ ", command, stdout, stderr, outputTruncated);
8193
- if (outputTruncated) {
8194
- resolve12({ output, isError: true });
8195
- return;
9584
+ return "";
9585
+ }
9586
+ case "key":
9587
+ await runXdotool(["key", keys.join("+")]);
9588
+ return "";
9589
+ case "hold_key":
9590
+ await keyDown();
9591
+ try {
9592
+ await delay((action.duration ?? 0) * 1e3, void 0, { signal });
9593
+ } finally {
9594
+ await keyUp();
8196
9595
  }
8197
- if (exitCode !== 0) {
8198
- const hint = exitCodeHint(command, exitCode);
8199
- output += hint ? `
8200
- Exit code ${String(exitCode)} (${hint})` : `
8201
- Exit code ${String(exitCode)}`;
9596
+ return "";
9597
+ case "type":
9598
+ await runXdotool(["type", "--delay", "1", "--", action.text ?? ""]);
9599
+ return "";
9600
+ }
9601
+ }
9602
+ async screenshot(signal, region) {
9603
+ const capture = await this.captureScreenshot(signal);
9604
+ const metadata = await sharp2(capture.bytes).metadata();
9605
+ if (!metadata.width || !metadata.height) {
9606
+ throw new Error("Unable to determine screenshot dimensions.");
9607
+ }
9608
+ if (region) {
9609
+ const left = Math.round(region[0] * this.coordinateScaleX);
9610
+ const top = Math.round(region[1] * this.coordinateScaleY);
9611
+ const right = Math.round(region[2] * this.coordinateScaleX);
9612
+ const bottom = Math.round(region[3] * this.coordinateScaleY);
9613
+ const rawScaleX = metadata.width / capture.width;
9614
+ const rawScaleY = metadata.height / capture.height;
9615
+ const rawLeft = Math.max(0, Math.min(metadata.width - 1, Math.round(left * rawScaleX)));
9616
+ const rawTop = Math.max(0, Math.min(metadata.height - 1, Math.round(top * rawScaleY)));
9617
+ const rawRight = Math.max(
9618
+ rawLeft + 1,
9619
+ Math.min(metadata.width, Math.round(right * rawScaleX))
9620
+ );
9621
+ const rawBottom = Math.max(
9622
+ rawTop + 1,
9623
+ Math.min(metadata.height, Math.round(bottom * rawScaleY))
9624
+ );
9625
+ const cropped = await sharp2(capture.bytes).extract({
9626
+ left: rawLeft,
9627
+ top: rawTop,
9628
+ width: rawRight - rawLeft,
9629
+ height: rawBottom - rawTop
9630
+ }).png({ compressionLevel: 8 }).toBuffer();
9631
+ return this.imageResult(cropped, `Zoomed screenshot of [${region.join(", ")}].`);
9632
+ }
9633
+ const targetScale = Math.min(
9634
+ 1,
9635
+ MAX_SCREENSHOT_WIDTH / capture.width,
9636
+ MAX_SCREENSHOT_HEIGHT / capture.height
9637
+ );
9638
+ const targetWidth = Math.max(1, Math.round(capture.width * targetScale));
9639
+ const targetHeight = Math.max(1, Math.round(capture.height * targetScale));
9640
+ const normalized = await sharp2(capture.bytes).resize(targetWidth, targetHeight, { fit: "fill" }).png({ compressionLevel: 8 }).toBuffer();
9641
+ const result = await this.imageResult(
9642
+ normalized,
9643
+ `Screenshot ${String(targetWidth)}x${String(targetHeight)}. Use this coordinate space for subsequent actions.`
9644
+ );
9645
+ const outputBlock = result.contentBlocks?.[0];
9646
+ if (outputBlock?.type === "image" && outputBlock.source.type === "base64") {
9647
+ const finalMetadata = await sharp2(Buffer.from(outputBlock.source.data, "base64")).metadata();
9648
+ if (finalMetadata.width && finalMetadata.height) {
9649
+ this.coordinateScaleX = capture.width / finalMetadata.width;
9650
+ this.coordinateScaleY = capture.height / finalMetadata.height;
9651
+ result.output = `Screenshot ${String(finalMetadata.width)}x${String(finalMetadata.height)}. Use this coordinate space for subsequent actions.`;
9652
+ }
9653
+ }
9654
+ return result;
9655
+ }
9656
+ async imageResult(bytes, output) {
9657
+ const image = await maybeResizeAndDownsampleImage(bytes, "image/png");
9658
+ const imageBlock = {
9659
+ type: "image",
9660
+ source: {
9661
+ type: "base64",
9662
+ media_type: image.mediaType,
9663
+ data: image.data
9664
+ }
9665
+ };
9666
+ return { output, contentBlocks: [imageBlock], isError: false };
9667
+ }
9668
+ async captureScreenshot(signal) {
9669
+ const directory = await mkdtemp(join22(tmpdir(), "swifty-computer-"));
9670
+ const screenshotPath = join22(directory, "screenshot.png");
9671
+ try {
9672
+ switch (this.platform) {
9673
+ case "darwin": {
9674
+ const capture = await this.run(
9675
+ "/usr/sbin/screencapture",
9676
+ ["-x", "-m", "-t", "png", screenshotPath],
9677
+ { signal }
9678
+ );
9679
+ if (capture.code !== 0) {
9680
+ throw commandError("screencapture", capture);
9681
+ }
9682
+ const size = await this.runMacPayload({ action: "screen_size" }, signal).catch(() => "");
9683
+ const [width, height] = size.split(",").map(Number);
9684
+ const bytes = await readFile5(screenshotPath);
9685
+ const metadata = await sharp2(bytes).metadata();
9686
+ return {
9687
+ bytes,
9688
+ width: Number.isFinite(width) && width > 0 ? width : metadata.width ?? 1,
9689
+ height: Number.isFinite(height) && height > 0 ? height : metadata.height ?? 1
9690
+ };
8202
9691
  }
8203
- if (code === null) {
8204
- output += `
8205
- Process terminated${signal ? ` by ${signal}` : " unexpectedly"}`;
9692
+ case "win32": {
9693
+ const capture = await this.run(
9694
+ "powershell.exe",
9695
+ ["-NoProfile", "-NonInteractive", "-Sta", "-Command", win32_script_default],
9696
+ {
9697
+ env: { ...process.env, SWIFTY_SCREENSHOT_PATH: screenshotPath },
9698
+ signal
9699
+ }
9700
+ );
9701
+ if (capture.code !== 0) {
9702
+ throw commandError("powershell.exe", capture);
9703
+ }
9704
+ const [width, height] = capture.stdout.toString("utf8").trim().split(",").map(Number);
9705
+ return { bytes: await readFile5(screenshotPath), width, height };
9706
+ }
9707
+ case "linux": {
9708
+ const backends = [
9709
+ ["gnome-screenshot", ["-f", screenshotPath]],
9710
+ ["scrot", [screenshotPath]],
9711
+ ["import", ["-window", "root", screenshotPath]]
9712
+ ];
9713
+ let lastError = "No screenshot backend succeeded.";
9714
+ for (const [command, args] of backends) {
9715
+ try {
9716
+ const capture = await this.run(command, args, { signal });
9717
+ if (capture.code === 0) {
9718
+ const bytes = await readFile5(screenshotPath);
9719
+ const metadata = await sharp2(bytes).metadata();
9720
+ return {
9721
+ bytes,
9722
+ width: metadata.width ?? 1,
9723
+ height: metadata.height ?? 1
9724
+ };
9725
+ }
9726
+ lastError = commandError(command, capture).message;
9727
+ } catch (err) {
9728
+ lastError = asErrorString(err);
9729
+ }
9730
+ }
9731
+ throw new Error(
9732
+ `${lastError} Install gnome-screenshot, scrot, or ImageMagick; xdotool is required for input control.`
9733
+ );
8206
9734
  }
8207
- resolve12({ output, isError: exitCode !== 0 || code === null });
8208
- });
8209
- });
9735
+ default:
9736
+ throw new Error(`ComputerUse is not supported on ${this.platform}.`);
9737
+ }
9738
+ } finally {
9739
+ await rm2(directory, { recursive: true, force: true });
9740
+ }
8210
9741
  }
8211
9742
  };
8212
9743
 
8213
9744
  // src/tools/powershell.ts
8214
- import { execFile as execFile2, spawn as spawn4 } from "child_process";
9745
+ import { execFile as execFile2, spawn as spawn5 } from "child_process";
8215
9746
  var MAX_TIMEOUT2 = 600;
8216
9747
  var KILL_GRACE_MS2 = 3e3;
8217
- function extractBaseCmd2(command) {
8218
- const lastSegment = command.split("|").pop()?.trim() ?? command;
8219
- const tokens = lastSegment.split(/\s+/);
8220
- for (const token of tokens) {
8221
- if (token.includes("=") && !token.startsWith("-")) {
8222
- continue;
8223
- }
8224
- const base = token.split(/[\\/]/).pop() ?? token;
8225
- return base.replace(/\.exe$/i, "").toLowerCase();
8226
- }
8227
- return "";
8228
- }
8229
- var exitCodeHints2 = /* @__PURE__ */ new Map([
8230
- ["findstr", /* @__PURE__ */ new Map([[1, "no matches found"]])],
8231
- ["where", /* @__PURE__ */ new Map([[1, "not found"]])],
8232
- ["grep", /* @__PURE__ */ new Map([[1, "no matches found"]])],
8233
- ["rg", /* @__PURE__ */ new Map([[1, "no matches found"]])],
8234
- ["diff", /* @__PURE__ */ new Map([[1, "files differ"]])]
8235
- ]);
8236
- function exitCodeHint2(command, exitCode) {
8237
- const baseCmd = extractBaseCmd2(command);
8238
- const hints = exitCodeHints2.get(baseCmd);
8239
- return hints?.get(exitCode) ?? "";
8240
- }
8241
9748
  var PowerShellTool = class {
8242
9749
  // Use a hardcoded string instead of PowerShellTool.name.replace("Tool", "")
8243
9750
  // because class names are not stable after minification — bundlers like
@@ -8287,7 +9794,10 @@ var PowerShellTool = class {
8287
9794
  }
8288
9795
  const shell = process.platform === "win32" ? "powershell.exe" : "pwsh";
8289
9796
  if (ctx.abortSignal?.aborted) {
8290
- return Promise.resolve({ output: "Error: command interrupted", isError: true });
9797
+ return Promise.resolve({
9798
+ output: "Error: command interrupted",
9799
+ isError: true
9800
+ });
8291
9801
  }
8292
9802
  return new Promise((resolve12) => {
8293
9803
  let timedOut = false;
@@ -8303,7 +9813,7 @@ var PowerShellTool = class {
8303
9813
  "-Command",
8304
9814
  shellCommand
8305
9815
  ] : ["-NoProfile", "-NonInteractive", "-Command", shellCommand];
8306
- const child = spawn4(shell, shellArgs, {
9816
+ const child = spawn5(shell, shellArgs, {
8307
9817
  cwd: ctx.workDir,
8308
9818
  // Only POSIX needs its own process group for kill(-pid); the Windows
8309
9819
  // tree kill goes through taskkill and needs no new group.
@@ -8382,16 +9892,10 @@ var PowerShellTool = class {
8382
9892
  appendChunk(chunk, "stderr");
8383
9893
  });
8384
9894
  const onAbort = () => {
8385
- if (alreadyExited()) {
8386
- return;
8387
- }
8388
9895
  aborted = true;
8389
9896
  terminate();
8390
9897
  };
8391
9898
  const timeoutTimer = setTimeout(() => {
8392
- if (alreadyExited()) {
8393
- return;
8394
- }
8395
9899
  timedOut = true;
8396
9900
  terminate();
8397
9901
  }, timeout * 1e3);
@@ -8442,7 +9946,7 @@ Error: command timed out after ${String(timeout)}s` : `Error: command timed out
8442
9946
  return;
8443
9947
  }
8444
9948
  if (exitCode !== 0) {
8445
- const hint = exitCodeHint2(command, exitCode);
9949
+ const hint = exitCodeHint(command, exitCode);
8446
9950
  output += hint ? `
8447
9951
  Exit code ${String(exitCode)} (${hint})` : `
8448
9952
  Exit code ${String(exitCode)}`;
@@ -8542,7 +10046,7 @@ var SyntheticOutputTool = class {
8542
10046
 
8543
10047
  // src/tools/tool-search.ts
8544
10048
  var ToolSearchTool = class {
8545
- name = TOOL_SEARCH_TOOL_NAME;
10049
+ name = "ToolSearch";
8546
10050
  description = "Search for and load deferred tools by name or keyword.";
8547
10051
  category = "read";
8548
10052
  registry;
@@ -8585,7 +10089,10 @@ var ToolSearchTool = class {
8585
10089
  query.slice("select:".length).split(",").map((name) => name.trim()).filter(Boolean)
8586
10090
  ) : this.registry.searchDeferred(query, Math.max(1, Math.min(maxResults, 50)));
8587
10091
  if (tools.length === 0) {
8588
- return Promise.resolve({ output: "No deferred tools matched the query.", isError: false });
10092
+ return Promise.resolve({
10093
+ output: "No deferred tools matched the query.",
10094
+ isError: false
10095
+ });
8589
10096
  }
8590
10097
  const mcp = tools.filter((tool) => tool.name.startsWith(MCP_TOOL_PREFIX));
8591
10098
  const local = tools.filter((tool) => !tool.name.startsWith(MCP_TOOL_PREFIX));
@@ -8657,6 +10164,7 @@ async function runPrintMode(args) {
8657
10164
  registry.register(new ReadFileTool());
8658
10165
  registry.register(new BashTool());
8659
10166
  registry.register(new PowerShellTool());
10167
+ registry.register(new ComputerUseTool());
8660
10168
  registry.register(new GlobTool());
8661
10169
  registry.register(new GrepTool());
8662
10170
  registry.register(new WriteFileTool());
@@ -8881,9 +10389,9 @@ function emitStreamJson(event) {
8881
10389
 
8882
10390
  // src/recover.ts
8883
10391
  import { appendFileSync, mkdirSync as mkdirSync14 } from "fs";
8884
- import { join as join22 } from "path";
10392
+ import { join as join23 } from "path";
8885
10393
  var LOG_DIR = ".swifty";
8886
- var LOG_PATH = join22(LOG_DIR, "crash.log");
10394
+ var LOG_PATH = join23(LOG_DIR, "crash.log");
8887
10395
  function record(text) {
8888
10396
  try {
8889
10397
  mkdirSync14(LOG_DIR, { recursive: true });
@@ -8977,11 +10485,45 @@ var AgentEventLogger = class {
8977
10485
  // src/remote/server.ts
8978
10486
  import { readFileSync as readFileSync23, existsSync as existsSync20, statSync as statSync11 } from "fs";
8979
10487
  import { createServer } from "http";
8980
- import { join as join26, extname as extname3, normalize } from "path";
10488
+ import { join as join27, extname as extname3, normalize } from "path";
8981
10489
  import { cwd } from "process";
8982
10490
  import Koa from "koa";
8983
10491
  import { WebSocketServer, WebSocket } from "ws";
8984
- import z18 from "zod";
10492
+ import z19 from "zod";
10493
+
10494
+ // src/remote/address.ts
10495
+ function parseRemoteAddress(address) {
10496
+ const value = address.trim();
10497
+ const match = /^(?:\[([^\]]+)\]|([^:]*))(?::(\d+))?$/.exec(value);
10498
+ if (!match) {
10499
+ throw new Error("Invalid remote address; use host:port or [IPv6]:port");
10500
+ }
10501
+ const host = match[1] || match[2] || "127.0.0.1";
10502
+ const port = Number(match[3] ?? "18888");
10503
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
10504
+ throw new Error("Remote port must be an integer between 1 and 65535");
10505
+ }
10506
+ return { host, port };
10507
+ }
10508
+
10509
+ // src/remote/session-state.ts
10510
+ function restoreRemoteSession(state, sessionId, saved) {
10511
+ const replay = rebuildFromSession(saved);
10512
+ state.conv.reset();
10513
+ state.conv.appendMessages(
10514
+ replay.map((message) => ({
10515
+ ...message,
10516
+ toolUses: message.toolUses?.map((tool) => ({ ...tool, arguments: tool.arguments ?? {} }))
10517
+ }))
10518
+ );
10519
+ state.sessionId = sessionId;
10520
+ state.fileHistory = new FileHistory(state.workDir, sessionId);
10521
+ state.fileStateCache = new FileStateCache();
10522
+ state.recoveryState = new RecoveryState();
10523
+ state.activeSkills.clear();
10524
+ state.toolFilter = null;
10525
+ return replay;
10526
+ }
8985
10527
 
8986
10528
  // src/config/provider-login.ts
8987
10529
  import { randomUUID } from "crypto";
@@ -8993,22 +10535,22 @@ import {
8993
10535
  unlinkSync as unlinkSync5,
8994
10536
  writeFileSync as writeFileSync13
8995
10537
  } from "fs";
8996
- import { dirname as dirname8, join as join23 } from "path";
10538
+ import { dirname as dirname8, join as join24 } from "path";
8997
10539
  import yaml5 from "js-yaml";
8998
- import { z as z13 } from "zod";
8999
- var tokenLimit = (fallback, min, max) => z13.preprocess(
10540
+ import { z as z14 } from "zod";
10541
+ var tokenLimit = (fallback, min, max) => z14.preprocess(
9000
10542
  (value) => value === void 0 || typeof value === "string" && !value.trim() ? fallback : value,
9001
- z13.union([
9002
- z13.number(),
9003
- z13.string().trim().regex(/^\d+$/, "Enter a whole number").transform(Number)
9004
- ]).pipe(z13.number().int().min(min).max(max))
10543
+ z14.union([
10544
+ z14.number(),
10545
+ z14.string().trim().regex(/^\d+$/, "Enter a whole number").transform(Number)
10546
+ ]).pipe(z14.number().int().min(min).max(max))
9005
10547
  );
9006
10548
  var ProviderLoginSchema = ProviderConfigSchema.extend({
9007
- name: z13.string().trim().min(1, "Name is required"),
9008
- base_url: z13.string().trim().url("Enter a valid HTTP(S) URL").refine((url) => /^https?:\/\//i.test(url), "Use an HTTP(S) URL"),
9009
- api_key: z13.string().trim().min(1, "API key is required"),
9010
- model: z13.string().trim().min(1, "Model is required"),
9011
- thinking: z13.enum(THINKING_LEVELS).optional(),
10549
+ name: z14.string().trim().min(1, "Name is required"),
10550
+ base_url: z14.url("Enter a valid HTTP(S) URL").refine((url) => /^https?:\/\//i.test(url), "Use an HTTP(S) URL"),
10551
+ api_key: z14.string().trim().min(1, "API key is required"),
10552
+ model: z14.string().trim().min(1, "Model is required"),
10553
+ thinking: z14.enum(THINKING_LEVELS).optional(),
9012
10554
  context_window: tokenLimit(DEFAULT_CONTEXT_WINDOW, 1e3, 1e7),
9013
10555
  max_output_tokens: tokenLimit(DEFAULT_MAX_OUTPUT_TOKENS, 1, 1e6)
9014
10556
  }).refine((provider) => provider.max_output_tokens <= provider.context_window, {
@@ -9028,12 +10570,12 @@ function readConfigRaw(path2) {
9028
10570
  } catch {
9029
10571
  throw new Error(`Unable to read existing ${path2}; it has not been changed.`);
9030
10572
  }
9031
- return z13.record(z13.string(), z13.unknown()).parse(raw ?? {});
10573
+ return z14.record(z14.string(), z14.unknown()).parse(raw ?? {});
9032
10574
  }
9033
10575
  function writeConfigAtomic(path2, raw) {
9034
10576
  const directory = dirname8(path2);
9035
10577
  mkdirSync15(directory, { recursive: true });
9036
- const temporary = join23(directory, `.config-${randomUUID()}.tmp`);
10578
+ const temporary = join24(directory, `.config-${randomUUID()}.tmp`);
9037
10579
  try {
9038
10580
  writeFileSync13(temporary, yaml5.dump(raw, { lineWidth: -1, noRefs: true }), {
9039
10581
  encoding: "utf-8",
@@ -9050,7 +10592,7 @@ function writeConfigAtomic(path2, raw) {
9050
10592
  function persistThinkingLevel(providerName, level) {
9051
10593
  const path2 = globalConfigPath();
9052
10594
  const config = readConfigRaw(path2);
9053
- const providers = z13.array(z13.record(z13.string(), z13.unknown())).parse(config.providers ?? []);
10595
+ const providers = z14.array(z14.record(z14.string(), z14.unknown())).parse(config.providers ?? []);
9054
10596
  const target = providers.find((entry) => entry.name === providerName);
9055
10597
  if (!target) {
9056
10598
  throw new Error(`Provider "${providerName}" not found in ${path2}.`);
@@ -9065,9 +10607,9 @@ function persistThinkingLevel(providerName, level) {
9065
10607
  // src/skills/catalog.ts
9066
10608
  import { readdirSync as readdirSync5, readFileSync as readFileSync21, existsSync as existsSync18, statSync as statSync10 } from "fs";
9067
10609
  import { homedir as homedir9 } from "os";
9068
- import { join as join24 } from "path";
10610
+ import { join as join25 } from "path";
9069
10611
  import yaml6 from "js-yaml";
9070
- import { z as z14 } from "zod";
10612
+ import { z as z15 } from "zod";
9071
10613
  var log30 = createChildLogger({ module: "skills" });
9072
10614
  var SkillCatalog = class {
9073
10615
  entries = /* @__PURE__ */ new Map();
@@ -9118,7 +10660,7 @@ var SkillCatalog = class {
9118
10660
  }
9119
10661
  skillDirPaths() {
9120
10662
  return [homedir9(), ...this.workDir ? [this.workDir] : []].flatMap(
9121
- (root) => [".agents"].map((ecosystem) => join24(root, ecosystem, "skills"))
10663
+ (root) => [".agents"].map((ecosystem) => join25(root, ecosystem, "skills"))
9122
10664
  );
9123
10665
  }
9124
10666
  scanDirectory(dir) {
@@ -9130,12 +10672,12 @@ var SkillCatalog = class {
9130
10672
  return;
9131
10673
  }
9132
10674
  for (const entry of dirEntries) {
9133
- const fullPath = join24(dir, entry);
10675
+ const fullPath = join25(dir, entry);
9134
10676
  try {
9135
10677
  const stat3 = statSync10(fullPath);
9136
10678
  if (stat3.isDirectory()) {
9137
10679
  this.dirModTimes.set(fullPath, stat3.mtimeMs);
9138
- const skillFile = join24(fullPath, "SKILL.md");
10680
+ const skillFile = join25(fullPath, "SKILL.md");
9139
10681
  if (existsSync18(skillFile)) {
9140
10682
  this.loadSkill(skillFile, fullPath, true);
9141
10683
  }
@@ -9223,12 +10765,12 @@ function resolveMode(raw) {
9223
10765
  }
9224
10766
  return strArg(asRecord(raw), "context") === "fork" ? "fork" : "inline";
9225
10767
  }
9226
- var YamlFrontmatterSchema3 = z14.looseObject({
9227
- name: z14.string().trim().min(1),
9228
- description: z14.string().optional(),
9229
- mode: z14.enum(["inline", "fork"]).optional(),
9230
- model: z14.string().optional(),
9231
- fork_context: z14.enum(["full", "none", "recent"]).optional()
10768
+ var YamlFrontmatterSchema3 = z15.looseObject({
10769
+ name: z15.string().trim().min(1),
10770
+ description: z15.string().optional(),
10771
+ mode: z15.enum(["inline", "fork"]).optional(),
10772
+ model: z15.string().optional(),
10773
+ fork_context: z15.enum(["full", "none", "recent"]).optional()
9232
10774
  });
9233
10775
  function parseSkillFile(content) {
9234
10776
  const normalized = content.replace(/^\uFEFF/, "");
@@ -9264,7 +10806,7 @@ function buildSkillSection(catalog, workDir) {
9264
10806
  if (metas.length === 0) {
9265
10807
  return "";
9266
10808
  }
9267
- const skillsDir = join24(workDir, ".agents", "skills");
10809
+ const skillsDir = join25(workDir, ".agents", "skills");
9268
10810
  const lines = [
9269
10811
  "## Skills",
9270
10812
  'Load relevant instructions with LoadSkill {name: "<skill-name>"}, or user command /<skill-name>. Mode inline activates in this conversation; fork runs in a subagent when available, otherwise inline. Load resources only as needed, relative to the skill directory. Tool access remains host-controlled.',
@@ -9398,26 +10940,26 @@ ${body}`,
9398
10940
 
9399
10941
  // src/todo/store.ts
9400
10942
  import { readFileSync as readFileSync22, writeFileSync as writeFileSync14, mkdirSync as mkdirSync16, existsSync as existsSync19 } from "fs";
9401
- import { join as join25, dirname as dirname9 } from "path";
9402
- import z15, { parse as parse8 } from "zod";
10943
+ import { join as join26, dirname as dirname9 } from "path";
10944
+ import z16, { parse as parse8 } from "zod";
9403
10945
  var log31 = createChildLogger({ module: "todo" });
9404
- var TaskStatusSchema = z15.enum(["pending", "in_progress", "completed"]);
9405
- var TaskSchema = z15.object({
9406
- id: z15.string(),
9407
- subject: z15.string(),
9408
- description: z15.string(),
10946
+ var TaskStatusSchema = z16.enum(["pending", "in_progress", "completed"]);
10947
+ var TaskSchema = z16.object({
10948
+ id: z16.string(),
10949
+ subject: z16.string(),
10950
+ description: z16.string(),
9409
10951
  status: TaskStatusSchema,
9410
- owner: z15.string().optional(),
9411
- activeForm: z15.string().optional(),
9412
- blocks: z15.array(z15.string()),
9413
- blockedBy: z15.array(z15.string()),
9414
- metadata: z15.record(z15.string(), z15.unknown())
10952
+ owner: z16.string().optional(),
10953
+ activeForm: z16.string().optional(),
10954
+ blocks: z16.array(z16.string()),
10955
+ blockedBy: z16.array(z16.string()),
10956
+ metadata: z16.record(z16.string(), z16.unknown())
9415
10957
  });
9416
10958
  var TaskStore = class {
9417
10959
  filePath;
9418
10960
  // Session-scoped store: .swifty/tasks/<listId>.json.
9419
10961
  constructor(workDir, listId) {
9420
- this.filePath = join25(workDir, ".swifty", "tasks", `${listId}.json`);
10962
+ this.filePath = join26(workDir, ".swifty", "tasks", `${listId}.json`);
9421
10963
  }
9422
10964
  load() {
9423
10965
  if (!existsSync19(this.filePath)) {
@@ -9426,7 +10968,7 @@ var TaskStore = class {
9426
10968
  try {
9427
10969
  const data = readFileSync22(this.filePath, "utf-8");
9428
10970
  const raw = JSON.parse(data);
9429
- const parsed = parse8(z15.array(TaskSchema), raw);
10971
+ const parsed = parse8(z16.array(TaskSchema), raw);
9430
10972
  return parsed;
9431
10973
  } catch (err) {
9432
10974
  log31.error({ err }, "todo operation failed");
@@ -9545,7 +11087,7 @@ var TaskList = class {
9545
11087
 
9546
11088
  // src/todo/tools.ts
9547
11089
  import { safeParseAsync } from "@modelcontextprotocol/sdk/server/zod-compat.js";
9548
- import z16 from "zod";
11090
+ import z17 from "zod";
9549
11091
  var TaskCreateTool2 = class {
9550
11092
  name = "TaskCreate";
9551
11093
  description = "Create a new task to track work.";
@@ -9732,27 +11274,27 @@ var TaskUpdateTool2 = class {
9732
11274
  return { output: `Updated task #${taskId} status`, isError: false };
9733
11275
  }
9734
11276
  };
9735
- var TaskUpdateArgsSchema = z16.object({
9736
- taskId: z16.string(),
9737
- subject: z16.string().optional(),
9738
- description: z16.string().optional(),
9739
- status: z16.enum(["pending", "in_progress", "completed", "deleted"]).optional(),
9740
- owner: z16.string().optional(),
9741
- addBlocks: z16.array(z16.string()).optional(),
9742
- addBlockedBy: z16.array(z16.string()).optional()
11277
+ var TaskUpdateArgsSchema = z17.object({
11278
+ taskId: z17.string(),
11279
+ subject: z17.string().optional(),
11280
+ description: z17.string().optional(),
11281
+ status: z17.enum(["pending", "in_progress", "completed", "deleted"]).optional(),
11282
+ owner: z17.string().optional(),
11283
+ addBlocks: z17.array(z17.string()).optional(),
11284
+ addBlockedBy: z17.array(z17.string()).optional()
9743
11285
  });
9744
11286
 
9745
11287
  // src/tools/ask-user.ts
9746
- import { safeParseAsync as safeParseAsync2, z as z17 } from "zod";
9747
- var QuestionOptionSchema = z17.object({
9748
- label: z17.string(),
9749
- description: z17.string().optional()
11288
+ import { safeParseAsync as safeParseAsync2, z as z18 } from "zod";
11289
+ var QuestionOptionSchema = z18.object({
11290
+ label: z18.string(),
11291
+ description: z18.string().optional()
9750
11292
  });
9751
- var QuestionSchema = z17.object({
9752
- question: z17.string(),
9753
- header: z17.string(),
9754
- options: z17.array(QuestionOptionSchema),
9755
- multiSelect: z17.boolean()
11293
+ var QuestionSchema = z18.object({
11294
+ question: z18.string(),
11295
+ header: z18.string(),
11296
+ options: z18.array(QuestionOptionSchema),
11297
+ multiSelect: z18.boolean()
9756
11298
  });
9757
11299
  var AskUserQuestionTool = class {
9758
11300
  constructor(ask) {
@@ -9834,7 +11376,7 @@ var AskUserQuestionTool = class {
9834
11376
  success,
9835
11377
  data: argsData,
9836
11378
  error
9837
- } = await safeParseAsync2(z17.object({ questions: z17.array(QuestionSchema) }), args);
11379
+ } = await safeParseAsync2(z18.object({ questions: z18.array(QuestionSchema) }), args);
9838
11380
  if (!success) {
9839
11381
  return {
9840
11382
  output: error.message,
@@ -10047,58 +11589,24 @@ Branch: ${branch}`,
10047
11589
  }
10048
11590
  };
10049
11591
 
10050
- // src/remote/address.ts
10051
- function parseRemoteAddress(address) {
10052
- const value = address.trim();
10053
- const match = /^(?:\[([^\]]+)\]|([^:]*))(?::(\d+))?$/.exec(value);
10054
- if (!match) {
10055
- throw new Error("Invalid remote address; use host:port or [IPv6]:port");
10056
- }
10057
- const host = match[1] || match[2] || "127.0.0.1";
10058
- const port = Number(match[3] ?? "18888");
10059
- if (!Number.isInteger(port) || port < 1 || port > 65535) {
10060
- throw new Error("Remote port must be an integer between 1 and 65535");
10061
- }
10062
- return { host, port };
10063
- }
10064
-
10065
- // src/remote/session-state.ts
10066
- function restoreRemoteSession(state, sessionId, saved) {
10067
- const replay = rebuildFromSession(saved);
10068
- state.conv.reset();
10069
- state.conv.appendMessages(
10070
- replay.map((message) => ({
10071
- ...message,
10072
- toolUses: message.toolUses?.map((tool) => ({ ...tool, arguments: tool.arguments ?? {} }))
10073
- }))
10074
- );
10075
- state.sessionId = sessionId;
10076
- state.fileHistory = new FileHistory(state.workDir, sessionId);
10077
- state.fileStateCache = new FileStateCache();
10078
- state.recoveryState = new RecoveryState();
10079
- state.activeSkills.clear();
10080
- state.toolFilter = null;
10081
- return replay;
10082
- }
10083
-
10084
11592
  // src/remote/server.ts
10085
11593
  var log34 = createChildLogger({ module: "remote" });
10086
- var WsInboundSchema = z18.object({
10087
- type: z18.string(),
10088
- data: z18.unknown()
11594
+ var WsInboundSchema = z19.object({
11595
+ type: z19.string(),
11596
+ data: z19.unknown()
10089
11597
  });
10090
- var UserMessageSchema = z18.object({
10091
- content: z18.string()
11598
+ var UserMessageSchema = z19.object({
11599
+ content: z19.string()
10092
11600
  });
10093
- var PermissionResponseSchema = z18.object({
10094
- id: z18.string(),
10095
- response: z18.enum(["allow", "deny", "allowAlways"])
11601
+ var PermissionResponseSchema = z19.object({
11602
+ id: z19.string(),
11603
+ response: z19.enum(["allow", "deny", "allowAlways"])
10096
11604
  });
10097
- var AskUserResponseSchema = z18.object({
10098
- id: z18.string(),
10099
- answers: z18.record(z18.string(), z18.string())
11605
+ var AskUserResponseSchema = z19.object({
11606
+ id: z19.string(),
11607
+ answers: z19.record(z19.string(), z19.string())
10100
11608
  });
10101
- var FE_DIST = join26(import.meta.dirname, "fe", "dist");
11609
+ var FE_DIST = join27(import.meta.dirname, "fe", "dist");
10102
11610
  var MIME_TYPES = {
10103
11611
  ".html": "text/html; charset=utf-8",
10104
11612
  ".js": "application/javascript; charset=utf-8",
@@ -10111,7 +11619,7 @@ var MIME_TYPES = {
10111
11619
  };
10112
11620
  function serveStatic(path2) {
10113
11621
  const cleanPath = normalize(path2).replace(/^(\.\.[/\\])+/, "");
10114
- const fullPath = join26(FE_DIST, cleanPath);
11622
+ const fullPath = join27(FE_DIST, cleanPath);
10115
11623
  if (!fullPath.startsWith(FE_DIST)) {
10116
11624
  return null;
10117
11625
  }
@@ -10282,13 +11790,13 @@ async function createRemoteAgent(opts) {
10282
11790
  if (!client) {
10283
11791
  throw new Error("no llm client (provider not initialized)");
10284
11792
  }
10285
- const { PermissionChecker: PC } = await import("./checker-BS4MK3O6.js");
10286
- const { Agent: AgentClass } = await import("./agent-IRY7KR5D.js");
11793
+ const { PermissionChecker: PC } = await import("./checker-M7RKMKJQ.js");
11794
+ const { Agent: AgentClass } = await import("./agent-YRTROHXU.js");
10287
11795
  const subConv = new ConversationManager();
10288
11796
  subConv.addUserMessage(prompt);
10289
11797
  const subAgent = new AgentClass({
10290
11798
  client,
10291
- registry,
11799
+ registry: filterToolsForAgent(registry, void 0, void 0, false),
10292
11800
  checker: new PC(workDir, "acceptEdits"),
10293
11801
  conversation: subConv,
10294
11802
  workDir,
@@ -10461,6 +11969,7 @@ function buildToolRegistry(workDir, sessionId) {
10461
11969
  registry.register(new ReadFileTool());
10462
11970
  registry.register(new BashTool());
10463
11971
  registry.register(new PowerShellTool());
11972
+ registry.register(new ComputerUseTool());
10464
11973
  registry.register(new GlobTool());
10465
11974
  registry.register(new GrepTool());
10466
11975
  registry.register(new WriteFileTool());
@@ -10492,6 +12001,7 @@ function wireSkillsToCommands(catalog, skillHost, cmdRegistry) {
10492
12001
  aliases: [],
10493
12002
  type: isFork ? "skill_fork" : "prompt",
10494
12003
  description: `${meta.description} [skill]`,
12004
+ isSkill: true,
10495
12005
  handler: isFork ? () => "" : (ctx) => runInline(skill, ctx.args, skillHost)
10496
12006
  });
10497
12007
  } catch {
@@ -11014,7 +12524,9 @@ ${lines.join("\n")}`
11014
12524
  handle.client.setThinkingLevel?.(level);
11015
12525
  handle.provider.thinking = handle.client.getThinkingLevel?.() ?? level;
11016
12526
  } : void 0,
11017
- persistThinkingLevel: (level) => persistThinkingLevel(handle.provider.name, level)
12527
+ persistThinkingLevel: (level) => {
12528
+ persistThinkingLevel(handle.provider.name, level);
12529
+ }
11018
12530
  };
11019
12531
  }
11020
12532
  /** Handles /compact command: force context compaction. */
@@ -11304,7 +12816,7 @@ Explore the codebase and design your approach.`
11304
12816
  };
11305
12817
 
11306
12818
  // src/teammate.ts
11307
- import { basename as basename6, dirname as dirname10, join as join28 } from "path";
12819
+ import { basename as basename6, dirname as dirname10, join as join29 } from "path";
11308
12820
 
11309
12821
  // src/skills/install-tool.ts
11310
12822
  import { randomUUID as randomUUID2 } from "crypto";
@@ -11319,7 +12831,7 @@ import {
11319
12831
  renameSync as renameSync2,
11320
12832
  rmSync as rmSync2
11321
12833
  } from "fs";
11322
- import { join as join27, resolve as resolve10 } from "path";
12834
+ import { join as join28, resolve as resolve10 } from "path";
11323
12835
  import yaml7 from "js-yaml";
11324
12836
  var log35 = createChildLogger({ module: "skills" });
11325
12837
  var InstallSkillTool = class {
@@ -11411,7 +12923,7 @@ ${parsed.body}
11411
12923
  ctx.abortSignal?.throwIfAborted();
11412
12924
  let dir = realpathSync(this.workDir);
11413
12925
  for (const segment of [".agents", "skills", name]) {
11414
- dir = join27(dir, segment);
12926
+ dir = join28(dir, segment);
11415
12927
  const stat4 = lstatSync(dir, { throwIfNoEntry: false });
11416
12928
  if (stat4) {
11417
12929
  if (stat4.isSymbolicLink() || !stat4.isDirectory()) {
@@ -11421,12 +12933,12 @@ ${parsed.body}
11421
12933
  mkdirSync17(dir);
11422
12934
  }
11423
12935
  }
11424
- const destination = join27(dir, "SKILL.md");
12936
+ const destination = join28(dir, "SKILL.md");
11425
12937
  const stat3 = lstatSync(destination, { throwIfNoEntry: false });
11426
12938
  if (stat3 && (stat3.isSymbolicLink() || !stat3.isFile())) {
11427
12939
  throw new Error(`Installation target must be a regular file: ${destination}`);
11428
12940
  }
11429
- const temporary = join27(dir, `.SKILL-${randomUUID2()}.tmp`);
12941
+ const temporary = join28(dir, `.SKILL-${randomUUID2()}.tmp`);
11430
12942
  const fd = openSync2(temporary, "wx", stat3?.mode ?? 438);
11431
12943
  try {
11432
12944
  try {
@@ -11550,7 +13062,7 @@ async function runTeammate(args) {
11550
13062
  initLogger({
11551
13063
  sessionId: `teammate-${safeMemberName}-${Date.now().toString(36)}`,
11552
13064
  mode: "teammate",
11553
- logDir: join28(dirname10(args.teamDir), "logs"),
13065
+ logDir: join29(dirname10(args.teamDir), "logs"),
11554
13066
  skipCleanup: true
11555
13067
  });
11556
13068
  process.on("exit", closeLogger);
@@ -11725,26 +13237,26 @@ function isDiffTool(toolName) {
11725
13237
  }
11726
13238
 
11727
13239
  // src/vscode/ide-client.ts
11728
- import { setTimeout as delay } from "timers/promises";
13240
+ import { setTimeout as delay2 } from "timers/promises";
11729
13241
  import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
11730
- import { z as z20 } from "zod";
13242
+ import { z as z21 } from "zod";
11731
13243
 
11732
13244
  // src/vscode/lockfile.ts
11733
- import { readdir as readdir2, readFile as readFile5 } from "fs/promises";
13245
+ import { readdir as readdir2, readFile as readFile6 } from "fs/promises";
11734
13246
  import { homedir as homedir10 } from "os";
11735
- import { join as join29, resolve as resolve11, sep as sep2 } from "path";
11736
- import { z as z19 } from "zod";
13247
+ import { join as join30, resolve as resolve11, sep as sep2 } from "path";
13248
+ import { z as z20 } from "zod";
11737
13249
  var log37 = createChildLogger({ module: "vscode" });
11738
- var LockfileSchema = z19.object({
11739
- workspaceFolders: z19.array(z19.string()).optional(),
11740
- pid: z19.number().optional(),
11741
- ideName: z19.string().optional(),
11742
- transport: z19.enum(["ws", "sse"]).optional(),
11743
- authToken: z19.string().optional()
13250
+ var LockfileSchema = z20.object({
13251
+ workspaceFolders: z20.array(z20.string()).optional(),
13252
+ pid: z20.number().optional(),
13253
+ ideName: z20.string().optional(),
13254
+ transport: z20.enum(["ws", "sse"]).optional(),
13255
+ authToken: z20.string().optional()
11744
13256
  });
11745
13257
  function ideLockDir() {
11746
- const configDir = process.env.CLAUDE_CONFIG_DIR ?? join29(homedir10(), ".claude");
11747
- return join29(configDir, "ide");
13258
+ const configDir = process.env.CLAUDE_CONFIG_DIR ?? join30(homedir10(), ".claude");
13259
+ return join30(configDir, "ide");
11748
13260
  }
11749
13261
  function cwdInWorkspace(cwd2, workspaceFolders) {
11750
13262
  const normalizedCwd = cwd2.normalize("NFC");
@@ -11762,7 +13274,7 @@ async function readLockfile(dir, filename) {
11762
13274
  return null;
11763
13275
  }
11764
13276
  try {
11765
- const content = await readFile5(join29(dir, filename), "utf-8");
13277
+ const content = await readFile6(join30(dir, filename), "utf-8");
11766
13278
  const parsed = LockfileSchema.parse(JSON.parse(content));
11767
13279
  if (parsed.transport !== "ws") {
11768
13280
  return null;
@@ -11893,12 +13405,12 @@ var WebSocketTransport = class {
11893
13405
 
11894
13406
  // src/vscode/ide-client.ts
11895
13407
  var log38 = createChildLogger({ module: "vscode" });
11896
- var AtMentionedSchema = z20.object({
11897
- method: z20.literal("at_mentioned"),
11898
- params: z20.object({
11899
- filePath: z20.string(),
11900
- lineStart: z20.number().optional(),
11901
- lineEnd: z20.number().optional()
13408
+ var AtMentionedSchema = z21.object({
13409
+ method: z21.literal("at_mentioned"),
13410
+ params: z21.object({
13411
+ filePath: z21.string(),
13412
+ lineStart: z21.number().optional(),
13413
+ lineEnd: z21.number().optional()
11902
13414
  })
11903
13415
  });
11904
13416
  function inIdeTerminal() {
@@ -11912,7 +13424,7 @@ async function connectToIde(opts) {
11912
13424
  let ide = await detectIde(opts.cwd);
11913
13425
  while (!ide && Date.now() < deadline) {
11914
13426
  try {
11915
- await delay(1e3, void 0, { signal: opts.signal });
13427
+ await delay2(1e3, void 0, { signal: opts.signal });
11916
13428
  } catch {
11917
13429
  return null;
11918
13430
  }
@@ -11994,6 +13506,7 @@ export {
11994
13506
  CodeReviewManager,
11995
13507
  CommandRegistry,
11996
13508
  CommandUsageTracker,
13509
+ ComputerUseTool,
11997
13510
  ConfigError,
11998
13511
  ContextTooLongError,
11999
13512
  ConversationManager,
@@ -12022,6 +13535,7 @@ export {
12022
13535
  LLMError,
12023
13536
  ListTeamsTool,
12024
13537
  LoadSkillTool,
13538
+ MAIN_AGENT_ONLY_TOOLS,
12025
13539
  MAX_DIMENSION_PX,
12026
13540
  MAX_HISTORY_ENTRIES,
12027
13541
  MAX_IMAGES_PER_MESSAGE,
@@ -12029,7 +13543,6 @@ export {
12029
13543
  MCPClient,
12030
13544
  MCPManager,
12031
13545
  MCPToolWrapper,
12032
- MCP_CALL_TOOL_NAME,
12033
13546
  MCP_NAME_SEP,
12034
13547
  MCP_TOOL_PREFIX,
12035
13548
  MIN_THINKING_ANSWER_TOKENS,
@@ -12076,7 +13589,6 @@ export {
12076
13589
  THINKING_BUDGETS,
12077
13590
  THINKING_LEVELS,
12078
13591
  TOOL_RESULT_PREVIEW_CHARS,
12079
- TOOL_SEARCH_TOOL_NAME,
12080
13592
  TaskCreateTool2 as TaskCreateTool,
12081
13593
  TaskGetTool2 as TaskGetTool,
12082
13594
  TaskList,