@swifty.js/swifty 0.0.35 → 0.0.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{acp-A4KRKOHD.js → acp-ZU5LZRUP.js} +1 -1
- package/dist/{agent-VDWIGFIV.js → agent-YDU6HB4S.js} +1 -1
- package/dist/{anthropic-AR2Y4FBR.js → anthropic-GDT73MMP.js} +1 -1
- package/dist/{chunk-IOLGXPUQ.js → chunk-EEEW3GG4.js} +1 -1
- package/dist/chunk-I7XJSLRI.js +4 -0
- package/dist/{chunk-TOM34LJB.js → chunk-MNP6IJ7C.js} +17 -17
- package/dist/{chunk-4B4SGVJH.js → chunk-MTAGHTBG.js} +21 -21
- package/dist/{chunk-TNF3EHFY.js → chunk-Q3NPRV5P.js} +1 -1
- package/dist/chunk-RDVRYI56.js +610 -0
- package/dist/esm-PDXUGWFK.js +229 -0
- package/dist/lib/{agent-TC4HSVMS.js → agent-ZMIS6IH6.js} +2 -2
- package/dist/lib/{chunk-RUDYC7OS.js → chunk-BHSNJT6Z.js} +1 -1
- package/dist/lib/{chunk-OJTISIYU.js → chunk-PJ7354ZG.js} +1 -1
- package/dist/lib/{chunk-S5VWPTME.js → chunk-YUDCYSKO.js} +22 -7
- package/dist/lib/index.d.ts +500 -98
- package/dist/lib/index.js +1140 -375
- package/dist/lib/{permissions-BW4QPZXV.js → permissions-77UJQITR.js} +1 -1
- package/dist/lib/{providers-ROWSB6ND.js → providers-5SJFTU2G.js} +1 -1
- package/dist/main.js +176 -175
- package/dist/{providers-2J4JJYQD.js → providers-FQCJFM3Q.js} +1 -1
- package/dist/{server-ZYK4L22D.js → server-C6H5ZWUG.js} +1 -1
- package/package.json +1 -1
- package/dist/chunk-F4FOFPKL.js +0 -611
- package/dist/chunk-YBS5ATGZ.js +0 -4
- package/dist/esm-VBV56AO5.js +0 -229
package/dist/lib/index.js
CHANGED
|
@@ -10,8 +10,10 @@ import {
|
|
|
10
10
|
ConversationManager,
|
|
11
11
|
FileHistory,
|
|
12
12
|
RecoveryState,
|
|
13
|
+
TOOL_RESULT_PREVIEW_CHARS,
|
|
13
14
|
agent_exports,
|
|
14
15
|
asImageMediaType,
|
|
16
|
+
buildPersistedOutputPreview,
|
|
15
17
|
captureTelemetryError,
|
|
16
18
|
compact_exports,
|
|
17
19
|
conversation_exports,
|
|
@@ -27,6 +29,7 @@ import {
|
|
|
27
29
|
loadSession,
|
|
28
30
|
maybeResizeAndDownsampleImage,
|
|
29
31
|
newSessionId,
|
|
32
|
+
persistLargeResult,
|
|
30
33
|
plan_file_exports,
|
|
31
34
|
plan_mode_exports,
|
|
32
35
|
prompts_exports,
|
|
@@ -37,13 +40,14 @@ import {
|
|
|
37
40
|
saveMessage,
|
|
38
41
|
session_exports,
|
|
39
42
|
shutdownTelemetry,
|
|
43
|
+
spillDir,
|
|
40
44
|
telemetry_exports,
|
|
41
45
|
tool_result_exports
|
|
42
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-YUDCYSKO.js";
|
|
43
47
|
import {
|
|
44
48
|
version,
|
|
45
49
|
version_exports
|
|
46
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-PJ7354ZG.js";
|
|
47
51
|
import {
|
|
48
52
|
anthropic_exports,
|
|
49
53
|
decideAndApply,
|
|
@@ -78,7 +82,7 @@ import {
|
|
|
78
82
|
PermissionChecker,
|
|
79
83
|
isSafeCommand,
|
|
80
84
|
permissions_exports
|
|
81
|
-
} from "./chunk-
|
|
85
|
+
} from "./chunk-BHSNJT6Z.js";
|
|
82
86
|
import {
|
|
83
87
|
MCPToolWrapper,
|
|
84
88
|
MCP_TOOL_PREFIX,
|
|
@@ -347,9 +351,9 @@ __export(server_exports, {
|
|
|
347
351
|
RemoteServer: () => RemoteServer,
|
|
348
352
|
createRemoteAgent: () => createRemoteAgent
|
|
349
353
|
});
|
|
350
|
-
import { readFileSync as readFileSync16, existsSync as existsSync16, statSync as
|
|
354
|
+
import { readFileSync as readFileSync16, existsSync as existsSync16, statSync as statSync12 } from "fs";
|
|
351
355
|
import { createServer } from "http";
|
|
352
|
-
import { join as
|
|
356
|
+
import { join as join21, extname as extname2, normalize } from "path";
|
|
353
357
|
import { cwd } from "process";
|
|
354
358
|
import Koa from "koa";
|
|
355
359
|
import { WebSocketServer, WebSocket } from "ws";
|
|
@@ -1927,11 +1931,14 @@ import { resolve as resolve4 } from "path";
|
|
|
1927
1931
|
// src/tools/descriptions.ts
|
|
1928
1932
|
var descriptions_exports = {};
|
|
1929
1933
|
__export(descriptions_exports, {
|
|
1934
|
+
BASH_BACKGROUND_DESCRIPTION: () => BASH_BACKGROUND_DESCRIPTION,
|
|
1930
1935
|
BASH_DESCRIPTION: () => BASH_DESCRIPTION,
|
|
1931
1936
|
EDIT_FILE_DESCRIPTION: () => EDIT_FILE_DESCRIPTION,
|
|
1932
1937
|
GLOB_DESCRIPTION: () => GLOB_DESCRIPTION,
|
|
1933
1938
|
GREP_DESCRIPTION: () => GREP_DESCRIPTION,
|
|
1939
|
+
JAVASCRIPT_BACKGROUND_DESCRIPTION: () => JAVASCRIPT_BACKGROUND_DESCRIPTION,
|
|
1934
1940
|
JAVASCRIPT_DESCRIPTION: () => JAVASCRIPT_DESCRIPTION,
|
|
1941
|
+
POWERSHELL_BACKGROUND_DESCRIPTION: () => POWERSHELL_BACKGROUND_DESCRIPTION,
|
|
1935
1942
|
POWERSHELL_DESCRIPTION: () => POWERSHELL_DESCRIPTION,
|
|
1936
1943
|
READ_FILE_DESCRIPTION: () => READ_FILE_DESCRIPTION,
|
|
1937
1944
|
WEB_FETCH_DESCRIPTION: () => WEB_FETCH_DESCRIPTION,
|
|
@@ -1943,15 +1950,18 @@ var BASH_DESCRIPTION = `Execute command in Bash; return stdout and stderr. Prefe
|
|
|
1943
1950
|
- Quote paths with spaces. To change directory, use cd "path" && command in the same call. Separate independent commands; chain dependent commands with &&, not ;.
|
|
1944
1951
|
- Prefer dedicated file/search tools over cat, head, tail, sed, awk, echo, or find. Scope searches to a directory, never the filesystem root. Diagnose failures rather than retrying in sleep loops.
|
|
1945
1952
|
${GIT_GUIDANCE}`;
|
|
1953
|
+
var BASH_BACKGROUND_DESCRIPTION = `- Set run_in_background to true to run the command in the background. The call returns a task ID immediately and the result arrives later as a task notification; do not poll or sleep waiting for it. Use this for long-running commands you do not need the result of right away. Use TaskStop with the task_id to kill a background command early. A foreground command that exceeds its timeout is moved to the background automatically instead of being killed, unless it is a bare sleep.`;
|
|
1946
1954
|
var POWERSHELL_DESCRIPTION = `Execute command in PowerShell; return stdout and stderr. Recommended on Windows (powershell.exe); uses pwsh elsewhere.
|
|
1947
1955
|
- timeout is in seconds: default 120, maximum 600. Each call starts a fresh, independent shell in the Agent's working directory; location, variables, and options do not persist.
|
|
1948
1956
|
- Quote paths with spaces; use Set-Location -LiteralPath "path" in the same call. Separate independent commands. For dependencies, check $LASTEXITCODE for native commands and use -ErrorAction Stop for cmdlets; ; does not stop on failure. Do not assume PowerShell 7 syntax.
|
|
1949
1957
|
- Prefer dedicated file/search tools over Get-Content, Select-String, or Write-Output. Scope recursion to a directory, not a drive root. Diagnose failures rather than retrying in Start-Sleep loops.
|
|
1950
1958
|
${GIT_GUIDANCE}`;
|
|
1959
|
+
var POWERSHELL_BACKGROUND_DESCRIPTION = `- Set run_in_background to true to run the command in the background. The call returns a task ID immediately and the result arrives later as a task notification; do not poll or Start-Sleep waiting for it. Use this for long-running commands you do not need the result of right away. Use TaskStop with the task_id to kill a background command early. A foreground command that exceeds its timeout is moved to the background automatically instead of being killed, unless it is a bare Start-Sleep.`;
|
|
1951
1960
|
var JAVASCRIPT_DESCRIPTION = `Execute JavaScript in a fresh isolated V8 context for pure computation.
|
|
1952
1961
|
- Provide a function body and use return to produce a JSON-compatible result. Optional input is available as globalThis.input.
|
|
1953
1962
|
- No filesystem, network, process, require, imports, timers, or host callbacks are available. Use Bash for Node.js or system operations.
|
|
1954
1963
|
- timeout_ms defaults to 1000 (max 10000); memory_limit_mb defaults to 64 (max 256). Requires Node.js 24 or newer.`;
|
|
1964
|
+
var JAVASCRIPT_BACKGROUND_DESCRIPTION = `- Set run_in_background to true to evaluate in the background. The call returns a task ID immediately and the result arrives later as a task notification; do not poll. timeout_ms still caps the evaluation (max 10000). Use TaskStop with the task_id to abort it early.`;
|
|
1955
1965
|
var READ_FILE_DESCRIPTION = `Read text with 1-based display line numbers, or images (png, jpg, jpeg, gif, webp) as visual content; not directories.
|
|
1956
1966
|
- file_path is absolute or relative to the Agent's working directory. offset skips lines (0-based, default 0); limit defaults to 2000 lines, with a 50KB text output cap. Displayed line 101 starts at offset=100. Follow continuation/readback instructions for partial output.
|
|
1957
1967
|
- Images ignore offset/limit. A successful read refreshes the file-state cache used by EditFile/WriteFile; re-read after an external-change error.`;
|
|
@@ -3178,7 +3188,9 @@ var MemoryManager = class {
|
|
|
3178
3188
|
return this.loadAll();
|
|
3179
3189
|
}
|
|
3180
3190
|
/**
|
|
3181
|
-
* Builds the memory index injected into the
|
|
3191
|
+
* Builds the memory index injected into the conversation as a
|
|
3192
|
+
* system-reminder message (not the system prompt, which stays
|
|
3193
|
+
* project-independent to preserve prompt caching).
|
|
3182
3194
|
*
|
|
3183
3195
|
* This content is re-sent to the model on every conversation turn, so each additional index line
|
|
3184
3196
|
* is a recurring cost. We therefore enforce both a line-count and a byte-size cap at the output
|
|
@@ -3268,8 +3280,9 @@ ${body}
|
|
|
3268
3280
|
// ── Feature 2: findRelevantMemories ────────────────────────────────
|
|
3269
3281
|
/**
|
|
3270
3282
|
* Scans all memory headers from both dirs, asks the LLM to select the
|
|
3271
|
-
* top 5 most relevant ones for the query, and returns
|
|
3272
|
-
*
|
|
3283
|
+
* top 5 most relevant ones for the query, and returns their paths (with
|
|
3284
|
+
* mtimes); the file contents are read later by renderReminder.
|
|
3285
|
+
* Best-effort: selector failures return an empty array.
|
|
3273
3286
|
*/
|
|
3274
3287
|
async findRelevantMemories(query, client, recentTools = [], alreadySurfaced = /* @__PURE__ */ new Set()) {
|
|
3275
3288
|
const allHeaders = [];
|
|
@@ -4543,27 +4556,37 @@ function parseAgentDefinition(content) {
|
|
|
4543
4556
|
// src/subagent/task-manager.ts
|
|
4544
4557
|
var task_manager_exports = {};
|
|
4545
4558
|
__export(task_manager_exports, {
|
|
4559
|
+
TaskFailure: () => TaskFailure,
|
|
4546
4560
|
TaskManager: () => TaskManager,
|
|
4547
4561
|
formatAgentTaskNotification: () => formatAgentTaskNotification
|
|
4548
4562
|
});
|
|
4563
|
+
var TaskFailure = class extends Error {
|
|
4564
|
+
constructor(output) {
|
|
4565
|
+
super("task failed");
|
|
4566
|
+
this.output = output;
|
|
4567
|
+
}
|
|
4568
|
+
output;
|
|
4569
|
+
};
|
|
4549
4570
|
var TaskManager = class {
|
|
4550
4571
|
tasks = /* @__PURE__ */ new Map();
|
|
4551
4572
|
notifiedTaskIds = /* @__PURE__ */ new Set();
|
|
4552
4573
|
listeners = /* @__PURE__ */ new Set();
|
|
4553
4574
|
nextId = 1;
|
|
4575
|
+
pendingTaskIds = /* @__PURE__ */ new Set();
|
|
4554
4576
|
create(name, runner, cancel, options = {}) {
|
|
4555
|
-
const id =
|
|
4577
|
+
const id = `${options.idPrefix ?? "agent"}-${String(this.nextId++)}`;
|
|
4556
4578
|
const task = {
|
|
4557
4579
|
id,
|
|
4558
4580
|
name,
|
|
4559
4581
|
...options.originToolCallId ? { originToolCallId: options.originToolCallId } : {},
|
|
4582
|
+
...options.kind ? { kind: options.kind } : {},
|
|
4560
4583
|
status: "running",
|
|
4561
4584
|
output: "",
|
|
4562
4585
|
cancel,
|
|
4563
4586
|
done: Promise.resolve()
|
|
4564
4587
|
};
|
|
4565
4588
|
this.tasks.set(id, task);
|
|
4566
|
-
this.
|
|
4589
|
+
this.pendingTaskIds.add(id);
|
|
4567
4590
|
task.done = Promise.resolve().then(() => runner(task)).then((output) => {
|
|
4568
4591
|
if (task.status === "running") {
|
|
4569
4592
|
task.status = "completed";
|
|
@@ -4573,10 +4596,17 @@ var TaskManager = class {
|
|
|
4573
4596
|
}).catch((error) => {
|
|
4574
4597
|
if (task.status === "running") {
|
|
4575
4598
|
task.status = "failed";
|
|
4576
|
-
task.output = `Error: ${asErrorString(error)}`;
|
|
4599
|
+
task.output = error instanceof TaskFailure ? error.output : `Error: ${asErrorString(error)}`;
|
|
4600
|
+
this.emitChange();
|
|
4601
|
+
} else if (task.status === "cancelled" && error instanceof TaskFailure) {
|
|
4602
|
+
task.output = error.output;
|
|
4577
4603
|
this.emitChange();
|
|
4578
4604
|
}
|
|
4605
|
+
}).finally(() => {
|
|
4606
|
+
this.pendingTaskIds.delete(id);
|
|
4607
|
+
this.emitChange();
|
|
4579
4608
|
});
|
|
4609
|
+
this.emitChange();
|
|
4580
4610
|
return task;
|
|
4581
4611
|
}
|
|
4582
4612
|
get(id) {
|
|
@@ -4614,25 +4644,32 @@ var TaskManager = class {
|
|
|
4614
4644
|
}
|
|
4615
4645
|
async stopAndWait(id) {
|
|
4616
4646
|
const task = this.tasks.get(id);
|
|
4617
|
-
if (!task
|
|
4647
|
+
if (!task) {
|
|
4618
4648
|
return false;
|
|
4619
4649
|
}
|
|
4650
|
+
const stopped = this.stop(id);
|
|
4620
4651
|
await task.done;
|
|
4621
|
-
return
|
|
4652
|
+
return stopped;
|
|
4622
4653
|
}
|
|
4623
4654
|
async stopAll() {
|
|
4624
|
-
const running = this.list().filter((task) => task.
|
|
4655
|
+
const running = this.list().filter((task) => this.pendingTaskIds.has(task.id));
|
|
4625
4656
|
for (const task of running) {
|
|
4626
4657
|
this.stop(task.id);
|
|
4627
4658
|
}
|
|
4628
4659
|
await Promise.allSettled(running.map((task) => task.done));
|
|
4629
4660
|
}
|
|
4630
|
-
|
|
4631
|
-
|
|
4661
|
+
/**
|
|
4662
|
+
* Wait for tasks to settle. An optional filter selects which tasks to wait
|
|
4663
|
+
* for (e.g. print-mode waits only for agent-kind tasks: shell/js tasks may
|
|
4664
|
+
* run indefinitely and are stopped, not awaited, at exit).
|
|
4665
|
+
*/
|
|
4666
|
+
async waitAll(filter) {
|
|
4667
|
+
const tasks = filter ? this.list().filter(filter) : this.list();
|
|
4668
|
+
await Promise.allSettled(tasks.map((task) => task.done));
|
|
4632
4669
|
}
|
|
4633
4670
|
drainNotifications() {
|
|
4634
4671
|
const completed = this.list().filter(
|
|
4635
|
-
(task) => task.status !== "running" && !this.notifiedTaskIds.has(task.id)
|
|
4672
|
+
(task) => task.status !== "running" && !this.pendingTaskIds.has(task.id) && !this.notifiedTaskIds.has(task.id)
|
|
4636
4673
|
);
|
|
4637
4674
|
for (const task of completed) {
|
|
4638
4675
|
this.notifiedTaskIds.add(task.id);
|
|
@@ -6218,6 +6255,7 @@ async function spawnSubagent(definition, prompt, parentClient, parentRegistry, p
|
|
|
6218
6255
|
const conversation = options.conversation ?? new ConversationManager();
|
|
6219
6256
|
conversation.addSystemReminder(buildSubagentInstructions(definition));
|
|
6220
6257
|
conversation.addUserMessage(prompt);
|
|
6258
|
+
const taskManager = options.backgroundTasks === false ? null : new TaskManager();
|
|
6221
6259
|
const agent2 = new Agent({
|
|
6222
6260
|
client,
|
|
6223
6261
|
registry,
|
|
@@ -6230,53 +6268,59 @@ async function spawnSubagent(definition, prompt, parentClient, parentRegistry, p
|
|
|
6230
6268
|
fileStateCache: new FileStateCache(),
|
|
6231
6269
|
instructions: loadInstructions(workDir),
|
|
6232
6270
|
contextWindow: getContextWindow(provider),
|
|
6233
|
-
maxOutput: getMaxOutputTokens(provider)
|
|
6271
|
+
maxOutput: getMaxOutputTokens(provider),
|
|
6272
|
+
taskManager,
|
|
6273
|
+
notificationFn: taskManager ? () => taskManager.drainNotifications().map(formatAgentTaskNotification) : void 0
|
|
6234
6274
|
});
|
|
6235
6275
|
let output = "";
|
|
6236
6276
|
let turn = 0;
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6277
|
+
try {
|
|
6278
|
+
for await (const event of agent2.run()) {
|
|
6279
|
+
switch (event.type) {
|
|
6280
|
+
case "stream_text":
|
|
6281
|
+
output += event.text;
|
|
6282
|
+
break;
|
|
6283
|
+
case "tool_use":
|
|
6284
|
+
onProgress?.({ lastTool: event.toolName });
|
|
6285
|
+
onEvent?.({
|
|
6286
|
+
type: "tool_use",
|
|
6287
|
+
toolId: event.toolId,
|
|
6288
|
+
toolName: event.toolName,
|
|
6289
|
+
args: event.args
|
|
6290
|
+
});
|
|
6291
|
+
break;
|
|
6292
|
+
case "tool_result":
|
|
6293
|
+
onEvent?.({
|
|
6294
|
+
type: "tool_result",
|
|
6295
|
+
toolId: event.toolId
|
|
6296
|
+
});
|
|
6297
|
+
break;
|
|
6298
|
+
case "usage":
|
|
6299
|
+
onEvent?.({
|
|
6300
|
+
type: "usage",
|
|
6301
|
+
usage: {
|
|
6302
|
+
inputTokens: event.usage.inputTokens,
|
|
6303
|
+
outputTokens: event.usage.outputTokens
|
|
6304
|
+
}
|
|
6305
|
+
});
|
|
6306
|
+
break;
|
|
6307
|
+
case "turn_complete":
|
|
6308
|
+
onProgress?.({ turn: ++turn });
|
|
6309
|
+
onEvent?.({ type: "turn_complete" });
|
|
6310
|
+
break;
|
|
6311
|
+
case "loop_complete":
|
|
6312
|
+
if (event.stopReason === "interrupted") {
|
|
6313
|
+
return `${output}${output ? "\n\n" : ""}${SUBAGENT_INTERRUPTED_MARKER}`;
|
|
6263
6314
|
}
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
onEvent?.({ type: "turn_complete" });
|
|
6269
|
-
break;
|
|
6270
|
-
case "loop_complete":
|
|
6271
|
-
if (event.stopReason === "interrupted") {
|
|
6272
|
-
return `${output}${output ? "\n\n" : ""}${SUBAGENT_INTERRUPTED_MARKER}`;
|
|
6273
|
-
}
|
|
6274
|
-
return output || "[No output]";
|
|
6275
|
-
case "error":
|
|
6276
|
-
throw event.error;
|
|
6315
|
+
return output || "[No output]";
|
|
6316
|
+
case "error":
|
|
6317
|
+
throw event.error;
|
|
6318
|
+
}
|
|
6277
6319
|
}
|
|
6320
|
+
return output || "[No output]";
|
|
6321
|
+
} finally {
|
|
6322
|
+
await taskManager?.stopAll();
|
|
6278
6323
|
}
|
|
6279
|
-
return output || "[No output]";
|
|
6280
6324
|
}
|
|
6281
6325
|
|
|
6282
6326
|
// src/teams/coordinator.ts
|
|
@@ -7063,7 +7107,7 @@ var TaskStopTool = class {
|
|
|
7063
7107
|
teamManager;
|
|
7064
7108
|
taskManager;
|
|
7065
7109
|
name = "TaskStop";
|
|
7066
|
-
description = "Stop a running teammate or background Agent
|
|
7110
|
+
description = "Stop a running teammate or background task (Agent, Bash, PowerShell or JavaScript). Pass exactly one of teammate or task_id.";
|
|
7067
7111
|
category = "command";
|
|
7068
7112
|
schema() {
|
|
7069
7113
|
return {
|
|
@@ -7078,20 +7122,21 @@ var TaskStopTool = class {
|
|
|
7078
7122
|
},
|
|
7079
7123
|
task_id: {
|
|
7080
7124
|
type: "string",
|
|
7081
|
-
description: "ID of a background Agent
|
|
7125
|
+
description: "ID of a background task (Agent, Bash, PowerShell or JavaScript)"
|
|
7082
7126
|
}
|
|
7083
7127
|
}
|
|
7084
7128
|
}
|
|
7085
7129
|
};
|
|
7086
7130
|
}
|
|
7087
|
-
async execute(
|
|
7131
|
+
async execute(ctx, args) {
|
|
7088
7132
|
const name = strArg(args, "teammate", "");
|
|
7089
7133
|
const taskId = strArg(args, "task_id", "");
|
|
7090
7134
|
if (!name && !taskId || name && taskId) {
|
|
7091
7135
|
return { output: "Error: pass exactly one of teammate or task_id", isError: true };
|
|
7092
7136
|
}
|
|
7093
7137
|
if (taskId) {
|
|
7094
|
-
const
|
|
7138
|
+
const manager = ctx.taskManager?.get(taskId) ? ctx.taskManager : this.taskManager;
|
|
7139
|
+
const task = manager?.get(taskId);
|
|
7095
7140
|
if (!task) {
|
|
7096
7141
|
return { output: `Error: background task '${taskId}' not found`, isError: true };
|
|
7097
7142
|
}
|
|
@@ -7101,7 +7146,7 @@ var TaskStopTool = class {
|
|
|
7101
7146
|
isError: false
|
|
7102
7147
|
};
|
|
7103
7148
|
}
|
|
7104
|
-
await
|
|
7149
|
+
await manager?.stopAndWait(taskId);
|
|
7105
7150
|
return { output: `Background task '${taskId}' stopped.`, isError: false };
|
|
7106
7151
|
}
|
|
7107
7152
|
for (const team of this.teamManager.list()) {
|
|
@@ -7425,16 +7470,7 @@ ${prompt}` };
|
|
|
7425
7470
|
shutdown: shutdownRequest("lead", "member deactivated")
|
|
7426
7471
|
};
|
|
7427
7472
|
}
|
|
7428
|
-
/**
|
|
7429
|
-
* Sends the teammate's completed plan to the Lead, blocks until approval is received,
|
|
7430
|
-
* and returns the prompt to feed the model on the next turn.
|
|
7431
|
-
*
|
|
7432
|
-
* The teammate holds read-only permissions at this point, so no matter how long the
|
|
7433
|
-
* wait, no damage can occur — hence no timeout is set here. Rather than timing out and
|
|
7434
|
-
* autonomously modifying files, it is better to wait indefinitely and let the user
|
|
7435
|
-
* drive progress from the Lead side. Returns null when the teammate has been
|
|
7436
|
-
* deactivated; the caller should exit the main loop.
|
|
7437
|
-
*/
|
|
7473
|
+
/** Reads the teammate's plan file for review; returns a fallback note when empty or unreadable. */
|
|
7438
7474
|
readPlanForReview() {
|
|
7439
7475
|
try {
|
|
7440
7476
|
const text = readFileSync14(getOrCreatePlanPath(this.workDir), "utf-8");
|
|
@@ -7445,6 +7481,16 @@ ${prompt}` };
|
|
|
7445
7481
|
}
|
|
7446
7482
|
return "(Plan file is empty \u2014 the teammate may not have written the plan as expected)";
|
|
7447
7483
|
}
|
|
7484
|
+
/**
|
|
7485
|
+
* Sends the teammate's completed plan to the Lead, blocks until approval is received,
|
|
7486
|
+
* and returns the prompt to feed the model on the next turn.
|
|
7487
|
+
*
|
|
7488
|
+
* The teammate holds read-only permissions at this point, so no matter how long the
|
|
7489
|
+
* wait, no damage can occur — hence no timeout is set here. Rather than timing out and
|
|
7490
|
+
* autonomously modifying files, it is better to wait indefinitely and let the user
|
|
7491
|
+
* drive progress from the Lead side. Returns null when the teammate has been
|
|
7492
|
+
* deactivated; the caller should exit the main loop.
|
|
7493
|
+
*/
|
|
7448
7494
|
async runPlanApproval(member, plan) {
|
|
7449
7495
|
const req = planApprovalRequest(member.name, plan);
|
|
7450
7496
|
await this.leadMailbox.send(member.name, req.text, req);
|
|
@@ -8139,8 +8185,50 @@ __export(bash_exports, {
|
|
|
8139
8185
|
BashTool: () => BashTool
|
|
8140
8186
|
});
|
|
8141
8187
|
import { spawn as spawn2 } from "child_process";
|
|
8188
|
+
import { statSync as statSync10 } from "fs";
|
|
8189
|
+
|
|
8190
|
+
// src/tools/shell-background.ts
|
|
8191
|
+
var shell_background_exports = {};
|
|
8192
|
+
__export(shell_background_exports, {
|
|
8193
|
+
BACKGROUNDABLE_TOOL_NAMES: () => BACKGROUNDABLE_TOOL_NAMES,
|
|
8194
|
+
BACKGROUND_MAX_OUTPUT_BYTES: () => BACKGROUND_MAX_OUTPUT_BYTES,
|
|
8195
|
+
BACKGROUND_NOTIFICATION_CHARS: () => BACKGROUND_NOTIFICATION_CHARS,
|
|
8196
|
+
SIZE_WATCHDOG_INTERVAL_MS: () => SIZE_WATCHDOG_INTERVAL_MS,
|
|
8197
|
+
attachBackgroundTaskManager: () => attachBackgroundTaskManager,
|
|
8198
|
+
backgroundAllForegroundTasks: () => backgroundAllForegroundTasks,
|
|
8199
|
+
backgroundMessage: () => backgroundMessage,
|
|
8200
|
+
backgroundTaskName: () => backgroundTaskName,
|
|
8201
|
+
buildBackgroundBody: () => buildBackgroundBody,
|
|
8202
|
+
createShellOutputFile: () => createShellOutputFile,
|
|
8203
|
+
discardFd: () => discardFd,
|
|
8204
|
+
formatFinalResult: () => formatFinalResult,
|
|
8205
|
+
hasAnyForegroundTasks: () => hasAnyForegroundTasks,
|
|
8206
|
+
isAutobackgroundingAllowed: () => isAutobackgroundingAllowed,
|
|
8207
|
+
openOutputFd: () => openOutputFd,
|
|
8208
|
+
readOutputFile: () => readOutputFile,
|
|
8209
|
+
sliceUtf8Safe: () => sliceUtf8Safe,
|
|
8210
|
+
unlinkQuiet: () => unlinkQuiet
|
|
8211
|
+
});
|
|
8212
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
8213
|
+
import {
|
|
8214
|
+
closeSync as closeSync2,
|
|
8215
|
+
constants as fsConstants,
|
|
8216
|
+
fstatSync,
|
|
8217
|
+
mkdirSync as mkdirSync12,
|
|
8218
|
+
openSync as openSync2,
|
|
8219
|
+
readSync,
|
|
8220
|
+
statSync as statSync9,
|
|
8221
|
+
unlinkSync as unlinkSync5
|
|
8222
|
+
} from "fs";
|
|
8223
|
+
import { tmpdir } from "os";
|
|
8224
|
+
import { join as join19 } from "path";
|
|
8142
8225
|
|
|
8143
8226
|
// src/tools/exit-code-hints.ts
|
|
8227
|
+
var exit_code_hints_exports = {};
|
|
8228
|
+
__export(exit_code_hints_exports, {
|
|
8229
|
+
exitCodeHint: () => exitCodeHint,
|
|
8230
|
+
extractBaseCmd: () => extractBaseCmd
|
|
8231
|
+
});
|
|
8144
8232
|
function extractBaseCmd(command) {
|
|
8145
8233
|
const lastSegment = command.split("|").pop()?.trim() ?? command;
|
|
8146
8234
|
const tokens = lastSegment.split(/\s+/);
|
|
@@ -8372,9 +8460,201 @@ function exitCodeHint(command, exitCode) {
|
|
|
8372
8460
|
return "";
|
|
8373
8461
|
}
|
|
8374
8462
|
|
|
8463
|
+
// src/tools/shell-background.ts
|
|
8464
|
+
var BACKGROUND_NOTIFICATION_CHARS = 3e4;
|
|
8465
|
+
var BACKGROUND_MAX_OUTPUT_BYTES = 5 * 1024 * 1024 * 1024;
|
|
8466
|
+
var SIZE_WATCHDOG_INTERVAL_MS = 500;
|
|
8467
|
+
function isAutobackgroundingAllowed(command, disallowed) {
|
|
8468
|
+
const first = /^\S+/.exec(command.trimStart())?.[0] ?? "";
|
|
8469
|
+
const base = first.replace(/^.*\//, "").replace(/^["']|["']$/g, "").toLowerCase();
|
|
8470
|
+
return !disallowed.has(base);
|
|
8471
|
+
}
|
|
8472
|
+
function backgroundTaskName(command) {
|
|
8473
|
+
const flat = command.replace(/\s+/g, " ").trim();
|
|
8474
|
+
return flat.length > 80 ? `${flat.slice(0, 77)}...` : flat;
|
|
8475
|
+
}
|
|
8476
|
+
function backgroundMessage(reason, taskId, timeout) {
|
|
8477
|
+
switch (reason) {
|
|
8478
|
+
case "explicit":
|
|
8479
|
+
return `Command running in background (task_id: ${taskId}). You will be notified when it completes; do not poll. Use TaskStop with task_id to kill it early.`;
|
|
8480
|
+
case "timeout":
|
|
8481
|
+
return `Command exceeded its ${String(timeout)}s timeout and was moved to the background (task_id: ${taskId}). It is still running \u2014 you will be notified when it completes.`;
|
|
8482
|
+
case "user":
|
|
8483
|
+
return `Command was manually backgrounded by the user (task_id: ${taskId}). It is still running \u2014 you will be notified when it completes.`;
|
|
8484
|
+
}
|
|
8485
|
+
}
|
|
8486
|
+
function sliceUtf8Safe(buf, maxBytes) {
|
|
8487
|
+
if (buf.length <= maxBytes) {
|
|
8488
|
+
return buf;
|
|
8489
|
+
}
|
|
8490
|
+
let end = maxBytes;
|
|
8491
|
+
while (end > 0 && (buf[end] & 192) === 128) {
|
|
8492
|
+
end--;
|
|
8493
|
+
}
|
|
8494
|
+
return buf.subarray(0, end);
|
|
8495
|
+
}
|
|
8496
|
+
function readOutputFile(path, maxBytes) {
|
|
8497
|
+
let fd;
|
|
8498
|
+
try {
|
|
8499
|
+
fd = openSync2(path, "r");
|
|
8500
|
+
} catch {
|
|
8501
|
+
return { text: "", size: 0, truncated: false };
|
|
8502
|
+
}
|
|
8503
|
+
try {
|
|
8504
|
+
const size = fstatSync(fd).size;
|
|
8505
|
+
const readLen = Math.min(size, maxBytes + 1);
|
|
8506
|
+
const buf = Buffer.alloc(readLen);
|
|
8507
|
+
let bytesRead = 0;
|
|
8508
|
+
while (bytesRead < readLen) {
|
|
8509
|
+
const count = readSync(fd, buf, bytesRead, readLen - bytesRead, bytesRead);
|
|
8510
|
+
if (count === 0) {
|
|
8511
|
+
break;
|
|
8512
|
+
}
|
|
8513
|
+
bytesRead += count;
|
|
8514
|
+
}
|
|
8515
|
+
return {
|
|
8516
|
+
text: sliceUtf8Safe(buf.subarray(0, bytesRead), maxBytes).toString("utf-8"),
|
|
8517
|
+
size,
|
|
8518
|
+
truncated: size > maxBytes
|
|
8519
|
+
};
|
|
8520
|
+
} finally {
|
|
8521
|
+
closeSync2(fd);
|
|
8522
|
+
}
|
|
8523
|
+
}
|
|
8524
|
+
function unlinkQuiet(path) {
|
|
8525
|
+
try {
|
|
8526
|
+
unlinkSync5(path);
|
|
8527
|
+
} catch {
|
|
8528
|
+
}
|
|
8529
|
+
}
|
|
8530
|
+
function discardFd(fd) {
|
|
8531
|
+
try {
|
|
8532
|
+
closeSync2(fd);
|
|
8533
|
+
} catch {
|
|
8534
|
+
}
|
|
8535
|
+
}
|
|
8536
|
+
function openOutputFd(path) {
|
|
8537
|
+
const flags = process.platform === "win32" ? "w" : fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_APPEND | fsConstants.O_NOFOLLOW;
|
|
8538
|
+
return openSync2(path, flags, 384);
|
|
8539
|
+
}
|
|
8540
|
+
function createShellOutputFile(workDir, sessionId) {
|
|
8541
|
+
const fileName = `shell-${randomBytes3(8).toString("hex")}.output`;
|
|
8542
|
+
const candidates = [];
|
|
8543
|
+
try {
|
|
8544
|
+
const dir = spillDir(workDir, sessionId);
|
|
8545
|
+
mkdirSync12(dir, { recursive: true });
|
|
8546
|
+
candidates.push(join19(dir, fileName));
|
|
8547
|
+
} catch {
|
|
8548
|
+
}
|
|
8549
|
+
candidates.push(join19(tmpdir(), fileName));
|
|
8550
|
+
let lastError = new Error("no candidate output directory");
|
|
8551
|
+
for (const path of candidates) {
|
|
8552
|
+
try {
|
|
8553
|
+
return { path, fd: openOutputFd(path) };
|
|
8554
|
+
} catch (error) {
|
|
8555
|
+
lastError = error;
|
|
8556
|
+
}
|
|
8557
|
+
}
|
|
8558
|
+
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
8559
|
+
}
|
|
8560
|
+
function formatFinalResult(prompt, command, exit, merged, truncated, timeout) {
|
|
8561
|
+
if (exit.spawnError) {
|
|
8562
|
+
return {
|
|
8563
|
+
output: `Error executing command: ${exit.spawnError}`,
|
|
8564
|
+
isError: true
|
|
8565
|
+
};
|
|
8566
|
+
}
|
|
8567
|
+
if (exit.aborted || exit.timedOut) {
|
|
8568
|
+
const captured = merged || truncated ? formatShellOutput(prompt, command, merged, "", truncated) : "";
|
|
8569
|
+
const error = exit.aborted ? "Error: command interrupted" : `Error: command timed out after ${String(timeout)}s`;
|
|
8570
|
+
return {
|
|
8571
|
+
output: captured ? `${captured}
|
|
8572
|
+
${error}` : error,
|
|
8573
|
+
isError: true
|
|
8574
|
+
};
|
|
8575
|
+
}
|
|
8576
|
+
const exitCode = exit.code ?? 0;
|
|
8577
|
+
let output = formatShellOutput(prompt, command, merged, "", truncated);
|
|
8578
|
+
if (!truncated) {
|
|
8579
|
+
if (exitCode !== 0) {
|
|
8580
|
+
const hint = exitCodeHint(command, exitCode);
|
|
8581
|
+
output += hint ? `
|
|
8582
|
+
Exit code ${String(exitCode)} (${hint})` : `
|
|
8583
|
+
Exit code ${String(exitCode)}`;
|
|
8584
|
+
}
|
|
8585
|
+
if (exit.code === null) {
|
|
8586
|
+
output += `
|
|
8587
|
+
Process terminated${exit.signal ? ` by ${exit.signal}` : " unexpectedly"}`;
|
|
8588
|
+
}
|
|
8589
|
+
}
|
|
8590
|
+
return { output, isError: truncated || exitCode !== 0 || exit.code === null };
|
|
8591
|
+
}
|
|
8592
|
+
function buildBackgroundBody(prompt, command, exit, outputPath, timeout, annotate) {
|
|
8593
|
+
let size = 0;
|
|
8594
|
+
try {
|
|
8595
|
+
size = statSync9(outputPath).size;
|
|
8596
|
+
} catch {
|
|
8597
|
+
}
|
|
8598
|
+
let result;
|
|
8599
|
+
if (size <= BACKGROUND_NOTIFICATION_CHARS) {
|
|
8600
|
+
const read = readOutputFile(outputPath, MAX_SHELL_OUTPUT_BYTES);
|
|
8601
|
+
const merged = annotate ? annotate(read.text) : read.text;
|
|
8602
|
+
result = formatFinalResult(prompt, command, exit, merged, read.truncated, timeout);
|
|
8603
|
+
unlinkQuiet(outputPath);
|
|
8604
|
+
} else {
|
|
8605
|
+
const header = formatFinalResult(prompt, command, exit, "", false, timeout);
|
|
8606
|
+
const preview2 = readOutputFile(outputPath, TOOL_RESULT_PREVIEW_CHARS).text;
|
|
8607
|
+
result = {
|
|
8608
|
+
output: `${header.output}
|
|
8609
|
+
${buildPersistedOutputPreview(size, annotate ? annotate(preview2) : preview2, outputPath)}`,
|
|
8610
|
+
isError: header.isError
|
|
8611
|
+
};
|
|
8612
|
+
}
|
|
8613
|
+
if (exit.sizeKilled) {
|
|
8614
|
+
result.output += "\nBackground command killed: output file exceeded 5GB";
|
|
8615
|
+
result.isError = true;
|
|
8616
|
+
}
|
|
8617
|
+
return result;
|
|
8618
|
+
}
|
|
8619
|
+
var BACKGROUNDABLE_TOOL_NAMES = ["Bash", "PowerShell", "JavaScript"];
|
|
8620
|
+
function asBackgroundable(tool) {
|
|
8621
|
+
if (typeof tool === "object" && tool !== null && "taskManager" in tool && "backgroundForegroundTasks" in tool) {
|
|
8622
|
+
return tool;
|
|
8623
|
+
}
|
|
8624
|
+
return null;
|
|
8625
|
+
}
|
|
8626
|
+
function attachBackgroundTaskManager(registry, manager) {
|
|
8627
|
+
for (const name of BACKGROUNDABLE_TOOL_NAMES) {
|
|
8628
|
+
const tool = asBackgroundable(registry.get(name));
|
|
8629
|
+
if (tool) {
|
|
8630
|
+
tool.taskManager = manager;
|
|
8631
|
+
}
|
|
8632
|
+
}
|
|
8633
|
+
}
|
|
8634
|
+
function hasAnyForegroundTasks(registry) {
|
|
8635
|
+
for (const name of BACKGROUNDABLE_TOOL_NAMES) {
|
|
8636
|
+
const tool = asBackgroundable(registry.get(name));
|
|
8637
|
+
if (tool?.hasForegroundTasks()) {
|
|
8638
|
+
return true;
|
|
8639
|
+
}
|
|
8640
|
+
}
|
|
8641
|
+
return false;
|
|
8642
|
+
}
|
|
8643
|
+
function backgroundAllForegroundTasks(registry) {
|
|
8644
|
+
let count = 0;
|
|
8645
|
+
for (const name of BACKGROUNDABLE_TOOL_NAMES) {
|
|
8646
|
+
const tool = asBackgroundable(registry.get(name));
|
|
8647
|
+
if (tool) {
|
|
8648
|
+
count += tool.backgroundForegroundTasks();
|
|
8649
|
+
}
|
|
8650
|
+
}
|
|
8651
|
+
return count;
|
|
8652
|
+
}
|
|
8653
|
+
|
|
8375
8654
|
// src/tools/bash.ts
|
|
8376
8655
|
var MAX_TIMEOUT = 600;
|
|
8377
8656
|
var KILL_GRACE_MS = 3e3;
|
|
8657
|
+
var DISALLOWED_AUTO_BACKGROUND_COMMANDS = /* @__PURE__ */ new Set(["sleep"]);
|
|
8378
8658
|
var BashTool = class {
|
|
8379
8659
|
// Use a hardcoded string instead of BashTool.name.replace("Tool", "")
|
|
8380
8660
|
// because class names are not stable after minification — bundlers like
|
|
@@ -8390,6 +8670,18 @@ var BashTool = class {
|
|
|
8390
8670
|
denyWrite: [],
|
|
8391
8671
|
networkEnabled: true
|
|
8392
8672
|
};
|
|
8673
|
+
/**
|
|
8674
|
+
* Background task registry, injected by the host (TUI / print mode / remote
|
|
8675
|
+
* server) — the same instance the Agent tool uses, so completion
|
|
8676
|
+
* notifications share one drain and TaskStop covers both. When null, Bash is
|
|
8677
|
+
* foreground-only: run_in_background disappears from the schema, timeouts
|
|
8678
|
+
* kill, and Ctrl+B is a no-op. Calls running inside a subagent loop carry
|
|
8679
|
+
* that loop's own manager in ctx.taskManager, which takes precedence.
|
|
8680
|
+
*/
|
|
8681
|
+
taskManager = null;
|
|
8682
|
+
/** Running foreground executions eligible for manual backgrounding (Ctrl+B). */
|
|
8683
|
+
foreground = /* @__PURE__ */ new Map();
|
|
8684
|
+
nextForegroundId = 1;
|
|
8393
8685
|
/**
|
|
8394
8686
|
* Read-only commands can run concurrently with other read-only tools;
|
|
8395
8687
|
* mutating commands must run exclusively.
|
|
@@ -8404,26 +8696,62 @@ var BashTool = class {
|
|
|
8404
8696
|
const command = args.command;
|
|
8405
8697
|
return typeof command === "string" && isSafeCommand(command);
|
|
8406
8698
|
}
|
|
8699
|
+
/**
|
|
8700
|
+
* The background subsystem needs a task manager and can be disabled
|
|
8701
|
+
* wholesale with SWIFTY_DISABLE_BACKGROUND_TASKS=1 (schema parameter
|
|
8702
|
+
* removed, timeouts kill, Ctrl+B becomes a no-op). Instance-level gate used
|
|
8703
|
+
* by the schema; execute() re-checks with the ctx-resolved manager.
|
|
8704
|
+
*/
|
|
8705
|
+
backgroundEnabled() {
|
|
8706
|
+
return this.taskManager !== null && process.env.SWIFTY_DISABLE_BACKGROUND_TASKS !== "1";
|
|
8707
|
+
}
|
|
8708
|
+
/** True while at least one foreground Bash command runs (gates the Ctrl+B handler). */
|
|
8709
|
+
hasForegroundTasks() {
|
|
8710
|
+
return this.foreground.size > 0;
|
|
8711
|
+
}
|
|
8712
|
+
/**
|
|
8713
|
+
* Move every running foreground Bash command to the background (Ctrl+B).
|
|
8714
|
+
* Returns how many commands were actually backgrounded.
|
|
8715
|
+
*/
|
|
8716
|
+
backgroundForegroundTasks() {
|
|
8717
|
+
let count = 0;
|
|
8718
|
+
for (const entry of [...this.foreground.values()]) {
|
|
8719
|
+
if (entry.background()) {
|
|
8720
|
+
count++;
|
|
8721
|
+
}
|
|
8722
|
+
}
|
|
8723
|
+
return count;
|
|
8724
|
+
}
|
|
8407
8725
|
schema() {
|
|
8408
|
-
const
|
|
8409
|
-
|
|
8410
|
-
|
|
8411
|
-
|
|
8412
|
-
type: "string",
|
|
8413
|
-
description: "Shell command to execute"
|
|
8414
|
-
},
|
|
8415
|
-
timeout: {
|
|
8416
|
-
type: "integer",
|
|
8417
|
-
description: "Timeout in seconds (max 600)",
|
|
8418
|
-
default: 120
|
|
8419
|
-
}
|
|
8726
|
+
const properties = {
|
|
8727
|
+
command: {
|
|
8728
|
+
type: "string",
|
|
8729
|
+
description: "Shell command to execute"
|
|
8420
8730
|
},
|
|
8421
|
-
|
|
8731
|
+
timeout: {
|
|
8732
|
+
type: "integer",
|
|
8733
|
+
description: "Timeout in seconds (max 600)",
|
|
8734
|
+
default: 120
|
|
8735
|
+
}
|
|
8422
8736
|
};
|
|
8737
|
+
let description = this.description;
|
|
8738
|
+
if (this.backgroundEnabled()) {
|
|
8739
|
+
properties.run_in_background = {
|
|
8740
|
+
type: "boolean",
|
|
8741
|
+
description: "Run the command in the background. Returns a task ID immediately; the result arrives later as a task notification.",
|
|
8742
|
+
default: false
|
|
8743
|
+
};
|
|
8744
|
+
description = `${this.description}
|
|
8745
|
+
${BASH_BACKGROUND_DESCRIPTION}`;
|
|
8746
|
+
}
|
|
8423
8747
|
return {
|
|
8424
8748
|
name: this.name,
|
|
8425
|
-
description
|
|
8426
|
-
input_schema:
|
|
8749
|
+
description,
|
|
8750
|
+
input_schema: {
|
|
8751
|
+
type: "object",
|
|
8752
|
+
properties,
|
|
8753
|
+
required: ["command"]
|
|
8754
|
+
}
|
|
8427
8755
|
};
|
|
8428
8756
|
}
|
|
8429
8757
|
async execute(ctx, args) {
|
|
@@ -8444,12 +8772,29 @@ var BashTool = class {
|
|
|
8444
8772
|
if (timeout > MAX_TIMEOUT) {
|
|
8445
8773
|
timeout = MAX_TIMEOUT;
|
|
8446
8774
|
}
|
|
8775
|
+
const manager = ctx.taskManager !== void 0 ? ctx.taskManager : this.taskManager;
|
|
8776
|
+
const backgroundAvailable = manager !== null && process.env.SWIFTY_DISABLE_BACKGROUND_TASKS !== "1";
|
|
8777
|
+
const runInBackground = boolArg(args, "run_in_background") && backgroundAvailable;
|
|
8778
|
+
let outputFile;
|
|
8779
|
+
try {
|
|
8780
|
+
outputFile = createShellOutputFile(ctx.workDir, ctx.sessionId ?? "");
|
|
8781
|
+
} catch (error) {
|
|
8782
|
+
return {
|
|
8783
|
+
output: `Error creating output file: ${asErrorString(error)}`,
|
|
8784
|
+
isError: true
|
|
8785
|
+
};
|
|
8786
|
+
}
|
|
8787
|
+
const discardOutputFile = () => {
|
|
8788
|
+
discardFd(outputFile.fd);
|
|
8789
|
+
unlinkQuiet(outputFile.path);
|
|
8790
|
+
};
|
|
8447
8791
|
let prepared = {
|
|
8448
8792
|
executable: "bash",
|
|
8449
8793
|
args: ["-c", command]
|
|
8450
8794
|
};
|
|
8451
8795
|
if (this.sandboxRequired || this.sandbox) {
|
|
8452
8796
|
if (!this.sandbox) {
|
|
8797
|
+
discardOutputFile();
|
|
8453
8798
|
return {
|
|
8454
8799
|
output: "Error: sandbox is enabled but unavailable; command was not executed",
|
|
8455
8800
|
isError: true
|
|
@@ -8457,17 +8802,28 @@ var BashTool = class {
|
|
|
8457
8802
|
}
|
|
8458
8803
|
try {
|
|
8459
8804
|
if (!await this.sandbox.available()) {
|
|
8805
|
+
discardOutputFile();
|
|
8460
8806
|
return {
|
|
8461
8807
|
output: `Error: ${this.sandbox.implementation} sandbox is unavailable; command was not executed`,
|
|
8462
8808
|
isError: true
|
|
8463
8809
|
};
|
|
8464
8810
|
}
|
|
8465
|
-
prepared = await this.sandbox.prepare(
|
|
8466
|
-
|
|
8467
|
-
|
|
8468
|
-
|
|
8469
|
-
|
|
8811
|
+
prepared = await this.sandbox.prepare(
|
|
8812
|
+
command,
|
|
8813
|
+
{
|
|
8814
|
+
...this.sandboxConfig,
|
|
8815
|
+
// The child writes its output file directly; grant write access to
|
|
8816
|
+
// that path even under a strict allowWrite config.
|
|
8817
|
+
allowWrite: [...this.sandboxConfig.allowWrite, outputFile.path]
|
|
8818
|
+
},
|
|
8819
|
+
{
|
|
8820
|
+
cwd: ctx.workDir,
|
|
8821
|
+
abortSignal: ctx.abortSignal,
|
|
8822
|
+
commandId: ctx.toolCallId
|
|
8823
|
+
}
|
|
8824
|
+
);
|
|
8470
8825
|
} catch (error) {
|
|
8826
|
+
discardOutputFile();
|
|
8471
8827
|
return {
|
|
8472
8828
|
output: `Error preparing sandbox: ${asErrorString(error)}`,
|
|
8473
8829
|
isError: true
|
|
@@ -8475,6 +8831,7 @@ var BashTool = class {
|
|
|
8475
8831
|
}
|
|
8476
8832
|
}
|
|
8477
8833
|
if (ctx.abortSignal?.aborted) {
|
|
8834
|
+
discardOutputFile();
|
|
8478
8835
|
try {
|
|
8479
8836
|
await prepared.cleanup?.();
|
|
8480
8837
|
} catch {
|
|
@@ -8484,22 +8841,58 @@ var BashTool = class {
|
|
|
8484
8841
|
isError: true
|
|
8485
8842
|
};
|
|
8486
8843
|
}
|
|
8487
|
-
|
|
8488
|
-
|
|
8844
|
+
const handle = this.startCommand(ctx, prepared, command, timeout, outputFile, manager);
|
|
8845
|
+
if (runInBackground) {
|
|
8846
|
+
const taskId = handle.background("explicit");
|
|
8847
|
+
if (taskId !== null) {
|
|
8848
|
+
return {
|
|
8849
|
+
output: backgroundMessage("explicit", taskId, timeout),
|
|
8850
|
+
isError: false
|
|
8851
|
+
};
|
|
8852
|
+
}
|
|
8853
|
+
}
|
|
8854
|
+
return handle.result;
|
|
8855
|
+
}
|
|
8856
|
+
/**
|
|
8857
|
+
* Spawn the command with stdout+stderr writing directly into the output
|
|
8858
|
+
* file (ccb's file-descriptor mode): output never flows through JS, so
|
|
8859
|
+
* backgrounding is a bookkeeping switch — no re-spawn, no buffer handover.
|
|
8860
|
+
* The returned handle exposes the tool-call promise plus a background()
|
|
8861
|
+
* trigger that transitions the running command into a TaskManager task.
|
|
8862
|
+
*/
|
|
8863
|
+
startCommand(ctx, prepared, command, timeout, outputFile, manager) {
|
|
8864
|
+
let backgroundFn;
|
|
8865
|
+
const result = new Promise((resolve12) => {
|
|
8489
8866
|
let aborted = false;
|
|
8490
8867
|
let terminating = false;
|
|
8491
8868
|
let settled = false;
|
|
8869
|
+
let backgrounded = false;
|
|
8870
|
+
let sizeKilled = false;
|
|
8492
8871
|
let escalateTimer = null;
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
8872
|
+
let child;
|
|
8873
|
+
try {
|
|
8874
|
+
child = spawn2(prepared.executable, prepared.args, {
|
|
8875
|
+
cwd: ctx.workDir,
|
|
8876
|
+
detached: true,
|
|
8877
|
+
env: prepared.env,
|
|
8878
|
+
// stdout and stderr share one O_APPEND fd: each write lands on disk
|
|
8879
|
+
// atomically and the streams interleave chronologically.
|
|
8880
|
+
stdio: ["ignore", outputFile.fd, outputFile.fd]
|
|
8881
|
+
});
|
|
8882
|
+
} catch (error) {
|
|
8883
|
+
discardFd(outputFile.fd);
|
|
8884
|
+
unlinkQuiet(outputFile.path);
|
|
8885
|
+
resolve12({
|
|
8886
|
+
output: `Error executing command: ${asErrorString(error)}`,
|
|
8887
|
+
isError: true
|
|
8888
|
+
});
|
|
8889
|
+
return;
|
|
8890
|
+
}
|
|
8891
|
+
discardFd(outputFile.fd);
|
|
8892
|
+
let doneResolve;
|
|
8893
|
+
const done = new Promise((resolveDone) => {
|
|
8894
|
+
doneResolve = resolveDone;
|
|
8498
8895
|
});
|
|
8499
|
-
let stdout = "";
|
|
8500
|
-
let stderr = "";
|
|
8501
|
-
let total = 0;
|
|
8502
|
-
let outputTruncated = false;
|
|
8503
8896
|
const killTree = (signal) => {
|
|
8504
8897
|
if (typeof child.pid !== "number") {
|
|
8505
8898
|
return;
|
|
@@ -8521,41 +8914,35 @@ var BashTool = class {
|
|
|
8521
8914
|
killTree("SIGTERM");
|
|
8522
8915
|
escalateTimer = setTimeout(() => {
|
|
8523
8916
|
killTree("SIGKILL");
|
|
8524
|
-
child.stdout.destroy();
|
|
8525
|
-
child.stderr.destroy();
|
|
8526
8917
|
}, KILL_GRACE_MS);
|
|
8527
8918
|
escalateTimer.unref();
|
|
8528
8919
|
};
|
|
8529
|
-
const
|
|
8530
|
-
|
|
8920
|
+
const watchdog = setInterval(() => {
|
|
8921
|
+
let size = 0;
|
|
8922
|
+
try {
|
|
8923
|
+
size = statSync10(outputFile.path).size;
|
|
8924
|
+
} catch {
|
|
8531
8925
|
return;
|
|
8532
8926
|
}
|
|
8533
|
-
const
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
outputTruncated = true;
|
|
8927
|
+
const cap = backgrounded ? BACKGROUND_MAX_OUTPUT_BYTES : MAX_SHELL_OUTPUT_BYTES;
|
|
8928
|
+
if (size > cap) {
|
|
8929
|
+
sizeKilled = backgrounded;
|
|
8930
|
+
clearInterval(watchdog);
|
|
8538
8931
|
terminate();
|
|
8539
8932
|
}
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
} else {
|
|
8543
|
-
stderr += piece;
|
|
8544
|
-
}
|
|
8545
|
-
};
|
|
8546
|
-
child.stdout.setEncoding("utf-8");
|
|
8547
|
-
child.stdout.on("data", (chunk) => {
|
|
8548
|
-
appendChunk(chunk, "stdout");
|
|
8549
|
-
});
|
|
8550
|
-
child.stderr.setEncoding("utf-8");
|
|
8551
|
-
child.stderr.on("data", (chunk) => {
|
|
8552
|
-
appendChunk(chunk, "stderr");
|
|
8553
|
-
});
|
|
8933
|
+
}, SIZE_WATCHDOG_INTERVAL_MS);
|
|
8934
|
+
watchdog.unref();
|
|
8554
8935
|
const onAbort = () => {
|
|
8555
8936
|
aborted = true;
|
|
8556
8937
|
terminate();
|
|
8557
8938
|
};
|
|
8939
|
+
const backgroundAvailableHere = manager !== null && process.env.SWIFTY_DISABLE_BACKGROUND_TASKS !== "1";
|
|
8940
|
+
const autoBackgroundAllowed = backgroundAvailableHere && isAutobackgroundingAllowed(command, DISALLOWED_AUTO_BACKGROUND_COMMANDS);
|
|
8941
|
+
let timedOut = false;
|
|
8558
8942
|
const timeoutTimer = setTimeout(() => {
|
|
8943
|
+
if (autoBackgroundAllowed && backgroundExecution("timeout") !== null) {
|
|
8944
|
+
return;
|
|
8945
|
+
}
|
|
8559
8946
|
timedOut = true;
|
|
8560
8947
|
terminate();
|
|
8561
8948
|
}, timeout * 1e3);
|
|
@@ -8564,14 +8951,17 @@ var BashTool = class {
|
|
|
8564
8951
|
if (ctx.abortSignal?.aborted) {
|
|
8565
8952
|
onAbort();
|
|
8566
8953
|
}
|
|
8954
|
+
const foregroundKey = `bash-${String(this.nextForegroundId++)}`;
|
|
8567
8955
|
const cleanup = () => {
|
|
8568
8956
|
clearTimeout(timeoutTimer);
|
|
8957
|
+
clearInterval(watchdog);
|
|
8569
8958
|
if (escalateTimer) {
|
|
8570
8959
|
clearTimeout(escalateTimer);
|
|
8571
8960
|
}
|
|
8572
8961
|
ctx.abortSignal?.removeEventListener("abort", onAbort);
|
|
8962
|
+
this.foreground.delete(foregroundKey);
|
|
8573
8963
|
};
|
|
8574
|
-
const settle = (
|
|
8964
|
+
const settle = (finalResult) => {
|
|
8575
8965
|
if (settled) {
|
|
8576
8966
|
return;
|
|
8577
8967
|
}
|
|
@@ -8582,54 +8972,105 @@ var BashTool = class {
|
|
|
8582
8972
|
};
|
|
8583
8973
|
void finalize().then(
|
|
8584
8974
|
() => {
|
|
8585
|
-
resolve12(
|
|
8975
|
+
resolve12(finalResult);
|
|
8586
8976
|
},
|
|
8587
8977
|
(error) => {
|
|
8588
8978
|
resolve12({
|
|
8589
|
-
output: `${
|
|
8979
|
+
output: `${finalResult.output}
|
|
8590
8980
|
Error cleaning up sandbox: ${asErrorString(error)}`,
|
|
8591
8981
|
isError: true
|
|
8592
8982
|
});
|
|
8593
8983
|
}
|
|
8594
8984
|
);
|
|
8595
8985
|
};
|
|
8596
|
-
|
|
8597
|
-
|
|
8598
|
-
output: `Error executing command: ${error.message}`,
|
|
8599
|
-
isError: true
|
|
8600
|
-
});
|
|
8601
|
-
});
|
|
8602
|
-
child.on("close", (code, signal) => {
|
|
8603
|
-
stderr = prepared.annotateStderr?.(stderr) ?? stderr;
|
|
8604
|
-
if (aborted || timedOut) {
|
|
8605
|
-
const captured = stdout || stderr || outputTruncated ? formatShellOutput("$ ", command, stdout, stderr, outputTruncated) : "";
|
|
8606
|
-
const error = aborted ? "Error: command interrupted" : `Error: command timed out after ${String(timeout)}s`;
|
|
8607
|
-
settle({
|
|
8608
|
-
output: captured ? `${captured}
|
|
8609
|
-
${error}` : error,
|
|
8610
|
-
isError: true
|
|
8611
|
-
});
|
|
8612
|
-
return;
|
|
8613
|
-
}
|
|
8614
|
-
const exitCode = code ?? 0;
|
|
8615
|
-
let output = formatShellOutput("$ ", command, stdout, stderr, outputTruncated);
|
|
8616
|
-
if (outputTruncated) {
|
|
8617
|
-
settle({ output, isError: true });
|
|
8986
|
+
const settleExit = (exit) => {
|
|
8987
|
+
if (backgrounded) {
|
|
8618
8988
|
return;
|
|
8619
8989
|
}
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
|
|
8623
|
-
|
|
8624
|
-
|
|
8990
|
+
const read = readOutputFile(outputFile.path, MAX_SHELL_OUTPUT_BYTES);
|
|
8991
|
+
const merged = prepared.annotateStderr?.(read.text) ?? read.text;
|
|
8992
|
+
const finalResult = formatFinalResult("$ ", command, exit, merged, read.truncated, timeout);
|
|
8993
|
+
unlinkQuiet(outputFile.path);
|
|
8994
|
+
settle(finalResult);
|
|
8995
|
+
};
|
|
8996
|
+
const backgroundExecution = (reason) => {
|
|
8997
|
+
if (backgrounded || settled || terminating || !manager || !backgroundAvailableHere) {
|
|
8998
|
+
return null;
|
|
8625
8999
|
}
|
|
8626
|
-
|
|
8627
|
-
|
|
8628
|
-
|
|
9000
|
+
backgrounded = true;
|
|
9001
|
+
settled = true;
|
|
9002
|
+
clearTimeout(timeoutTimer);
|
|
9003
|
+
ctx.abortSignal?.removeEventListener("abort", onAbort);
|
|
9004
|
+
this.foreground.delete(foregroundKey);
|
|
9005
|
+
const task = manager.create(
|
|
9006
|
+
backgroundTaskName(command),
|
|
9007
|
+
async () => {
|
|
9008
|
+
const exit = await done;
|
|
9009
|
+
try {
|
|
9010
|
+
await prepared.cleanup?.();
|
|
9011
|
+
} catch {
|
|
9012
|
+
}
|
|
9013
|
+
const annotate = prepared.annotateStderr ? (text) => prepared.annotateStderr?.(text) ?? text : void 0;
|
|
9014
|
+
const body = buildBackgroundBody(
|
|
9015
|
+
"$ ",
|
|
9016
|
+
command,
|
|
9017
|
+
exit,
|
|
9018
|
+
outputFile.path,
|
|
9019
|
+
timeout,
|
|
9020
|
+
annotate
|
|
9021
|
+
);
|
|
9022
|
+
if (body.isError) {
|
|
9023
|
+
throw new TaskFailure(body.output);
|
|
9024
|
+
}
|
|
9025
|
+
return body.output;
|
|
9026
|
+
},
|
|
9027
|
+
() => {
|
|
9028
|
+
killTree("SIGKILL");
|
|
9029
|
+
},
|
|
9030
|
+
{ originToolCallId: ctx.toolCallId, idPrefix: "bash", kind: "shell" }
|
|
9031
|
+
);
|
|
9032
|
+
resolve12({
|
|
9033
|
+
output: backgroundMessage(reason, task.id, timeout),
|
|
9034
|
+
isError: false
|
|
9035
|
+
});
|
|
9036
|
+
return task.id;
|
|
9037
|
+
};
|
|
9038
|
+
if (backgroundAvailableHere) {
|
|
9039
|
+
this.foreground.set(foregroundKey, {
|
|
9040
|
+
background: () => backgroundExecution("user") !== null
|
|
9041
|
+
});
|
|
9042
|
+
}
|
|
9043
|
+
const stopTimers = () => {
|
|
9044
|
+
clearInterval(watchdog);
|
|
9045
|
+
if (escalateTimer) {
|
|
9046
|
+
clearTimeout(escalateTimer);
|
|
8629
9047
|
}
|
|
8630
|
-
|
|
9048
|
+
};
|
|
9049
|
+
child.on("error", (error) => {
|
|
9050
|
+
stopTimers();
|
|
9051
|
+
const exit = {
|
|
9052
|
+
code: null,
|
|
9053
|
+
signal: null,
|
|
9054
|
+
aborted,
|
|
9055
|
+
timedOut,
|
|
9056
|
+
sizeKilled,
|
|
9057
|
+
spawnError: error.message
|
|
9058
|
+
};
|
|
9059
|
+
doneResolve?.(exit);
|
|
9060
|
+
settleExit(exit);
|
|
9061
|
+
});
|
|
9062
|
+
child.on("close", (code, signal) => {
|
|
9063
|
+
stopTimers();
|
|
9064
|
+
const exit = { code, signal, aborted, timedOut, sizeKilled };
|
|
9065
|
+
doneResolve?.(exit);
|
|
9066
|
+
settleExit(exit);
|
|
8631
9067
|
});
|
|
9068
|
+
backgroundFn = backgroundExecution;
|
|
8632
9069
|
});
|
|
9070
|
+
return {
|
|
9071
|
+
result,
|
|
9072
|
+
background: (reason) => backgroundFn?.(reason) ?? null
|
|
9073
|
+
};
|
|
8633
9074
|
}
|
|
8634
9075
|
};
|
|
8635
9076
|
|
|
@@ -8640,8 +9081,8 @@ __export(computer_use_exports, {
|
|
|
8640
9081
|
});
|
|
8641
9082
|
import { spawn as spawn3 } from "child_process";
|
|
8642
9083
|
import { mkdtemp, readFile as readFile4, rm, writeFile as writeFile2 } from "fs/promises";
|
|
8643
|
-
import { tmpdir } from "os";
|
|
8644
|
-
import { join as
|
|
9084
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
9085
|
+
import { join as join20 } from "path";
|
|
8645
9086
|
import { setTimeout as delay } from "timers/promises";
|
|
8646
9087
|
import sharp from "sharp";
|
|
8647
9088
|
import { safeParse as safeParse2, z as z14 } from "zod";
|
|
@@ -9718,9 +10159,9 @@ var ComputerUseTool = class {
|
|
|
9718
10159
|
}
|
|
9719
10160
|
}
|
|
9720
10161
|
async compileMacHelper(signal) {
|
|
9721
|
-
const directory = await mkdtemp(
|
|
9722
|
-
const sourcePath =
|
|
9723
|
-
const executablePath =
|
|
10162
|
+
const directory = await mkdtemp(join20(tmpdir2(), "swifty-computer-helper-"));
|
|
10163
|
+
const sourcePath = join20(directory, "main.swift");
|
|
10164
|
+
const executablePath = join20(directory, "computer-helper");
|
|
9724
10165
|
try {
|
|
9725
10166
|
await writeFile2(sourcePath, MACOS_SNIPPET, "utf8");
|
|
9726
10167
|
const result = await this.run(
|
|
@@ -9937,8 +10378,8 @@ var ComputerUseTool = class {
|
|
|
9937
10378
|
return { output, contentBlocks: [imageBlock], isError: false };
|
|
9938
10379
|
}
|
|
9939
10380
|
async captureScreenshot(signal) {
|
|
9940
|
-
const directory = await mkdtemp(
|
|
9941
|
-
const screenshotPath =
|
|
10381
|
+
const directory = await mkdtemp(join20(tmpdir2(), "swifty-computer-"));
|
|
10382
|
+
const screenshotPath = join20(directory, "screenshot.png");
|
|
9942
10383
|
try {
|
|
9943
10384
|
switch (this.platform) {
|
|
9944
10385
|
case "darwin": {
|
|
@@ -10215,8 +10656,10 @@ __export(powershell_exports, {
|
|
|
10215
10656
|
PowerShellTool: () => PowerShellTool
|
|
10216
10657
|
});
|
|
10217
10658
|
import { execFile as execFile2, spawn as spawn4 } from "child_process";
|
|
10659
|
+
import { statSync as statSync11 } from "fs";
|
|
10218
10660
|
var MAX_TIMEOUT2 = 600;
|
|
10219
10661
|
var KILL_GRACE_MS2 = 3e3;
|
|
10662
|
+
var DISALLOWED_AUTO_BACKGROUND_COMMANDS2 = /* @__PURE__ */ new Set(["start-sleep", "sleep"]);
|
|
10220
10663
|
var PowerShellTool = class {
|
|
10221
10664
|
// Use a hardcoded string instead of PowerShellTool.name.replace("Tool", "")
|
|
10222
10665
|
// because class names are not stable after minification — bundlers like
|
|
@@ -10224,57 +10667,130 @@ var PowerShellTool = class {
|
|
|
10224
10667
|
name = "PowerShell";
|
|
10225
10668
|
description = POWERSHELL_DESCRIPTION;
|
|
10226
10669
|
category = "command";
|
|
10670
|
+
/**
|
|
10671
|
+
* Background task registry, injected by the host — same contract as
|
|
10672
|
+
* BashTool.taskManager (schema gating, timeout auto-background, Ctrl+B).
|
|
10673
|
+
* Calls running inside a subagent loop carry that loop's own manager in
|
|
10674
|
+
* ctx.taskManager, which takes precedence.
|
|
10675
|
+
*/
|
|
10676
|
+
taskManager = null;
|
|
10677
|
+
/** Running foreground executions eligible for manual backgrounding (Ctrl+B). */
|
|
10678
|
+
foreground = /* @__PURE__ */ new Map();
|
|
10679
|
+
nextForegroundId = 1;
|
|
10680
|
+
/** Instance-level background gate; see BashTool.backgroundEnabled. */
|
|
10681
|
+
backgroundEnabled() {
|
|
10682
|
+
return this.taskManager !== null && process.env.SWIFTY_DISABLE_BACKGROUND_TASKS !== "1";
|
|
10683
|
+
}
|
|
10684
|
+
/** True while at least one foreground PowerShell command runs. */
|
|
10685
|
+
hasForegroundTasks() {
|
|
10686
|
+
return this.foreground.size > 0;
|
|
10687
|
+
}
|
|
10688
|
+
/** Move every running foreground command to the background (Ctrl+B). */
|
|
10689
|
+
backgroundForegroundTasks() {
|
|
10690
|
+
let count = 0;
|
|
10691
|
+
for (const entry of [...this.foreground.values()]) {
|
|
10692
|
+
if (entry.background()) {
|
|
10693
|
+
count++;
|
|
10694
|
+
}
|
|
10695
|
+
}
|
|
10696
|
+
return count;
|
|
10697
|
+
}
|
|
10227
10698
|
schema() {
|
|
10228
|
-
const
|
|
10229
|
-
|
|
10230
|
-
|
|
10231
|
-
|
|
10232
|
-
type: "string",
|
|
10233
|
-
description: "PowerShell command to execute"
|
|
10234
|
-
},
|
|
10235
|
-
timeout: {
|
|
10236
|
-
type: "integer",
|
|
10237
|
-
description: "Timeout in seconds (max 600)",
|
|
10238
|
-
default: 120
|
|
10239
|
-
}
|
|
10699
|
+
const properties = {
|
|
10700
|
+
command: {
|
|
10701
|
+
type: "string",
|
|
10702
|
+
description: "PowerShell command to execute"
|
|
10240
10703
|
},
|
|
10241
|
-
|
|
10704
|
+
timeout: {
|
|
10705
|
+
type: "integer",
|
|
10706
|
+
description: "Timeout in seconds (max 600)",
|
|
10707
|
+
default: 120
|
|
10708
|
+
}
|
|
10242
10709
|
};
|
|
10710
|
+
let description = this.description;
|
|
10711
|
+
if (this.backgroundEnabled()) {
|
|
10712
|
+
properties.run_in_background = {
|
|
10713
|
+
type: "boolean",
|
|
10714
|
+
description: "Run the command in the background. Returns a task ID immediately; the result arrives later as a task notification.",
|
|
10715
|
+
default: false
|
|
10716
|
+
};
|
|
10717
|
+
description = `${this.description}
|
|
10718
|
+
${POWERSHELL_BACKGROUND_DESCRIPTION}`;
|
|
10719
|
+
}
|
|
10243
10720
|
return {
|
|
10244
10721
|
name: this.name,
|
|
10245
|
-
description
|
|
10246
|
-
input_schema:
|
|
10722
|
+
description,
|
|
10723
|
+
input_schema: {
|
|
10724
|
+
type: "object",
|
|
10725
|
+
properties,
|
|
10726
|
+
required: ["command"]
|
|
10727
|
+
}
|
|
10247
10728
|
};
|
|
10248
10729
|
}
|
|
10249
|
-
execute(ctx, args) {
|
|
10730
|
+
async execute(ctx, args) {
|
|
10250
10731
|
const command = strArg(args, "command");
|
|
10251
10732
|
if (!command) {
|
|
10252
|
-
return
|
|
10733
|
+
return {
|
|
10253
10734
|
output: "Error: command is required",
|
|
10254
10735
|
isError: true
|
|
10255
|
-
}
|
|
10736
|
+
};
|
|
10256
10737
|
}
|
|
10257
10738
|
let timeout = intArg(args, "timeout", 120);
|
|
10258
10739
|
if (!Number.isFinite(timeout) || timeout <= 0) {
|
|
10259
|
-
return
|
|
10740
|
+
return {
|
|
10260
10741
|
output: "Error: timeout must be a finite number greater than 0 seconds",
|
|
10261
10742
|
isError: true
|
|
10262
|
-
}
|
|
10743
|
+
};
|
|
10263
10744
|
}
|
|
10264
10745
|
if (timeout > MAX_TIMEOUT2) {
|
|
10265
10746
|
timeout = MAX_TIMEOUT2;
|
|
10266
10747
|
}
|
|
10748
|
+
const manager = ctx.taskManager !== void 0 ? ctx.taskManager : this.taskManager;
|
|
10749
|
+
const backgroundAvailable = manager !== null && process.env.SWIFTY_DISABLE_BACKGROUND_TASKS !== "1";
|
|
10750
|
+
const runInBackground = boolArg(args, "run_in_background") && backgroundAvailable;
|
|
10267
10751
|
const shell = process.platform === "win32" ? "powershell.exe" : "pwsh";
|
|
10752
|
+
let outputFile;
|
|
10753
|
+
try {
|
|
10754
|
+
outputFile = createShellOutputFile(ctx.workDir, ctx.sessionId ?? "");
|
|
10755
|
+
} catch (error) {
|
|
10756
|
+
return {
|
|
10757
|
+
output: `Error creating output file: ${asErrorString(error)}`,
|
|
10758
|
+
isError: true
|
|
10759
|
+
};
|
|
10760
|
+
}
|
|
10268
10761
|
if (ctx.abortSignal?.aborted) {
|
|
10269
|
-
|
|
10762
|
+
discardFd(outputFile.fd);
|
|
10763
|
+
unlinkQuiet(outputFile.path);
|
|
10764
|
+
return {
|
|
10270
10765
|
output: "Error: command interrupted",
|
|
10271
10766
|
isError: true
|
|
10272
|
-
}
|
|
10767
|
+
};
|
|
10273
10768
|
}
|
|
10274
|
-
|
|
10275
|
-
|
|
10769
|
+
const handle = this.startCommand(ctx, shell, command, timeout, outputFile, manager);
|
|
10770
|
+
if (runInBackground) {
|
|
10771
|
+
const taskId = handle.background("explicit");
|
|
10772
|
+
if (taskId !== null) {
|
|
10773
|
+
return {
|
|
10774
|
+
output: backgroundMessage("explicit", taskId, timeout),
|
|
10775
|
+
isError: false
|
|
10776
|
+
};
|
|
10777
|
+
}
|
|
10778
|
+
}
|
|
10779
|
+
return handle.result;
|
|
10780
|
+
}
|
|
10781
|
+
/**
|
|
10782
|
+
* Spawn PowerShell with stdout+stderr writing directly into the output file
|
|
10783
|
+
* (fd mode, shared with BashTool): output never flows through JS, so
|
|
10784
|
+
* backgrounding is a bookkeeping switch — no re-spawn, no buffer handover.
|
|
10785
|
+
*/
|
|
10786
|
+
startCommand(ctx, shell, command, timeout, outputFile, manager) {
|
|
10787
|
+
let backgroundFn;
|
|
10788
|
+
const result = new Promise((resolve12) => {
|
|
10276
10789
|
let aborted = false;
|
|
10277
10790
|
let terminating = false;
|
|
10791
|
+
let settled = false;
|
|
10792
|
+
let backgrounded = false;
|
|
10793
|
+
let sizeKilled = false;
|
|
10278
10794
|
let escalateTimer = null;
|
|
10279
10795
|
const shellCommand = process.platform === "win32" ? "try { [Console]::OutputEncoding=[System.Text.Encoding]::UTF8 } catch {}\n" + command : command;
|
|
10280
10796
|
const shellArgs = process.platform === "win32" ? [
|
|
@@ -10285,17 +10801,31 @@ var PowerShellTool = class {
|
|
|
10285
10801
|
"-Command",
|
|
10286
10802
|
shellCommand
|
|
10287
10803
|
] : ["-NoProfile", "-NonInteractive", "-Command", shellCommand];
|
|
10288
|
-
|
|
10289
|
-
|
|
10290
|
-
|
|
10291
|
-
|
|
10292
|
-
|
|
10293
|
-
|
|
10804
|
+
let child;
|
|
10805
|
+
try {
|
|
10806
|
+
child = spawn4(shell, shellArgs, {
|
|
10807
|
+
cwd: ctx.workDir,
|
|
10808
|
+
// Only POSIX needs its own process group for kill(-pid); the Windows
|
|
10809
|
+
// tree kill goes through taskkill and needs no new group.
|
|
10810
|
+
detached: process.platform !== "win32",
|
|
10811
|
+
// stdout and stderr share one O_APPEND fd: each write lands on disk
|
|
10812
|
+
// atomically and the streams interleave chronologically.
|
|
10813
|
+
stdio: ["ignore", outputFile.fd, outputFile.fd]
|
|
10814
|
+
});
|
|
10815
|
+
} catch (error) {
|
|
10816
|
+
discardFd(outputFile.fd);
|
|
10817
|
+
unlinkQuiet(outputFile.path);
|
|
10818
|
+
resolve12({
|
|
10819
|
+
output: `Error executing command: ${asErrorString(error)}`,
|
|
10820
|
+
isError: true
|
|
10821
|
+
});
|
|
10822
|
+
return;
|
|
10823
|
+
}
|
|
10824
|
+
discardFd(outputFile.fd);
|
|
10825
|
+
let doneResolve;
|
|
10826
|
+
const done = new Promise((resolveDone) => {
|
|
10827
|
+
doneResolve = resolveDone;
|
|
10294
10828
|
});
|
|
10295
|
-
let stdout = "";
|
|
10296
|
-
let stderr = "";
|
|
10297
|
-
let total = 0;
|
|
10298
|
-
let outputTruncated = false;
|
|
10299
10829
|
const alreadyExited = () => child.exitCode !== null || child.signalCode !== null;
|
|
10300
10830
|
const killTree = (signal) => {
|
|
10301
10831
|
if (typeof child.pid !== "number") {
|
|
@@ -10333,41 +10863,35 @@ var PowerShellTool = class {
|
|
|
10333
10863
|
killTree("SIGTERM");
|
|
10334
10864
|
escalateTimer = setTimeout(() => {
|
|
10335
10865
|
killTree("SIGKILL");
|
|
10336
|
-
child.stdout.destroy();
|
|
10337
|
-
child.stderr.destroy();
|
|
10338
10866
|
}, KILL_GRACE_MS2);
|
|
10339
10867
|
escalateTimer.unref();
|
|
10340
10868
|
};
|
|
10341
|
-
const
|
|
10342
|
-
|
|
10869
|
+
const watchdog = setInterval(() => {
|
|
10870
|
+
let size = 0;
|
|
10871
|
+
try {
|
|
10872
|
+
size = statSync11(outputFile.path).size;
|
|
10873
|
+
} catch {
|
|
10343
10874
|
return;
|
|
10344
10875
|
}
|
|
10345
|
-
const
|
|
10346
|
-
|
|
10347
|
-
|
|
10348
|
-
|
|
10349
|
-
outputTruncated = true;
|
|
10876
|
+
const cap = backgrounded ? BACKGROUND_MAX_OUTPUT_BYTES : MAX_SHELL_OUTPUT_BYTES;
|
|
10877
|
+
if (size > cap) {
|
|
10878
|
+
sizeKilled = backgrounded;
|
|
10879
|
+
clearInterval(watchdog);
|
|
10350
10880
|
terminate();
|
|
10351
10881
|
}
|
|
10352
|
-
|
|
10353
|
-
|
|
10354
|
-
} else {
|
|
10355
|
-
stderr += piece;
|
|
10356
|
-
}
|
|
10357
|
-
};
|
|
10358
|
-
child.stdout.setEncoding("utf-8");
|
|
10359
|
-
child.stdout.on("data", (chunk) => {
|
|
10360
|
-
appendChunk(chunk, "stdout");
|
|
10361
|
-
});
|
|
10362
|
-
child.stderr.setEncoding("utf-8");
|
|
10363
|
-
child.stderr.on("data", (chunk) => {
|
|
10364
|
-
appendChunk(chunk, "stderr");
|
|
10365
|
-
});
|
|
10882
|
+
}, SIZE_WATCHDOG_INTERVAL_MS);
|
|
10883
|
+
watchdog.unref();
|
|
10366
10884
|
const onAbort = () => {
|
|
10367
10885
|
aborted = true;
|
|
10368
10886
|
terminate();
|
|
10369
10887
|
};
|
|
10888
|
+
const backgroundAvailableHere = manager !== null && process.env.SWIFTY_DISABLE_BACKGROUND_TASKS !== "1";
|
|
10889
|
+
const autoBackgroundAllowed = backgroundAvailableHere && isAutobackgroundingAllowed(command, DISALLOWED_AUTO_BACKGROUND_COMMANDS2);
|
|
10890
|
+
let timedOut = false;
|
|
10370
10891
|
const timeoutTimer = setTimeout(() => {
|
|
10892
|
+
if (autoBackgroundAllowed && backgroundExecution("timeout") !== null) {
|
|
10893
|
+
return;
|
|
10894
|
+
}
|
|
10371
10895
|
timedOut = true;
|
|
10372
10896
|
terminate();
|
|
10373
10897
|
}, timeout * 1e3);
|
|
@@ -10376,60 +10900,107 @@ var PowerShellTool = class {
|
|
|
10376
10900
|
if (ctx.abortSignal?.aborted) {
|
|
10377
10901
|
onAbort();
|
|
10378
10902
|
}
|
|
10903
|
+
const foregroundKey = `ps-${String(this.nextForegroundId++)}`;
|
|
10379
10904
|
const cleanup = () => {
|
|
10380
10905
|
clearTimeout(timeoutTimer);
|
|
10906
|
+
clearInterval(watchdog);
|
|
10381
10907
|
if (escalateTimer) {
|
|
10382
10908
|
clearTimeout(escalateTimer);
|
|
10383
10909
|
}
|
|
10384
10910
|
ctx.abortSignal?.removeEventListener("abort", onAbort);
|
|
10911
|
+
this.foreground.delete(foregroundKey);
|
|
10385
10912
|
};
|
|
10386
|
-
|
|
10387
|
-
|
|
10388
|
-
const hint = strArg(asRecord(err), "code") === "ENOENT" && process.platform !== "win32" ? " (pwsh is required on macOS/Linux \u2014 install PowerShell Core)" : "";
|
|
10389
|
-
resolve12({
|
|
10390
|
-
output: `Error executing command: ${err.message}${hint}`,
|
|
10391
|
-
isError: true
|
|
10392
|
-
});
|
|
10393
|
-
});
|
|
10394
|
-
child.on("close", (code, signal) => {
|
|
10395
|
-
cleanup();
|
|
10396
|
-
if (aborted) {
|
|
10397
|
-
const captured = stdout || stderr || outputTruncated ? formatShellOutput("PS> ", command, stdout, stderr, outputTruncated) : "";
|
|
10398
|
-
resolve12({
|
|
10399
|
-
output: captured ? `${captured}
|
|
10400
|
-
Error: command interrupted` : "Error: command interrupted",
|
|
10401
|
-
isError: true
|
|
10402
|
-
});
|
|
10403
|
-
return;
|
|
10404
|
-
}
|
|
10405
|
-
if (timedOut) {
|
|
10406
|
-
const captured = stdout || stderr || outputTruncated ? formatShellOutput("PS> ", command, stdout, stderr, outputTruncated) : "";
|
|
10407
|
-
resolve12({
|
|
10408
|
-
output: captured ? `${captured}
|
|
10409
|
-
Error: command timed out after ${String(timeout)}s` : `Error: command timed out after ${String(timeout)}s`,
|
|
10410
|
-
isError: true
|
|
10411
|
-
});
|
|
10913
|
+
const settle = (finalResult) => {
|
|
10914
|
+
if (settled) {
|
|
10412
10915
|
return;
|
|
10413
10916
|
}
|
|
10414
|
-
|
|
10415
|
-
|
|
10416
|
-
|
|
10417
|
-
|
|
10917
|
+
settled = true;
|
|
10918
|
+
cleanup();
|
|
10919
|
+
resolve12(finalResult);
|
|
10920
|
+
};
|
|
10921
|
+
const settleExit = (exit) => {
|
|
10922
|
+
if (backgrounded) {
|
|
10418
10923
|
return;
|
|
10419
10924
|
}
|
|
10420
|
-
|
|
10421
|
-
|
|
10422
|
-
|
|
10423
|
-
|
|
10424
|
-
|
|
10925
|
+
const read = readOutputFile(outputFile.path, MAX_SHELL_OUTPUT_BYTES);
|
|
10926
|
+
const finalResult = formatFinalResult(
|
|
10927
|
+
"PS> ",
|
|
10928
|
+
command,
|
|
10929
|
+
exit,
|
|
10930
|
+
read.text,
|
|
10931
|
+
read.truncated,
|
|
10932
|
+
timeout
|
|
10933
|
+
);
|
|
10934
|
+
unlinkQuiet(outputFile.path);
|
|
10935
|
+
settle(finalResult);
|
|
10936
|
+
};
|
|
10937
|
+
const backgroundExecution = (reason) => {
|
|
10938
|
+
if (backgrounded || settled || terminating || !manager || !backgroundAvailableHere) {
|
|
10939
|
+
return null;
|
|
10425
10940
|
}
|
|
10426
|
-
|
|
10427
|
-
|
|
10428
|
-
|
|
10941
|
+
backgrounded = true;
|
|
10942
|
+
settled = true;
|
|
10943
|
+
clearTimeout(timeoutTimer);
|
|
10944
|
+
ctx.abortSignal?.removeEventListener("abort", onAbort);
|
|
10945
|
+
this.foreground.delete(foregroundKey);
|
|
10946
|
+
const task = manager.create(
|
|
10947
|
+
backgroundTaskName(command),
|
|
10948
|
+
async () => {
|
|
10949
|
+
const exit = await done;
|
|
10950
|
+
const body = buildBackgroundBody("PS> ", command, exit, outputFile.path, timeout);
|
|
10951
|
+
if (body.isError) {
|
|
10952
|
+
throw new TaskFailure(body.output);
|
|
10953
|
+
}
|
|
10954
|
+
return body.output;
|
|
10955
|
+
},
|
|
10956
|
+
() => {
|
|
10957
|
+
killTree("SIGKILL");
|
|
10958
|
+
},
|
|
10959
|
+
{ originToolCallId: ctx.toolCallId, idPrefix: "ps", kind: "shell" }
|
|
10960
|
+
);
|
|
10961
|
+
resolve12({
|
|
10962
|
+
output: backgroundMessage(reason, task.id, timeout),
|
|
10963
|
+
isError: false
|
|
10964
|
+
});
|
|
10965
|
+
return task.id;
|
|
10966
|
+
};
|
|
10967
|
+
if (backgroundAvailableHere) {
|
|
10968
|
+
this.foreground.set(foregroundKey, {
|
|
10969
|
+
background: () => backgroundExecution("user") !== null
|
|
10970
|
+
});
|
|
10971
|
+
}
|
|
10972
|
+
const stopTimers = () => {
|
|
10973
|
+
clearInterval(watchdog);
|
|
10974
|
+
if (escalateTimer) {
|
|
10975
|
+
clearTimeout(escalateTimer);
|
|
10429
10976
|
}
|
|
10430
|
-
|
|
10977
|
+
};
|
|
10978
|
+
child.on("error", (err) => {
|
|
10979
|
+
stopTimers();
|
|
10980
|
+
const hint = strArg(asRecord(err), "code") === "ENOENT" && process.platform !== "win32" ? " (pwsh is required on macOS/Linux \u2014 install PowerShell Core)" : "";
|
|
10981
|
+
const exit = {
|
|
10982
|
+
code: null,
|
|
10983
|
+
signal: null,
|
|
10984
|
+
aborted,
|
|
10985
|
+
timedOut,
|
|
10986
|
+
sizeKilled,
|
|
10987
|
+
spawnError: `${err.message}${hint}`
|
|
10988
|
+
};
|
|
10989
|
+
doneResolve?.(exit);
|
|
10990
|
+
settleExit(exit);
|
|
10991
|
+
});
|
|
10992
|
+
child.on("close", (code, signal) => {
|
|
10993
|
+
stopTimers();
|
|
10994
|
+
const exit = { code, signal, aborted, timedOut, sizeKilled };
|
|
10995
|
+
doneResolve?.(exit);
|
|
10996
|
+
settleExit(exit);
|
|
10431
10997
|
});
|
|
10998
|
+
backgroundFn = backgroundExecution;
|
|
10432
10999
|
});
|
|
11000
|
+
return {
|
|
11001
|
+
result,
|
|
11002
|
+
background: (reason) => backgroundFn?.(reason) ?? null
|
|
11003
|
+
};
|
|
10433
11004
|
}
|
|
10434
11005
|
};
|
|
10435
11006
|
|
|
@@ -10621,7 +11192,7 @@ var AskUserResponseSchema = z15.object({
|
|
|
10621
11192
|
id: z15.string(),
|
|
10622
11193
|
answers: z15.record(z15.string(), z15.string())
|
|
10623
11194
|
});
|
|
10624
|
-
var FE_DIST =
|
|
11195
|
+
var FE_DIST = join21(import.meta.dirname, "fe", "dist");
|
|
10625
11196
|
var MIME_TYPES = {
|
|
10626
11197
|
".html": "text/html; charset=utf-8",
|
|
10627
11198
|
".js": "application/javascript; charset=utf-8",
|
|
@@ -10634,11 +11205,11 @@ var MIME_TYPES = {
|
|
|
10634
11205
|
};
|
|
10635
11206
|
function serveStatic(path) {
|
|
10636
11207
|
const cleanPath = normalize(path).replace(/^(\.\.[/\\])+/, "");
|
|
10637
|
-
const fullPath =
|
|
11208
|
+
const fullPath = join21(FE_DIST, cleanPath);
|
|
10638
11209
|
if (!fullPath.startsWith(FE_DIST)) {
|
|
10639
11210
|
return null;
|
|
10640
11211
|
}
|
|
10641
|
-
if (!existsSync16(fullPath) || !
|
|
11212
|
+
if (!existsSync16(fullPath) || !statSync12(fullPath).isFile()) {
|
|
10642
11213
|
return null;
|
|
10643
11214
|
}
|
|
10644
11215
|
const body = readFileSync16(fullPath);
|
|
@@ -10814,31 +11385,38 @@ async function createRemoteAgent(opts) {
|
|
|
10814
11385
|
if (!client) {
|
|
10815
11386
|
throw new Error("no llm client (provider not initialized)");
|
|
10816
11387
|
}
|
|
10817
|
-
const { PermissionChecker: PC } = await import("./permissions-
|
|
10818
|
-
const { Agent: AgentClass } = await import("./agent-
|
|
11388
|
+
const { PermissionChecker: PC } = await import("./permissions-77UJQITR.js");
|
|
11389
|
+
const { Agent: AgentClass } = await import("./agent-ZMIS6IH6.js");
|
|
10819
11390
|
const subConv = new ConversationManager();
|
|
10820
11391
|
subConv.addUserMessage(prompt);
|
|
11392
|
+
const taskManager = new TaskManager();
|
|
10821
11393
|
const subAgent = new AgentClass({
|
|
10822
11394
|
client,
|
|
10823
11395
|
registry: filterToolsForAgent(registry, void 0, void 0, false),
|
|
10824
11396
|
checker: new PC(workDir, "acceptEdits"),
|
|
10825
11397
|
conversation: subConv,
|
|
10826
11398
|
workDir,
|
|
10827
|
-
maxIterations: 200
|
|
11399
|
+
maxIterations: 200,
|
|
11400
|
+
taskManager,
|
|
11401
|
+
notificationFn: () => taskManager.drainNotifications().map(formatAgentTaskNotification)
|
|
10828
11402
|
});
|
|
10829
11403
|
let output = "";
|
|
10830
|
-
|
|
10831
|
-
|
|
10832
|
-
|
|
10833
|
-
|
|
10834
|
-
|
|
10835
|
-
|
|
10836
|
-
|
|
10837
|
-
|
|
10838
|
-
|
|
11404
|
+
try {
|
|
11405
|
+
for await (const event of subAgent.run()) {
|
|
11406
|
+
switch (event.type) {
|
|
11407
|
+
case "stream_text":
|
|
11408
|
+
output += event.text;
|
|
11409
|
+
break;
|
|
11410
|
+
case "loop_complete":
|
|
11411
|
+
return output || "[No output]";
|
|
11412
|
+
case "error":
|
|
11413
|
+
throw event.error;
|
|
11414
|
+
}
|
|
10839
11415
|
}
|
|
11416
|
+
return output || "[No output]";
|
|
11417
|
+
} finally {
|
|
11418
|
+
await taskManager.stopAll();
|
|
10840
11419
|
}
|
|
10841
|
-
return output || "[No output]";
|
|
10842
11420
|
}
|
|
10843
11421
|
};
|
|
10844
11422
|
registry.register(new LoadSkillTool(catalog, skillHost, skillForkHost));
|
|
@@ -10856,10 +11434,12 @@ async function createRemoteAgent(opts) {
|
|
|
10856
11434
|
onEvent,
|
|
10857
11435
|
void 0,
|
|
10858
11436
|
teamChecker,
|
|
10859
|
-
|
|
11437
|
+
// Teammates stay purely foreground: see SubagentRunOptions.backgroundTasks.
|
|
11438
|
+
{ abortSignal, backgroundTasks: false }
|
|
10860
11439
|
);
|
|
10861
11440
|
const teamManager = new TeamManager(workDir);
|
|
10862
11441
|
const backgroundTaskManager = new TaskManager();
|
|
11442
|
+
attachBackgroundTaskManager(registry, backgroundTaskManager);
|
|
10863
11443
|
registry.register(new TeamCreateTool(teamManager));
|
|
10864
11444
|
registry.register(new SendMessageTool(teamManager));
|
|
10865
11445
|
registry.register(new TeamDeleteTool(teamManager));
|
|
@@ -10898,6 +11478,7 @@ async function createRemoteAgent(opts) {
|
|
|
10898
11478
|
const forkClient = modelOverride ? await createClient({ ...provider, model: resolvedModel }, forkSystemPrompt) : client;
|
|
10899
11479
|
const checker = context?.permissionChecker ?? new PermissionChecker(forkWorkDir, "acceptEdits");
|
|
10900
11480
|
forkConv.addUserMessage(prompt);
|
|
11481
|
+
const forkTaskManager = new TaskManager();
|
|
10901
11482
|
const agent2 = new Agent({
|
|
10902
11483
|
client: forkClient,
|
|
10903
11484
|
registry: forkRegistry,
|
|
@@ -10909,23 +11490,29 @@ async function createRemoteAgent(opts) {
|
|
|
10909
11490
|
onPermissionRequest: context?.onPermissionRequest,
|
|
10910
11491
|
fileStateCache: new FileStateCache(),
|
|
10911
11492
|
instructions,
|
|
10912
|
-
memoryContent: memReminder
|
|
11493
|
+
memoryContent: memReminder,
|
|
11494
|
+
taskManager: forkTaskManager,
|
|
11495
|
+
notificationFn: () => forkTaskManager.drainNotifications().map(formatAgentTaskNotification)
|
|
10913
11496
|
});
|
|
10914
11497
|
let output = "";
|
|
10915
|
-
|
|
10916
|
-
|
|
10917
|
-
|
|
10918
|
-
|
|
10919
|
-
|
|
10920
|
-
|
|
10921
|
-
|
|
10922
|
-
|
|
10923
|
-
|
|
11498
|
+
try {
|
|
11499
|
+
for await (const event of agent2.run()) {
|
|
11500
|
+
switch (event.type) {
|
|
11501
|
+
case "stream_text":
|
|
11502
|
+
output += event.text;
|
|
11503
|
+
break;
|
|
11504
|
+
case "loop_complete":
|
|
11505
|
+
return output || "[No output]";
|
|
11506
|
+
case "error":
|
|
11507
|
+
return output ? `${output}
|
|
10924
11508
|
|
|
10925
11509
|
[Error: ${event.error.message}]` : `Error: ${event.error.message}`;
|
|
11510
|
+
}
|
|
10926
11511
|
}
|
|
11512
|
+
return output || "[No output]";
|
|
11513
|
+
} finally {
|
|
11514
|
+
await forkTaskManager.stopAll();
|
|
10927
11515
|
}
|
|
10928
|
-
return output || "[No output]";
|
|
10929
11516
|
},
|
|
10930
11517
|
backgroundTaskManager
|
|
10931
11518
|
);
|
|
@@ -12423,6 +13010,7 @@ __export(javascript_exports, {
|
|
|
12423
13010
|
evaluateJavaScript: () => evaluateJavaScript,
|
|
12424
13011
|
isolatedVmSupported: () => isolatedVmSupported
|
|
12425
13012
|
});
|
|
13013
|
+
import { randomBytes as randomBytes4 } from "crypto";
|
|
12426
13014
|
import { z as z16 } from "zod";
|
|
12427
13015
|
var DEFAULT_TIMEOUT_MS = 1e3;
|
|
12428
13016
|
var MAX_TIMEOUT_MS = 1e4;
|
|
@@ -12435,7 +13023,10 @@ var JavaScriptArgumentsSchema = z16.object({
|
|
|
12435
13023
|
code: z16.string().min(1),
|
|
12436
13024
|
input: z16.json().optional(),
|
|
12437
13025
|
timeout_ms: z16.number().int().min(1).max(MAX_TIMEOUT_MS).default(DEFAULT_TIMEOUT_MS),
|
|
12438
|
-
memory_limit_mb: z16.number().int().min(8).max(MAX_MEMORY_LIMIT_MB).default(DEFAULT_MEMORY_LIMIT_MB)
|
|
13026
|
+
memory_limit_mb: z16.number().int().min(8).max(MAX_MEMORY_LIMIT_MB).default(DEFAULT_MEMORY_LIMIT_MB),
|
|
13027
|
+
// Accepted even when backgrounding is unavailable (then simply ignored):
|
|
13028
|
+
// the JSON schema gates what the model sees per host.
|
|
13029
|
+
run_in_background: z16.boolean().optional()
|
|
12439
13030
|
});
|
|
12440
13031
|
var JavaScriptEvaluationOptionsSchema = z16.object({
|
|
12441
13032
|
input: z16.json().optional(),
|
|
@@ -12471,14 +13062,22 @@ async function runJavaScript(code, input, timeoutMs, memoryLimitMb, abortSignal)
|
|
|
12471
13062
|
}
|
|
12472
13063
|
abortSignal?.throwIfAborted();
|
|
12473
13064
|
const module = await import("isolated-vm");
|
|
13065
|
+
abortSignal?.throwIfAborted();
|
|
12474
13066
|
const ivm = module.default;
|
|
12475
13067
|
const isolate = new ivm.Isolate({ memoryLimit: memoryLimitMb });
|
|
12476
13068
|
const context = await isolate.createContext();
|
|
12477
13069
|
const onAbort = () => {
|
|
12478
|
-
isolate.
|
|
13070
|
+
if (!isolate.isDisposed) {
|
|
13071
|
+
isolate.dispose();
|
|
13072
|
+
}
|
|
12479
13073
|
};
|
|
12480
13074
|
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
13075
|
+
let wallTimer;
|
|
12481
13076
|
try {
|
|
13077
|
+
if (abortSignal?.aborted) {
|
|
13078
|
+
onAbort();
|
|
13079
|
+
abortSignal.throwIfAborted();
|
|
13080
|
+
}
|
|
12482
13081
|
await context.global.set("input", input ?? null, { copy: true });
|
|
12483
13082
|
const source = `
|
|
12484
13083
|
(async () => {
|
|
@@ -12523,12 +13122,19 @@ ${code}
|
|
|
12523
13122
|
return __payload;
|
|
12524
13123
|
})()
|
|
12525
13124
|
`;
|
|
12526
|
-
const
|
|
13125
|
+
const evaluation = context.eval(source, {
|
|
12527
13126
|
timeout: timeoutMs,
|
|
12528
13127
|
promise: true,
|
|
12529
13128
|
copy: true,
|
|
12530
13129
|
filename: "swifty-sandbox.js"
|
|
12531
13130
|
});
|
|
13131
|
+
const deadline = new Promise((_, reject) => {
|
|
13132
|
+
wallTimer = setTimeout(() => {
|
|
13133
|
+
reject(new Error(`JavaScript evaluation timed out after ${String(timeoutMs)}ms`));
|
|
13134
|
+
onAbort();
|
|
13135
|
+
}, timeoutMs);
|
|
13136
|
+
});
|
|
13137
|
+
const raw = await Promise.race([evaluation, deadline]);
|
|
12532
13138
|
if (typeof raw !== "string") {
|
|
12533
13139
|
throw new Error("JavaScript sandbox returned an invalid result");
|
|
12534
13140
|
}
|
|
@@ -12538,6 +13144,7 @@ ${code}
|
|
|
12538
13144
|
const decoded = JSON.parse(raw);
|
|
12539
13145
|
return JavaScriptEvaluationSchema.parse(decoded);
|
|
12540
13146
|
} finally {
|
|
13147
|
+
clearTimeout(wallTimer);
|
|
12541
13148
|
abortSignal?.removeEventListener("abort", onAbort);
|
|
12542
13149
|
try {
|
|
12543
13150
|
context.release();
|
|
@@ -12548,37 +13155,94 @@ ${code}
|
|
|
12548
13155
|
}
|
|
12549
13156
|
}
|
|
12550
13157
|
}
|
|
13158
|
+
function formatEvaluation(result) {
|
|
13159
|
+
const sections = [];
|
|
13160
|
+
if (result.logs.length > 0) {
|
|
13161
|
+
sections.push(`Console:
|
|
13162
|
+
${result.logs.join("\n")}`);
|
|
13163
|
+
}
|
|
13164
|
+
if (result.logsTruncated) {
|
|
13165
|
+
sections.push("Console output truncated");
|
|
13166
|
+
}
|
|
13167
|
+
sections.push(`Result:
|
|
13168
|
+
${JSON.stringify(result.value, null, 2)}`);
|
|
13169
|
+
return sections.join("\n\n");
|
|
13170
|
+
}
|
|
13171
|
+
function jsBackgroundMessage(reason, taskId) {
|
|
13172
|
+
return reason === "user" ? `JavaScript evaluation was manually backgrounded by the user (task_id: ${taskId}). It is still running \u2014 you will be notified when it completes.` : `JavaScript running in background (task_id: ${taskId}). You will be notified when it completes; do not poll. Use TaskStop with task_id to abort it early.`;
|
|
13173
|
+
}
|
|
12551
13174
|
var JavaScriptTool = class {
|
|
12552
13175
|
name = "JavaScript";
|
|
12553
13176
|
description = JAVASCRIPT_DESCRIPTION;
|
|
12554
13177
|
category = "command";
|
|
13178
|
+
/**
|
|
13179
|
+
* Background task registry, injected by the host — same contract as
|
|
13180
|
+
* BashTool.taskManager. Unlike the shell tools there is no timeout
|
|
13181
|
+
* auto-background: timeout_ms is the isolate's hard safety cap — V8 kills a
|
|
13182
|
+
* synchronous script at the cap, and a wall-clock deadline disposes the
|
|
13183
|
+
* isolate when an evaluation is suspended on a promise — so nothing is left
|
|
13184
|
+
* to keep running.
|
|
13185
|
+
*/
|
|
13186
|
+
taskManager = null;
|
|
13187
|
+
/** Running foreground evaluations eligible for manual backgrounding (Ctrl+B). */
|
|
13188
|
+
foreground = /* @__PURE__ */ new Map();
|
|
13189
|
+
nextForegroundId = 1;
|
|
13190
|
+
/** Instance-level background gate; see BashTool.backgroundEnabled. */
|
|
13191
|
+
backgroundEnabled() {
|
|
13192
|
+
return this.taskManager !== null && process.env.SWIFTY_DISABLE_BACKGROUND_TASKS !== "1";
|
|
13193
|
+
}
|
|
13194
|
+
/** True while at least one foreground evaluation runs. */
|
|
13195
|
+
hasForegroundTasks() {
|
|
13196
|
+
return this.foreground.size > 0;
|
|
13197
|
+
}
|
|
13198
|
+
/** Move every running foreground evaluation to the background (Ctrl+B). */
|
|
13199
|
+
backgroundForegroundTasks() {
|
|
13200
|
+
let count = 0;
|
|
13201
|
+
for (const entry of [...this.foreground.values()]) {
|
|
13202
|
+
if (entry.background()) {
|
|
13203
|
+
count++;
|
|
13204
|
+
}
|
|
13205
|
+
}
|
|
13206
|
+
return count;
|
|
13207
|
+
}
|
|
12555
13208
|
schema() {
|
|
13209
|
+
const properties = {
|
|
13210
|
+
code: {
|
|
13211
|
+
type: "string",
|
|
13212
|
+
description: "JavaScript function body to execute; use return to produce a result"
|
|
13213
|
+
},
|
|
13214
|
+
input: {
|
|
13215
|
+
description: "Optional JSON-compatible value exposed as globalThis.input"
|
|
13216
|
+
},
|
|
13217
|
+
timeout_ms: {
|
|
13218
|
+
type: "integer",
|
|
13219
|
+
minimum: 1,
|
|
13220
|
+
maximum: MAX_TIMEOUT_MS,
|
|
13221
|
+
default: DEFAULT_TIMEOUT_MS
|
|
13222
|
+
},
|
|
13223
|
+
memory_limit_mb: {
|
|
13224
|
+
type: "integer",
|
|
13225
|
+
minimum: 8,
|
|
13226
|
+
maximum: MAX_MEMORY_LIMIT_MB,
|
|
13227
|
+
default: DEFAULT_MEMORY_LIMIT_MB
|
|
13228
|
+
}
|
|
13229
|
+
};
|
|
13230
|
+
let description = this.description;
|
|
13231
|
+
if (this.backgroundEnabled()) {
|
|
13232
|
+
properties.run_in_background = {
|
|
13233
|
+
type: "boolean",
|
|
13234
|
+
description: "Run the evaluation in the background. Returns a task ID immediately; the result arrives later as a task notification.",
|
|
13235
|
+
default: false
|
|
13236
|
+
};
|
|
13237
|
+
description = `${this.description}
|
|
13238
|
+
${JAVASCRIPT_BACKGROUND_DESCRIPTION}`;
|
|
13239
|
+
}
|
|
12556
13240
|
return {
|
|
12557
13241
|
name: this.name,
|
|
12558
|
-
description
|
|
13242
|
+
description,
|
|
12559
13243
|
input_schema: {
|
|
12560
13244
|
type: "object",
|
|
12561
|
-
properties
|
|
12562
|
-
code: {
|
|
12563
|
-
type: "string",
|
|
12564
|
-
description: "JavaScript function body to execute; use return to produce a result"
|
|
12565
|
-
},
|
|
12566
|
-
input: {
|
|
12567
|
-
description: "Optional JSON-compatible value exposed as globalThis.input"
|
|
12568
|
-
},
|
|
12569
|
-
timeout_ms: {
|
|
12570
|
-
type: "integer",
|
|
12571
|
-
minimum: 1,
|
|
12572
|
-
maximum: MAX_TIMEOUT_MS,
|
|
12573
|
-
default: DEFAULT_TIMEOUT_MS
|
|
12574
|
-
},
|
|
12575
|
-
memory_limit_mb: {
|
|
12576
|
-
type: "integer",
|
|
12577
|
-
minimum: 8,
|
|
12578
|
-
maximum: MAX_MEMORY_LIMIT_MB,
|
|
12579
|
-
default: DEFAULT_MEMORY_LIMIT_MB
|
|
12580
|
-
}
|
|
12581
|
-
},
|
|
13245
|
+
properties,
|
|
12582
13246
|
required: ["code"],
|
|
12583
13247
|
additionalProperties: false
|
|
12584
13248
|
}
|
|
@@ -12589,32 +13253,128 @@ var JavaScriptTool = class {
|
|
|
12589
13253
|
if (!parsed.success) {
|
|
12590
13254
|
return { output: `Error: ${parsed.error.message}`, isError: true };
|
|
12591
13255
|
}
|
|
12592
|
-
|
|
12593
|
-
|
|
12594
|
-
|
|
12595
|
-
|
|
12596
|
-
|
|
12597
|
-
|
|
12598
|
-
|
|
12599
|
-
|
|
12600
|
-
|
|
12601
|
-
|
|
12602
|
-
|
|
12603
|
-
${result.logs.join("\n")}`);
|
|
12604
|
-
}
|
|
12605
|
-
if (result.logsTruncated) {
|
|
12606
|
-
sections.push("Console output truncated");
|
|
13256
|
+
const manager = ctx.taskManager !== void 0 ? ctx.taskManager : this.taskManager;
|
|
13257
|
+
const backgroundAvailable = manager !== null && process.env.SWIFTY_DISABLE_BACKGROUND_TASKS !== "1";
|
|
13258
|
+
const runInBackground = parsed.data.run_in_background === true && backgroundAvailable;
|
|
13259
|
+
const handle = this.startEvaluation(ctx, parsed.data, manager, backgroundAvailable);
|
|
13260
|
+
if (runInBackground) {
|
|
13261
|
+
const taskId = handle.background("explicit");
|
|
13262
|
+
if (taskId !== null) {
|
|
13263
|
+
return {
|
|
13264
|
+
output: jsBackgroundMessage("explicit", taskId),
|
|
13265
|
+
isError: false
|
|
13266
|
+
};
|
|
12607
13267
|
}
|
|
12608
|
-
sections.push(`Result:
|
|
12609
|
-
${JSON.stringify(result.value, null, 2)}`);
|
|
12610
|
-
return { output: sections.join("\n\n"), isError: false };
|
|
12611
|
-
} catch (error) {
|
|
12612
|
-
return { output: `Error executing JavaScript: ${asErrorString(error)}`, isError: true };
|
|
12613
13268
|
}
|
|
13269
|
+
return handle.result;
|
|
13270
|
+
}
|
|
13271
|
+
/**
|
|
13272
|
+
* Start the isolated evaluation. The returned handle mirrors the shell
|
|
13273
|
+
* tools: background() transitions the pending evaluation into a TaskManager
|
|
13274
|
+
* task — the isolate keeps running on its own thread, the tool call
|
|
13275
|
+
* resolves immediately, and completion arrives as a task notification.
|
|
13276
|
+
* While foreground, the caller's abort signal (Esc) disposes the isolate;
|
|
13277
|
+
* once backgrounded, only TaskStop or session shutdown can abort it.
|
|
13278
|
+
*/
|
|
13279
|
+
startEvaluation(ctx, data, manager, backgroundAvailable) {
|
|
13280
|
+
let backgroundFn;
|
|
13281
|
+
const result = new Promise((resolve12) => {
|
|
13282
|
+
let settled = false;
|
|
13283
|
+
let backgrounded = false;
|
|
13284
|
+
const controller = new AbortController();
|
|
13285
|
+
const onCallerAbort = () => {
|
|
13286
|
+
controller.abort();
|
|
13287
|
+
};
|
|
13288
|
+
if (ctx.abortSignal?.aborted) {
|
|
13289
|
+
resolve12({ output: "Error: command interrupted", isError: true });
|
|
13290
|
+
return;
|
|
13291
|
+
}
|
|
13292
|
+
ctx.abortSignal?.addEventListener("abort", onCallerAbort, { once: true });
|
|
13293
|
+
const evalPromise = runJavaScript(
|
|
13294
|
+
data.code,
|
|
13295
|
+
data.input,
|
|
13296
|
+
data.timeout_ms,
|
|
13297
|
+
data.memory_limit_mb,
|
|
13298
|
+
controller.signal
|
|
13299
|
+
);
|
|
13300
|
+
const foregroundKey = `js-${String(this.nextForegroundId++)}`;
|
|
13301
|
+
const settle = (finalResult) => {
|
|
13302
|
+
if (settled) {
|
|
13303
|
+
return;
|
|
13304
|
+
}
|
|
13305
|
+
settled = true;
|
|
13306
|
+
ctx.abortSignal?.removeEventListener("abort", onCallerAbort);
|
|
13307
|
+
this.foreground.delete(foregroundKey);
|
|
13308
|
+
resolve12(finalResult);
|
|
13309
|
+
};
|
|
13310
|
+
const backgroundExecution = (reason) => {
|
|
13311
|
+
if (backgrounded || settled || controller.signal.aborted || !manager || !backgroundAvailable) {
|
|
13312
|
+
return null;
|
|
13313
|
+
}
|
|
13314
|
+
backgrounded = true;
|
|
13315
|
+
settled = true;
|
|
13316
|
+
ctx.abortSignal?.removeEventListener("abort", onCallerAbort);
|
|
13317
|
+
this.foreground.delete(foregroundKey);
|
|
13318
|
+
const task = manager.create(
|
|
13319
|
+
backgroundTaskName(data.code),
|
|
13320
|
+
async (backgroundTask) => {
|
|
13321
|
+
const cap = (text) => text.length > BACKGROUND_NOTIFICATION_CHARS ? persistLargeResult(
|
|
13322
|
+
ctx.workDir,
|
|
13323
|
+
ctx.sessionId ?? "",
|
|
13324
|
+
`${backgroundTask.id}-${randomBytes4(4).toString("hex")}`,
|
|
13325
|
+
text
|
|
13326
|
+
) : text;
|
|
13327
|
+
try {
|
|
13328
|
+
const evaluation = await evalPromise;
|
|
13329
|
+
return cap(formatEvaluation(evaluation));
|
|
13330
|
+
} catch (error) {
|
|
13331
|
+
if (backgroundTask.status === "cancelled") {
|
|
13332
|
+
throw error;
|
|
13333
|
+
}
|
|
13334
|
+
throw new TaskFailure(cap(`Error executing JavaScript: ${asErrorString(error)}`));
|
|
13335
|
+
}
|
|
13336
|
+
},
|
|
13337
|
+
() => {
|
|
13338
|
+
controller.abort();
|
|
13339
|
+
},
|
|
13340
|
+
{ originToolCallId: ctx.toolCallId, idPrefix: "js", kind: "js" }
|
|
13341
|
+
);
|
|
13342
|
+
resolve12({
|
|
13343
|
+
output: jsBackgroundMessage(reason, task.id),
|
|
13344
|
+
isError: false
|
|
13345
|
+
});
|
|
13346
|
+
return task.id;
|
|
13347
|
+
};
|
|
13348
|
+
if (backgroundAvailable) {
|
|
13349
|
+
this.foreground.set(foregroundKey, {
|
|
13350
|
+
background: () => backgroundExecution("user") !== null
|
|
13351
|
+
});
|
|
13352
|
+
}
|
|
13353
|
+
evalPromise.then(
|
|
13354
|
+
(evaluation) => {
|
|
13355
|
+
settle({ output: formatEvaluation(evaluation), isError: false });
|
|
13356
|
+
},
|
|
13357
|
+
(error) => {
|
|
13358
|
+
settle({
|
|
13359
|
+
output: `Error executing JavaScript: ${asErrorString(error)}`,
|
|
13360
|
+
isError: true
|
|
13361
|
+
});
|
|
13362
|
+
}
|
|
13363
|
+
);
|
|
13364
|
+
backgroundFn = backgroundExecution;
|
|
13365
|
+
});
|
|
13366
|
+
return {
|
|
13367
|
+
result,
|
|
13368
|
+
background: (reason) => backgroundFn?.(reason) ?? null
|
|
13369
|
+
};
|
|
12614
13370
|
}
|
|
12615
13371
|
};
|
|
12616
13372
|
|
|
12617
13373
|
// src/tools/web-fetch.ts
|
|
13374
|
+
var web_fetch_exports = {};
|
|
13375
|
+
__export(web_fetch_exports, {
|
|
13376
|
+
WebFetchTool: () => WebFetchTool
|
|
13377
|
+
});
|
|
12618
13378
|
var log27 = createChildLogger({ module: "tools" });
|
|
12619
13379
|
var MAX_CONTENT_BYTES = 10 * 1024 * 1024;
|
|
12620
13380
|
var FETCH_TIMEOUT_MS = 6e4;
|
|
@@ -13615,8 +14375,8 @@ __export(manager_exports3, {
|
|
|
13615
14375
|
CodeReviewManager: () => CodeReviewManager,
|
|
13616
14376
|
createDefaultCodeReviewTeam: () => createDefaultCodeReviewTeam
|
|
13617
14377
|
});
|
|
13618
|
-
import { readFileSync as readFileSync17, writeFileSync as writeFileSync11, existsSync as existsSync18, mkdirSync as
|
|
13619
|
-
import { join as
|
|
14378
|
+
import { readFileSync as readFileSync17, writeFileSync as writeFileSync11, existsSync as existsSync18, mkdirSync as mkdirSync13 } from "fs";
|
|
14379
|
+
import { join as join22 } from "path";
|
|
13620
14380
|
import z17 from "zod";
|
|
13621
14381
|
var log30 = createChildLogger({ module: "code-review" });
|
|
13622
14382
|
var CodeReviewMemberSchema = z17.object({
|
|
@@ -13639,7 +14399,7 @@ var CodeReviewManager = class {
|
|
|
13639
14399
|
workDir;
|
|
13640
14400
|
constructor(workDir, teamManager) {
|
|
13641
14401
|
this.workDir = workDir;
|
|
13642
|
-
this.configPath =
|
|
14402
|
+
this.configPath = join22(workDir, ".swifty", "code-review-teams.json");
|
|
13643
14403
|
this.teamManager = teamManager;
|
|
13644
14404
|
this.loadTeams();
|
|
13645
14405
|
}
|
|
@@ -13658,9 +14418,9 @@ var CodeReviewManager = class {
|
|
|
13658
14418
|
}
|
|
13659
14419
|
}
|
|
13660
14420
|
saveTeams() {
|
|
13661
|
-
const dir =
|
|
14421
|
+
const dir = join22(this.workDir, ".swifty");
|
|
13662
14422
|
if (!existsSync18(dir)) {
|
|
13663
|
-
|
|
14423
|
+
mkdirSync13(dir, { recursive: true });
|
|
13664
14424
|
}
|
|
13665
14425
|
const teams = [...this.teams.values()];
|
|
13666
14426
|
writeFileSync11(this.configPath, JSON.stringify(teams, null, 2));
|
|
@@ -13806,8 +14566,8 @@ var usage_tracker_exports = {};
|
|
|
13806
14566
|
__export(usage_tracker_exports, {
|
|
13807
14567
|
CommandUsageTracker: () => CommandUsageTracker
|
|
13808
14568
|
});
|
|
13809
|
-
import { readFileSync as readFileSync18, writeFileSync as writeFileSync12, mkdirSync as
|
|
13810
|
-
import { join as
|
|
14569
|
+
import { readFileSync as readFileSync18, writeFileSync as writeFileSync12, mkdirSync as mkdirSync14, existsSync as existsSync19 } from "fs";
|
|
14570
|
+
import { join as join23, dirname as dirname9 } from "path";
|
|
13811
14571
|
import { safeParse as safeParse3, z as z18 } from "zod";
|
|
13812
14572
|
var log31 = createChildLogger({ module: "commands" });
|
|
13813
14573
|
var UsageEntrySchema = z18.object({
|
|
@@ -13818,10 +14578,10 @@ var CommandUsageTracker = class {
|
|
|
13818
14578
|
usage = /* @__PURE__ */ new Map();
|
|
13819
14579
|
filePath;
|
|
13820
14580
|
constructor(workDir) {
|
|
13821
|
-
const dir =
|
|
13822
|
-
this.filePath =
|
|
14581
|
+
const dir = join23(workDir, ".swifty");
|
|
14582
|
+
this.filePath = join23(dir, "command_usage.json");
|
|
13823
14583
|
if (!existsSync19(dir)) {
|
|
13824
|
-
|
|
14584
|
+
mkdirSync14(dir, { recursive: true });
|
|
13825
14585
|
}
|
|
13826
14586
|
this.load();
|
|
13827
14587
|
}
|
|
@@ -13862,7 +14622,7 @@ var CommandUsageTracker = class {
|
|
|
13862
14622
|
}
|
|
13863
14623
|
save() {
|
|
13864
14624
|
try {
|
|
13865
|
-
|
|
14625
|
+
mkdirSync14(dirname9(this.filePath), { recursive: true });
|
|
13866
14626
|
writeFileSync12(this.filePath, JSON.stringify(Object.fromEntries(this.usage), null, 2));
|
|
13867
14627
|
} catch (err) {
|
|
13868
14628
|
log31.error({ err }, "commands operation failed");
|
|
@@ -13885,15 +14645,15 @@ __export(history_exports, {
|
|
|
13885
14645
|
append: () => append,
|
|
13886
14646
|
load: () => load
|
|
13887
14647
|
});
|
|
13888
|
-
import { readFileSync as readFileSync19, writeFileSync as writeFileSync13, mkdirSync as
|
|
13889
|
-
import { join as
|
|
14648
|
+
import { readFileSync as readFileSync19, writeFileSync as writeFileSync13, mkdirSync as mkdirSync15, existsSync as existsSync20 } from "fs";
|
|
14649
|
+
import { join as join24 } from "path";
|
|
13890
14650
|
import { parse as parse8, z as z19 } from "zod";
|
|
13891
14651
|
var log32 = createChildLogger({ module: "history" });
|
|
13892
14652
|
var MAX_HISTORY_ENTRIES = 200;
|
|
13893
14653
|
var FILENAME = "prompt_history.jsonl";
|
|
13894
14654
|
var JSONLSchema = z19.looseObject({ text: z19.string() });
|
|
13895
14655
|
function load(dir) {
|
|
13896
|
-
const filePath =
|
|
14656
|
+
const filePath = join24(dir, FILENAME);
|
|
13897
14657
|
if (!existsSync20(filePath)) {
|
|
13898
14658
|
return [];
|
|
13899
14659
|
}
|
|
@@ -13915,8 +14675,8 @@ function load(dir) {
|
|
|
13915
14675
|
}
|
|
13916
14676
|
}
|
|
13917
14677
|
function append(dir, text) {
|
|
13918
|
-
const filePath =
|
|
13919
|
-
|
|
14678
|
+
const filePath = join24(dir, FILENAME);
|
|
14679
|
+
mkdirSync15(dir, { recursive: true });
|
|
13920
14680
|
const entries = load(dir);
|
|
13921
14681
|
if (entries.length === 0 || entries[entries.length - 1] !== text) {
|
|
13922
14682
|
entries.push(text);
|
|
@@ -14132,17 +14892,17 @@ __export(install_tool_exports, {
|
|
|
14132
14892
|
});
|
|
14133
14893
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
14134
14894
|
import {
|
|
14135
|
-
mkdirSync as
|
|
14895
|
+
mkdirSync as mkdirSync16,
|
|
14136
14896
|
writeFileSync as writeFileSync14,
|
|
14137
14897
|
readFileSync as readFileSync20,
|
|
14138
14898
|
lstatSync,
|
|
14139
14899
|
realpathSync,
|
|
14140
|
-
openSync as
|
|
14141
|
-
closeSync as
|
|
14900
|
+
openSync as openSync3,
|
|
14901
|
+
closeSync as closeSync3,
|
|
14142
14902
|
renameSync as renameSync2,
|
|
14143
14903
|
rmSync as rmSync2
|
|
14144
14904
|
} from "fs";
|
|
14145
|
-
import { join as
|
|
14905
|
+
import { join as join25, resolve as resolve10 } from "path";
|
|
14146
14906
|
import yaml7 from "js-yaml";
|
|
14147
14907
|
var log33 = createChildLogger({ module: "skills" });
|
|
14148
14908
|
var InstallSkillTool = class {
|
|
@@ -14234,28 +14994,28 @@ ${parsed.body}
|
|
|
14234
14994
|
ctx.abortSignal?.throwIfAborted();
|
|
14235
14995
|
let dir = realpathSync(this.workDir);
|
|
14236
14996
|
for (const segment of [".agents", "skills", name]) {
|
|
14237
|
-
dir =
|
|
14997
|
+
dir = join25(dir, segment);
|
|
14238
14998
|
const stat4 = lstatSync(dir, { throwIfNoEntry: false });
|
|
14239
14999
|
if (stat4) {
|
|
14240
15000
|
if (stat4.isSymbolicLink() || !stat4.isDirectory()) {
|
|
14241
15001
|
throw new Error(`Installation directory must be a real directory: ${dir}`);
|
|
14242
15002
|
}
|
|
14243
15003
|
} else {
|
|
14244
|
-
|
|
15004
|
+
mkdirSync16(dir);
|
|
14245
15005
|
}
|
|
14246
15006
|
}
|
|
14247
|
-
const destination =
|
|
15007
|
+
const destination = join25(dir, "SKILL.md");
|
|
14248
15008
|
const stat3 = lstatSync(destination, { throwIfNoEntry: false });
|
|
14249
15009
|
if (stat3 && (stat3.isSymbolicLink() || !stat3.isFile())) {
|
|
14250
15010
|
throw new Error(`Installation target must be a regular file: ${destination}`);
|
|
14251
15011
|
}
|
|
14252
|
-
const temporary =
|
|
14253
|
-
const fd =
|
|
15012
|
+
const temporary = join25(dir, `.SKILL-${randomUUID2()}.tmp`);
|
|
15013
|
+
const fd = openSync3(temporary, "wx", stat3?.mode ?? 438);
|
|
14254
15014
|
try {
|
|
14255
15015
|
try {
|
|
14256
15016
|
writeFileSync14(fd, content, "utf-8");
|
|
14257
15017
|
} finally {
|
|
14258
|
-
|
|
15018
|
+
closeSync3(fd);
|
|
14259
15019
|
}
|
|
14260
15020
|
renameSync2(temporary, destination);
|
|
14261
15021
|
} finally {
|
|
@@ -14298,6 +15058,7 @@ __export(tools_exports3, {
|
|
|
14298
15058
|
Diff: () => diff_exports,
|
|
14299
15059
|
EditFile: () => edit_file_exports,
|
|
14300
15060
|
EnterWorktree: () => enter_worktree_exports,
|
|
15061
|
+
ExitCodeHints: () => exit_code_hints_exports,
|
|
14301
15062
|
ExitPlanMode: () => exit_plan_mode_exports,
|
|
14302
15063
|
ExitWorktree: () => exit_worktree_exports,
|
|
14303
15064
|
FileMutationQueue: () => file_mutation_queue_exports,
|
|
@@ -14310,11 +15071,13 @@ __export(tools_exports3, {
|
|
|
14310
15071
|
PowerShell: () => powershell_exports,
|
|
14311
15072
|
ReadFile: () => read_file_exports,
|
|
14312
15073
|
Registry: () => registry_exports,
|
|
15074
|
+
ShellBackground: () => shell_background_exports,
|
|
14313
15075
|
ShellOutput: () => shell_output_exports,
|
|
14314
15076
|
Snippets: () => snippets_exports,
|
|
14315
15077
|
SyntheticOutput: () => synthetic_output_exports,
|
|
14316
15078
|
ToolSearch: () => tool_search_exports,
|
|
14317
15079
|
Types: () => types_exports,
|
|
15080
|
+
WebFetchTool: () => web_fetch_exports,
|
|
14318
15081
|
WriteFile: () => write_file_exports
|
|
14319
15082
|
});
|
|
14320
15083
|
|
|
@@ -14600,7 +15363,7 @@ __export(lockfile_exports, {
|
|
|
14600
15363
|
});
|
|
14601
15364
|
import { readdir as readdir2, readFile as readFile5 } from "fs/promises";
|
|
14602
15365
|
import { homedir as homedir10 } from "os";
|
|
14603
|
-
import { join as
|
|
15366
|
+
import { join as join26, resolve as resolve11, sep as sep2 } from "path";
|
|
14604
15367
|
import { z as z21 } from "zod";
|
|
14605
15368
|
var log34 = createChildLogger({ module: "vscode" });
|
|
14606
15369
|
var LockfileSchema = z21.object({
|
|
@@ -14611,8 +15374,8 @@ var LockfileSchema = z21.object({
|
|
|
14611
15374
|
authToken: z21.string().optional()
|
|
14612
15375
|
});
|
|
14613
15376
|
function ideLockDir() {
|
|
14614
|
-
const configDir = process.env.CLAUDE_CONFIG_DIR ??
|
|
14615
|
-
return
|
|
15377
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR ?? join26(homedir10(), ".claude");
|
|
15378
|
+
return join26(configDir, "ide");
|
|
14616
15379
|
}
|
|
14617
15380
|
function cwdInWorkspace(cwd2, workspaceFolders) {
|
|
14618
15381
|
const normalizedCwd = cwd2.normalize("NFC");
|
|
@@ -14630,7 +15393,7 @@ async function readLockfile(dir, filename) {
|
|
|
14630
15393
|
return null;
|
|
14631
15394
|
}
|
|
14632
15395
|
try {
|
|
14633
|
-
const content = await readFile5(
|
|
15396
|
+
const content = await readFile5(join26(dir, filename), "utf-8");
|
|
14634
15397
|
const parsed = LockfileSchema.parse(JSON.parse(content));
|
|
14635
15398
|
if (parsed.transport !== "ws") {
|
|
14636
15399
|
return null;
|
|
@@ -14975,6 +15738,7 @@ async function runPrintMode(args) {
|
|
|
14975
15738
|
registry.register(new ToolSearchTool(registry));
|
|
14976
15739
|
const teamManager = new TeamManager(workDir);
|
|
14977
15740
|
const backgroundTaskManager = new TaskManager();
|
|
15741
|
+
attachBackgroundTaskManager(registry, backgroundTaskManager);
|
|
14978
15742
|
registry.register(new TeamCreateTool(teamManager));
|
|
14979
15743
|
registry.register(new SendMessageTool(teamManager));
|
|
14980
15744
|
registry.register(new TeamDeleteTool(teamManager));
|
|
@@ -15036,7 +15800,8 @@ async function runPrintMode(args) {
|
|
|
15036
15800
|
onEvent,
|
|
15037
15801
|
void 0,
|
|
15038
15802
|
teamChecker,
|
|
15039
|
-
|
|
15803
|
+
// Teammates stay purely foreground: see SubagentRunOptions.backgroundTasks.
|
|
15804
|
+
{ abortSignal, backgroundTasks: false }
|
|
15040
15805
|
),
|
|
15041
15806
|
provider.base_url
|
|
15042
15807
|
);
|
|
@@ -15124,7 +15889,7 @@ Error: ${event.error.message}`);
|
|
|
15124
15889
|
break;
|
|
15125
15890
|
}
|
|
15126
15891
|
}
|
|
15127
|
-
await backgroundTaskManager.waitAll();
|
|
15892
|
+
await backgroundTaskManager.waitAll((task) => (task.kind ?? "agent") === "agent");
|
|
15128
15893
|
const backgroundNotifications = backgroundTaskManager.drainNotifications();
|
|
15129
15894
|
const durationMs = Date.now() - startTime;
|
|
15130
15895
|
if (args.outputFormat === "text" && resultText && !resultText.endsWith("\n")) {
|
|
@@ -15217,13 +15982,13 @@ __export(recover_exports, {
|
|
|
15217
15982
|
recordExit: () => recordExit,
|
|
15218
15983
|
recover: () => recover
|
|
15219
15984
|
});
|
|
15220
|
-
import { appendFileSync, mkdirSync as
|
|
15221
|
-
import { join as
|
|
15985
|
+
import { appendFileSync, mkdirSync as mkdirSync17 } from "fs";
|
|
15986
|
+
import { join as join27 } from "path";
|
|
15222
15987
|
var LOG_DIR = ".swifty";
|
|
15223
|
-
var LOG_PATH =
|
|
15988
|
+
var LOG_PATH = join27(LOG_DIR, "crash.log");
|
|
15224
15989
|
function record(text) {
|
|
15225
15990
|
try {
|
|
15226
|
-
|
|
15991
|
+
mkdirSync17(LOG_DIR, { recursive: true });
|
|
15227
15992
|
appendFileSync(LOG_PATH, `[${(/* @__PURE__ */ new Date()).toISOString()}] ${text}
|
|
15228
15993
|
`, "utf8");
|
|
15229
15994
|
} catch {
|
|
@@ -15303,7 +16068,7 @@ __export(teammate_exports, {
|
|
|
15303
16068
|
parseTeammateFlags: () => parseTeammateFlags,
|
|
15304
16069
|
runTeammate: () => runTeammate
|
|
15305
16070
|
});
|
|
15306
|
-
import { basename as basename6, dirname as dirname10, join as
|
|
16071
|
+
import { basename as basename6, dirname as dirname10, join as join28 } from "path";
|
|
15307
16072
|
function parseTeammateFlags(args) {
|
|
15308
16073
|
if (!args.includes("--teammate")) {
|
|
15309
16074
|
return null;
|
|
@@ -15399,7 +16164,7 @@ async function runTeammate(args) {
|
|
|
15399
16164
|
initLogger({
|
|
15400
16165
|
sessionId: `teammate-${safeMemberName}-${Date.now().toString(36)}`,
|
|
15401
16166
|
mode: "teammate",
|
|
15402
|
-
logDir:
|
|
16167
|
+
logDir: join28(dirname10(args.teamDir), "logs"),
|
|
15403
16168
|
skipCleanup: true
|
|
15404
16169
|
});
|
|
15405
16170
|
process.on("exit", closeLogger);
|