@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.
- package/README.md +14 -0
- package/dist/agent-O6C4FCQY.js +4 -0
- package/dist/anthropic-CFM2DXMB.js +4 -0
- package/dist/checker-A6PGACKZ.js +4 -0
- package/dist/chunk-4ABFUBO4.js +601 -0
- package/dist/{chunk-K5ZK27BX.js → chunk-AGJYBJ5M.js} +1 -1
- package/dist/chunk-MVVSONAE.js +4 -0
- package/dist/chunk-OQCEP2F5.js +4 -0
- package/dist/{chunk-QFSCPD63.js → chunk-TCQ4EB4G.js} +1 -1
- package/dist/chunk-Y2UUXOQA.js +4 -0
- package/dist/chunk-YAXZ2OUD.js +150 -0
- package/dist/lib/agent-YRTROHXU.js +10 -0
- package/dist/lib/{anthropic-CL5IK7KN.js → anthropic-R6DMXE7U.js} +3 -4
- package/dist/lib/{checker-BS4MK3O6.js → checker-M7RKMKJQ.js} +2 -3
- package/dist/lib/{chunk-IYPTOY3Y.js → chunk-4MDCRPVV.js} +35 -26
- package/dist/lib/{chunk-RMB3J46W.js → chunk-A4MXZA5Q.js} +197 -36
- package/dist/lib/{chunk-7QPDFSQE.js → chunk-EWE5IWZE.js} +20 -10
- package/dist/lib/{chunk-KG4MJGKQ.js → chunk-FZ4Y6MBN.js} +1 -4
- package/dist/lib/{chunk-OOSNCCI7.js → chunk-J6AV7SIF.js} +143 -148
- package/dist/lib/{chunk-I7OPU4K2.js → chunk-OEPCNATU.js} +192 -32
- package/dist/lib/{chunk-PIL7M52F.js → chunk-XT67KGWE.js} +16 -3
- package/dist/lib/index.d.ts +856 -778
- package/dist/lib/index.js +2261 -749
- package/dist/lib/{openai-LH4E7ZQS.js → openai-5N42ZYIJ.js} +1 -1
- package/dist/lib/{tool-filter-CS6W2GMU.js → tool-filter-XG2GXFVN.js} +3 -2
- package/dist/main.js +140 -140
- package/dist/openai-NQYARTT6.js +34 -0
- package/dist/{server-IVIRQTO7.js → server-BNT2IIKB.js} +17 -17
- package/dist/tool-filter-5NDJMSTD.js +4 -0
- package/package.json +1 -1
- package/dist/agent-TCNIRVHD.js +0 -4
- package/dist/anthropic-7RIVSEDF.js +0 -4
- package/dist/checker-3LHDOATB.js +0 -4
- package/dist/chunk-2IBMB3ZM.js +0 -150
- package/dist/chunk-E55KH72M.js +0 -4
- package/dist/chunk-FA26MQ7K.js +0 -257
- package/dist/chunk-QCKJLO6X.js +0 -4
- package/dist/chunk-TKADLB2Q.js +0 -4
- package/dist/chunk-VA64DJNN.js +0 -4
- package/dist/lib/agent-IRY7KR5D.js +0 -11
- package/dist/lib/chunk-GHF2PSEW.js +0 -8
- package/dist/openai-RZLCY55V.js +0 -34
- 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-
|
|
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-
|
|
58
|
+
} from "./chunk-4MDCRPVV.js";
|
|
59
59
|
import {
|
|
60
60
|
SKIP_DIRS,
|
|
61
61
|
isToolResultContentBlock,
|
|
62
62
|
normalizeToolResultContentBlock
|
|
63
|
-
} from "./chunk-
|
|
63
|
+
} from "./chunk-XT67KGWE.js";
|
|
64
64
|
import {
|
|
65
65
|
OpenAIClient,
|
|
66
66
|
OpenAICompatClient,
|
|
67
67
|
buildChatCompletionMessages,
|
|
68
68
|
buildOpenAIInput
|
|
69
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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.
|
|
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" ?
|
|
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
|
|
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
|
|
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 =
|
|
3104
|
+
const absolutePath = resolve3(filePath);
|
|
3051
3105
|
try {
|
|
3052
3106
|
return await realpath(absolutePath);
|
|
3053
3107
|
} catch {
|
|
3054
3108
|
try {
|
|
3055
|
-
return
|
|
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 =
|
|
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
|
|
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 =
|
|
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(
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
3869
|
+
const filePath = resolve8(ctx.workDir, requestedPath);
|
|
3816
3870
|
return withFileMutationQueue(filePath, () => {
|
|
3817
3871
|
if (ctx.abortSignal?.aborted) {
|
|
3818
|
-
return Promise.resolve({
|
|
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
|
|
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
|
-
|
|
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
|
|
5921
|
+
import { dirname as dirname5, isAbsolute as isAbsolute3, join as join17 } from "path";
|
|
5812
5922
|
import { promisify } from "util";
|
|
5813
|
-
var
|
|
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
|
-
|
|
5947
|
+
log25.error({ err }, "worktree operation failed");
|
|
5838
5948
|
return false;
|
|
5839
5949
|
}
|
|
5840
5950
|
}
|
|
5841
5951
|
async function resolveGitDir(root) {
|
|
5842
|
-
const gitPath =
|
|
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 :
|
|
5965
|
+
return isAbsolute3(rel) ? rel : join17(root, rel);
|
|
5856
5966
|
}
|
|
5857
5967
|
async function getCommonDir(gitDir) {
|
|
5858
5968
|
try {
|
|
5859
|
-
const commonDir =
|
|
5969
|
+
const commonDir = join17(gitDir, "commondir");
|
|
5860
5970
|
const raw = (await readFile4(commonDir, "utf-8")).trim();
|
|
5861
|
-
return isAbsolute3(raw) ? raw :
|
|
5971
|
+
return isAbsolute3(raw) ? raw : join17(gitDir, raw);
|
|
5862
5972
|
} catch (err) {
|
|
5863
|
-
|
|
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(
|
|
5980
|
+
raw = (await readFile4(join17(gitDir, "HEAD"), "utf-8")).trim();
|
|
5871
5981
|
} catch (err) {
|
|
5872
|
-
|
|
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(
|
|
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
|
-
|
|
6020
|
+
log25.error({ err }, "worktree operation failed");
|
|
5911
6021
|
}
|
|
5912
6022
|
try {
|
|
5913
|
-
const packed = await readFile4(
|
|
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
|
-
|
|
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(
|
|
6059
|
+
raw = (await readFile4(join17(worktreePath, ".git"), "utf-8")).trim();
|
|
5950
6060
|
} catch (err) {
|
|
5951
|
-
|
|
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 :
|
|
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 =
|
|
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], {
|
|
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
|
-
|
|
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 =
|
|
6177
|
+
const swiftyDir = join17(repoRoot, ".swifty");
|
|
6066
6178
|
if (!await pathExists(swiftyDir)) {
|
|
6067
6179
|
return;
|
|
6068
6180
|
}
|
|
6069
|
-
const dstRoot =
|
|
6181
|
+
const dstRoot = join17(wtPath, ".swifty");
|
|
6070
6182
|
try {
|
|
6071
6183
|
await mkdir2(dstRoot, { recursive: true });
|
|
6072
6184
|
} catch (err) {
|
|
6073
|
-
|
|
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 =
|
|
6189
|
+
const src = join17(swiftyDir, entry);
|
|
6078
6190
|
if (!await pathExists(src)) {
|
|
6079
6191
|
continue;
|
|
6080
6192
|
}
|
|
6081
6193
|
try {
|
|
6082
|
-
await cp(src,
|
|
6194
|
+
await cp(src, join17(dstRoot, entry), { recursive: true });
|
|
6083
6195
|
} catch (err) {
|
|
6084
|
-
|
|
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 =
|
|
6201
|
+
const agentsDir = join17(repoRoot, ".agents");
|
|
6090
6202
|
if (!await pathExists(agentsDir)) {
|
|
6091
6203
|
return;
|
|
6092
6204
|
}
|
|
6093
|
-
const dstRoot =
|
|
6205
|
+
const dstRoot = join17(wtPath, ".agents");
|
|
6094
6206
|
try {
|
|
6095
6207
|
await mkdir2(dstRoot, { recursive: true });
|
|
6096
6208
|
} catch (err) {
|
|
6097
|
-
|
|
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 =
|
|
6213
|
+
const src = join17(agentsDir, entry);
|
|
6102
6214
|
if (!await pathExists(src)) {
|
|
6103
6215
|
continue;
|
|
6104
6216
|
}
|
|
6105
6217
|
try {
|
|
6106
|
-
await cp(src,
|
|
6218
|
+
await cp(src, join17(dstRoot, entry), { recursive: true });
|
|
6107
6219
|
} catch (err) {
|
|
6108
|
-
|
|
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 = [
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
6251
|
+
const src = join17(repoRoot, "node_modules");
|
|
6140
6252
|
if (!await pathExists(src)) {
|
|
6141
6253
|
return;
|
|
6142
6254
|
}
|
|
6143
|
-
const dst =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
6277
|
+
const src = join17(repoRoot, relPath);
|
|
6166
6278
|
if (!await pathExists(src)) {
|
|
6167
6279
|
continue;
|
|
6168
6280
|
}
|
|
6169
|
-
const dst =
|
|
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
|
-
|
|
6290
|
+
log25.error({ err }, "worktree operation failed");
|
|
6179
6291
|
}
|
|
6180
6292
|
}
|
|
6181
6293
|
} catch (err) {
|
|
6182
|
-
|
|
6294
|
+
log25.error({ err }, "failed to process .worktreeinclude");
|
|
6183
6295
|
}
|
|
6184
6296
|
}
|
|
6185
6297
|
|
|
6186
|
-
// src/subagent/
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
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-
|
|
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/
|
|
7497
|
-
var
|
|
7498
|
-
|
|
7499
|
-
|
|
7500
|
-
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
|
|
7509
|
-
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
|
|
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 = () =>
|
|
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
|
-
|
|
7970
|
-
|
|
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
|
-
|
|
7985
|
+
const base = token.split(/[\\/]/).pop() ?? token;
|
|
7986
|
+
return base.replace(/\.exe$/i, "").toLowerCase();
|
|
7979
7987
|
}
|
|
7980
7988
|
return "";
|
|
7981
7989
|
}
|
|
7982
|
-
var
|
|
7983
|
-
|
|
7984
|
-
[
|
|
7985
|
-
|
|
7986
|
-
|
|
7987
|
-
|
|
7988
|
-
|
|
7989
|
-
|
|
7990
|
-
|
|
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
|
|
7995
|
-
|
|
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({
|
|
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
|
-
|
|
8096
|
-
|
|
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
|
-
|
|
8158
|
-
|
|
8159
|
-
|
|
8160
|
-
|
|
8161
|
-
|
|
8162
|
-
|
|
8163
|
-
|
|
8164
|
-
|
|
8165
|
-
|
|
8166
|
-
|
|
8167
|
-
|
|
8168
|
-
|
|
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
|
-
|
|
8172
|
-
|
|
8173
|
-
|
|
8174
|
-
|
|
8175
|
-
|
|
8176
|
-
|
|
8177
|
-
|
|
8178
|
-
|
|
8179
|
-
|
|
8180
|
-
|
|
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
|
-
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
8201
|
-
|
|
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
|
-
|
|
8204
|
-
|
|
8205
|
-
|
|
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
|
-
|
|
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
|
|
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({
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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({
|
|
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
|
|
10392
|
+
import { join as join23 } from "path";
|
|
8885
10393
|
var LOG_DIR = ".swifty";
|
|
8886
|
-
var LOG_PATH =
|
|
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
|
|
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
|
|
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
|
|
10538
|
+
import { dirname as dirname8, join as join24 } from "path";
|
|
8997
10539
|
import yaml5 from "js-yaml";
|
|
8998
|
-
import { z as
|
|
8999
|
-
var tokenLimit = (fallback, min, max) =>
|
|
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
|
-
|
|
9002
|
-
|
|
9003
|
-
|
|
9004
|
-
]).pipe(
|
|
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:
|
|
9008
|
-
base_url:
|
|
9009
|
-
api_key:
|
|
9010
|
-
model:
|
|
9011
|
-
thinking:
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
10610
|
+
import { join as join25 } from "path";
|
|
9069
10611
|
import yaml6 from "js-yaml";
|
|
9070
|
-
import { z as
|
|
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) =>
|
|
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 =
|
|
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 =
|
|
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 =
|
|
9227
|
-
name:
|
|
9228
|
-
description:
|
|
9229
|
-
mode:
|
|
9230
|
-
model:
|
|
9231
|
-
fork_context:
|
|
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 =
|
|
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
|
|
9402
|
-
import
|
|
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 =
|
|
9405
|
-
var TaskSchema =
|
|
9406
|
-
id:
|
|
9407
|
-
subject:
|
|
9408
|
-
description:
|
|
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:
|
|
9411
|
-
activeForm:
|
|
9412
|
-
blocks:
|
|
9413
|
-
blockedBy:
|
|
9414
|
-
metadata:
|
|
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 =
|
|
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(
|
|
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
|
|
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 =
|
|
9736
|
-
taskId:
|
|
9737
|
-
subject:
|
|
9738
|
-
description:
|
|
9739
|
-
status:
|
|
9740
|
-
owner:
|
|
9741
|
-
addBlocks:
|
|
9742
|
-
addBlockedBy:
|
|
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
|
|
9747
|
-
var QuestionOptionSchema =
|
|
9748
|
-
label:
|
|
9749
|
-
description:
|
|
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 =
|
|
9752
|
-
question:
|
|
9753
|
-
header:
|
|
9754
|
-
options:
|
|
9755
|
-
multiSelect:
|
|
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(
|
|
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 =
|
|
10087
|
-
type:
|
|
10088
|
-
data:
|
|
11594
|
+
var WsInboundSchema = z19.object({
|
|
11595
|
+
type: z19.string(),
|
|
11596
|
+
data: z19.unknown()
|
|
10089
11597
|
});
|
|
10090
|
-
var UserMessageSchema =
|
|
10091
|
-
content:
|
|
11598
|
+
var UserMessageSchema = z19.object({
|
|
11599
|
+
content: z19.string()
|
|
10092
11600
|
});
|
|
10093
|
-
var PermissionResponseSchema =
|
|
10094
|
-
id:
|
|
10095
|
-
response:
|
|
11601
|
+
var PermissionResponseSchema = z19.object({
|
|
11602
|
+
id: z19.string(),
|
|
11603
|
+
response: z19.enum(["allow", "deny", "allowAlways"])
|
|
10096
11604
|
});
|
|
10097
|
-
var AskUserResponseSchema =
|
|
10098
|
-
id:
|
|
10099
|
-
answers:
|
|
11605
|
+
var AskUserResponseSchema = z19.object({
|
|
11606
|
+
id: z19.string(),
|
|
11607
|
+
answers: z19.record(z19.string(), z19.string())
|
|
10100
11608
|
});
|
|
10101
|
-
var 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 =
|
|
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-
|
|
10286
|
-
const { Agent: AgentClass } = await import("./agent-
|
|
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) =>
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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:
|
|
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
|
|
13240
|
+
import { setTimeout as delay2 } from "timers/promises";
|
|
11729
13241
|
import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
|
|
11730
|
-
import { z as
|
|
13242
|
+
import { z as z21 } from "zod";
|
|
11731
13243
|
|
|
11732
13244
|
// src/vscode/lockfile.ts
|
|
11733
|
-
import { readdir as readdir2, readFile as
|
|
13245
|
+
import { readdir as readdir2, readFile as readFile6 } from "fs/promises";
|
|
11734
13246
|
import { homedir as homedir10 } from "os";
|
|
11735
|
-
import { join as
|
|
11736
|
-
import { z as
|
|
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 =
|
|
11739
|
-
workspaceFolders:
|
|
11740
|
-
pid:
|
|
11741
|
-
ideName:
|
|
11742
|
-
transport:
|
|
11743
|
-
authToken:
|
|
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 ??
|
|
11747
|
-
return
|
|
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
|
|
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 =
|
|
11897
|
-
method:
|
|
11898
|
-
params:
|
|
11899
|
-
filePath:
|
|
11900
|
-
lineStart:
|
|
11901
|
-
lineEnd:
|
|
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
|
|
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,
|