@swifty.js/swifty 0.0.32 → 0.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -0
- package/dist/acp-5WRTN2LL.js +14 -0
- package/dist/agent-3CAPJCPD.js +4 -0
- package/dist/anthropic-FXRRA63E.js +4 -0
- package/dist/{bwrap-TPE6G4BM.js → bwrap-ZTS67W2K.js} +1 -1
- package/dist/checker-3O7QF7XV.js +4 -0
- package/dist/chunk-2U4IWZ6E.js +4 -0
- package/dist/chunk-35X3GW25.js +4 -0
- package/dist/{chunk-NERVCUWE.js → chunk-4C3KJLJV.js} +1 -1
- package/dist/chunk-4HSQBZBI.js +4 -0
- package/dist/{chunk-JMF2DWGY.js → chunk-4PQYUHSN.js} +1 -1
- package/dist/chunk-6I3HEMIE.js +4 -0
- package/dist/{chunk-TKQ5T5VO.js → chunk-7JQHFZRD.js} +2 -2
- package/dist/{chunk-S35E7OYL.js → chunk-C5WLP3DH.js} +1 -1
- package/dist/chunk-FUKVR3XO.js +608 -0
- package/dist/chunk-FZORZF22.js +16 -0
- package/dist/chunk-G2TE2QZM.js +4 -0
- package/dist/chunk-I7XJSLRI.js +4 -0
- package/dist/{chunk-2EV72T5X.js → chunk-IKQTIDRL.js} +1 -1
- package/dist/chunk-IZ7QOBSB.js +4 -0
- package/dist/chunk-LC7ZWLIH.js +4 -0
- package/dist/chunk-LHSUYZO5.js +4 -0
- package/dist/chunk-M4D66APS.js +67 -0
- package/dist/chunk-NAAAN2TD.js +60 -0
- package/dist/chunk-NCZR3AGA.js +65 -0
- package/dist/{server-QMMIYNXW.js → chunk-PXQD6GWP.js} +15 -15
- package/dist/{chunk-FPQ2LBL4.js → chunk-QQVDL4QT.js} +1 -1
- package/dist/{chunk-TCQ4EB4G.js → chunk-TNF3EHFY.js} +1 -1
- package/dist/chunk-XGUH7SGV.js +4 -0
- package/dist/chunk-YIKI3EPG.js +150 -0
- package/dist/chunk-YKWIZE5D.js +4 -0
- package/dist/{devtools-OJ75EP3T.js → devtools-WJM4UVVT.js} +1 -1
- package/dist/esm-46WSO26W.js +4 -0
- package/dist/esm-4DBD7ONK.js +229 -0
- package/dist/esm-6EDGM6QS.js +4 -0
- package/dist/esm-D6BB3FBR.js +4 -0
- package/dist/esm-P2KEXEEK.js +4 -0
- package/dist/esm-XYKZYVLV.js +4 -0
- package/dist/esm-YJVVI2QX.js +4 -0
- package/dist/getMachineId-bsd-NBOQ3GRQ.js +4 -0
- package/dist/getMachineId-darwin-6VBNSUFR.js +5 -0
- package/dist/getMachineId-linux-RQ33NIOO.js +4 -0
- package/dist/getMachineId-unsupported-IWOYPUHZ.js +4 -0
- package/dist/getMachineId-win-ITT3WXP6.js +4 -0
- package/dist/lib/{agent-3OEC4SOB.js → agent-NNYCUIOF.js} +1 -1
- package/dist/lib/{checker-QNMXPZ7C.js → checker-UPY4CXUL.js} +1 -1
- package/dist/lib/{chunk-UH5M7FDP.js → chunk-AFYPVA5C.js} +252 -20
- package/dist/lib/{chunk-SOOSTGHI.js → chunk-CFYAZWDA.js} +8 -2
- package/dist/lib/chunk-GTHYQZRL.js +31 -0
- package/dist/lib/{chunk-4BPKIIQD.js → chunk-LDWFI6MR.js} +6 -4
- package/dist/lib/index.d.ts +229 -152
- package/dist/lib/index.js +1096 -575
- package/dist/lib/providers-UJEWP7C7.js +419 -0
- package/dist/lib/{tool-filter-IMIU7CW2.js → tool-filter-BFEINZIA.js} +1 -1
- package/dist/main.js +189 -181
- package/dist/{node-GXMM4JG3.js → node-A6KBFKOV.js} +1 -1
- package/dist/{openai-M7B2TITO.js → openai-WPEICAZ4.js} +1 -1
- package/dist/providers-WNYZUMZ3.js +6 -0
- package/dist/{seatbelt-3GM2YGXV.js → seatbelt-PSPJ5LBR.js} +1 -1
- package/dist/server-YPBLBRDN.js +4 -0
- package/dist/src-3A5BCUSZ.js +4 -0
- package/dist/src-CRYFVH7Y.js +12 -0
- package/dist/src-Y77IB66J.js +4 -0
- package/dist/src-ZCW4GCG2.js +4 -0
- package/dist/{tool-filter-7LLVAHD5.js → tool-filter-X6IYME2K.js} +1 -1
- package/dist/turndown.es-WNOSVZF6.js +83 -0
- package/package.json +30 -4
- package/dist/agent-2DUTUJFX.js +0 -4
- package/dist/anthropic-KR5MXB5I.js +0 -4
- package/dist/checker-RKAWOOXC.js +0 -4
- package/dist/chunk-4YVJP2CO.js +0 -4
- package/dist/chunk-BWWNI2FK.js +0 -121
- package/dist/chunk-LTFPTWX4.js +0 -612
- package/dist/chunk-TW7RHCDS.js +0 -150
- package/dist/chunk-X3UA5OZL.js +0 -4
package/dist/lib/index.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SeatbeltSandbox
|
|
3
|
+
} from "./chunk-VUD72RTY.js";
|
|
4
|
+
import {
|
|
5
|
+
version
|
|
6
|
+
} from "./chunk-GTHYQZRL.js";
|
|
1
7
|
import {
|
|
2
8
|
Agent,
|
|
3
9
|
AutoCompactTrackingState,
|
|
@@ -14,6 +20,7 @@ import {
|
|
|
14
20
|
buildPlanModeReminder,
|
|
15
21
|
buildSummaryInstructions,
|
|
16
22
|
buildSummaryPrompt,
|
|
23
|
+
captureTelemetryError,
|
|
17
24
|
cleanExpiredSessions,
|
|
18
25
|
computeCompactThreshold,
|
|
19
26
|
computeKeepStartIndex,
|
|
@@ -34,25 +41,28 @@ import {
|
|
|
34
41
|
persistLargeResult,
|
|
35
42
|
planExists,
|
|
36
43
|
rebuildFromSession,
|
|
44
|
+
registerLlmClient,
|
|
37
45
|
replaceToolResultContent,
|
|
38
46
|
resetPlanPath,
|
|
39
47
|
saveCompactBoundary,
|
|
40
48
|
saveMessage,
|
|
41
49
|
savePlan,
|
|
50
|
+
shutdownTelemetry,
|
|
42
51
|
toDisplayPreview,
|
|
43
52
|
toolResultsToRecords,
|
|
44
53
|
toolUsesToRecords
|
|
45
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-AFYPVA5C.js";
|
|
46
55
|
import {
|
|
47
56
|
PathSandbox,
|
|
48
57
|
PermissionChecker,
|
|
49
58
|
RuleEngine,
|
|
50
59
|
canonicalPath,
|
|
60
|
+
compactPath,
|
|
51
61
|
evaluateRules,
|
|
52
62
|
extractContent,
|
|
53
63
|
isPathWithin,
|
|
54
64
|
isSafeCommand
|
|
55
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-CFYAZWDA.js";
|
|
56
66
|
import {
|
|
57
67
|
AnthropicClient,
|
|
58
68
|
CHARS_PER_TOKEN,
|
|
@@ -160,13 +170,10 @@ import {
|
|
|
160
170
|
ToolRegistry,
|
|
161
171
|
cloneRegistryForFork,
|
|
162
172
|
filterToolsForAgent
|
|
163
|
-
} from "./chunk-
|
|
173
|
+
} from "./chunk-LDWFI6MR.js";
|
|
164
174
|
import {
|
|
165
175
|
BwrapSandbox
|
|
166
176
|
} from "./chunk-ORSYNBMM.js";
|
|
167
|
-
import {
|
|
168
|
-
SeatbeltSandbox
|
|
169
|
-
} from "./chunk-VUD72RTY.js";
|
|
170
177
|
|
|
171
178
|
// src/bootstrap/interaction-summary.ts
|
|
172
179
|
function formatDuration(milliseconds) {
|
|
@@ -721,6 +728,10 @@ var GREP_DESCRIPTION = `Search file content with a case-insensitive, line-by-lin
|
|
|
721
728
|
- include is an optional glob: "*.ts" matches names at any depth; patterns with "/" match working-directory-relative paths. A direct file path is searched without this filter.
|
|
722
729
|
- Traversal includes dotfiles but skips fixed directories such as .git, .agents, .swifty, node_modules, dist, and __pycache__, not .gitignore rules. Binary/unreadable files are skipped; directory symlinks are not traversed.
|
|
723
730
|
- At most 500 matching lines. Narrow searches; never search the filesystem root. Use ReadFile for context and this tool instead of shell grep/rg.`;
|
|
731
|
+
var WEB_FETCH_DESCRIPTION = `Fetch a URL over HTTP(S) and return its content as Markdown. Use it to retrieve and analyze web pages.
|
|
732
|
+
- url must be a fully-formed http or https URL; redirects are followed automatically and the final URL is reported when it differs.
|
|
733
|
+
- HTML is converted to Markdown; other text formats (plain text, JSON, XML, Markdown) are returned as-is. Binary content (images, PDFs, archives) is rejected.
|
|
734
|
+
- Responses over 10MB are rejected and results are truncated at 100K characters. Successful fetches are cached for 15 minutes.`;
|
|
724
735
|
|
|
725
736
|
// src/tools/exit-code-hints.ts
|
|
726
737
|
function extractBaseCmd(command) {
|
|
@@ -1217,8 +1228,9 @@ import { setTimeout as delay } from "timers/promises";
|
|
|
1217
1228
|
import sharp2 from "sharp";
|
|
1218
1229
|
import { safeParse, z as z3 } from "zod";
|
|
1219
1230
|
|
|
1220
|
-
//
|
|
1221
|
-
var
|
|
1231
|
+
// src/tools/snippets.ts
|
|
1232
|
+
var MACOS_SNIPPET = `
|
|
1233
|
+
import AppKit
|
|
1222
1234
|
import ApplicationServices
|
|
1223
1235
|
import Foundation
|
|
1224
1236
|
|
|
@@ -1242,7 +1254,7 @@ let keyCodes: [String: CGKeyCode] = [
|
|
|
1242
1254
|
"Y": 16, "T": 17, "1": 18, "2": 19, "3": 20, "4": 21, "6": 22,
|
|
1243
1255
|
"5": 23, "=": 24, "9": 25, "7": 26, "-": 27, "8": 28, "0": 29,
|
|
1244
1256
|
"]": 30, "O": 31, "U": 32, "[": 33, "I": 34, "P": 35, "RETURN": 36,
|
|
1245
|
-
"ENTER": 36, "L": 37, "J": 38, "'": 39, "K": 40, ";": 41, "
|
|
1257
|
+
"ENTER": 36, "L": 37, "J": 38, "'": 39, "K": 40, ";": 41, "\\": 42,
|
|
1246
1258
|
",": 43, "/": 44, "N": 45, "M": 46, ".": 47, "TAB": 48, "SPACE": 49,
|
|
1247
1259
|
"GRAVE": 50, "BACKTICK": 50, "BACKSPACE": 51, "DELETE": 51, "ESC": 53, "ESCAPE": 53,
|
|
1248
1260
|
"CMD": 55, "COMMAND": 55, "META": 55, "SHIFT": 56, "CAPSLOCK": 57,
|
|
@@ -1394,38 +1406,86 @@ default:
|
|
|
1394
1406
|
fail("Unsupported action: " + action)
|
|
1395
1407
|
}
|
|
1396
1408
|
`;
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
var win32_script_default = `$ErrorActionPreference = 'Stop';
|
|
1400
|
-
|
|
1409
|
+
var WINDOWS_PWSH_SNIPPET = `
|
|
1410
|
+
$ErrorActionPreference = 'Stop';
|
|
1401
1411
|
Add-Type -AssemblyName System.Drawing;
|
|
1402
|
-
|
|
1403
1412
|
Add-Type -AssemblyName System.Windows.Forms;
|
|
1404
|
-
|
|
1405
1413
|
$bounds = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds;
|
|
1406
|
-
|
|
1407
1414
|
$bitmap = New-Object System.Drawing.Bitmap $bounds.Width, $bounds.Height;
|
|
1408
|
-
|
|
1409
1415
|
$graphics = [System.Drawing.Graphics]::FromImage($bitmap);
|
|
1410
|
-
|
|
1411
1416
|
$graphics.CopyFromScreen($bounds.X, $bounds.Y, 0, 0, $bounds.Size);
|
|
1412
|
-
|
|
1413
1417
|
$bitmap.Save($env:SWIFTY_SCREENSHOT_PATH, [System.Drawing.Imaging.ImageFormat]::Png);
|
|
1414
|
-
|
|
1415
1418
|
$graphics.Dispose(); $bitmap.Dispose();
|
|
1416
|
-
|
|
1417
1419
|
Write-Output "$($bounds.Width),$($bounds.Height)"
|
|
1418
1420
|
`;
|
|
1421
|
+
var WINDOWS_CSHARP_SNIPPET = `
|
|
1422
|
+
using System;
|
|
1423
|
+
using System.Drawing;
|
|
1424
|
+
using System.Runtime.InteropServices;
|
|
1425
|
+
using System.Windows.Forms;
|
|
1426
|
+
|
|
1427
|
+
public static class SwiftyComputer
|
|
1428
|
+
{
|
|
1429
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
1430
|
+
private struct INPUT { public uint type; public InputUnion value; }
|
|
1431
|
+
[StructLayout(LayoutKind.Explicit)]
|
|
1432
|
+
private struct InputUnion { [FieldOffset(0)] public KEYBDINPUT keyboard; }
|
|
1433
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
1434
|
+
private struct KEYBDINPUT
|
|
1435
|
+
{
|
|
1436
|
+
public ushort virtualKey;
|
|
1437
|
+
public ushort scanCode;
|
|
1438
|
+
public uint flags;
|
|
1439
|
+
public uint time;
|
|
1440
|
+
public UIntPtr extraInfo;
|
|
1441
|
+
}
|
|
1419
1442
|
|
|
1420
|
-
|
|
1421
|
-
|
|
1443
|
+
[DllImport("user32.dll")]
|
|
1444
|
+
private static extern void mouse_event(uint flags, uint dx, uint dy, int data, UIntPtr extraInfo);
|
|
1445
|
+
[DllImport("user32.dll", SetLastError = true)]
|
|
1446
|
+
private static extern uint SendInput(uint count, INPUT[] inputs, int size);
|
|
1422
1447
|
|
|
1423
|
-
|
|
1424
|
-
|
|
1448
|
+
private static void SendKeyboard(ushort virtualKey, ushort scanCode, uint flags)
|
|
1449
|
+
{
|
|
1450
|
+
var inputs = new[] {
|
|
1451
|
+
new INPUT {
|
|
1452
|
+
type = 1,
|
|
1453
|
+
value = new InputUnion {
|
|
1454
|
+
keyboard = new KEYBDINPUT {
|
|
1455
|
+
virtualKey = virtualKey,
|
|
1456
|
+
scanCode = scanCode,
|
|
1457
|
+
flags = flags,
|
|
1458
|
+
extraInfo = UIntPtr.Zero
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
};
|
|
1463
|
+
if (SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT))) != 1)
|
|
1464
|
+
{
|
|
1465
|
+
throw new InvalidOperationException("SendInput failed.");
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
public static void Move(int x, int y) { Cursor.Position = new Point(x, y); }
|
|
1470
|
+
public static Point Position() { return Cursor.Position; }
|
|
1471
|
+
public static void Mouse(uint flags, int data = 0) { mouse_event(flags, 0, 0, data, UIntPtr.Zero); }
|
|
1472
|
+
public static void Key(int virtualKey, bool down) { SendKeyboard((ushort)virtualKey, 0, down ? 0u : 2u); }
|
|
1473
|
+
public static void Text(string text)
|
|
1474
|
+
{
|
|
1475
|
+
foreach (char value in text)
|
|
1476
|
+
{
|
|
1477
|
+
SendKeyboard(0, value, 4u);
|
|
1478
|
+
SendKeyboard(0, value, 6u);
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
`;
|
|
1483
|
+
var WINDOWS_PWSH_INCLUDES_CSHARP_SNIPPET = `
|
|
1484
|
+
$ErrorActionPreference = 'Stop'
|
|
1425
1485
|
Add-Type -AssemblyName System.Drawing
|
|
1426
1486
|
Add-Type -AssemblyName System.Windows.Forms
|
|
1427
1487
|
Add-Type -TypeDefinition @'
|
|
1428
|
-
|
|
1488
|
+
${WINDOWS_CSHARP_SNIPPET}
|
|
1429
1489
|
'@
|
|
1430
1490
|
|
|
1431
1491
|
$payload = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($env:SWIFTY_COMPUTER_INPUT)) | ConvertFrom-Json
|
|
@@ -1541,11 +1601,11 @@ var mediaType = {
|
|
|
1541
1601
|
".gif": "image/gif",
|
|
1542
1602
|
".webp": "image/webp"
|
|
1543
1603
|
};
|
|
1544
|
-
function getMediaType(
|
|
1545
|
-
return mediaType[extname(
|
|
1604
|
+
function getMediaType(path) {
|
|
1605
|
+
return mediaType[extname(path).toLowerCase()] ?? null;
|
|
1546
1606
|
}
|
|
1547
|
-
function isImagePath(
|
|
1548
|
-
return getMediaType(
|
|
1607
|
+
function isImagePath(path) {
|
|
1608
|
+
return getMediaType(path) !== null;
|
|
1549
1609
|
}
|
|
1550
1610
|
function sniffMediaType(buf) {
|
|
1551
1611
|
if (buf.length >= 8 && buf[0] === 137 && buf[1] === 80 && buf[2] === 78 && buf[3] === 71) {
|
|
@@ -1969,7 +2029,6 @@ function openaiActionToFlat(item) {
|
|
|
1969
2029
|
return { action: "wait" };
|
|
1970
2030
|
}
|
|
1971
2031
|
}
|
|
1972
|
-
var WINDOWS_POWERSHELL = windows_default2.replace("${WINDOWS.CS}", () => windows_default);
|
|
1973
2032
|
function commandError(command, result) {
|
|
1974
2033
|
return new Error(
|
|
1975
2034
|
`${command} failed: ${result.stderr || result.stdout.toString("utf8").trim() || `exit ${String(result.code)}`}`
|
|
@@ -2366,7 +2425,7 @@ var ComputerUseTool = class {
|
|
|
2366
2425
|
const sourcePath = join2(directory, "main.swift");
|
|
2367
2426
|
const executablePath = join2(directory, "computer-helper");
|
|
2368
2427
|
try {
|
|
2369
|
-
await writeFile(sourcePath,
|
|
2428
|
+
await writeFile(sourcePath, MACOS_SNIPPET, "utf8");
|
|
2370
2429
|
const result = await this.run(
|
|
2371
2430
|
"/usr/bin/xcrun",
|
|
2372
2431
|
["swiftc", "-O", sourcePath, "-o", executablePath],
|
|
@@ -2384,7 +2443,7 @@ var ComputerUseTool = class {
|
|
|
2384
2443
|
async executeWindows(action, signal) {
|
|
2385
2444
|
const result = await this.run(
|
|
2386
2445
|
"powershell.exe",
|
|
2387
|
-
["-NoProfile", "-NonInteractive", "-Sta", "-Command",
|
|
2446
|
+
["-NoProfile", "-NonInteractive", "-Sta", "-Command", WINDOWS_PWSH_INCLUDES_CSHARP_SNIPPET],
|
|
2388
2447
|
{
|
|
2389
2448
|
env: {
|
|
2390
2449
|
...process.env,
|
|
@@ -2466,15 +2525,15 @@ var ComputerUseTool = class {
|
|
|
2466
2525
|
return "";
|
|
2467
2526
|
}
|
|
2468
2527
|
case "left_click_drag": {
|
|
2469
|
-
const
|
|
2470
|
-
if (
|
|
2528
|
+
const path = action.path ?? [];
|
|
2529
|
+
if (path.length < 2) {
|
|
2471
2530
|
throw new Error("Drag path must contain at least two points.");
|
|
2472
2531
|
}
|
|
2473
2532
|
await withKeys(async () => {
|
|
2474
|
-
await runXdotool(["mousemove", "--sync", String(
|
|
2533
|
+
await runXdotool(["mousemove", "--sync", String(path[0].x), String(path[0].y)]);
|
|
2475
2534
|
await runXdotool(["mousedown", "1"]);
|
|
2476
2535
|
try {
|
|
2477
|
-
for (const point of
|
|
2536
|
+
for (const point of path.slice(1)) {
|
|
2478
2537
|
await runXdotool(["mousemove", "--sync", String(point.x), String(point.y)]);
|
|
2479
2538
|
}
|
|
2480
2539
|
} finally {
|
|
@@ -2607,7 +2666,7 @@ var ComputerUseTool = class {
|
|
|
2607
2666
|
case "win32": {
|
|
2608
2667
|
const capture = await this.run(
|
|
2609
2668
|
"powershell.exe",
|
|
2610
|
-
["-NoProfile", "-NonInteractive", "-Sta", "-Command",
|
|
2669
|
+
["-NoProfile", "-NonInteractive", "-Sta", "-Command", WINDOWS_PWSH_SNIPPET],
|
|
2611
2670
|
{
|
|
2612
2671
|
env: { ...process.env, SWIFTY_SCREENSHOT_PATH: screenshotPath },
|
|
2613
2672
|
signal
|
|
@@ -2858,7 +2917,8 @@ var EditFileTool = class {
|
|
|
2858
2917
|
isError: true
|
|
2859
2918
|
};
|
|
2860
2919
|
}
|
|
2861
|
-
const
|
|
2920
|
+
const literal = () => newString;
|
|
2921
|
+
const newContent = replaceAll ? content.replaceAll(oldString, literal) : content.replace(oldString, literal);
|
|
2862
2922
|
try {
|
|
2863
2923
|
await writeFile2(filePath, newContent, "utf-8");
|
|
2864
2924
|
ctx.fileStateCache?.update(filePath);
|
|
@@ -2901,9 +2961,9 @@ function isSafeRefName(name) {
|
|
|
2901
2961
|
}
|
|
2902
2962
|
return SAFE_REF_RE.test(name);
|
|
2903
2963
|
}
|
|
2904
|
-
async function pathExists(
|
|
2964
|
+
async function pathExists(path) {
|
|
2905
2965
|
try {
|
|
2906
|
-
await access(
|
|
2966
|
+
await access(path);
|
|
2907
2967
|
return true;
|
|
2908
2968
|
} catch (err) {
|
|
2909
2969
|
log4.error({ err }, "worktree operation failed");
|
|
@@ -3100,21 +3160,21 @@ async function createAgentWorktree(slug, gitRoot) {
|
|
|
3100
3160
|
gitRoot: root
|
|
3101
3161
|
};
|
|
3102
3162
|
}
|
|
3103
|
-
async function removeAgentWorktree(
|
|
3104
|
-
await execFileAsync("git", ["worktree", "remove", "--",
|
|
3163
|
+
async function removeAgentWorktree(path, branch, gitRoot) {
|
|
3164
|
+
await execFileAsync("git", ["worktree", "remove", "--", path], {
|
|
3105
3165
|
cwd: gitRoot
|
|
3106
3166
|
});
|
|
3107
3167
|
await execFileAsync("git", ["branch", "-d", "--", branch], { cwd: gitRoot });
|
|
3108
3168
|
}
|
|
3109
|
-
async function hasWorktreeChanges(
|
|
3169
|
+
async function hasWorktreeChanges(path, headCommit) {
|
|
3110
3170
|
try {
|
|
3111
3171
|
const { stdout: status } = await execFileAsync("git", ["status", "--porcelain"], {
|
|
3112
|
-
cwd:
|
|
3172
|
+
cwd: path
|
|
3113
3173
|
});
|
|
3114
3174
|
if (status.trim()) {
|
|
3115
3175
|
return true;
|
|
3116
3176
|
}
|
|
3117
|
-
const currentHead = await readWorktreeHeadSha(
|
|
3177
|
+
const currentHead = await readWorktreeHeadSha(path) || (await execFileAsync("git", ["rev-parse", "HEAD"], { cwd: path })).stdout.trim();
|
|
3118
3178
|
return currentHead !== headCommit;
|
|
3119
3179
|
} catch (err) {
|
|
3120
3180
|
log4.error({ err }, "worktree operation failed");
|
|
@@ -3401,11 +3461,11 @@ var ExitWorktreeTool = class {
|
|
|
3401
3461
|
};
|
|
3402
3462
|
}
|
|
3403
3463
|
async execute(ctx, args) {
|
|
3404
|
-
const
|
|
3464
|
+
const path = strArg(args, "path");
|
|
3405
3465
|
const branch = strArg(args, "branch");
|
|
3406
3466
|
const gitRoot = strArg(args, "git_root");
|
|
3407
3467
|
const headCommit = strArg(args, "head_commit");
|
|
3408
|
-
if (!
|
|
3468
|
+
if (!path || !branch || !gitRoot) {
|
|
3409
3469
|
return {
|
|
3410
3470
|
output: "Error: path, branch and git_root are required",
|
|
3411
3471
|
isError: true
|
|
@@ -3413,17 +3473,17 @@ var ExitWorktreeTool = class {
|
|
|
3413
3473
|
}
|
|
3414
3474
|
if (!headCommit) {
|
|
3415
3475
|
return {
|
|
3416
|
-
output: `Original head_commit was not provided; worktree kept at: ${
|
|
3476
|
+
output: `Original head_commit was not provided; worktree kept at: ${path}
|
|
3417
3477
|
Branch: ${branch}`,
|
|
3418
3478
|
isError: false
|
|
3419
3479
|
};
|
|
3420
3480
|
}
|
|
3421
|
-
const hasChanges = await hasWorktreeChanges(
|
|
3481
|
+
const hasChanges = await hasWorktreeChanges(path, headCommit);
|
|
3422
3482
|
if (!hasChanges) {
|
|
3423
3483
|
try {
|
|
3424
|
-
await removeAgentWorktree(
|
|
3484
|
+
await removeAgentWorktree(path, branch, gitRoot);
|
|
3425
3485
|
return {
|
|
3426
|
-
output: `Worktree cleaned up (no changes): ${
|
|
3486
|
+
output: `Worktree cleaned up (no changes): ${path}`,
|
|
3427
3487
|
isError: false
|
|
3428
3488
|
};
|
|
3429
3489
|
} catch (err) {
|
|
@@ -3435,7 +3495,7 @@ Branch: ${branch}`,
|
|
|
3435
3495
|
}
|
|
3436
3496
|
}
|
|
3437
3497
|
return {
|
|
3438
|
-
output: `Worktree has changes, kept at: ${
|
|
3498
|
+
output: `Worktree has changes, kept at: ${path}
|
|
3439
3499
|
Branch: ${branch}`,
|
|
3440
3500
|
isError: false
|
|
3441
3501
|
};
|
|
@@ -4209,10 +4269,211 @@ var ToolSearchTool = class {
|
|
|
4209
4269
|
}
|
|
4210
4270
|
};
|
|
4211
4271
|
|
|
4272
|
+
// src/tools/web-fetch.ts
|
|
4273
|
+
var log10 = createChildLogger({ module: "tools" });
|
|
4274
|
+
var MAX_CONTENT_BYTES = 10 * 1024 * 1024;
|
|
4275
|
+
var FETCH_TIMEOUT_MS = 6e4;
|
|
4276
|
+
var MAX_MARKDOWN_LENGTH = 1e5;
|
|
4277
|
+
var CACHE_TTL_MS = 15 * 60 * 1e3;
|
|
4278
|
+
var MAX_CACHE_SIZE_BYTES = 50 * 1024 * 1024;
|
|
4279
|
+
var urlCache = /* @__PURE__ */ new Map();
|
|
4280
|
+
var urlCacheBytes = 0;
|
|
4281
|
+
function cacheGet(url) {
|
|
4282
|
+
const entry = urlCache.get(url);
|
|
4283
|
+
if (!entry) {
|
|
4284
|
+
return void 0;
|
|
4285
|
+
}
|
|
4286
|
+
if (entry.expiresAt <= Date.now()) {
|
|
4287
|
+
urlCache.delete(url);
|
|
4288
|
+
urlCacheBytes -= entry.size;
|
|
4289
|
+
return void 0;
|
|
4290
|
+
}
|
|
4291
|
+
return entry;
|
|
4292
|
+
}
|
|
4293
|
+
function cacheSet(url, markdown, finalUrl) {
|
|
4294
|
+
const existing = urlCache.get(url);
|
|
4295
|
+
if (existing) {
|
|
4296
|
+
urlCacheBytes -= existing.size;
|
|
4297
|
+
}
|
|
4298
|
+
const size = Math.max(1, Buffer.byteLength(markdown));
|
|
4299
|
+
urlCache.set(url, { markdown, finalUrl, expiresAt: Date.now() + CACHE_TTL_MS, size });
|
|
4300
|
+
urlCacheBytes += size;
|
|
4301
|
+
for (const key of urlCache.keys()) {
|
|
4302
|
+
if (urlCacheBytes <= MAX_CACHE_SIZE_BYTES) {
|
|
4303
|
+
break;
|
|
4304
|
+
}
|
|
4305
|
+
const evicted = urlCache.get(key);
|
|
4306
|
+
urlCache.delete(key);
|
|
4307
|
+
if (evicted) {
|
|
4308
|
+
urlCacheBytes -= evicted.size;
|
|
4309
|
+
}
|
|
4310
|
+
}
|
|
4311
|
+
}
|
|
4312
|
+
var turndownServicePromise;
|
|
4313
|
+
function getTurndownService() {
|
|
4314
|
+
return turndownServicePromise ??= import("turndown").then(
|
|
4315
|
+
// Strip <script>/<style>: turndown's default rules keep their text content,
|
|
4316
|
+
// which pollutes the markdown with JS/CSS noise.
|
|
4317
|
+
(mod) => new mod.default().remove(["script", "style"])
|
|
4318
|
+
);
|
|
4319
|
+
}
|
|
4320
|
+
function isBinaryContentType(contentType) {
|
|
4321
|
+
if (!contentType) {
|
|
4322
|
+
return false;
|
|
4323
|
+
}
|
|
4324
|
+
const mt = (contentType.split(";")[0] ?? "").trim().toLowerCase();
|
|
4325
|
+
if (mt.startsWith("text/")) {
|
|
4326
|
+
return false;
|
|
4327
|
+
}
|
|
4328
|
+
if (mt.endsWith("+json") || mt === "application/json") {
|
|
4329
|
+
return false;
|
|
4330
|
+
}
|
|
4331
|
+
if (mt.endsWith("+xml") || mt === "application/xml") {
|
|
4332
|
+
return false;
|
|
4333
|
+
}
|
|
4334
|
+
if (mt.startsWith("application/javascript")) {
|
|
4335
|
+
return false;
|
|
4336
|
+
}
|
|
4337
|
+
if (mt === "application/x-www-form-urlencoded") {
|
|
4338
|
+
return false;
|
|
4339
|
+
}
|
|
4340
|
+
return true;
|
|
4341
|
+
}
|
|
4342
|
+
function truncateMarkdown(content) {
|
|
4343
|
+
if (content.length <= MAX_MARKDOWN_LENGTH) {
|
|
4344
|
+
return content;
|
|
4345
|
+
}
|
|
4346
|
+
return `${content.slice(0, MAX_MARKDOWN_LENGTH)}
|
|
4347
|
+
|
|
4348
|
+
[Content truncated due to length...]`;
|
|
4349
|
+
}
|
|
4350
|
+
function formatResult(markdown, requestedUrl, finalUrl) {
|
|
4351
|
+
const content = truncateMarkdown(markdown);
|
|
4352
|
+
if (finalUrl && finalUrl !== requestedUrl) {
|
|
4353
|
+
return `[Redirected to ${finalUrl}]
|
|
4354
|
+
|
|
4355
|
+
${content}`;
|
|
4356
|
+
}
|
|
4357
|
+
return content;
|
|
4358
|
+
}
|
|
4359
|
+
var WebFetchTool = class {
|
|
4360
|
+
// Use a hardcoded string instead of WebFetchTool.name.replace("Tool", "")
|
|
4361
|
+
// because class names are not stable after minification — bundlers like
|
|
4362
|
+
// Terser/esbuild may rename or mangle them, producing incorrect tool names at runtime.
|
|
4363
|
+
name = "WebFetch";
|
|
4364
|
+
description = WEB_FETCH_DESCRIPTION;
|
|
4365
|
+
category = "read";
|
|
4366
|
+
schema() {
|
|
4367
|
+
const inputSchema = {
|
|
4368
|
+
type: "object",
|
|
4369
|
+
properties: {
|
|
4370
|
+
url: {
|
|
4371
|
+
type: "string",
|
|
4372
|
+
description: "The http or https URL to fetch content from."
|
|
4373
|
+
}
|
|
4374
|
+
},
|
|
4375
|
+
required: ["url"]
|
|
4376
|
+
};
|
|
4377
|
+
return {
|
|
4378
|
+
name: this.name,
|
|
4379
|
+
description: this.description,
|
|
4380
|
+
input_schema: inputSchema
|
|
4381
|
+
};
|
|
4382
|
+
}
|
|
4383
|
+
async execute(ctx, args) {
|
|
4384
|
+
const url = strArg(args, "url");
|
|
4385
|
+
if (!url) {
|
|
4386
|
+
return { output: "Error: url is required", isError: true };
|
|
4387
|
+
}
|
|
4388
|
+
let parsed;
|
|
4389
|
+
try {
|
|
4390
|
+
parsed = new URL(url);
|
|
4391
|
+
} catch {
|
|
4392
|
+
return { output: `Error: invalid URL "${url}"`, isError: true };
|
|
4393
|
+
}
|
|
4394
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
4395
|
+
return {
|
|
4396
|
+
output: `Error: unsupported protocol "${parsed.protocol}" \u2014 only http and https URLs can be fetched`,
|
|
4397
|
+
isError: true
|
|
4398
|
+
};
|
|
4399
|
+
}
|
|
4400
|
+
const cached = cacheGet(url);
|
|
4401
|
+
if (cached) {
|
|
4402
|
+
return { output: formatResult(cached.markdown, url, cached.finalUrl), isError: false };
|
|
4403
|
+
}
|
|
4404
|
+
const timeoutSignal = AbortSignal.timeout(FETCH_TIMEOUT_MS);
|
|
4405
|
+
const signal = ctx.abortSignal ? AbortSignal.any([ctx.abortSignal, timeoutSignal]) : timeoutSignal;
|
|
4406
|
+
let response;
|
|
4407
|
+
try {
|
|
4408
|
+
response = await fetch(url, {
|
|
4409
|
+
signal,
|
|
4410
|
+
redirect: "follow",
|
|
4411
|
+
headers: {
|
|
4412
|
+
Accept: "text/markdown, text/html, */*"
|
|
4413
|
+
// "User-Agent": `swifty/${version}`,
|
|
4414
|
+
}
|
|
4415
|
+
});
|
|
4416
|
+
} catch (err) {
|
|
4417
|
+
log10.error({ err, url }, "web fetch failed");
|
|
4418
|
+
return { output: `Error: fetch failed: ${asErrorString(err)}`, isError: true };
|
|
4419
|
+
}
|
|
4420
|
+
if (!response.ok) {
|
|
4421
|
+
return {
|
|
4422
|
+
output: `Error: HTTP ${String(response.status)} ${response.statusText} for ${url}`,
|
|
4423
|
+
isError: true
|
|
4424
|
+
};
|
|
4425
|
+
}
|
|
4426
|
+
const declaredBytes = Number.parseInt(response.headers.get("content-length") ?? "", 10);
|
|
4427
|
+
if (!Number.isNaN(declaredBytes) && declaredBytes > MAX_CONTENT_BYTES) {
|
|
4428
|
+
return {
|
|
4429
|
+
output: `Error: response is ${String(declaredBytes)} bytes, over the ${String(MAX_CONTENT_BYTES)}-byte limit`,
|
|
4430
|
+
isError: true
|
|
4431
|
+
};
|
|
4432
|
+
}
|
|
4433
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
4434
|
+
if (isBinaryContentType(contentType)) {
|
|
4435
|
+
return {
|
|
4436
|
+
output: `Error: binary content (${contentType}) is not supported; only text content can be fetched`,
|
|
4437
|
+
isError: true
|
|
4438
|
+
};
|
|
4439
|
+
}
|
|
4440
|
+
let body;
|
|
4441
|
+
try {
|
|
4442
|
+
body = await response.arrayBuffer();
|
|
4443
|
+
} catch (err) {
|
|
4444
|
+
log10.error({ err, url }, "reading response body failed");
|
|
4445
|
+
return {
|
|
4446
|
+
output: `Error: reading response body failed: ${asErrorString(err)}`,
|
|
4447
|
+
isError: true
|
|
4448
|
+
};
|
|
4449
|
+
}
|
|
4450
|
+
if (body.byteLength > MAX_CONTENT_BYTES) {
|
|
4451
|
+
return {
|
|
4452
|
+
output: `Error: response is ${String(body.byteLength)} bytes, over the ${String(MAX_CONTENT_BYTES)}-byte limit`,
|
|
4453
|
+
isError: true
|
|
4454
|
+
};
|
|
4455
|
+
}
|
|
4456
|
+
const raw = Buffer.from(body).toString("utf-8");
|
|
4457
|
+
let markdown;
|
|
4458
|
+
try {
|
|
4459
|
+
markdown = contentType.includes("text/html") ? (await getTurndownService()).turndown(raw) : raw;
|
|
4460
|
+
} catch (err) {
|
|
4461
|
+
log10.error({ err, url }, "HTML to Markdown conversion failed");
|
|
4462
|
+
return {
|
|
4463
|
+
output: `Error: HTML to Markdown conversion failed: ${asErrorString(err)}`,
|
|
4464
|
+
isError: true
|
|
4465
|
+
};
|
|
4466
|
+
}
|
|
4467
|
+
const finalUrl = response.url || url;
|
|
4468
|
+
cacheSet(url, markdown, finalUrl);
|
|
4469
|
+
return { output: formatResult(markdown, url, finalUrl), isError: false };
|
|
4470
|
+
}
|
|
4471
|
+
};
|
|
4472
|
+
|
|
4212
4473
|
// src/tools/write-file.ts
|
|
4213
4474
|
import { existsSync as existsSync3, mkdirSync, writeFileSync } from "fs";
|
|
4214
4475
|
import { dirname as dirname3, resolve as resolve6 } from "path";
|
|
4215
|
-
var
|
|
4476
|
+
var log11 = createChildLogger({ module: "tools" });
|
|
4216
4477
|
var WriteFileTool = class {
|
|
4217
4478
|
// Use a hardcoded string instead of WriteFileTool.name.replace("Tool", "")
|
|
4218
4479
|
// because class names are not stable after minification — bundlers like
|
|
@@ -4281,7 +4542,7 @@ var WriteFileTool = class {
|
|
|
4281
4542
|
isError: false
|
|
4282
4543
|
});
|
|
4283
4544
|
} catch (err) {
|
|
4284
|
-
|
|
4545
|
+
log11.error({ err }, "tool operation failed");
|
|
4285
4546
|
return Promise.resolve({
|
|
4286
4547
|
output: `Error writing file: ${asErrorString(err)}`,
|
|
4287
4548
|
isError: true
|
|
@@ -4321,6 +4582,7 @@ function createToolRegistry(workDir, taskList) {
|
|
|
4321
4582
|
registry.register(new WriteFileTool());
|
|
4322
4583
|
registry.register(new GlobTool());
|
|
4323
4584
|
registry.register(new GrepTool());
|
|
4585
|
+
registry.register(new WebFetchTool());
|
|
4324
4586
|
return registry;
|
|
4325
4587
|
}
|
|
4326
4588
|
function wireSkillsToRegistry(catalog, commandRegistry, skillHost) {
|
|
@@ -4352,7 +4614,7 @@ function buildComposedToolFilter(coordinator, skillFilter) {
|
|
|
4352
4614
|
}
|
|
4353
4615
|
|
|
4354
4616
|
// src/code-review/session.ts
|
|
4355
|
-
var
|
|
4617
|
+
var log12 = createChildLogger({ module: "code-review" });
|
|
4356
4618
|
function isCriticEvaluation(str) {
|
|
4357
4619
|
return str === "reasonable" || str === "unreasonable" || str === "partially-reasonable";
|
|
4358
4620
|
}
|
|
@@ -4360,7 +4622,7 @@ function asCriticEvaluation(str) {
|
|
|
4360
4622
|
if (isCriticEvaluation(str)) {
|
|
4361
4623
|
return str;
|
|
4362
4624
|
} else {
|
|
4363
|
-
|
|
4625
|
+
log12.error({ str }, "code-review operation failed");
|
|
4364
4626
|
throw new Error(str);
|
|
4365
4627
|
}
|
|
4366
4628
|
}
|
|
@@ -4771,7 +5033,7 @@ Created: ${new Date(request.createdAt).toLocaleString()}`;
|
|
|
4771
5033
|
};
|
|
4772
5034
|
|
|
4773
5035
|
// src/code-review/handler.ts
|
|
4774
|
-
var
|
|
5036
|
+
var log13 = createChildLogger({ module: "code-review" });
|
|
4775
5037
|
function handleCodeReviewCommand(ctx, manager, session) {
|
|
4776
5038
|
const args = ctx.args.trim().split(/\s+/);
|
|
4777
5039
|
const command = args[0]?.toLowerCase();
|
|
@@ -4818,7 +5080,7 @@ function handleCodeReviewCommand(ctx, manager, session) {
|
|
|
4818
5080
|
return showCodeReviewHelp();
|
|
4819
5081
|
}
|
|
4820
5082
|
} catch (err) {
|
|
4821
|
-
|
|
5083
|
+
log13.error({ err }, "code-review operation failed");
|
|
4822
5084
|
return `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
4823
5085
|
}
|
|
4824
5086
|
}
|
|
@@ -5070,7 +5332,7 @@ import z4 from "zod";
|
|
|
5070
5332
|
|
|
5071
5333
|
// src/teams/backend.ts
|
|
5072
5334
|
import { execSync, spawn as spawn4 } from "child_process";
|
|
5073
|
-
var
|
|
5335
|
+
var log14 = createChildLogger({ module: "teams" });
|
|
5074
5336
|
function detectBackend() {
|
|
5075
5337
|
if (process.platform === "win32") {
|
|
5076
5338
|
return "in-process";
|
|
@@ -5117,7 +5379,7 @@ function spawnTeammate(config) {
|
|
|
5117
5379
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5118
5380
|
});
|
|
5119
5381
|
} catch (err) {
|
|
5120
|
-
|
|
5382
|
+
log14.error({ err }, "teams operation failed");
|
|
5121
5383
|
execSync(`tmux new-session -d -s "${sessionName}" -n teammate "${cmd}"`, {
|
|
5122
5384
|
cwd: config.cwd,
|
|
5123
5385
|
encoding: "utf-8",
|
|
@@ -5131,7 +5393,7 @@ function spawnTeammate(config) {
|
|
|
5131
5393
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5132
5394
|
});
|
|
5133
5395
|
} catch (err) {
|
|
5134
|
-
|
|
5396
|
+
log14.error({ err }, "teams operation failed");
|
|
5135
5397
|
}
|
|
5136
5398
|
},
|
|
5137
5399
|
paneId: sessionName
|
|
@@ -5166,7 +5428,7 @@ function spawnTeammate(config) {
|
|
|
5166
5428
|
}
|
|
5167
5429
|
|
|
5168
5430
|
// src/code-review/manager.ts
|
|
5169
|
-
var
|
|
5431
|
+
var log15 = createChildLogger({ module: "code-review" });
|
|
5170
5432
|
var CodeReviewMemberSchema = z4.object({
|
|
5171
5433
|
name: z4.string(),
|
|
5172
5434
|
email: z4.string(),
|
|
@@ -5201,7 +5463,7 @@ var CodeReviewManager = class {
|
|
|
5201
5463
|
this.teams.set(team.name, team);
|
|
5202
5464
|
}
|
|
5203
5465
|
} catch (err) {
|
|
5204
|
-
|
|
5466
|
+
log15.error({ err }, "code-review operation failed");
|
|
5205
5467
|
}
|
|
5206
5468
|
}
|
|
5207
5469
|
}
|
|
@@ -5752,7 +6014,7 @@ ${args}`;
|
|
|
5752
6014
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync6 } from "fs";
|
|
5753
6015
|
import { join as join9, dirname as dirname4 } from "path";
|
|
5754
6016
|
import { safeParse as safeParse2, z as z6 } from "zod";
|
|
5755
|
-
var
|
|
6017
|
+
var log16 = createChildLogger({ module: "commands" });
|
|
5756
6018
|
var UsageEntrySchema = z6.object({
|
|
5757
6019
|
usageCount: z6.coerce.number(),
|
|
5758
6020
|
lastUsedAt: z6.coerce.number()
|
|
@@ -5808,7 +6070,7 @@ var CommandUsageTracker = class {
|
|
|
5808
6070
|
mkdirSync3(dirname4(this.filePath), { recursive: true });
|
|
5809
6071
|
writeFileSync3(this.filePath, JSON.stringify(Object.fromEntries(this.usage), null, 2));
|
|
5810
6072
|
} catch (err) {
|
|
5811
|
-
|
|
6073
|
+
log16.error({ err }, "commands operation failed");
|
|
5812
6074
|
}
|
|
5813
6075
|
}
|
|
5814
6076
|
};
|
|
@@ -5848,20 +6110,20 @@ var ProviderLoginSchema = ProviderConfigSchema.extend({
|
|
|
5848
6110
|
...provider,
|
|
5849
6111
|
thinking: getThinkingLevel(provider)
|
|
5850
6112
|
}));
|
|
5851
|
-
function readConfigRaw(
|
|
5852
|
-
if (!existsSync7(
|
|
6113
|
+
function readConfigRaw(path) {
|
|
6114
|
+
if (!existsSync7(path)) {
|
|
5853
6115
|
return {};
|
|
5854
6116
|
}
|
|
5855
6117
|
let raw;
|
|
5856
6118
|
try {
|
|
5857
|
-
raw = yaml3.load(readFileSync7(
|
|
6119
|
+
raw = yaml3.load(readFileSync7(path, "utf-8"));
|
|
5858
6120
|
} catch {
|
|
5859
|
-
throw new Error(`Unable to read existing ${
|
|
6121
|
+
throw new Error(`Unable to read existing ${path}; it has not been changed.`);
|
|
5860
6122
|
}
|
|
5861
6123
|
return z7.record(z7.string(), z7.unknown()).parse(raw ?? {});
|
|
5862
6124
|
}
|
|
5863
|
-
function writeConfigAtomic(
|
|
5864
|
-
const directory = dirname5(
|
|
6125
|
+
function writeConfigAtomic(path, raw) {
|
|
6126
|
+
const directory = dirname5(path);
|
|
5865
6127
|
mkdirSync4(directory, { recursive: true });
|
|
5866
6128
|
const temporary = join10(directory, `.config-${randomUUID()}.tmp`);
|
|
5867
6129
|
try {
|
|
@@ -5870,7 +6132,7 @@ function writeConfigAtomic(path2, raw) {
|
|
|
5870
6132
|
mode: 384,
|
|
5871
6133
|
flag: "wx"
|
|
5872
6134
|
});
|
|
5873
|
-
renameSync(temporary,
|
|
6135
|
+
renameSync(temporary, path);
|
|
5874
6136
|
} finally {
|
|
5875
6137
|
if (existsSync7(temporary)) {
|
|
5876
6138
|
unlinkSync(temporary);
|
|
@@ -5879,8 +6141,8 @@ function writeConfigAtomic(path2, raw) {
|
|
|
5879
6141
|
}
|
|
5880
6142
|
function saveProvider(input, available) {
|
|
5881
6143
|
const provider = ProviderLoginSchema.parse(input);
|
|
5882
|
-
const
|
|
5883
|
-
const config = readConfigRaw(
|
|
6144
|
+
const path = globalConfigPath();
|
|
6145
|
+
const config = readConfigRaw(path);
|
|
5884
6146
|
const existing = z7.array(z7.record(z7.string(), z7.unknown())).parse(config.providers ?? []);
|
|
5885
6147
|
const current = [...existing];
|
|
5886
6148
|
for (const entry of available) {
|
|
@@ -5905,16 +6167,16 @@ function saveProvider(input, available) {
|
|
|
5905
6167
|
stored.push(provider);
|
|
5906
6168
|
}
|
|
5907
6169
|
const providers = stored.map((entry) => ProviderConfigSchema.parse(entry));
|
|
5908
|
-
writeConfigAtomic(
|
|
5909
|
-
return { provider, providers, replaced, path
|
|
6170
|
+
writeConfigAtomic(path, { ...config, providers: stored });
|
|
6171
|
+
return { provider, providers, replaced, path };
|
|
5910
6172
|
}
|
|
5911
6173
|
function persistThinkingLevel(baseUrl, level) {
|
|
5912
|
-
const
|
|
5913
|
-
const config = readConfigRaw(
|
|
6174
|
+
const path = globalConfigPath();
|
|
6175
|
+
const config = readConfigRaw(path);
|
|
5914
6176
|
const providers = z7.array(z7.record(z7.string(), z7.unknown())).parse(config.providers ?? []);
|
|
5915
6177
|
const targets = providers.filter((entry) => entry.base_url === baseUrl);
|
|
5916
6178
|
if (targets.length === 0) {
|
|
5917
|
-
throw new Error(`Provider with base URL "${baseUrl}" not found in ${
|
|
6179
|
+
throw new Error(`Provider with base URL "${baseUrl}" not found in ${path}.`);
|
|
5918
6180
|
}
|
|
5919
6181
|
const changed = targets.filter((entry) => entry.thinking !== level);
|
|
5920
6182
|
if (changed.length === 0) {
|
|
@@ -5923,13 +6185,13 @@ function persistThinkingLevel(baseUrl, level) {
|
|
|
5923
6185
|
for (const entry of changed) {
|
|
5924
6186
|
entry.thinking = level;
|
|
5925
6187
|
}
|
|
5926
|
-
writeConfigAtomic(
|
|
6188
|
+
writeConfigAtomic(path, { ...config, providers });
|
|
5927
6189
|
}
|
|
5928
6190
|
|
|
5929
6191
|
// src/conversation/at-expand.ts
|
|
5930
6192
|
import { readFileSync as readFileSync8, statSync as statSync6 } from "fs";
|
|
5931
6193
|
import { isAbsolute as isAbsolute2, join as join11 } from "path";
|
|
5932
|
-
var
|
|
6194
|
+
var log17 = createChildLogger({ module: "tui" });
|
|
5933
6195
|
var MAX_INLINE_BYTES = 1e5;
|
|
5934
6196
|
var MAX_RANGE_FILE_BYTES = 1e7;
|
|
5935
6197
|
function parseRef(ref) {
|
|
@@ -5994,7 +6256,7 @@ ${readFileSync8(p, "utf-8")}
|
|
|
5994
6256
|
</file>`;
|
|
5995
6257
|
}
|
|
5996
6258
|
} catch (err) {
|
|
5997
|
-
|
|
6259
|
+
log17.error({ err }, "TUI operation failed");
|
|
5998
6260
|
}
|
|
5999
6261
|
}
|
|
6000
6262
|
return appendix ? text + appendix : text;
|
|
@@ -6040,7 +6302,7 @@ Error: too many images attached (limit ${String(MAX_IMAGES_PER_MESSAGE)} per mes
|
|
|
6040
6302
|
|
|
6041
6303
|
<image type="base64" media_type="${attachment.mediaType}" path="${refPath}" />`;
|
|
6042
6304
|
} catch (err) {
|
|
6043
|
-
|
|
6305
|
+
log17.error({ err }, "TUI operation failed");
|
|
6044
6306
|
}
|
|
6045
6307
|
} else if (lineStart !== void 0 && lineEnd !== void 0) {
|
|
6046
6308
|
if (st.size <= MAX_RANGE_FILE_BYTES) {
|
|
@@ -6061,7 +6323,7 @@ ${readFileSync8(p, "utf-8")}
|
|
|
6061
6323
|
</file>`;
|
|
6062
6324
|
}
|
|
6063
6325
|
} catch (err) {
|
|
6064
|
-
|
|
6326
|
+
log17.error({ err }, "TUI operation failed");
|
|
6065
6327
|
}
|
|
6066
6328
|
}
|
|
6067
6329
|
const expanded = appendix ? text + appendix : text;
|
|
@@ -6075,7 +6337,7 @@ ${readFileSync8(p, "utf-8")}
|
|
|
6075
6337
|
import { createHash } from "crypto";
|
|
6076
6338
|
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync9, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
6077
6339
|
import { join as join12, resolve as resolve7 } from "path";
|
|
6078
|
-
var
|
|
6340
|
+
var log18 = createChildLogger({ module: "file-history" });
|
|
6079
6341
|
var MAX_SNAPSHOTS = 100;
|
|
6080
6342
|
var MAX_SUMMARY_TEXT_LENGTH = 60;
|
|
6081
6343
|
function getBackupName(filePath, version2) {
|
|
@@ -6094,8 +6356,8 @@ var FileHistory = class {
|
|
|
6094
6356
|
this.sessionDir = fileHistoryDir(baseDir, sessionID);
|
|
6095
6357
|
mkdirSync5(this.sessionDir, { recursive: true });
|
|
6096
6358
|
}
|
|
6097
|
-
trackEdit(
|
|
6098
|
-
const absPath = resolve7(
|
|
6359
|
+
trackEdit(path) {
|
|
6360
|
+
const absPath = resolve7(path);
|
|
6099
6361
|
const version2 = this.trackedFiles.get(absPath) ?? 0;
|
|
6100
6362
|
const newVersion = version2 + 1;
|
|
6101
6363
|
if (existsSync8(absPath)) {
|
|
@@ -6104,7 +6366,7 @@ var FileHistory = class {
|
|
|
6104
6366
|
const backupName = getBackupName(absPath, newVersion);
|
|
6105
6367
|
writeFileSync5(join12(this.sessionDir, backupName), content);
|
|
6106
6368
|
} catch (err) {
|
|
6107
|
-
|
|
6369
|
+
log18.error({ err }, "file-history operation failed");
|
|
6108
6370
|
}
|
|
6109
6371
|
}
|
|
6110
6372
|
this.trackedFiles.set(absPath, newVersion);
|
|
@@ -6122,7 +6384,7 @@ var FileHistory = class {
|
|
|
6122
6384
|
try {
|
|
6123
6385
|
writeFileSync5(backupPath, readFileSync9(filePath));
|
|
6124
6386
|
} catch (err) {
|
|
6125
|
-
|
|
6387
|
+
log18.error({ err }, "file-history operation failed");
|
|
6126
6388
|
}
|
|
6127
6389
|
}
|
|
6128
6390
|
backups[filePath] = {
|
|
@@ -6152,12 +6414,12 @@ var FileHistory = class {
|
|
|
6152
6414
|
try {
|
|
6153
6415
|
backupData = readFileSync9(backup.backupPath);
|
|
6154
6416
|
} catch (err) {
|
|
6155
|
-
|
|
6417
|
+
log18.error({ err }, "file-history operation failed");
|
|
6156
6418
|
if (existsSync8(filePath)) {
|
|
6157
6419
|
try {
|
|
6158
6420
|
unlinkSync2(filePath);
|
|
6159
6421
|
} catch (err2) {
|
|
6160
|
-
|
|
6422
|
+
log18.error({ err: err2 }, "file-history operation failed");
|
|
6161
6423
|
}
|
|
6162
6424
|
}
|
|
6163
6425
|
continue;
|
|
@@ -6166,7 +6428,7 @@ var FileHistory = class {
|
|
|
6166
6428
|
try {
|
|
6167
6429
|
currentData = readFileSync9(filePath);
|
|
6168
6430
|
} catch (err) {
|
|
6169
|
-
|
|
6431
|
+
log18.error({ err }, "file-history operation failed");
|
|
6170
6432
|
}
|
|
6171
6433
|
const backupStr = backupData.toString();
|
|
6172
6434
|
const currentStr = currentData?.toString();
|
|
@@ -6175,7 +6437,7 @@ var FileHistory = class {
|
|
|
6175
6437
|
writeFileSync5(filePath, backupData);
|
|
6176
6438
|
changed.push(filePath);
|
|
6177
6439
|
} catch (err) {
|
|
6178
|
-
|
|
6440
|
+
log18.error({ err }, "file-history operation failed");
|
|
6179
6441
|
}
|
|
6180
6442
|
}
|
|
6181
6443
|
}
|
|
@@ -6212,7 +6474,7 @@ var FileHistory = class {
|
|
|
6212
6474
|
import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, existsSync as existsSync9 } from "fs";
|
|
6213
6475
|
import { join as join13 } from "path";
|
|
6214
6476
|
import { parse as parse3, z as z8 } from "zod";
|
|
6215
|
-
var
|
|
6477
|
+
var log19 = createChildLogger({ module: "history" });
|
|
6216
6478
|
var MAX_HISTORY_ENTRIES = 200;
|
|
6217
6479
|
var FILENAME = "prompt_history.jsonl";
|
|
6218
6480
|
var JSONLSchema = z8.looseObject({ text: z8.string() });
|
|
@@ -6229,12 +6491,12 @@ function load(dir) {
|
|
|
6229
6491
|
const { text } = parse3(JSONLSchema, entry);
|
|
6230
6492
|
return text;
|
|
6231
6493
|
} catch (err) {
|
|
6232
|
-
|
|
6494
|
+
log19.error({ err }, "parse history line failed");
|
|
6233
6495
|
return "";
|
|
6234
6496
|
}
|
|
6235
6497
|
}).filter(Boolean).slice(-MAX_HISTORY_ENTRIES);
|
|
6236
6498
|
} catch (err2) {
|
|
6237
|
-
|
|
6499
|
+
log19.error({ err: err2 }, "load history failed");
|
|
6238
6500
|
return [];
|
|
6239
6501
|
}
|
|
6240
6502
|
}
|
|
@@ -6253,7 +6515,7 @@ function append(dir, text) {
|
|
|
6253
6515
|
|
|
6254
6516
|
// src/hooks/hooks.ts
|
|
6255
6517
|
import { exec } from "child_process";
|
|
6256
|
-
var
|
|
6518
|
+
var log20 = createChildLogger({ module: "hooks" });
|
|
6257
6519
|
function execHookAsync(command, opts) {
|
|
6258
6520
|
return new Promise((resolve12, reject) => {
|
|
6259
6521
|
exec(
|
|
@@ -6332,7 +6594,7 @@ var HookEngine = class {
|
|
|
6332
6594
|
break;
|
|
6333
6595
|
}
|
|
6334
6596
|
} catch (err) {
|
|
6335
|
-
|
|
6597
|
+
log20.error({ err }, "hooks operation failed");
|
|
6336
6598
|
const onError = hook.on_error ?? "ignore";
|
|
6337
6599
|
if (onError === "fail") {
|
|
6338
6600
|
const msg = `Hook error: ${asErrorString(err)}`;
|
|
@@ -6386,7 +6648,7 @@ var HookEngine = class {
|
|
|
6386
6648
|
reject: hook.reject ?? false
|
|
6387
6649
|
};
|
|
6388
6650
|
} catch (err) {
|
|
6389
|
-
|
|
6651
|
+
log20.error({ err }, "hooks operation failed");
|
|
6390
6652
|
throw err;
|
|
6391
6653
|
}
|
|
6392
6654
|
}
|
|
@@ -6418,7 +6680,7 @@ var HookEngine = class {
|
|
|
6418
6680
|
reject: hook.reject ?? false
|
|
6419
6681
|
};
|
|
6420
6682
|
} catch (err) {
|
|
6421
|
-
|
|
6683
|
+
log20.error({ err }, "hooks operation failed");
|
|
6422
6684
|
throw err;
|
|
6423
6685
|
}
|
|
6424
6686
|
}
|
|
@@ -6431,7 +6693,7 @@ var HookEngine = class {
|
|
|
6431
6693
|
const output = await this.agentRunner(prompt, context);
|
|
6432
6694
|
return { output, success: true, reject: hook.reject ?? false };
|
|
6433
6695
|
} catch (err) {
|
|
6434
|
-
|
|
6696
|
+
log20.error({ err }, "hooks operation failed");
|
|
6435
6697
|
throw err;
|
|
6436
6698
|
}
|
|
6437
6699
|
}
|
|
@@ -6482,7 +6744,7 @@ function evaluateSingleCondition(expr, ctx) {
|
|
|
6482
6744
|
try {
|
|
6483
6745
|
return new RegExp(regexMatch[2]).test(value);
|
|
6484
6746
|
} catch (err) {
|
|
6485
|
-
|
|
6747
|
+
log20.error({ err }, "hooks operation failed");
|
|
6486
6748
|
return false;
|
|
6487
6749
|
}
|
|
6488
6750
|
}
|
|
@@ -6506,7 +6768,7 @@ function evaluateSingleCondition(expr, ctx) {
|
|
|
6506
6768
|
try {
|
|
6507
6769
|
return new RegExp(`^${pattern}$`).test(value);
|
|
6508
6770
|
} catch (err) {
|
|
6509
|
-
|
|
6771
|
+
log20.error({ err }, "hooks operation failed");
|
|
6510
6772
|
return false;
|
|
6511
6773
|
}
|
|
6512
6774
|
}
|
|
@@ -6615,9 +6877,9 @@ async function storeClipboardImage(workDir, sessionId, bytes) {
|
|
|
6615
6877
|
}
|
|
6616
6878
|
const dir = fileHistoryDir(workDir, sessionId);
|
|
6617
6879
|
await mkdir2(dir, { recursive: true });
|
|
6618
|
-
const
|
|
6619
|
-
await writeFile3(
|
|
6620
|
-
return
|
|
6880
|
+
const path = join14(dir, clipboardImageFileName(bytes));
|
|
6881
|
+
await writeFile3(path, bytes);
|
|
6882
|
+
return path;
|
|
6621
6883
|
}
|
|
6622
6884
|
function run(command, args, extraEnv) {
|
|
6623
6885
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
@@ -6815,15 +7077,24 @@ async function createClient(config, systemPrompt) {
|
|
|
6815
7077
|
switch (config.protocol) {
|
|
6816
7078
|
case "anthropic": {
|
|
6817
7079
|
const { AnthropicClient: AnthropicClient2 } = await import("./anthropic-B33M2AVN.js");
|
|
6818
|
-
return new AnthropicClient2(config, systemPrompt)
|
|
7080
|
+
return registerLlmClient(new AnthropicClient2(config, systemPrompt), {
|
|
7081
|
+
model: config.model,
|
|
7082
|
+
protocol: config.protocol
|
|
7083
|
+
});
|
|
6819
7084
|
}
|
|
6820
7085
|
case "openai": {
|
|
6821
7086
|
const { OpenAIClient: OpenAIClient2 } = await import("./openai-C2PZFGJ2.js");
|
|
6822
|
-
return new OpenAIClient2(config, systemPrompt)
|
|
7087
|
+
return registerLlmClient(new OpenAIClient2(config, systemPrompt), {
|
|
7088
|
+
model: config.model,
|
|
7089
|
+
protocol: config.protocol
|
|
7090
|
+
});
|
|
6823
7091
|
}
|
|
6824
7092
|
case "openai-compat": {
|
|
6825
7093
|
const { OpenAICompatClient: OpenAICompatClient2 } = await import("./openai-C2PZFGJ2.js");
|
|
6826
|
-
return new OpenAICompatClient2(config, systemPrompt)
|
|
7094
|
+
return registerLlmClient(new OpenAICompatClient2(config, systemPrompt), {
|
|
7095
|
+
model: config.model,
|
|
7096
|
+
protocol: config.protocol
|
|
7097
|
+
});
|
|
6827
7098
|
}
|
|
6828
7099
|
default:
|
|
6829
7100
|
throw new Error(`Unknown protocol: ${String(config.protocol)}`);
|
|
@@ -6854,12 +7125,12 @@ function modelListUrl(protocol, baseUrl) {
|
|
|
6854
7125
|
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) {
|
|
6855
7126
|
return void 0;
|
|
6856
7127
|
}
|
|
6857
|
-
let
|
|
6858
|
-
|
|
6859
|
-
if (!
|
|
6860
|
-
|
|
7128
|
+
let path = url.pathname.replace(/\/+$/, "");
|
|
7129
|
+
path = path.replace(/\/(?:chat\/completions|completions|responses|messages|models)$/, "");
|
|
7130
|
+
if (!path || protocol === "anthropic" && !path.endsWith("/v1")) {
|
|
7131
|
+
path += "/v1";
|
|
6861
7132
|
}
|
|
6862
|
-
url.pathname = `${
|
|
7133
|
+
url.pathname = `${path}/models`;
|
|
6863
7134
|
url.search = "";
|
|
6864
7135
|
url.hash = "";
|
|
6865
7136
|
return url.toString();
|
|
@@ -6962,37 +7233,7 @@ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
|
|
|
6962
7233
|
import {
|
|
6963
7234
|
StreamableHTTPClientTransport
|
|
6964
7235
|
} from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
6965
|
-
|
|
6966
|
-
// src/version.ts
|
|
6967
|
-
import { readFileSync as readFileSync11 } from "fs";
|
|
6968
|
-
import path from "path";
|
|
6969
|
-
import { fileURLToPath } from "url";
|
|
6970
|
-
import { z as z10 } from "zod";
|
|
6971
|
-
var PackageJsonSchema = z10.object({
|
|
6972
|
-
version: z10.string()
|
|
6973
|
-
});
|
|
6974
|
-
function resolveVersion() {
|
|
6975
|
-
if (true) {
|
|
6976
|
-
return "0.0.32";
|
|
6977
|
-
}
|
|
6978
|
-
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
6979
|
-
for (const levels of ["..", "../.."]) {
|
|
6980
|
-
const candidate = path.resolve(here, levels, "package.json");
|
|
6981
|
-
try {
|
|
6982
|
-
const raw = JSON.parse(readFileSync11(candidate, "utf-8"));
|
|
6983
|
-
const parsed = PackageJsonSchema.safeParse(raw);
|
|
6984
|
-
if (parsed.success) {
|
|
6985
|
-
return parsed.data.version;
|
|
6986
|
-
}
|
|
6987
|
-
} catch {
|
|
6988
|
-
}
|
|
6989
|
-
}
|
|
6990
|
-
throw new Error("Could not resolve package version");
|
|
6991
|
-
}
|
|
6992
|
-
var version = resolveVersion();
|
|
6993
|
-
|
|
6994
|
-
// src/mcp/client.ts
|
|
6995
|
-
var log20 = createChildLogger({ module: "mcp" });
|
|
7236
|
+
var log21 = createChildLogger({ module: "mcp" });
|
|
6996
7237
|
function expandEnv(value, environment) {
|
|
6997
7238
|
return value.replace(
|
|
6998
7239
|
/\$\{([A-Za-z_]\w*)(?::-([^}]*))?\}|\$([A-Za-z_]\w*)/g,
|
|
@@ -7058,7 +7299,7 @@ async function mcpContentToToolOutput(content) {
|
|
|
7058
7299
|
hasImage = true;
|
|
7059
7300
|
continue;
|
|
7060
7301
|
} catch (err) {
|
|
7061
|
-
|
|
7302
|
+
log21.warn({ err }, "mcp image dropped (too large to fit the API limit)");
|
|
7062
7303
|
const note = "[note: an image returned by the tool was too large and was dropped]";
|
|
7063
7304
|
textParts.push(note);
|
|
7064
7305
|
contentBlocks.push({ type: "text", text: note });
|
|
@@ -7163,7 +7404,7 @@ var MCPClient = class {
|
|
|
7163
7404
|
try {
|
|
7164
7405
|
await this.client?.close();
|
|
7165
7406
|
} catch (err) {
|
|
7166
|
-
|
|
7407
|
+
log21.error({ err }, "mcp operation failed");
|
|
7167
7408
|
}
|
|
7168
7409
|
this.client = null;
|
|
7169
7410
|
this.transport = null;
|
|
@@ -7215,7 +7456,7 @@ ${removed.join("\n")}`
|
|
|
7215
7456
|
|
|
7216
7457
|
// src/mcp/manager.ts
|
|
7217
7458
|
import { isDeepStrictEqual } from "util";
|
|
7218
|
-
var
|
|
7459
|
+
var log22 = createChildLogger({ module: "mcp" });
|
|
7219
7460
|
var MCPManager = class {
|
|
7220
7461
|
clients = /* @__PURE__ */ new Map();
|
|
7221
7462
|
configs = /* @__PURE__ */ new Map();
|
|
@@ -7270,7 +7511,7 @@ var MCPManager = class {
|
|
|
7270
7511
|
});
|
|
7271
7512
|
}
|
|
7272
7513
|
} catch (err) {
|
|
7273
|
-
|
|
7514
|
+
log22.error({ err }, "mcp operation failed");
|
|
7274
7515
|
result.errors.push({
|
|
7275
7516
|
serverName: cfg.name,
|
|
7276
7517
|
error: asErrorString(err)
|
|
@@ -7356,7 +7597,7 @@ var MCPManager = class {
|
|
|
7356
7597
|
import {
|
|
7357
7598
|
existsSync as existsSync10,
|
|
7358
7599
|
statSync as statSync8,
|
|
7359
|
-
readFileSync as
|
|
7600
|
+
readFileSync as readFileSync11,
|
|
7360
7601
|
writeFileSync as writeFileSync7,
|
|
7361
7602
|
mkdirSync as mkdirSync7,
|
|
7362
7603
|
unlinkSync as unlinkSync3,
|
|
@@ -7389,15 +7630,15 @@ var MemoryPermissionChecker = class extends PermissionChecker {
|
|
|
7389
7630
|
reason: "Background memory tasks only support scoped file operations"
|
|
7390
7631
|
};
|
|
7391
7632
|
}
|
|
7392
|
-
const
|
|
7393
|
-
const insideMemory = this.memoryRoots.some((root) => isPathWithin(canonicalPath(root),
|
|
7633
|
+
const path = canonicalPath(resolve8(this.memoryWorkDir, requested));
|
|
7634
|
+
const insideMemory = this.memoryRoots.some((root) => isPathWithin(canonicalPath(root), path));
|
|
7394
7635
|
if (category === "write") {
|
|
7395
|
-
return insideMemory && extname2(
|
|
7636
|
+
return insideMemory && extname2(path) === ".md" ? { effect: "allow", reason: "Memory file update" } : {
|
|
7396
7637
|
effect: "deny",
|
|
7397
7638
|
reason: "Background memory writes must stay in the memory directories and use .md files"
|
|
7398
7639
|
};
|
|
7399
7640
|
}
|
|
7400
|
-
return insideMemory || this.allowProjectReads && isPathWithin(canonicalPath(this.memoryWorkDir),
|
|
7641
|
+
return insideMemory || this.allowProjectReads && isPathWithin(canonicalPath(this.memoryWorkDir), path) ? { effect: "allow", reason: "Memory task read" } : { effect: "deny", reason: "Read outside the memory task's scope" };
|
|
7401
7642
|
}
|
|
7402
7643
|
};
|
|
7403
7644
|
|
|
@@ -7421,7 +7662,7 @@ function extractWrittenPaths(messages) {
|
|
|
7421
7662
|
|
|
7422
7663
|
// src/tools/file-state-cache.ts
|
|
7423
7664
|
import { statSync as statSync7 } from "fs";
|
|
7424
|
-
var
|
|
7665
|
+
var log23 = createChildLogger({ module: "file-state-cache" });
|
|
7425
7666
|
var FileStateCache = class {
|
|
7426
7667
|
cache = /* @__PURE__ */ new Map();
|
|
7427
7668
|
// path -> mtimeMs
|
|
@@ -7447,7 +7688,7 @@ var FileStateCache = class {
|
|
|
7447
7688
|
try {
|
|
7448
7689
|
currentModifiedTime = statSync7(filePath).mtimeMs;
|
|
7449
7690
|
} catch (err) {
|
|
7450
|
-
|
|
7691
|
+
log23.error({ err }, "file state cache operation failed");
|
|
7451
7692
|
return {
|
|
7452
7693
|
ok: false,
|
|
7453
7694
|
error: "Error: file was deleted or is no longer accessible; read it again before editing."
|
|
@@ -7470,7 +7711,7 @@ var FileStateCache = class {
|
|
|
7470
7711
|
try {
|
|
7471
7712
|
lastModifiedTimeMs = statSync7(filePath).mtimeMs;
|
|
7472
7713
|
} catch (err) {
|
|
7473
|
-
|
|
7714
|
+
log23.error({ err }, "file state cache operation failed");
|
|
7474
7715
|
this.cache.delete(filePath);
|
|
7475
7716
|
return;
|
|
7476
7717
|
}
|
|
@@ -7479,7 +7720,7 @@ var FileStateCache = class {
|
|
|
7479
7720
|
};
|
|
7480
7721
|
|
|
7481
7722
|
// src/memory/consolidation.ts
|
|
7482
|
-
var
|
|
7723
|
+
var log24 = createChildLogger({ module: "memory" });
|
|
7483
7724
|
var DEFAULT_MIN_HOURS = 24;
|
|
7484
7725
|
var DEFAULT_MIN_SESSIONS = 5;
|
|
7485
7726
|
var SCAN_THROTTLE_MS = 10 * 60 * 1e3;
|
|
@@ -7571,30 +7812,30 @@ function lockPath(memDir) {
|
|
|
7571
7812
|
return join16(memDir, LOCK_FILE);
|
|
7572
7813
|
}
|
|
7573
7814
|
function readLastConsolidatedAt(memDir) {
|
|
7574
|
-
const
|
|
7575
|
-
if (!existsSync10(
|
|
7815
|
+
const path = lockPath(memDir);
|
|
7816
|
+
if (!existsSync10(path)) {
|
|
7576
7817
|
return 0;
|
|
7577
7818
|
}
|
|
7578
7819
|
try {
|
|
7579
|
-
return statSync8(
|
|
7820
|
+
return statSync8(path).mtimeMs;
|
|
7580
7821
|
} catch {
|
|
7581
7822
|
return 0;
|
|
7582
7823
|
}
|
|
7583
7824
|
}
|
|
7584
7825
|
function tryAcquireLock(memDir) {
|
|
7585
|
-
const
|
|
7826
|
+
const path = lockPath(memDir);
|
|
7586
7827
|
let mtimeMs;
|
|
7587
7828
|
let holderPid;
|
|
7588
|
-
if (existsSync10(
|
|
7829
|
+
if (existsSync10(path)) {
|
|
7589
7830
|
try {
|
|
7590
|
-
mtimeMs = statSync8(
|
|
7591
|
-
const raw =
|
|
7831
|
+
mtimeMs = statSync8(path).mtimeMs;
|
|
7832
|
+
const raw = readFileSync11(path, "utf-8").trim();
|
|
7592
7833
|
const parsed = parseInt(raw, 10);
|
|
7593
7834
|
if (Number.isFinite(parsed)) {
|
|
7594
7835
|
holderPid = parsed;
|
|
7595
7836
|
}
|
|
7596
7837
|
} catch (err) {
|
|
7597
|
-
|
|
7838
|
+
log24.error({ err }, "failed to read consolidation lock file");
|
|
7598
7839
|
}
|
|
7599
7840
|
}
|
|
7600
7841
|
if (mtimeMs !== void 0 && Date.now() - mtimeMs < HOLDER_STALE_MS) {
|
|
@@ -7603,9 +7844,9 @@ function tryAcquireLock(memDir) {
|
|
|
7603
7844
|
}
|
|
7604
7845
|
}
|
|
7605
7846
|
mkdirSync7(memDir, { recursive: true });
|
|
7606
|
-
writeFileSync7(
|
|
7847
|
+
writeFileSync7(path, String(process.pid));
|
|
7607
7848
|
try {
|
|
7608
|
-
const verify =
|
|
7849
|
+
const verify = readFileSync11(path, "utf-8").trim();
|
|
7609
7850
|
if (parseInt(verify, 10) !== process.pid) {
|
|
7610
7851
|
return null;
|
|
7611
7852
|
}
|
|
@@ -7615,17 +7856,17 @@ function tryAcquireLock(memDir) {
|
|
|
7615
7856
|
return mtimeMs ?? 0;
|
|
7616
7857
|
}
|
|
7617
7858
|
function rollbackLock(memDir, priorMtime) {
|
|
7618
|
-
const
|
|
7859
|
+
const path = lockPath(memDir);
|
|
7619
7860
|
try {
|
|
7620
7861
|
if (priorMtime === 0) {
|
|
7621
|
-
unlinkSync3(
|
|
7862
|
+
unlinkSync3(path);
|
|
7622
7863
|
return;
|
|
7623
7864
|
}
|
|
7624
|
-
writeFileSync7(
|
|
7865
|
+
writeFileSync7(path, "");
|
|
7625
7866
|
const t = priorMtime / 1e3;
|
|
7626
|
-
utimesSync(
|
|
7867
|
+
utimesSync(path, t, t);
|
|
7627
7868
|
} catch (err) {
|
|
7628
|
-
|
|
7869
|
+
log24.error({ err }, "failed to rollback consolidation lock");
|
|
7629
7870
|
}
|
|
7630
7871
|
}
|
|
7631
7872
|
function isProcessRunning(pid) {
|
|
@@ -7679,14 +7920,14 @@ function buildConsolidationPrompt(memDir, userMemDir, transcriptDir2, sessionIDs
|
|
|
7679
7920
|
}
|
|
7680
7921
|
|
|
7681
7922
|
// src/memory/extractor.ts
|
|
7682
|
-
import { readdirSync as readdirSync4, readFileSync as
|
|
7923
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync13, existsSync as existsSync12, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
7683
7924
|
import { homedir as homedir6 } from "os";
|
|
7684
7925
|
import { join as join18, basename as basename5 } from "path";
|
|
7685
7926
|
import yaml5 from "js-yaml";
|
|
7686
7927
|
|
|
7687
7928
|
// src/memory/manager.ts
|
|
7688
7929
|
import {
|
|
7689
|
-
readFileSync as
|
|
7930
|
+
readFileSync as readFileSync12,
|
|
7690
7931
|
readdirSync as readdirSync3,
|
|
7691
7932
|
unlinkSync as unlinkSync4,
|
|
7692
7933
|
existsSync as existsSync11,
|
|
@@ -7697,7 +7938,7 @@ import {
|
|
|
7697
7938
|
import { homedir as homedir5 } from "os";
|
|
7698
7939
|
import { join as join17, relative as relative2, basename as basename4 } from "path";
|
|
7699
7940
|
import yaml4 from "js-yaml";
|
|
7700
|
-
import
|
|
7941
|
+
import z10, { parse as parse4 } from "zod";
|
|
7701
7942
|
|
|
7702
7943
|
// src/memory/memory-age.ts
|
|
7703
7944
|
function memoryAgeDays(mtimeMs) {
|
|
@@ -7722,7 +7963,7 @@ function memoryFreshnessText(mtimeMs) {
|
|
|
7722
7963
|
}
|
|
7723
7964
|
|
|
7724
7965
|
// src/memory/manager.ts
|
|
7725
|
-
var
|
|
7966
|
+
var log25 = createChildLogger({ module: "memory" });
|
|
7726
7967
|
var MAX_ENTRYPOINT_LINES2 = 200;
|
|
7727
7968
|
var MAX_ENTRYPOINT_BYTES = 25e3;
|
|
7728
7969
|
var MEMORY_INDEX_NAME = "MEMORY.md";
|
|
@@ -7750,8 +7991,8 @@ function formatFileSize(bytes) {
|
|
|
7750
7991
|
}
|
|
7751
7992
|
return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
|
|
7752
7993
|
}
|
|
7753
|
-
var SelectedMemoriesSchema =
|
|
7754
|
-
selected_memories:
|
|
7994
|
+
var SelectedMemoriesSchema = z10.object({
|
|
7995
|
+
selected_memories: z10.array(z10.string())
|
|
7755
7996
|
});
|
|
7756
7997
|
var SELECT_MEMORIES_SYSTEM_PROMPT = `# Task
|
|
7757
7998
|
Select up to 5 listed memories clearly useful for the query, based on their paths and descriptions. Omit uncertain matches; return an empty list when none qualify.
|
|
@@ -7774,7 +8015,7 @@ var MemoryManager = class {
|
|
|
7774
8015
|
try {
|
|
7775
8016
|
const stat3 = statSync9(fullPath);
|
|
7776
8017
|
fingerprint = `${String(stat3.mtimeMs)}:${String(stat3.size)}`;
|
|
7777
|
-
const parsed = parseFrontmatter(
|
|
8018
|
+
const parsed = parseFrontmatter(readFileSync12(fullPath, "utf-8"));
|
|
7778
8019
|
this.malformedFingerprints.delete(fullPath);
|
|
7779
8020
|
return {
|
|
7780
8021
|
memory: {
|
|
@@ -7789,7 +8030,7 @@ var MemoryManager = class {
|
|
|
7789
8030
|
} catch (err) {
|
|
7790
8031
|
if (this.malformedFingerprints.get(fullPath) !== fingerprint) {
|
|
7791
8032
|
this.malformedFingerprints.set(fullPath, fingerprint);
|
|
7792
|
-
|
|
8033
|
+
log25.error({ err, path: fullPath }, "memory operation failed");
|
|
7793
8034
|
}
|
|
7794
8035
|
return null;
|
|
7795
8036
|
}
|
|
@@ -7806,7 +8047,7 @@ var MemoryManager = class {
|
|
|
7806
8047
|
(file) => file.endsWith(".md") && file !== MEMORY_INDEX_NAME
|
|
7807
8048
|
);
|
|
7808
8049
|
} catch (err) {
|
|
7809
|
-
|
|
8050
|
+
log25.error({ err, path: dir }, "memory operation failed");
|
|
7810
8051
|
continue;
|
|
7811
8052
|
}
|
|
7812
8053
|
for (const file of files) {
|
|
@@ -7876,7 +8117,7 @@ ${body}
|
|
|
7876
8117
|
try {
|
|
7877
8118
|
unlinkSync4(join17(dir, file));
|
|
7878
8119
|
} catch (err) {
|
|
7879
|
-
|
|
8120
|
+
log25.error({ err }, "memory operation failed");
|
|
7880
8121
|
continue;
|
|
7881
8122
|
}
|
|
7882
8123
|
}
|
|
@@ -7906,7 +8147,7 @@ ${body}
|
|
|
7906
8147
|
mkdirSync8(this.projectDir, { recursive: true });
|
|
7907
8148
|
if (existsSync11(indexPath)) {
|
|
7908
8149
|
try {
|
|
7909
|
-
if (
|
|
8150
|
+
if (readFileSync12(indexPath, "utf-8") === content) {
|
|
7910
8151
|
return;
|
|
7911
8152
|
}
|
|
7912
8153
|
} catch {
|
|
@@ -7954,7 +8195,7 @@ ${manifest}${toolsSection}`;
|
|
|
7954
8195
|
}
|
|
7955
8196
|
}
|
|
7956
8197
|
} catch (err) {
|
|
7957
|
-
|
|
8198
|
+
log25.error({ err }, "memory operation failed");
|
|
7958
8199
|
return [];
|
|
7959
8200
|
}
|
|
7960
8201
|
const jsonStr = extractJSONObject(rawResponse);
|
|
@@ -7966,7 +8207,7 @@ ${manifest}${toolsSection}`;
|
|
|
7966
8207
|
const raw = JSON.parse(jsonStr);
|
|
7967
8208
|
parsed = parse4(SelectedMemoriesSchema, raw);
|
|
7968
8209
|
} catch (err) {
|
|
7969
|
-
|
|
8210
|
+
log25.error({ err }, "memory operation failed");
|
|
7970
8211
|
return [];
|
|
7971
8212
|
}
|
|
7972
8213
|
const byKey = /* @__PURE__ */ new Map();
|
|
@@ -7994,7 +8235,7 @@ ${manifest}${toolsSection}`;
|
|
|
7994
8235
|
try {
|
|
7995
8236
|
files = readdirSync3(dir).filter((file) => file.endsWith(".md") && file !== MEMORY_INDEX_NAME);
|
|
7996
8237
|
} catch (err) {
|
|
7997
|
-
|
|
8238
|
+
log25.error({ err, path: dir }, "memory operation failed");
|
|
7998
8239
|
return [];
|
|
7999
8240
|
}
|
|
8000
8241
|
const headers = [];
|
|
@@ -8023,7 +8264,7 @@ ${manifest}${toolsSection}`;
|
|
|
8023
8264
|
for (const mem of memories) {
|
|
8024
8265
|
let content;
|
|
8025
8266
|
try {
|
|
8026
|
-
content =
|
|
8267
|
+
content = readFileSync12(mem.path, "utf-8");
|
|
8027
8268
|
} catch {
|
|
8028
8269
|
continue;
|
|
8029
8270
|
}
|
|
@@ -8048,11 +8289,11 @@ function formatMemoryManifest(memories) {
|
|
|
8048
8289
|
const scope = m.scope ? `[${m.scope}-scope] ` : "";
|
|
8049
8290
|
const tag = m.type ? `[${m.type}] ` : "";
|
|
8050
8291
|
const ts = new Date(m.mtimeMs).toISOString();
|
|
8051
|
-
const
|
|
8292
|
+
const path = m.filePath || m.filename;
|
|
8052
8293
|
if (m.description) {
|
|
8053
|
-
lines.push(`- ${scope}${tag}${
|
|
8294
|
+
lines.push(`- ${scope}${tag}${path} (${ts}): ${m.description}`);
|
|
8054
8295
|
} else {
|
|
8055
|
-
lines.push(`- ${scope}${tag}${
|
|
8296
|
+
lines.push(`- ${scope}${tag}${path} (${ts})`);
|
|
8056
8297
|
}
|
|
8057
8298
|
}
|
|
8058
8299
|
return lines.join("\n");
|
|
@@ -8072,12 +8313,12 @@ function extractJSONObject(raw) {
|
|
|
8072
8313
|
}
|
|
8073
8314
|
return trimmed.slice(start, end + 1);
|
|
8074
8315
|
}
|
|
8075
|
-
var FrontmatterSchema =
|
|
8076
|
-
name:
|
|
8077
|
-
description:
|
|
8078
|
-
type:
|
|
8079
|
-
metadata:
|
|
8080
|
-
type:
|
|
8316
|
+
var FrontmatterSchema = z10.looseObject({
|
|
8317
|
+
name: z10.string().optional(),
|
|
8318
|
+
description: z10.string().optional(),
|
|
8319
|
+
type: z10.string().optional(),
|
|
8320
|
+
metadata: z10.looseObject({
|
|
8321
|
+
type: z10.string().optional()
|
|
8081
8322
|
}).optional()
|
|
8082
8323
|
});
|
|
8083
8324
|
function parseFrontmatter(content) {
|
|
@@ -8156,7 +8397,7 @@ var MemoryExtractor = class {
|
|
|
8156
8397
|
const files = readdirSync4(dir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md");
|
|
8157
8398
|
for (const file of files) {
|
|
8158
8399
|
try {
|
|
8159
|
-
const content =
|
|
8400
|
+
const content = readFileSync13(join18(dir, file), "utf-8");
|
|
8160
8401
|
const typeMatch = /type:\s*(.+)/.exec(content);
|
|
8161
8402
|
const descMatch = /description:\s*(.+)/.exec(content);
|
|
8162
8403
|
const type = typeMatch?.[1]?.trim() ?? "reference";
|
|
@@ -8351,10 +8592,10 @@ ${mem.body}
|
|
|
8351
8592
|
};
|
|
8352
8593
|
|
|
8353
8594
|
// src/memory/instructions.ts
|
|
8354
|
-
import { readFileSync as
|
|
8595
|
+
import { readFileSync as readFileSync14, existsSync as existsSync13 } from "fs";
|
|
8355
8596
|
import { homedir as homedir7 } from "os";
|
|
8356
8597
|
import { join as join19, dirname as dirname6, resolve as resolve9, isAbsolute as isAbsolute3, relative as relative3 } from "path";
|
|
8357
|
-
var
|
|
8598
|
+
var log26 = createChildLogger({ module: "memory" });
|
|
8358
8599
|
var MAX_INCLUDE_DEPTH = 5;
|
|
8359
8600
|
function loadInstructions(workDir) {
|
|
8360
8601
|
const sources = discoverInstructions(workDir);
|
|
@@ -8370,7 +8611,7 @@ function loadInstructions(workDir) {
|
|
|
8370
8611
|
label = rel;
|
|
8371
8612
|
}
|
|
8372
8613
|
} catch (err) {
|
|
8373
|
-
|
|
8614
|
+
log26.error({ err }, "memory operation failed");
|
|
8374
8615
|
}
|
|
8375
8616
|
parts.push(`Contents of ${label}:
|
|
8376
8617
|
|
|
@@ -8385,7 +8626,7 @@ function discoverInstructions(workDir) {
|
|
|
8385
8626
|
const home = homedir7();
|
|
8386
8627
|
addSource(sources, seen, join19(home, ".swifty", "AGENTS.md"));
|
|
8387
8628
|
} catch (err) {
|
|
8388
|
-
|
|
8629
|
+
log26.error({ err }, "memory operation failed");
|
|
8389
8630
|
}
|
|
8390
8631
|
const dirs = projectInstructionDirs(workDir);
|
|
8391
8632
|
for (const dir of dirs) {
|
|
@@ -8399,7 +8640,7 @@ function addSource(out, seen, filePath) {
|
|
|
8399
8640
|
try {
|
|
8400
8641
|
abs = resolve9(filePath);
|
|
8401
8642
|
} catch (err) {
|
|
8402
|
-
|
|
8643
|
+
log26.error({ err }, "memory operation failed");
|
|
8403
8644
|
return;
|
|
8404
8645
|
}
|
|
8405
8646
|
if (seen.has(abs)) {
|
|
@@ -8410,9 +8651,9 @@ function addSource(out, seen, filePath) {
|
|
|
8410
8651
|
}
|
|
8411
8652
|
let data;
|
|
8412
8653
|
try {
|
|
8413
|
-
data =
|
|
8654
|
+
data = readFileSync14(abs, "utf-8");
|
|
8414
8655
|
} catch (err) {
|
|
8415
|
-
|
|
8656
|
+
log26.error({ err }, "memory operation failed");
|
|
8416
8657
|
return;
|
|
8417
8658
|
}
|
|
8418
8659
|
seen.add(abs);
|
|
@@ -8442,19 +8683,19 @@ function expandIncludes(content, baseDir, seen, depth) {
|
|
|
8442
8683
|
try {
|
|
8443
8684
|
abs = resolve9(resolved);
|
|
8444
8685
|
} catch (err) {
|
|
8445
|
-
|
|
8686
|
+
log26.error({ err }, "memory operation failed");
|
|
8446
8687
|
out.push(line);
|
|
8447
8688
|
continue;
|
|
8448
8689
|
}
|
|
8449
8690
|
if (!seen.has(abs)) {
|
|
8450
8691
|
try {
|
|
8451
|
-
const data =
|
|
8692
|
+
const data = readFileSync14(abs, "utf-8");
|
|
8452
8693
|
seen.add(abs);
|
|
8453
8694
|
out.push(`<!-- included from ${includePath} -->`);
|
|
8454
8695
|
out.push(expandIncludes(data, dirname6(abs), seen, depth + 1));
|
|
8455
8696
|
continue;
|
|
8456
8697
|
} catch (err) {
|
|
8457
|
-
|
|
8698
|
+
log26.error({ err }, "memory operation failed");
|
|
8458
8699
|
}
|
|
8459
8700
|
}
|
|
8460
8701
|
}
|
|
@@ -8485,7 +8726,7 @@ function resolveInclude(p, baseDir) {
|
|
|
8485
8726
|
try {
|
|
8486
8727
|
return join19(homedir7(), p.slice(2));
|
|
8487
8728
|
} catch (err) {
|
|
8488
|
-
|
|
8729
|
+
log26.error({ err }, "memory operation failed");
|
|
8489
8730
|
return "";
|
|
8490
8731
|
}
|
|
8491
8732
|
}
|
|
@@ -8499,7 +8740,7 @@ function projectInstructionDirs(workDir) {
|
|
|
8499
8740
|
try {
|
|
8500
8741
|
abs = resolve9(workDir);
|
|
8501
8742
|
} catch (err) {
|
|
8502
|
-
|
|
8743
|
+
log26.error({ err }, "memory operation failed");
|
|
8503
8744
|
return [workDir];
|
|
8504
8745
|
}
|
|
8505
8746
|
const root = findGitRoot(abs);
|
|
@@ -8530,7 +8771,7 @@ function findGitRoot(start) {
|
|
|
8530
8771
|
return cur;
|
|
8531
8772
|
}
|
|
8532
8773
|
} catch (err) {
|
|
8533
|
-
|
|
8774
|
+
log26.error({ err }, "memory operation failed");
|
|
8534
8775
|
}
|
|
8535
8776
|
const parent = dirname6(cur);
|
|
8536
8777
|
if (parent === cur) {
|
|
@@ -8594,7 +8835,7 @@ function usingToolsSection() {
|
|
|
8594
8835
|
- ReadFile offsets are 0-based; displayed lines are 1-based. Read before editing or overwriting existing files; stale file-state errors require a fresh read and revised edit. Exclude display line numbers from edits.
|
|
8595
8836
|
- Narrow searches; follow truncation/readback instructions rather than treating partial output as exhaustive. Parallelize independent reads or disjoint tasks, not dependent operations or writes to shared files.
|
|
8596
8837
|
- Use available task tools for complex work, not trivial requests. Delegate bounded work with Agent only when useful, supplying scope, paths, edit permissions, and expected evidence. Forks inherit a snapshot; other subagents need self-contained context.
|
|
8597
|
-
- One-shot Agent results return inline
|
|
8838
|
+
- One-shot Agent results return inline by default. With run_in_background=true, Agent returns a task ID immediately and reports completion through a task notification. Persistent teammates require TeamCreate and Agent team_name, with SendMessage for follow-ups. Worktrees isolate changes but do not merge them.
|
|
8598
8839
|
- Load relevant skills before using their procedures; respect execution mode and resolve resources relative to the skill directory.
|
|
8599
8840
|
- Discover deferred tools with ToolSearch (query "select:<exact-tool-name>"). Follow returned instructions: dispatch-mode MCP tools use McpCall with the target arguments; other modes expose callable tools directly.`
|
|
8600
8841
|
};
|
|
@@ -8638,7 +8879,7 @@ function environmentSection(env) {
|
|
|
8638
8879
|
}
|
|
8639
8880
|
|
|
8640
8881
|
// src/prompt/builder.ts
|
|
8641
|
-
var
|
|
8882
|
+
var log27 = createChildLogger({ module: "prompt" });
|
|
8642
8883
|
var PromptBuilder = class {
|
|
8643
8884
|
sections = [];
|
|
8644
8885
|
add(s) {
|
|
@@ -8677,7 +8918,7 @@ function detectEnvironment(workDir) {
|
|
|
8677
8918
|
}).trim();
|
|
8678
8919
|
}
|
|
8679
8920
|
} catch (err) {
|
|
8680
|
-
|
|
8921
|
+
log27.error({ err }, "prompt operation failed");
|
|
8681
8922
|
}
|
|
8682
8923
|
return env;
|
|
8683
8924
|
}
|
|
@@ -8739,8 +8980,8 @@ function preview(text) {
|
|
|
8739
8980
|
return `${text.slice(0, TEXT_PREVIEW_CHARS)}\u2026 (+${String(text.length - TEXT_PREVIEW_CHARS)} chars)`;
|
|
8740
8981
|
}
|
|
8741
8982
|
var AgentEventLogger = class {
|
|
8742
|
-
constructor(
|
|
8743
|
-
this.log =
|
|
8983
|
+
constructor(log40) {
|
|
8984
|
+
this.log = log40;
|
|
8744
8985
|
}
|
|
8745
8986
|
log;
|
|
8746
8987
|
/**
|
|
@@ -8783,24 +9024,19 @@ var AgentEventLogger = class {
|
|
|
8783
9024
|
};
|
|
8784
9025
|
|
|
8785
9026
|
// src/remote/server.ts
|
|
8786
|
-
import { readFileSync as
|
|
9027
|
+
import { readFileSync as readFileSync22, existsSync as existsSync20, statSync as statSync11 } from "fs";
|
|
8787
9028
|
import { createServer } from "http";
|
|
8788
9029
|
import { join as join26, extname as extname3, normalize } from "path";
|
|
8789
9030
|
import { cwd } from "process";
|
|
8790
9031
|
import Koa from "koa";
|
|
8791
9032
|
import { WebSocketServer, WebSocket } from "ws";
|
|
8792
|
-
import
|
|
9033
|
+
import z19 from "zod";
|
|
8793
9034
|
|
|
8794
9035
|
// src/remote/session-state.ts
|
|
8795
9036
|
function restoreRemoteSession(state, sessionId, saved) {
|
|
8796
9037
|
const replay = rebuildFromSession(saved);
|
|
8797
9038
|
state.conv.reset();
|
|
8798
|
-
state.conv.appendMessages(
|
|
8799
|
-
replay.map((message) => ({
|
|
8800
|
-
...message,
|
|
8801
|
-
toolUses: message.toolUses?.map((tool) => ({ ...tool, arguments: tool.arguments ?? {} }))
|
|
8802
|
-
}))
|
|
8803
|
-
);
|
|
9039
|
+
state.conv.appendMessages(replay);
|
|
8804
9040
|
state.sessionId = sessionId;
|
|
8805
9041
|
state.fileHistory = new FileHistory(state.workDir, sessionId);
|
|
8806
9042
|
state.fileStateCache = new FileStateCache();
|
|
@@ -8873,11 +9109,11 @@ ${body}`,
|
|
|
8873
9109
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
8874
9110
|
|
|
8875
9111
|
// src/subagent/loader.ts
|
|
8876
|
-
import { readdirSync as readdirSync5, readFileSync as
|
|
9112
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync15, existsSync as existsSync14 } from "fs";
|
|
8877
9113
|
import { homedir as homedir8 } from "os";
|
|
8878
9114
|
import { join as join20 } from "path";
|
|
8879
9115
|
import yaml6 from "js-yaml";
|
|
8880
|
-
import
|
|
9116
|
+
import z11, { parse as parse5 } from "zod";
|
|
8881
9117
|
|
|
8882
9118
|
// src/subagent/definition.ts
|
|
8883
9119
|
var BUILTIN_AGENTS = [
|
|
@@ -8901,7 +9137,7 @@ var BUILTIN_AGENTS = [
|
|
|
8901
9137
|
];
|
|
8902
9138
|
|
|
8903
9139
|
// src/subagent/loader.ts
|
|
8904
|
-
var
|
|
9140
|
+
var log28 = createChildLogger({ module: "subagent" });
|
|
8905
9141
|
function loadAgentDefinitions(workDir) {
|
|
8906
9142
|
const definitions = [...BUILTIN_AGENTS];
|
|
8907
9143
|
const home = homedir8();
|
|
@@ -8921,7 +9157,7 @@ function loadDir(dir, definitions) {
|
|
|
8921
9157
|
const files = readdirSync5(dir).filter((f) => f.endsWith(".md"));
|
|
8922
9158
|
for (const file of files) {
|
|
8923
9159
|
try {
|
|
8924
|
-
const content =
|
|
9160
|
+
const content = readFileSync15(join20(dir, file), "utf-8");
|
|
8925
9161
|
const def = parseAgentDefinition(content);
|
|
8926
9162
|
if (def) {
|
|
8927
9163
|
const existing = definitions.findIndex((d) => d.name === def.name);
|
|
@@ -8932,21 +9168,21 @@ function loadDir(dir, definitions) {
|
|
|
8932
9168
|
}
|
|
8933
9169
|
}
|
|
8934
9170
|
} catch (err) {
|
|
8935
|
-
|
|
9171
|
+
log28.error({ err }, "subagent operation failed");
|
|
8936
9172
|
continue;
|
|
8937
9173
|
}
|
|
8938
9174
|
}
|
|
8939
9175
|
}
|
|
8940
|
-
var YamlFrontmatterSchema3 =
|
|
8941
|
-
name:
|
|
8942
|
-
description:
|
|
8943
|
-
tools:
|
|
8944
|
-
disallowed_tools:
|
|
8945
|
-
system_prompt:
|
|
8946
|
-
max_turns:
|
|
8947
|
-
model:
|
|
8948
|
-
background:
|
|
8949
|
-
isolation:
|
|
9176
|
+
var YamlFrontmatterSchema3 = z11.looseObject({
|
|
9177
|
+
name: z11.string(),
|
|
9178
|
+
description: z11.string().optional(),
|
|
9179
|
+
tools: z11.array(z11.string()).optional(),
|
|
9180
|
+
disallowed_tools: z11.array(z11.string()).optional(),
|
|
9181
|
+
system_prompt: z11.string().optional(),
|
|
9182
|
+
max_turns: z11.number().optional(),
|
|
9183
|
+
model: z11.string().optional(),
|
|
9184
|
+
background: z11.boolean().optional(),
|
|
9185
|
+
isolation: z11.literal("worktree").optional()
|
|
8950
9186
|
});
|
|
8951
9187
|
function parseAgentDefinition(content) {
|
|
8952
9188
|
if (!content.startsWith("---")) {
|
|
@@ -8974,11 +9210,120 @@ function parseAgentDefinition(content) {
|
|
|
8974
9210
|
initialPrompt: body
|
|
8975
9211
|
};
|
|
8976
9212
|
} catch (err) {
|
|
8977
|
-
|
|
9213
|
+
log28.error({ err }, "subagent operation failed");
|
|
8978
9214
|
return null;
|
|
8979
9215
|
}
|
|
8980
9216
|
}
|
|
8981
9217
|
|
|
9218
|
+
// src/subagent/task-manager.ts
|
|
9219
|
+
var TaskManager = class {
|
|
9220
|
+
tasks = /* @__PURE__ */ new Map();
|
|
9221
|
+
notifiedTaskIds = /* @__PURE__ */ new Set();
|
|
9222
|
+
listeners = /* @__PURE__ */ new Set();
|
|
9223
|
+
nextId = 1;
|
|
9224
|
+
create(name, runner, cancel, options = {}) {
|
|
9225
|
+
const id = `agent-${String(this.nextId++)}`;
|
|
9226
|
+
const task = {
|
|
9227
|
+
id,
|
|
9228
|
+
name,
|
|
9229
|
+
...options.originToolCallId ? { originToolCallId: options.originToolCallId } : {},
|
|
9230
|
+
status: "running",
|
|
9231
|
+
output: "",
|
|
9232
|
+
cancel,
|
|
9233
|
+
done: Promise.resolve()
|
|
9234
|
+
};
|
|
9235
|
+
this.tasks.set(id, task);
|
|
9236
|
+
this.emitChange();
|
|
9237
|
+
task.done = Promise.resolve().then(() => runner(task)).then((output) => {
|
|
9238
|
+
if (task.status === "running") {
|
|
9239
|
+
task.status = "completed";
|
|
9240
|
+
task.output = output;
|
|
9241
|
+
this.emitChange();
|
|
9242
|
+
}
|
|
9243
|
+
}).catch((error) => {
|
|
9244
|
+
if (task.status === "running") {
|
|
9245
|
+
task.status = "failed";
|
|
9246
|
+
task.output = `Error: ${asErrorString(error)}`;
|
|
9247
|
+
this.emitChange();
|
|
9248
|
+
}
|
|
9249
|
+
});
|
|
9250
|
+
return task;
|
|
9251
|
+
}
|
|
9252
|
+
get(id) {
|
|
9253
|
+
return this.tasks.get(id);
|
|
9254
|
+
}
|
|
9255
|
+
list() {
|
|
9256
|
+
return [...this.tasks.values()];
|
|
9257
|
+
}
|
|
9258
|
+
subscribe(listener) {
|
|
9259
|
+
this.listeners.add(listener);
|
|
9260
|
+
listener(this.list());
|
|
9261
|
+
return () => {
|
|
9262
|
+
this.listeners.delete(listener);
|
|
9263
|
+
};
|
|
9264
|
+
}
|
|
9265
|
+
emitChange() {
|
|
9266
|
+
const tasks = this.list();
|
|
9267
|
+
for (const listener of this.listeners) {
|
|
9268
|
+
listener(tasks);
|
|
9269
|
+
}
|
|
9270
|
+
}
|
|
9271
|
+
hasRunning() {
|
|
9272
|
+
return this.list().some((task) => task.status === "running");
|
|
9273
|
+
}
|
|
9274
|
+
stop(id) {
|
|
9275
|
+
const task = this.tasks.get(id);
|
|
9276
|
+
if (task?.status !== "running") {
|
|
9277
|
+
return false;
|
|
9278
|
+
}
|
|
9279
|
+
task.status = "cancelled";
|
|
9280
|
+
task.output = "Stopped by user";
|
|
9281
|
+
task.cancel();
|
|
9282
|
+
this.emitChange();
|
|
9283
|
+
return true;
|
|
9284
|
+
}
|
|
9285
|
+
async stopAndWait(id) {
|
|
9286
|
+
const task = this.tasks.get(id);
|
|
9287
|
+
if (!task || !this.stop(id)) {
|
|
9288
|
+
return false;
|
|
9289
|
+
}
|
|
9290
|
+
await task.done;
|
|
9291
|
+
return true;
|
|
9292
|
+
}
|
|
9293
|
+
async stopAll() {
|
|
9294
|
+
const running = this.list().filter((task) => task.status === "running");
|
|
9295
|
+
for (const task of running) {
|
|
9296
|
+
this.stop(task.id);
|
|
9297
|
+
}
|
|
9298
|
+
await Promise.allSettled(running.map((task) => task.done));
|
|
9299
|
+
}
|
|
9300
|
+
async waitAll() {
|
|
9301
|
+
await Promise.allSettled(this.list().map((task) => task.done));
|
|
9302
|
+
}
|
|
9303
|
+
drainNotifications() {
|
|
9304
|
+
const completed = this.list().filter(
|
|
9305
|
+
(task) => task.status !== "running" && !this.notifiedTaskIds.has(task.id)
|
|
9306
|
+
);
|
|
9307
|
+
for (const task of completed) {
|
|
9308
|
+
this.notifiedTaskIds.add(task.id);
|
|
9309
|
+
}
|
|
9310
|
+
return completed;
|
|
9311
|
+
}
|
|
9312
|
+
clear() {
|
|
9313
|
+
this.tasks.clear();
|
|
9314
|
+
this.notifiedTaskIds.clear();
|
|
9315
|
+
this.emitChange();
|
|
9316
|
+
}
|
|
9317
|
+
};
|
|
9318
|
+
function formatAgentTaskNotification(task) {
|
|
9319
|
+
return [
|
|
9320
|
+
`<task-notification task_id="${task.id}" status="${task.status}">`,
|
|
9321
|
+
`name=${task.name}`,
|
|
9322
|
+
task.output,
|
|
9323
|
+
"</task-notification>"
|
|
9324
|
+
].join("\n");
|
|
9325
|
+
}
|
|
9326
|
+
|
|
8982
9327
|
// src/teams/task-tools.ts
|
|
8983
9328
|
var VALID_STATUSES = /* @__PURE__ */ new Set(["pending", "in_progress", "completed", "blocked"]);
|
|
8984
9329
|
var TeamTaskCreateTool = class {
|
|
@@ -9365,14 +9710,14 @@ function getNameRegistry() {
|
|
|
9365
9710
|
}
|
|
9366
9711
|
|
|
9367
9712
|
// src/teams/tools.ts
|
|
9368
|
-
var
|
|
9713
|
+
var log29 = createChildLogger({ module: "teams" });
|
|
9369
9714
|
var TeamCreateTool = class {
|
|
9370
9715
|
constructor(mgr) {
|
|
9371
9716
|
this.mgr = mgr;
|
|
9372
9717
|
}
|
|
9373
9718
|
mgr;
|
|
9374
9719
|
name = "TeamCreate";
|
|
9375
|
-
description = "Create a team for coordinating multiple agents.";
|
|
9720
|
+
description = "Create a team for coordinating multiple agents. At most one team exists at a time: creating a team deletes any other team, stopping its members.";
|
|
9376
9721
|
category = "read";
|
|
9377
9722
|
schema() {
|
|
9378
9723
|
return {
|
|
@@ -9399,12 +9744,9 @@ var TeamCreateTool = class {
|
|
|
9399
9744
|
isError: true
|
|
9400
9745
|
});
|
|
9401
9746
|
}
|
|
9402
|
-
|
|
9403
|
-
for (let i = 2; this.mgr.get(name); i++) {
|
|
9404
|
-
name = `${requested}-${String(i)}`;
|
|
9405
|
-
}
|
|
9747
|
+
await this.mgr.deleteAll();
|
|
9406
9748
|
const description = strArg(args, "description");
|
|
9407
|
-
const team = this.mgr.create(
|
|
9749
|
+
const team = this.mgr.create(requested, void 0, {
|
|
9408
9750
|
leadAgentId: "lead",
|
|
9409
9751
|
description
|
|
9410
9752
|
});
|
|
@@ -9460,7 +9802,11 @@ var SpawnTeammateTool = class {
|
|
|
9460
9802
|
isError: true
|
|
9461
9803
|
});
|
|
9462
9804
|
}
|
|
9463
|
-
|
|
9805
|
+
let t = this.mgr.get(team);
|
|
9806
|
+
if (!t) {
|
|
9807
|
+
await this.mgr.deleteAll();
|
|
9808
|
+
t = this.mgr.create(team);
|
|
9809
|
+
}
|
|
9464
9810
|
t.spawnTeammate(name, task, this.runAgent, void 0, this.providerBaseUrl);
|
|
9465
9811
|
return Promise.resolve({
|
|
9466
9812
|
output: `Teammate '${name}' spawned in team '${team}'. Its result will arrive on the team channel; keep working and watch for it.`,
|
|
@@ -9592,7 +9938,7 @@ var SendMessageTool = class {
|
|
|
9592
9938
|
try {
|
|
9593
9939
|
await t.sendMessage(this.senderName, recipient, message);
|
|
9594
9940
|
} catch (err) {
|
|
9595
|
-
|
|
9941
|
+
log29.error({ err }, "teams operation failed");
|
|
9596
9942
|
return {
|
|
9597
9943
|
output: `Error: ${asErrorString(err)}`,
|
|
9598
9944
|
isError: true
|
|
@@ -9670,7 +10016,7 @@ var TeamDeleteTool = class {
|
|
|
9670
10016
|
};
|
|
9671
10017
|
|
|
9672
10018
|
// src/subagent/agent-tool.ts
|
|
9673
|
-
var
|
|
10019
|
+
var log30 = createChildLogger({ module: "subagent" });
|
|
9674
10020
|
var GENERAL_PURPOSE_AGENT_TYPE = "general-purpose";
|
|
9675
10021
|
function newAgentSlug() {
|
|
9676
10022
|
return `agent-a${randomBytes2(4).toString("hex").slice(0, 7)}`;
|
|
@@ -9688,6 +10034,7 @@ var AgentTool = class {
|
|
|
9688
10034
|
definitions;
|
|
9689
10035
|
registry;
|
|
9690
10036
|
conversation;
|
|
10037
|
+
taskManager;
|
|
9691
10038
|
// Identifies the derived context of the current AgentTool instance;
|
|
9692
10039
|
// re-forking is prohibited when non-empty and equal to FORK_QUERY_SOURCE
|
|
9693
10040
|
querySource = "";
|
|
@@ -9710,13 +10057,14 @@ var AgentTool = class {
|
|
|
9710
10057
|
teamProviderBaseUrl;
|
|
9711
10058
|
spawnHandler;
|
|
9712
10059
|
forkHandler;
|
|
9713
|
-
constructor(workDir, registry, spawnHandler, conversation, forkHandler) {
|
|
10060
|
+
constructor(workDir, registry, spawnHandler, conversation, forkHandler, taskManager = new TaskManager()) {
|
|
9714
10061
|
this.definitions = loadAgentDefinitions(workDir);
|
|
9715
10062
|
this.workDir = workDir;
|
|
9716
10063
|
this.registry = registry;
|
|
9717
10064
|
this.spawnHandler = spawnHandler;
|
|
9718
10065
|
this.conversation = conversation;
|
|
9719
10066
|
this.forkHandler = forkHandler;
|
|
10067
|
+
this.taskManager = taskManager;
|
|
9720
10068
|
}
|
|
9721
10069
|
/**
|
|
9722
10070
|
* Sets the team manager and teammate run callback, enabling the team_name parameter.
|
|
@@ -9754,7 +10102,7 @@ var AgentTool = class {
|
|
|
9754
10102
|
},
|
|
9755
10103
|
run_in_background: {
|
|
9756
10104
|
type: "boolean",
|
|
9757
|
-
description: "
|
|
10105
|
+
description: "Run this one-shot subagent asynchronously. Returns a task ID immediately and delivers the final result through a task notification.",
|
|
9758
10106
|
default: false
|
|
9759
10107
|
},
|
|
9760
10108
|
isolation: {
|
|
@@ -9785,7 +10133,7 @@ var AgentTool = class {
|
|
|
9785
10133
|
Available roles (pass a role as subagent_type, not as a tool name):
|
|
9786
10134
|
${roles.join("\n")}
|
|
9787
10135
|
|
|
9788
|
-
|
|
10136
|
+
Foreground calls return results inline. With run_in_background=true, the call returns a task ID immediately and the final result arrives through a task notification. Use team_name for persistent teammates and SendMessage for their follow-up assignments. Do not predict results before receiving them.
|
|
9789
10137
|
|
|
9790
10138
|
Launch independent tasks together; avoid concurrent writes to the same files. Review returned evidence and integrate it before reporting completion. Worktree isolation separates edits but does not merge them.`;
|
|
9791
10139
|
}
|
|
@@ -9809,10 +10157,26 @@ Launch independent tasks together; avoid concurrent writes to the same files. Re
|
|
|
9809
10157
|
description,
|
|
9810
10158
|
prompt,
|
|
9811
10159
|
args.plan_mode_required === true,
|
|
9812
|
-
isolation === "worktree"
|
|
10160
|
+
isolation === "worktree",
|
|
10161
|
+
ctx.toolCallId
|
|
9813
10162
|
);
|
|
9814
10163
|
}
|
|
9815
10164
|
if (!subagentType) {
|
|
10165
|
+
if (background && this.conversation && this.forkHandler) {
|
|
10166
|
+
const snapshot = this.conversation.fork();
|
|
10167
|
+
return this.startBackground(
|
|
10168
|
+
description,
|
|
10169
|
+
ctx,
|
|
10170
|
+
(backgroundContext) => this.runFork(
|
|
10171
|
+
prompt,
|
|
10172
|
+
description,
|
|
10173
|
+
modelOverride,
|
|
10174
|
+
backgroundContext,
|
|
10175
|
+
isolation === "worktree",
|
|
10176
|
+
snapshot
|
|
10177
|
+
)
|
|
10178
|
+
);
|
|
10179
|
+
}
|
|
9816
10180
|
return this.runFork(prompt, description, modelOverride, ctx, isolation === "worktree");
|
|
9817
10181
|
}
|
|
9818
10182
|
const definition = this.definitions.find((d) => d.name === subagentType);
|
|
@@ -9838,44 +10202,76 @@ ${prompt}`;
|
|
|
9838
10202
|
};
|
|
9839
10203
|
}
|
|
9840
10204
|
}
|
|
9841
|
-
|
|
9842
|
-
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
|
|
9846
|
-
|
|
9847
|
-
|
|
9848
|
-
|
|
9849
|
-
|
|
9850
|
-
|
|
9851
|
-
|
|
10205
|
+
const run2 = async (runContext) => {
|
|
10206
|
+
try {
|
|
10207
|
+
const output = await this.spawnHandler(
|
|
10208
|
+
definition,
|
|
10209
|
+
effectivePrompt,
|
|
10210
|
+
background || !!definition.background,
|
|
10211
|
+
modelOverride,
|
|
10212
|
+
workDirOverride,
|
|
10213
|
+
runContext
|
|
10214
|
+
);
|
|
10215
|
+
return {
|
|
10216
|
+
output: workDirOverride ? `${output}
|
|
9852
10217
|
|
|
9853
10218
|
Worktree retained at: ${workDirOverride}` : output,
|
|
9854
|
-
|
|
9855
|
-
|
|
9856
|
-
|
|
9857
|
-
|
|
9858
|
-
|
|
10219
|
+
isError: false
|
|
10220
|
+
};
|
|
10221
|
+
} catch (err) {
|
|
10222
|
+
return {
|
|
10223
|
+
output: `Agent error: ${asErrorString(err)}${workDirOverride ? `
|
|
9859
10224
|
Worktree retained at: ${workDirOverride}` : ""}`,
|
|
9860
|
-
|
|
9861
|
-
|
|
9862
|
-
|
|
10225
|
+
isError: true
|
|
10226
|
+
};
|
|
10227
|
+
}
|
|
10228
|
+
};
|
|
10229
|
+
return background ? this.startBackground(description, ctx, run2) : run2(ctx);
|
|
10230
|
+
}
|
|
10231
|
+
startBackground(description, ctx, runner) {
|
|
10232
|
+
const controller = new AbortController();
|
|
10233
|
+
const task = this.taskManager.create(
|
|
10234
|
+
description,
|
|
10235
|
+
async (backgroundTask) => {
|
|
10236
|
+
const result = await runner({
|
|
10237
|
+
...ctx,
|
|
10238
|
+
backgroundTaskId: backgroundTask.id,
|
|
10239
|
+
abortSignal: controller.signal
|
|
10240
|
+
});
|
|
10241
|
+
if (result.isError) {
|
|
10242
|
+
throw new Error(result.output);
|
|
10243
|
+
}
|
|
10244
|
+
return result.output;
|
|
10245
|
+
},
|
|
10246
|
+
() => {
|
|
10247
|
+
controller.abort();
|
|
10248
|
+
},
|
|
10249
|
+
{ originToolCallId: ctx.toolCallId }
|
|
10250
|
+
);
|
|
10251
|
+
return {
|
|
10252
|
+
output: `Background agent '${description}' started (task_id: ${task.id}). Its result will arrive as a task notification.`,
|
|
10253
|
+
isError: false
|
|
10254
|
+
};
|
|
9863
10255
|
}
|
|
9864
10256
|
/**
|
|
9865
10257
|
* Team-member mode: Spawns a persistent teammate in the specified team.
|
|
9866
10258
|
* Delegates to Team.spawnTeammate() to start the idle-poll main loop.
|
|
9867
10259
|
*/
|
|
9868
|
-
async runAsTeammate(teamName, description, prompt, planModeRequired, worktreeIsolation) {
|
|
10260
|
+
async runAsTeammate(teamName, description, prompt, planModeRequired, worktreeIsolation, originToolCallId) {
|
|
9869
10261
|
if (!this.teamManager) {
|
|
9870
10262
|
return {
|
|
9871
10263
|
output: `Error: team manager '${teamName}' not found.`,
|
|
9872
10264
|
isError: true
|
|
9873
10265
|
};
|
|
9874
10266
|
}
|
|
9875
|
-
|
|
9876
|
-
|
|
9877
|
-
|
|
9878
|
-
|
|
10267
|
+
let team = this.teamManager.get(teamName);
|
|
10268
|
+
if (!team) {
|
|
10269
|
+
await this.teamManager.deleteAll();
|
|
10270
|
+
team = this.teamManager.create(teamName, void 0, {
|
|
10271
|
+
leadAgentId: "lead",
|
|
10272
|
+
description
|
|
10273
|
+
});
|
|
10274
|
+
}
|
|
9879
10275
|
let memberName = description.replace(/\s+/g, "-").toLowerCase().slice(0, 30);
|
|
9880
10276
|
let suffix = 2;
|
|
9881
10277
|
const base = memberName;
|
|
@@ -9917,14 +10313,20 @@ ${prompt}`;
|
|
|
9917
10313
|
const checker = planModeRequired ? new PermissionChecker(memberWorkDir, "plan") : void 0;
|
|
9918
10314
|
const runAgent = this.teamRunAgentFactory?.(teammateRegistry, checker, memberWorkDir);
|
|
9919
10315
|
if (runAgent) {
|
|
9920
|
-
team.spawnTeammate(
|
|
10316
|
+
team.spawnTeammate(
|
|
10317
|
+
memberName,
|
|
10318
|
+
teammatePrompt,
|
|
10319
|
+
runAgent,
|
|
10320
|
+
checker,
|
|
10321
|
+
this.teamProviderBaseUrl,
|
|
10322
|
+
originToolCallId
|
|
10323
|
+
);
|
|
9921
10324
|
if (worktreeIsolation) {
|
|
9922
10325
|
team.setMemberMeta(memberName, { worktreePath: memberWorkDir });
|
|
9923
10326
|
}
|
|
9924
10327
|
}
|
|
9925
10328
|
return {
|
|
9926
|
-
output: `Teammate '${memberName}' spawned in team '${teamName}' (mode: ${team.mode})${planModeRequired ? ", starting in plan mode" : ""}
|
|
9927
|
-
The teammate is now working on the assigned task.`,
|
|
10329
|
+
output: `Teammate '${memberName}' spawned in team '${teamName}' (mode: ${team.mode})${planModeRequired ? ", starting in plan mode" : ""}`,
|
|
9928
10330
|
isError: false
|
|
9929
10331
|
};
|
|
9930
10332
|
}
|
|
@@ -9933,7 +10335,7 @@ ${prompt}`;
|
|
|
9933
10335
|
* Unlike definition mode, the forked subagent can see the full history of the parent conversation,
|
|
9934
10336
|
* achieving byte alignment for the prompt-cache prefix to improve cache hit rate.
|
|
9935
10337
|
*/
|
|
9936
|
-
async runFork(prompt, description, modelOverride, ctx, isolate) {
|
|
10338
|
+
async runFork(prompt, description, modelOverride, ctx, isolate, conversationSnapshot) {
|
|
9937
10339
|
if (!this.conversation || !this.forkHandler) {
|
|
9938
10340
|
return {
|
|
9939
10341
|
output: "Error: fork requires parent conversation context",
|
|
@@ -9969,9 +10371,9 @@ ${prompt}`;
|
|
|
9969
10371
|
permissionChecker: ctx.permissionChecker?.forWorkDir(worktree.path)
|
|
9970
10372
|
};
|
|
9971
10373
|
}
|
|
9972
|
-
const { cloneRegistryForFork: cloneRegistryForFork2 } = await import("./tool-filter-
|
|
10374
|
+
const { cloneRegistryForFork: cloneRegistryForFork2 } = await import("./tool-filter-BFEINZIA.js");
|
|
9973
10375
|
const forkedRegistry = cloneRegistryForFork2(this.registry);
|
|
9974
|
-
const snapshot = this.conversation.fork();
|
|
10376
|
+
const snapshot = conversationSnapshot ?? this.conversation.fork();
|
|
9975
10377
|
const output = await this.forkHandler(
|
|
9976
10378
|
`${FORK_BOILERPLATE}
|
|
9977
10379
|
|
|
@@ -9989,7 +10391,7 @@ Worktree retained at: ${worktreePath}` : ""}`,
|
|
|
9989
10391
|
isError: false
|
|
9990
10392
|
};
|
|
9991
10393
|
} catch (err) {
|
|
9992
|
-
|
|
10394
|
+
log30.error({ err }, "subagent operation failed");
|
|
9993
10395
|
return {
|
|
9994
10396
|
output: `Fork error: ${asErrorString(err)}${worktreePath ? `
|
|
9995
10397
|
Worktree retained at: ${worktreePath}` : ""}`,
|
|
@@ -10000,6 +10402,7 @@ Worktree retained at: ${worktreePath}` : ""}`,
|
|
|
10000
10402
|
};
|
|
10001
10403
|
|
|
10002
10404
|
// src/subagent/spawn.ts
|
|
10405
|
+
var SUBAGENT_INTERRUPTED_MARKER = "[Interrupted]";
|
|
10003
10406
|
async function spawnSubagent(definition, prompt, parentClient, parentRegistry, parentProvider, workDir, onProgress, onEvent, modelOverride, checkerOverride, options = {}) {
|
|
10004
10407
|
options.abortSignal?.throwIfAborted();
|
|
10005
10408
|
const effectiveModel = modelOverride ?? definition.model;
|
|
@@ -10049,10 +10452,17 @@ async function spawnSubagent(definition, prompt, parentClient, parentRegistry, p
|
|
|
10049
10452
|
onProgress?.({ lastTool: event.toolName });
|
|
10050
10453
|
onEvent?.({
|
|
10051
10454
|
type: "tool_use",
|
|
10455
|
+
toolId: event.toolId,
|
|
10052
10456
|
toolName: event.toolName,
|
|
10053
10457
|
args: event.args
|
|
10054
10458
|
});
|
|
10055
10459
|
break;
|
|
10460
|
+
case "tool_result":
|
|
10461
|
+
onEvent?.({
|
|
10462
|
+
type: "tool_result",
|
|
10463
|
+
toolId: event.toolId
|
|
10464
|
+
});
|
|
10465
|
+
break;
|
|
10056
10466
|
case "usage":
|
|
10057
10467
|
onEvent?.({
|
|
10058
10468
|
type: "usage",
|
|
@@ -10064,16 +10474,15 @@ async function spawnSubagent(definition, prompt, parentClient, parentRegistry, p
|
|
|
10064
10474
|
break;
|
|
10065
10475
|
case "turn_complete":
|
|
10066
10476
|
onProgress?.({ turn: ++turn });
|
|
10477
|
+
onEvent?.({ type: "turn_complete" });
|
|
10067
10478
|
break;
|
|
10068
10479
|
case "loop_complete":
|
|
10069
10480
|
if (event.stopReason === "interrupted") {
|
|
10070
|
-
return `${output}${output ? "\n\n" : ""}
|
|
10481
|
+
return `${output}${output ? "\n\n" : ""}${SUBAGENT_INTERRUPTED_MARKER}`;
|
|
10071
10482
|
}
|
|
10072
10483
|
return output || "[No output]";
|
|
10073
10484
|
case "error":
|
|
10074
|
-
|
|
10075
|
-
|
|
10076
|
-
[Error: ${event.error.message}]` : `Error: ${event.error.message}`;
|
|
10485
|
+
throw event.error;
|
|
10077
10486
|
}
|
|
10078
10487
|
}
|
|
10079
10488
|
return output || "[No output]";
|
|
@@ -10102,12 +10511,14 @@ function coordinatorActive(enabled = false) {
|
|
|
10102
10511
|
|
|
10103
10512
|
// src/teams/task-stop.ts
|
|
10104
10513
|
var TaskStopTool = class {
|
|
10105
|
-
constructor(teamManager) {
|
|
10514
|
+
constructor(teamManager, taskManager) {
|
|
10106
10515
|
this.teamManager = teamManager;
|
|
10516
|
+
this.taskManager = taskManager;
|
|
10107
10517
|
}
|
|
10108
10518
|
teamManager;
|
|
10519
|
+
taskManager;
|
|
10109
10520
|
name = "TaskStop";
|
|
10110
|
-
description = "Stop a running teammate
|
|
10521
|
+
description = "Stop a running teammate or background Agent task. Pass exactly one of teammate or task_id.";
|
|
10111
10522
|
category = "command";
|
|
10112
10523
|
schema() {
|
|
10113
10524
|
return {
|
|
@@ -10119,16 +10530,34 @@ var TaskStopTool = class {
|
|
|
10119
10530
|
teammate: {
|
|
10120
10531
|
type: "string",
|
|
10121
10532
|
description: "Name of the teammate to stop, exactly as it appears in the from= field of a team-notification"
|
|
10533
|
+
},
|
|
10534
|
+
task_id: {
|
|
10535
|
+
type: "string",
|
|
10536
|
+
description: "ID of a background Agent task"
|
|
10122
10537
|
}
|
|
10123
|
-
}
|
|
10124
|
-
required: ["teammate"]
|
|
10538
|
+
}
|
|
10125
10539
|
}
|
|
10126
10540
|
};
|
|
10127
10541
|
}
|
|
10128
10542
|
async execute(_ctx, args) {
|
|
10129
10543
|
const name = strArg(args, "teammate", "");
|
|
10130
|
-
|
|
10131
|
-
|
|
10544
|
+
const taskId = strArg(args, "task_id", "");
|
|
10545
|
+
if (!name && !taskId || name && taskId) {
|
|
10546
|
+
return { output: "Error: pass exactly one of teammate or task_id", isError: true };
|
|
10547
|
+
}
|
|
10548
|
+
if (taskId) {
|
|
10549
|
+
const task = this.taskManager?.get(taskId);
|
|
10550
|
+
if (!task) {
|
|
10551
|
+
return { output: `Error: background task '${taskId}' not found`, isError: true };
|
|
10552
|
+
}
|
|
10553
|
+
if (task.status !== "running") {
|
|
10554
|
+
return {
|
|
10555
|
+
output: `Background task '${taskId}' is ${task.status}, nothing to stop`,
|
|
10556
|
+
isError: false
|
|
10557
|
+
};
|
|
10558
|
+
}
|
|
10559
|
+
await this.taskManager?.stopAndWait(taskId);
|
|
10560
|
+
return { output: `Background task '${taskId}' stopped.`, isError: false };
|
|
10132
10561
|
}
|
|
10133
10562
|
for (const team of this.teamManager.list()) {
|
|
10134
10563
|
const member = team.members.get(name);
|
|
@@ -10165,12 +10594,12 @@ var TaskStopTool = class {
|
|
|
10165
10594
|
};
|
|
10166
10595
|
|
|
10167
10596
|
// src/teams/team.ts
|
|
10168
|
-
import { mkdirSync as mkdirSync14, readFileSync as
|
|
10597
|
+
import { mkdirSync as mkdirSync14, readFileSync as readFileSync20, rmSync } from "fs";
|
|
10169
10598
|
import { join as join24 } from "path";
|
|
10170
10599
|
|
|
10171
10600
|
// src/teams/file-mailbox.ts
|
|
10172
10601
|
import {
|
|
10173
|
-
readFileSync as
|
|
10602
|
+
readFileSync as readFileSync16,
|
|
10174
10603
|
writeFileSync as writeFileSync10,
|
|
10175
10604
|
mkdirSync as mkdirSync10,
|
|
10176
10605
|
existsSync as existsSync15,
|
|
@@ -10180,35 +10609,35 @@ import {
|
|
|
10180
10609
|
closeSync
|
|
10181
10610
|
} from "fs";
|
|
10182
10611
|
import { join as join21 } from "path";
|
|
10183
|
-
import
|
|
10184
|
-
var
|
|
10185
|
-
var FileMailMessageSchema =
|
|
10186
|
-
from:
|
|
10187
|
-
text:
|
|
10188
|
-
timestamp:
|
|
10612
|
+
import z12, { safeParse as safeParse3 } from "zod";
|
|
10613
|
+
var log31 = createChildLogger({ module: "teams" });
|
|
10614
|
+
var FileMailMessageSchema = z12.object({
|
|
10615
|
+
from: z12.string(),
|
|
10616
|
+
text: z12.string(),
|
|
10617
|
+
timestamp: z12.string(),
|
|
10189
10618
|
// timestamp: z
|
|
10190
10619
|
// .union([z.string(), z.number()])
|
|
10191
10620
|
// .nullish()
|
|
10192
10621
|
// .transform((v) => (!v ? "" : String(v))),
|
|
10193
10622
|
// Read marker: false on delivery, set to true once the message is read or explicitly marked.
|
|
10194
|
-
read:
|
|
10623
|
+
read: z12.boolean().optional(),
|
|
10195
10624
|
// Three fields for structured messages; left empty for plain-text messages.
|
|
10196
10625
|
// See the constants in protocol.ts for type values; requestId correlates responses
|
|
10197
10626
|
// to their originating requests; approve uses an optional field to distinguish
|
|
10198
10627
|
// "no response yet" from "explicitly rejected".
|
|
10199
|
-
type:
|
|
10200
|
-
requestId:
|
|
10201
|
-
approve:
|
|
10628
|
+
type: z12.string().optional(),
|
|
10629
|
+
requestId: z12.string().optional(),
|
|
10630
|
+
approve: z12.boolean().optional()
|
|
10202
10631
|
});
|
|
10203
10632
|
var LOCK_ACQUIRE_TIMEOUT_MS = 5e3;
|
|
10204
10633
|
var LOCK_STALE_MS = 1e4;
|
|
10205
10634
|
var LOCK_MIN_BACKOFF_MS = 5;
|
|
10206
10635
|
var LOCK_MAX_BACKOFF_MS = 80;
|
|
10207
|
-
var ErrnoExceptionSchema =
|
|
10208
|
-
errno:
|
|
10209
|
-
code:
|
|
10210
|
-
path:
|
|
10211
|
-
syscall:
|
|
10636
|
+
var ErrnoExceptionSchema = z12.looseObject({
|
|
10637
|
+
errno: z12.number().optional(),
|
|
10638
|
+
code: z12.string().optional(),
|
|
10639
|
+
path: z12.string().optional(),
|
|
10640
|
+
syscall: z12.string().optional()
|
|
10212
10641
|
});
|
|
10213
10642
|
function sleepSync(ms) {
|
|
10214
10643
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
@@ -10222,7 +10651,7 @@ function acquireLock(lockFile) {
|
|
|
10222
10651
|
closeSync(fd);
|
|
10223
10652
|
return;
|
|
10224
10653
|
} catch (err) {
|
|
10225
|
-
|
|
10654
|
+
log31.error({ err }, "teams operation failed");
|
|
10226
10655
|
const { data, success } = safeParse3(ErrnoExceptionSchema, err);
|
|
10227
10656
|
let code = "";
|
|
10228
10657
|
if (success && data.code) {
|
|
@@ -10238,11 +10667,11 @@ function acquireLock(lockFile) {
|
|
|
10238
10667
|
unlinkSync5(lockFile);
|
|
10239
10668
|
continue;
|
|
10240
10669
|
} catch (err2) {
|
|
10241
|
-
|
|
10670
|
+
log31.error({ err: err2 }, "teams operation failed");
|
|
10242
10671
|
}
|
|
10243
10672
|
}
|
|
10244
10673
|
} catch (err2) {
|
|
10245
|
-
|
|
10674
|
+
log31.error({ err: err2 }, "teams operation failed");
|
|
10246
10675
|
}
|
|
10247
10676
|
if (Date.now() >= deadline) {
|
|
10248
10677
|
throw new Error(
|
|
@@ -10258,7 +10687,7 @@ function releaseLock(lockFile) {
|
|
|
10258
10687
|
try {
|
|
10259
10688
|
unlinkSync5(lockFile);
|
|
10260
10689
|
} catch (err) {
|
|
10261
|
-
|
|
10690
|
+
log31.error({ err }, "teams operation failed");
|
|
10262
10691
|
}
|
|
10263
10692
|
}
|
|
10264
10693
|
function withLock(filePath, fn) {
|
|
@@ -10281,7 +10710,7 @@ var FileMailbox = class {
|
|
|
10281
10710
|
return [];
|
|
10282
10711
|
}
|
|
10283
10712
|
try {
|
|
10284
|
-
const raw = JSON.parse(
|
|
10713
|
+
const raw = JSON.parse(readFileSync16(this.filePath, "utf-8"));
|
|
10285
10714
|
const data = [];
|
|
10286
10715
|
if (Array.isArray(raw)) {
|
|
10287
10716
|
for (const item of raw) {
|
|
@@ -10293,7 +10722,7 @@ var FileMailbox = class {
|
|
|
10293
10722
|
}
|
|
10294
10723
|
return data;
|
|
10295
10724
|
} catch (err) {
|
|
10296
|
-
|
|
10725
|
+
log31.error({ err }, "teams operation failed");
|
|
10297
10726
|
return [];
|
|
10298
10727
|
}
|
|
10299
10728
|
}
|
|
@@ -10367,44 +10796,52 @@ var FileMailbox = class {
|
|
|
10367
10796
|
};
|
|
10368
10797
|
|
|
10369
10798
|
// src/teams/progress.ts
|
|
10370
|
-
import { z as
|
|
10371
|
-
var ToolActivitySchema =
|
|
10372
|
-
toolName:
|
|
10373
|
-
input:
|
|
10374
|
-
activityDescription:
|
|
10799
|
+
import { z as z13 } from "zod";
|
|
10800
|
+
var ToolActivitySchema = z13.object({
|
|
10801
|
+
toolName: z13.string(),
|
|
10802
|
+
input: z13.record(z13.string(), z13.unknown()),
|
|
10803
|
+
activityDescription: z13.string()
|
|
10375
10804
|
// e.g. "Reading src/foo.ts"
|
|
10376
10805
|
});
|
|
10377
|
-
var
|
|
10378
|
-
|
|
10379
|
-
|
|
10806
|
+
var ActiveToolSchema = z13.object({
|
|
10807
|
+
toolId: z13.string(),
|
|
10808
|
+
toolName: z13.string()
|
|
10809
|
+
});
|
|
10810
|
+
var AgentProgressSchema = z13.object({
|
|
10811
|
+
toolUseCount: z13.number(),
|
|
10812
|
+
turnCount: z13.number(),
|
|
10813
|
+
tokenCount: z13.number(),
|
|
10814
|
+
activeTools: z13.array(ActiveToolSchema),
|
|
10380
10815
|
lastActivity: ToolActivitySchema.optional(),
|
|
10381
|
-
recentActivities:
|
|
10816
|
+
recentActivities: z13.array(ToolActivitySchema)
|
|
10382
10817
|
// circular buffer, max 5
|
|
10383
10818
|
});
|
|
10384
|
-
var TeammateUIStateSchema =
|
|
10385
|
-
name:
|
|
10386
|
-
teamName:
|
|
10387
|
-
status:
|
|
10819
|
+
var TeammateUIStateSchema = z13.object({
|
|
10820
|
+
name: z13.string(),
|
|
10821
|
+
teamName: z13.string(),
|
|
10822
|
+
status: z13.enum(["running", "idle", "completed", "failed", "stopped"]),
|
|
10388
10823
|
progress: AgentProgressSchema,
|
|
10389
|
-
|
|
10390
|
-
|
|
10391
|
-
|
|
10824
|
+
originToolCallId: z13.string().optional(),
|
|
10825
|
+
startTime: z13.number(),
|
|
10826
|
+
spinnerVerb: z13.string(),
|
|
10827
|
+
lastMessage: z13.string().optional()
|
|
10392
10828
|
});
|
|
10393
10829
|
function createProgress() {
|
|
10394
10830
|
return {
|
|
10395
10831
|
toolUseCount: 0,
|
|
10832
|
+
turnCount: 0,
|
|
10396
10833
|
tokenCount: 0,
|
|
10834
|
+
activeTools: [],
|
|
10397
10835
|
lastActivity: void 0,
|
|
10398
10836
|
recentActivities: []
|
|
10399
10837
|
};
|
|
10400
10838
|
}
|
|
10401
10839
|
function recordToolUse(p, toolName, input) {
|
|
10402
10840
|
p.toolUseCount++;
|
|
10403
|
-
const desc = describeToolActivity(toolName, input);
|
|
10404
10841
|
const activity = {
|
|
10405
10842
|
toolName,
|
|
10406
10843
|
input,
|
|
10407
|
-
activityDescription:
|
|
10844
|
+
activityDescription: describeToolActivity(toolName, input)
|
|
10408
10845
|
};
|
|
10409
10846
|
p.lastActivity = activity;
|
|
10410
10847
|
p.recentActivities.push(activity);
|
|
@@ -10412,8 +10849,22 @@ function recordToolUse(p, toolName, input) {
|
|
|
10412
10849
|
p.recentActivities.shift();
|
|
10413
10850
|
}
|
|
10414
10851
|
}
|
|
10852
|
+
function recordToolStart(p, toolId, toolName, input) {
|
|
10853
|
+
recordToolUse(p, toolName, input);
|
|
10854
|
+
p.activeTools = [...p.activeTools.filter((tool) => tool.toolId !== toolId), { toolId, toolName }];
|
|
10855
|
+
}
|
|
10856
|
+
function recordToolResult(p, toolId) {
|
|
10857
|
+
p.activeTools = p.activeTools.filter((tool) => tool.toolId !== toolId);
|
|
10858
|
+
}
|
|
10859
|
+
function recordTurnComplete(p) {
|
|
10860
|
+
p.turnCount++;
|
|
10861
|
+
p.activeTools = [];
|
|
10862
|
+
}
|
|
10863
|
+
function clearActiveTools(p) {
|
|
10864
|
+
p.activeTools = [];
|
|
10865
|
+
}
|
|
10415
10866
|
function recordTokens(p, inputTokens, outputTokens) {
|
|
10416
|
-
p.tokenCount
|
|
10867
|
+
p.tokenCount += inputTokens + outputTokens;
|
|
10417
10868
|
}
|
|
10418
10869
|
function describeToolActivity(toolName, input) {
|
|
10419
10870
|
switch (toolName) {
|
|
@@ -10432,6 +10883,8 @@ function describeToolActivity(toolName, input) {
|
|
|
10432
10883
|
return `Searching ${strArg(input, "pattern", "files")}`;
|
|
10433
10884
|
case "Grep":
|
|
10434
10885
|
return `Grepping ${strArg(input, "pattern", "pattern")}`;
|
|
10886
|
+
case "WebFetch":
|
|
10887
|
+
return `Fetching ${strArg(input, "url", "page")}`;
|
|
10435
10888
|
default:
|
|
10436
10889
|
return toolName;
|
|
10437
10890
|
}
|
|
@@ -10453,29 +10906,29 @@ function formatTokens(n) {
|
|
|
10453
10906
|
}
|
|
10454
10907
|
|
|
10455
10908
|
// src/teams/shared-task.ts
|
|
10456
|
-
import { readFileSync as
|
|
10909
|
+
import { readFileSync as readFileSync17, writeFileSync as writeFileSync11, mkdirSync as mkdirSync11, existsSync as existsSync16 } from "fs";
|
|
10457
10910
|
import { dirname as dirname7 } from "path";
|
|
10458
|
-
import
|
|
10459
|
-
var SerializedTaskSchema =
|
|
10460
|
-
id:
|
|
10461
|
-
title:
|
|
10462
|
-
description:
|
|
10463
|
-
status:
|
|
10464
|
-
assignee:
|
|
10465
|
-
blocks:
|
|
10466
|
-
blocked_by:
|
|
10467
|
-
created_by:
|
|
10911
|
+
import z14, { parse as parse6 } from "zod";
|
|
10912
|
+
var SerializedTaskSchema = z14.object({
|
|
10913
|
+
id: z14.string(),
|
|
10914
|
+
title: z14.string(),
|
|
10915
|
+
description: z14.string().default(""),
|
|
10916
|
+
status: z14.string().default("pending"),
|
|
10917
|
+
assignee: z14.string().default(""),
|
|
10918
|
+
blocks: z14.array(z14.string()).default([]),
|
|
10919
|
+
blocked_by: z14.array(z14.string()).default([]),
|
|
10920
|
+
created_by: z14.string().default("")
|
|
10468
10921
|
});
|
|
10469
|
-
var StoreDataSchema =
|
|
10470
|
-
next_id:
|
|
10471
|
-
tasks:
|
|
10922
|
+
var StoreDataSchema = z14.object({
|
|
10923
|
+
next_id: z14.number().int().positive().default(1),
|
|
10924
|
+
tasks: z14.array(SerializedTaskSchema).default([])
|
|
10472
10925
|
});
|
|
10473
10926
|
var SharedTaskStore = class {
|
|
10474
10927
|
path;
|
|
10475
10928
|
nextId = 1;
|
|
10476
10929
|
tasks = [];
|
|
10477
|
-
constructor(
|
|
10478
|
-
this.path =
|
|
10930
|
+
constructor(path) {
|
|
10931
|
+
this.path = path;
|
|
10479
10932
|
this.load();
|
|
10480
10933
|
}
|
|
10481
10934
|
load() {
|
|
@@ -10483,7 +10936,7 @@ var SharedTaskStore = class {
|
|
|
10483
10936
|
return;
|
|
10484
10937
|
}
|
|
10485
10938
|
try {
|
|
10486
|
-
const raw = JSON.parse(
|
|
10939
|
+
const raw = JSON.parse(readFileSync17(this.path, "utf-8"));
|
|
10487
10940
|
const data = parse6(StoreDataSchema, raw);
|
|
10488
10941
|
this.nextId = data.next_id;
|
|
10489
10942
|
this.tasks = data.tasks.map((t) => ({
|
|
@@ -10591,26 +11044,26 @@ var SharedTaskStore = class {
|
|
|
10591
11044
|
};
|
|
10592
11045
|
|
|
10593
11046
|
// src/teams/team-file.ts
|
|
10594
|
-
import { existsSync as existsSync17, mkdirSync as mkdirSync12, readFileSync as
|
|
11047
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync12, readdirSync as readdirSync6, readFileSync as readFileSync18, writeFileSync as writeFileSync12 } from "fs";
|
|
10595
11048
|
import { homedir as homedir9 } from "os";
|
|
10596
11049
|
import { dirname as dirname8, join as join22 } from "path";
|
|
10597
|
-
import
|
|
10598
|
-
var TeamMemberEntrySchema =
|
|
10599
|
-
agentId:
|
|
10600
|
-
name:
|
|
10601
|
-
agentType:
|
|
10602
|
-
model:
|
|
10603
|
-
joinedAt:
|
|
10604
|
-
worktreePath:
|
|
10605
|
-
backendType:
|
|
10606
|
-
isActive:
|
|
11050
|
+
import z15 from "zod";
|
|
11051
|
+
var TeamMemberEntrySchema = z15.object({
|
|
11052
|
+
agentId: z15.string(),
|
|
11053
|
+
name: z15.string(),
|
|
11054
|
+
agentType: z15.string().optional(),
|
|
11055
|
+
model: z15.string().optional(),
|
|
11056
|
+
joinedAt: z15.number(),
|
|
11057
|
+
worktreePath: z15.string().optional(),
|
|
11058
|
+
backendType: z15.string().optional(),
|
|
11059
|
+
isActive: z15.boolean().optional()
|
|
10607
11060
|
});
|
|
10608
|
-
var TeamFileSchema =
|
|
10609
|
-
name:
|
|
10610
|
-
description:
|
|
10611
|
-
createdAt:
|
|
10612
|
-
leadAgentId:
|
|
10613
|
-
members:
|
|
11061
|
+
var TeamFileSchema = z15.object({
|
|
11062
|
+
name: z15.string(),
|
|
11063
|
+
description: z15.string().optional(),
|
|
11064
|
+
createdAt: z15.number(),
|
|
11065
|
+
leadAgentId: z15.string(),
|
|
11066
|
+
members: z15.array(TeamMemberEntrySchema)
|
|
10614
11067
|
});
|
|
10615
11068
|
function teamsBaseDir() {
|
|
10616
11069
|
return join22(homedir9(), ".swifty", "teams");
|
|
@@ -10621,16 +11074,23 @@ function sanitizeTeamName(name) {
|
|
|
10621
11074
|
function teamDir(name) {
|
|
10622
11075
|
return join22(teamsBaseDir(), sanitizeTeamName(name));
|
|
10623
11076
|
}
|
|
11077
|
+
function listTeamNames() {
|
|
11078
|
+
try {
|
|
11079
|
+
return readdirSync6(teamsBaseDir(), { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
11080
|
+
} catch {
|
|
11081
|
+
return [];
|
|
11082
|
+
}
|
|
11083
|
+
}
|
|
10624
11084
|
function teamConfigPath(name) {
|
|
10625
11085
|
return join22(teamDir(name), "config.json");
|
|
10626
11086
|
}
|
|
10627
11087
|
function readTeamFile(name) {
|
|
10628
|
-
const
|
|
10629
|
-
if (!existsSync17(
|
|
11088
|
+
const path = teamConfigPath(name);
|
|
11089
|
+
if (!existsSync17(path)) {
|
|
10630
11090
|
return null;
|
|
10631
11091
|
}
|
|
10632
11092
|
try {
|
|
10633
|
-
const raw = JSON.parse(
|
|
11093
|
+
const raw = JSON.parse(readFileSync18(path, "utf-8"));
|
|
10634
11094
|
return TeamFileSchema.parse(raw);
|
|
10635
11095
|
} catch {
|
|
10636
11096
|
return null;
|
|
@@ -10638,33 +11098,33 @@ function readTeamFile(name) {
|
|
|
10638
11098
|
}
|
|
10639
11099
|
function writeTeamFile(name, file) {
|
|
10640
11100
|
try {
|
|
10641
|
-
const
|
|
10642
|
-
mkdirSync12(dirname8(
|
|
10643
|
-
writeFileSync12(
|
|
11101
|
+
const path = teamConfigPath(name);
|
|
11102
|
+
mkdirSync12(dirname8(path), { recursive: true });
|
|
11103
|
+
writeFileSync12(path, JSON.stringify(file, null, 2), "utf-8");
|
|
10644
11104
|
} catch {
|
|
10645
11105
|
}
|
|
10646
11106
|
}
|
|
10647
11107
|
|
|
10648
11108
|
// src/teams/transcript.ts
|
|
10649
|
-
import { mkdirSync as mkdirSync13, readFileSync as
|
|
11109
|
+
import { mkdirSync as mkdirSync13, readFileSync as readFileSync19, writeFileSync as writeFileSync13, existsSync as existsSync18 } from "fs";
|
|
10650
11110
|
import { join as join23 } from "path";
|
|
10651
|
-
import
|
|
10652
|
-
var
|
|
10653
|
-
var TranscriptToolUseSchema =
|
|
10654
|
-
tool_use_id:
|
|
10655
|
-
tool_name:
|
|
10656
|
-
arguments:
|
|
11111
|
+
import z16, { parse as parse7 } from "zod";
|
|
11112
|
+
var log32 = createChildLogger({ module: "teams" });
|
|
11113
|
+
var TranscriptToolUseSchema = z16.object({
|
|
11114
|
+
tool_use_id: z16.string(),
|
|
11115
|
+
tool_name: z16.string(),
|
|
11116
|
+
arguments: z16.record(z16.string(), z16.unknown())
|
|
10657
11117
|
});
|
|
10658
|
-
var TranscriptToolResultSchema =
|
|
10659
|
-
tool_use_id:
|
|
10660
|
-
content:
|
|
10661
|
-
is_error:
|
|
11118
|
+
var TranscriptToolResultSchema = z16.object({
|
|
11119
|
+
tool_use_id: z16.string(),
|
|
11120
|
+
content: z16.string(),
|
|
11121
|
+
is_error: z16.boolean().optional()
|
|
10662
11122
|
});
|
|
10663
|
-
var TranscriptEntrySchema =
|
|
10664
|
-
role:
|
|
10665
|
-
content:
|
|
10666
|
-
tool_uses:
|
|
10667
|
-
tool_results:
|
|
11123
|
+
var TranscriptEntrySchema = z16.object({
|
|
11124
|
+
role: z16.string(),
|
|
11125
|
+
content: z16.string().optional(),
|
|
11126
|
+
tool_uses: z16.array(TranscriptToolUseSchema).optional(),
|
|
11127
|
+
tool_results: z16.array(TranscriptToolResultSchema).optional()
|
|
10668
11128
|
});
|
|
10669
11129
|
function serializeConversation(conversation) {
|
|
10670
11130
|
const entries = [];
|
|
@@ -10697,22 +11157,22 @@ function transcriptDir(workDir, teamName) {
|
|
|
10697
11157
|
function saveTranscript(workDir, teamName, agentId, conversation) {
|
|
10698
11158
|
const dir = transcriptDir(workDir, teamName);
|
|
10699
11159
|
mkdirSync13(dir, { recursive: true });
|
|
10700
|
-
const
|
|
11160
|
+
const path = join23(dir, `${agentId}.json`);
|
|
10701
11161
|
const data = serializeConversation(conversation);
|
|
10702
|
-
writeFileSync13(
|
|
10703
|
-
return
|
|
11162
|
+
writeFileSync13(path, JSON.stringify(data, null, 2), "utf-8");
|
|
11163
|
+
return path;
|
|
10704
11164
|
}
|
|
10705
11165
|
function loadTranscript(workDir, teamName, agentId) {
|
|
10706
|
-
const
|
|
10707
|
-
if (!existsSync18(
|
|
11166
|
+
const path = join23(transcriptDir(workDir, teamName), `${agentId}.json`);
|
|
11167
|
+
if (!existsSync18(path)) {
|
|
10708
11168
|
return null;
|
|
10709
11169
|
}
|
|
10710
11170
|
try {
|
|
10711
|
-
const raw = JSON.parse(
|
|
10712
|
-
const parsed = parse7(
|
|
11171
|
+
const raw = JSON.parse(readFileSync19(path, "utf-8"));
|
|
11172
|
+
const parsed = parse7(z16.array(TranscriptEntrySchema), raw);
|
|
10713
11173
|
return parsed;
|
|
10714
11174
|
} catch (err) {
|
|
10715
|
-
|
|
11175
|
+
log32.error({ err }, "teams operation failed");
|
|
10716
11176
|
return null;
|
|
10717
11177
|
}
|
|
10718
11178
|
}
|
|
@@ -10854,7 +11314,7 @@ function randomCompletionVerb() {
|
|
|
10854
11314
|
}
|
|
10855
11315
|
|
|
10856
11316
|
// src/teams/team.ts
|
|
10857
|
-
var
|
|
11317
|
+
var log33 = createChildLogger({ module: "teams" });
|
|
10858
11318
|
var Team = class _Team {
|
|
10859
11319
|
name;
|
|
10860
11320
|
mode;
|
|
@@ -10941,15 +11401,15 @@ var Team = class _Team {
|
|
|
10941
11401
|
* Falls back to in-process when the external backend is unavailable (tmux not installed,
|
|
10942
11402
|
* non-iTerm environment, etc.) to avoid crashes.
|
|
10943
11403
|
*/
|
|
10944
|
-
spawnTeammate(name, task, runAgent, checker, providerBaseUrl) {
|
|
11404
|
+
spawnTeammate(name, task, runAgent, checker, providerBaseUrl, originToolCallId) {
|
|
10945
11405
|
if (this.mode === "in-process") {
|
|
10946
|
-
this.spawnInProcess(name, task, runAgent, checker);
|
|
11406
|
+
this.spawnInProcess(name, task, runAgent, checker, originToolCallId);
|
|
10947
11407
|
return;
|
|
10948
11408
|
}
|
|
10949
11409
|
try {
|
|
10950
|
-
this.spawnExternal(name, task, providerBaseUrl);
|
|
11410
|
+
this.spawnExternal(name, task, providerBaseUrl, originToolCallId);
|
|
10951
11411
|
} catch {
|
|
10952
|
-
this.spawnInProcess(name, task, runAgent, checker);
|
|
11412
|
+
this.spawnInProcess(name, task, runAgent, checker, originToolCallId);
|
|
10953
11413
|
}
|
|
10954
11414
|
}
|
|
10955
11415
|
/**
|
|
@@ -10958,7 +11418,7 @@ var Team = class _Team {
|
|
|
10958
11418
|
* by `--team-dir`; task assignments from the lead and idle/result notifications from the
|
|
10959
11419
|
* worker all land in this directory, keeping both sides in sync.
|
|
10960
11420
|
*/
|
|
10961
|
-
spawnExternal(name, task, providerBaseUrl) {
|
|
11421
|
+
spawnExternal(name, task, providerBaseUrl, originToolCallId) {
|
|
10962
11422
|
const member = this.addMember(name);
|
|
10963
11423
|
member.active = true;
|
|
10964
11424
|
getNameRegistry().register(name, name);
|
|
@@ -10967,6 +11427,7 @@ var Team = class _Team {
|
|
|
10967
11427
|
teamName: this.name,
|
|
10968
11428
|
status: "running",
|
|
10969
11429
|
progress: createProgress(),
|
|
11430
|
+
...originToolCallId ? { originToolCallId } : {},
|
|
10970
11431
|
startTime: Date.now(),
|
|
10971
11432
|
spinnerVerb: randomVerb()
|
|
10972
11433
|
};
|
|
@@ -11002,7 +11463,7 @@ var Team = class _Team {
|
|
|
11002
11463
|
* Exits the loop upon receiving a shutdown message or being canceled.
|
|
11003
11464
|
* Uses the idle-poll-continue pattern: after each turn completes, reports idle and waits for the next task.
|
|
11004
11465
|
*/
|
|
11005
|
-
spawnInProcess(name, task, runAgent, checker) {
|
|
11466
|
+
spawnInProcess(name, task, runAgent, checker, originToolCallId) {
|
|
11006
11467
|
const member = this.addMember(name);
|
|
11007
11468
|
member.active = true;
|
|
11008
11469
|
member.checker = checker;
|
|
@@ -11016,6 +11477,7 @@ var Team = class _Team {
|
|
|
11016
11477
|
teamName: this.name,
|
|
11017
11478
|
status: "running",
|
|
11018
11479
|
progress: createProgress(),
|
|
11480
|
+
...originToolCallId ? { originToolCallId } : {},
|
|
11019
11481
|
startTime: Date.now(),
|
|
11020
11482
|
spinnerVerb: randomVerb()
|
|
11021
11483
|
};
|
|
@@ -11023,19 +11485,16 @@ var Team = class _Team {
|
|
|
11023
11485
|
const onEvent = (event) => {
|
|
11024
11486
|
switch (event.type) {
|
|
11025
11487
|
case "tool_use":
|
|
11026
|
-
|
|
11027
|
-
|
|
11028
|
-
|
|
11488
|
+
recordToolStart(uiState.progress, event.toolId, event.toolName, event.args);
|
|
11489
|
+
break;
|
|
11490
|
+
case "tool_result":
|
|
11491
|
+
recordToolResult(uiState.progress, event.toolId);
|
|
11029
11492
|
break;
|
|
11030
11493
|
case "usage":
|
|
11031
|
-
|
|
11032
|
-
recordTokens(uiState.progress, event.usage.inputTokens, event.usage.outputTokens);
|
|
11033
|
-
}
|
|
11494
|
+
recordTokens(uiState.progress, event.usage.inputTokens, event.usage.outputTokens);
|
|
11034
11495
|
break;
|
|
11035
|
-
case "
|
|
11036
|
-
|
|
11037
|
-
uiState.lastMessage = event.text;
|
|
11038
|
-
}
|
|
11496
|
+
case "turn_complete":
|
|
11497
|
+
recordTurnComplete(uiState.progress);
|
|
11039
11498
|
break;
|
|
11040
11499
|
}
|
|
11041
11500
|
};
|
|
@@ -11050,7 +11509,13 @@ var Team = class _Team {
|
|
|
11050
11509
|
onEvent,
|
|
11051
11510
|
abortController.signal
|
|
11052
11511
|
);
|
|
11512
|
+
clearActiveTools(uiState.progress);
|
|
11053
11513
|
uiState.lastMessage = result.length > 200 ? result.slice(0, 200) + "..." : result;
|
|
11514
|
+
if (abortController.signal.aborted || !member.active) {
|
|
11515
|
+
uiState.status = "stopped";
|
|
11516
|
+
await this.leadMailbox.send(name, `[idle] ${name} (reason: stopped)`);
|
|
11517
|
+
break;
|
|
11518
|
+
}
|
|
11054
11519
|
if (member.checker?.mode === "plan") {
|
|
11055
11520
|
uiState.status = "idle";
|
|
11056
11521
|
const next = await this.runPlanApproval(member, this.readPlanForReview());
|
|
@@ -11079,20 +11544,23 @@ var Team = class _Team {
|
|
|
11079
11544
|
}
|
|
11080
11545
|
nextPrompt = pollResult.prompt;
|
|
11081
11546
|
}
|
|
11082
|
-
uiState.status
|
|
11547
|
+
if (uiState.status !== "stopped") {
|
|
11548
|
+
uiState.status = "completed";
|
|
11549
|
+
}
|
|
11083
11550
|
} catch (err) {
|
|
11084
11551
|
if (abortController.signal.aborted || !member.active) {
|
|
11085
11552
|
uiState.status = "stopped";
|
|
11086
11553
|
uiState.lastMessage = "Stopped";
|
|
11087
11554
|
await this.leadMailbox.send(name, `[idle] ${name} (reason: stopped)`);
|
|
11088
11555
|
} else {
|
|
11089
|
-
|
|
11556
|
+
log33.error({ err }, "teams operation failed");
|
|
11090
11557
|
uiState.status = "failed";
|
|
11091
11558
|
uiState.lastMessage = asErrorString(err);
|
|
11092
11559
|
await this.leadMailbox.send(name, `[idle] ${name} (reason: failed)`);
|
|
11093
11560
|
}
|
|
11094
11561
|
} finally {
|
|
11095
11562
|
member.active = false;
|
|
11563
|
+
clearActiveTools(uiState.progress);
|
|
11096
11564
|
if (uiState.status === "running") {
|
|
11097
11565
|
uiState.status = "idle";
|
|
11098
11566
|
}
|
|
@@ -11100,7 +11568,7 @@ var Team = class _Team {
|
|
|
11100
11568
|
try {
|
|
11101
11569
|
saveTranscript(this.workDir, this.name, name, member.conversation);
|
|
11102
11570
|
} catch (err) {
|
|
11103
|
-
|
|
11571
|
+
log33.error({ err }, "teams operation failed");
|
|
11104
11572
|
}
|
|
11105
11573
|
}
|
|
11106
11574
|
}
|
|
@@ -11144,7 +11612,7 @@ ${prompt}` };
|
|
|
11144
11612
|
*/
|
|
11145
11613
|
readPlanForReview() {
|
|
11146
11614
|
try {
|
|
11147
|
-
const text =
|
|
11615
|
+
const text = readFileSync20(getOrCreatePlanPath(this.workDir), "utf-8");
|
|
11148
11616
|
if (text.trim()) {
|
|
11149
11617
|
return text;
|
|
11150
11618
|
}
|
|
@@ -11177,6 +11645,9 @@ Please revise the plan accordingly and resubmit.`;
|
|
|
11177
11645
|
if (!member) {
|
|
11178
11646
|
throw new Error(`Member '${to}' not found in team '${this.name}'`);
|
|
11179
11647
|
}
|
|
11648
|
+
if (member.active && member.uiState) {
|
|
11649
|
+
member.uiState.status = "running";
|
|
11650
|
+
}
|
|
11180
11651
|
await member.mailbox.send(from, content);
|
|
11181
11652
|
}
|
|
11182
11653
|
async stopMember(name) {
|
|
@@ -11187,9 +11658,7 @@ Please revise the plan accordingly and resubmit.`;
|
|
|
11187
11658
|
}
|
|
11188
11659
|
}
|
|
11189
11660
|
async stopAll() {
|
|
11190
|
-
|
|
11191
|
-
await this.stopOne(member);
|
|
11192
|
-
}
|
|
11661
|
+
await Promise.allSettled([...this.members.values()].map((member) => this.stopOne(member)));
|
|
11193
11662
|
}
|
|
11194
11663
|
/**
|
|
11195
11664
|
* Stops a single teammate: marks it inactive and updates UI state.
|
|
@@ -11199,7 +11668,7 @@ Please revise the plan accordingly and resubmit.`;
|
|
|
11199
11668
|
*/
|
|
11200
11669
|
async stopOne(member) {
|
|
11201
11670
|
member.active = false;
|
|
11202
|
-
if (member.uiState?.status === "running") {
|
|
11671
|
+
if (member.uiState?.status === "running" || member.uiState?.status === "idle") {
|
|
11203
11672
|
member.uiState.status = "stopped";
|
|
11204
11673
|
}
|
|
11205
11674
|
if (member.external) {
|
|
@@ -11286,6 +11755,9 @@ var TeamManager = class {
|
|
|
11286
11755
|
list() {
|
|
11287
11756
|
return [...this.teams.values()];
|
|
11288
11757
|
}
|
|
11758
|
+
async stopAll() {
|
|
11759
|
+
await Promise.allSettled(this.list().map((team) => team.stopAll()));
|
|
11760
|
+
}
|
|
11289
11761
|
async delete(name) {
|
|
11290
11762
|
const team = this.teams.get(name);
|
|
11291
11763
|
if (team) {
|
|
@@ -11299,6 +11771,20 @@ var TeamManager = class {
|
|
|
11299
11771
|
this.taskStores.delete(name);
|
|
11300
11772
|
rmSync(teamDir(name), { recursive: true, force: true });
|
|
11301
11773
|
}
|
|
11774
|
+
/**
|
|
11775
|
+
* Deletes every team: in-memory teams are stopped and unregistered, then any
|
|
11776
|
+
* residual team directories on disk (e.g. leftovers from previous sessions,
|
|
11777
|
+
* which never appear in list()) are removed too. Enforces the single-team
|
|
11778
|
+
* invariant before a new team is created.
|
|
11779
|
+
*/
|
|
11780
|
+
async deleteAll() {
|
|
11781
|
+
for (const team of this.list()) {
|
|
11782
|
+
await this.delete(team.name);
|
|
11783
|
+
}
|
|
11784
|
+
for (const name of listTeamNames()) {
|
|
11785
|
+
await this.delete(name);
|
|
11786
|
+
}
|
|
11787
|
+
}
|
|
11302
11788
|
getAllTeammateStates() {
|
|
11303
11789
|
return this.list().flatMap((t) => t.getTeammateStates());
|
|
11304
11790
|
}
|
|
@@ -11316,6 +11802,11 @@ var TeamManager = class {
|
|
|
11316
11802
|
const lines = [];
|
|
11317
11803
|
lines.push(`<task-notification team="${team.name}">`);
|
|
11318
11804
|
for (const msg of msgs) {
|
|
11805
|
+
const member = team.getMember(msg.from);
|
|
11806
|
+
if (member?.active && member.uiState && msg.text.startsWith("[idle]")) {
|
|
11807
|
+
member.uiState.status = "idle";
|
|
11808
|
+
clearActiveTools(member.uiState.progress);
|
|
11809
|
+
}
|
|
11319
11810
|
lines.push(`from=${msg.from}: ${msg.text}`);
|
|
11320
11811
|
}
|
|
11321
11812
|
lines.push("</task-notification>");
|
|
@@ -11329,21 +11820,21 @@ function isTeamMode(mode) {
|
|
|
11329
11820
|
}
|
|
11330
11821
|
|
|
11331
11822
|
// src/todo/store.ts
|
|
11332
|
-
import { readFileSync as
|
|
11823
|
+
import { readFileSync as readFileSync21, writeFileSync as writeFileSync14, mkdirSync as mkdirSync15, existsSync as existsSync19 } from "fs";
|
|
11333
11824
|
import { join as join25, dirname as dirname9 } from "path";
|
|
11334
|
-
import
|
|
11335
|
-
var
|
|
11336
|
-
var StoredTaskStatusSchema =
|
|
11337
|
-
var StoredTaskSchema =
|
|
11338
|
-
id:
|
|
11339
|
-
subject:
|
|
11340
|
-
description:
|
|
11825
|
+
import z17, { parse as parse8 } from "zod";
|
|
11826
|
+
var log34 = createChildLogger({ module: "todo" });
|
|
11827
|
+
var StoredTaskStatusSchema = z17.enum(["pending", "in_progress", "completed"]);
|
|
11828
|
+
var StoredTaskSchema = z17.object({
|
|
11829
|
+
id: z17.string(),
|
|
11830
|
+
subject: z17.string(),
|
|
11831
|
+
description: z17.string(),
|
|
11341
11832
|
status: StoredTaskStatusSchema,
|
|
11342
|
-
owner:
|
|
11343
|
-
activeForm:
|
|
11344
|
-
blocks:
|
|
11345
|
-
blockedBy:
|
|
11346
|
-
metadata:
|
|
11833
|
+
owner: z17.string().optional(),
|
|
11834
|
+
activeForm: z17.string().optional(),
|
|
11835
|
+
blocks: z17.array(z17.string()),
|
|
11836
|
+
blockedBy: z17.array(z17.string()),
|
|
11837
|
+
metadata: z17.record(z17.string(), z17.unknown())
|
|
11347
11838
|
});
|
|
11348
11839
|
var TaskStore = class {
|
|
11349
11840
|
filePath;
|
|
@@ -11356,12 +11847,12 @@ var TaskStore = class {
|
|
|
11356
11847
|
return [];
|
|
11357
11848
|
}
|
|
11358
11849
|
try {
|
|
11359
|
-
const data =
|
|
11850
|
+
const data = readFileSync21(this.filePath, "utf-8");
|
|
11360
11851
|
const raw = JSON.parse(data);
|
|
11361
|
-
const parsed = parse8(
|
|
11852
|
+
const parsed = parse8(z17.array(StoredTaskSchema), raw);
|
|
11362
11853
|
return parsed;
|
|
11363
11854
|
} catch (err) {
|
|
11364
|
-
|
|
11855
|
+
log34.error({ err }, "todo operation failed");
|
|
11365
11856
|
return [];
|
|
11366
11857
|
}
|
|
11367
11858
|
}
|
|
@@ -11476,16 +11967,16 @@ var TaskList = class {
|
|
|
11476
11967
|
};
|
|
11477
11968
|
|
|
11478
11969
|
// src/tools/ask-user.ts
|
|
11479
|
-
import { safeParseAsync as safeParseAsync2, z as
|
|
11480
|
-
var QuestionOptionSchema =
|
|
11481
|
-
label:
|
|
11482
|
-
description:
|
|
11970
|
+
import { safeParseAsync as safeParseAsync2, z as z18 } from "zod";
|
|
11971
|
+
var QuestionOptionSchema = z18.object({
|
|
11972
|
+
label: z18.string(),
|
|
11973
|
+
description: z18.string().optional()
|
|
11483
11974
|
});
|
|
11484
|
-
var QuestionSchema =
|
|
11485
|
-
question:
|
|
11486
|
-
header:
|
|
11487
|
-
options:
|
|
11488
|
-
multiSelect:
|
|
11975
|
+
var QuestionSchema = z18.object({
|
|
11976
|
+
question: z18.string(),
|
|
11977
|
+
header: z18.string(),
|
|
11978
|
+
options: z18.array(QuestionOptionSchema),
|
|
11979
|
+
multiSelect: z18.boolean()
|
|
11489
11980
|
});
|
|
11490
11981
|
var AskUserQuestionTool = class {
|
|
11491
11982
|
constructor(ask) {
|
|
@@ -11567,7 +12058,7 @@ var AskUserQuestionTool = class {
|
|
|
11567
12058
|
success,
|
|
11568
12059
|
data: argsData,
|
|
11569
12060
|
error
|
|
11570
|
-
} = await safeParseAsync2(
|
|
12061
|
+
} = await safeParseAsync2(z18.object({ questions: z18.array(QuestionSchema) }), args);
|
|
11571
12062
|
if (!success) {
|
|
11572
12063
|
return {
|
|
11573
12064
|
output: error.message,
|
|
@@ -11679,21 +12170,21 @@ var SyntheticOutputTool = class {
|
|
|
11679
12170
|
};
|
|
11680
12171
|
|
|
11681
12172
|
// src/remote/server.ts
|
|
11682
|
-
var
|
|
11683
|
-
var WsInboundSchema =
|
|
11684
|
-
type:
|
|
11685
|
-
data:
|
|
12173
|
+
var log35 = createChildLogger({ module: "remote" });
|
|
12174
|
+
var WsInboundSchema = z19.object({
|
|
12175
|
+
type: z19.string(),
|
|
12176
|
+
data: z19.unknown()
|
|
11686
12177
|
});
|
|
11687
|
-
var UserMessageSchema =
|
|
11688
|
-
content:
|
|
12178
|
+
var UserMessageSchema = z19.object({
|
|
12179
|
+
content: z19.string()
|
|
11689
12180
|
});
|
|
11690
|
-
var PermissionResponseSchema =
|
|
11691
|
-
id:
|
|
11692
|
-
response:
|
|
12181
|
+
var PermissionResponseSchema = z19.object({
|
|
12182
|
+
id: z19.string(),
|
|
12183
|
+
response: z19.enum(["allow", "deny", "allowAlways"])
|
|
11693
12184
|
});
|
|
11694
|
-
var AskUserResponseSchema =
|
|
11695
|
-
id:
|
|
11696
|
-
answers:
|
|
12185
|
+
var AskUserResponseSchema = z19.object({
|
|
12186
|
+
id: z19.string(),
|
|
12187
|
+
answers: z19.record(z19.string(), z19.string())
|
|
11697
12188
|
});
|
|
11698
12189
|
var FE_DIST = join26(import.meta.dirname, "fe", "dist");
|
|
11699
12190
|
var MIME_TYPES = {
|
|
@@ -11706,8 +12197,8 @@ var MIME_TYPES = {
|
|
|
11706
12197
|
".json": "application/json; charset=utf-8",
|
|
11707
12198
|
".woff2": "font/woff2"
|
|
11708
12199
|
};
|
|
11709
|
-
function serveStatic(
|
|
11710
|
-
const cleanPath = normalize(
|
|
12200
|
+
function serveStatic(path) {
|
|
12201
|
+
const cleanPath = normalize(path).replace(/^(\.\.[/\\])+/, "");
|
|
11711
12202
|
const fullPath = join26(FE_DIST, cleanPath);
|
|
11712
12203
|
if (!fullPath.startsWith(FE_DIST)) {
|
|
11713
12204
|
return null;
|
|
@@ -11715,7 +12206,7 @@ function serveStatic(path2) {
|
|
|
11715
12206
|
if (!existsSync20(fullPath) || !statSync11(fullPath).isFile()) {
|
|
11716
12207
|
return null;
|
|
11717
12208
|
}
|
|
11718
|
-
const body =
|
|
12209
|
+
const body = readFileSync22(fullPath);
|
|
11719
12210
|
const mime = MIME_TYPES[extname3(fullPath)] ?? "application/octet-stream";
|
|
11720
12211
|
return { body, mime };
|
|
11721
12212
|
}
|
|
@@ -11734,6 +12225,7 @@ var AgentHandleImpl = class {
|
|
|
11734
12225
|
hookEngine;
|
|
11735
12226
|
recoveryState;
|
|
11736
12227
|
teamManager;
|
|
12228
|
+
backgroundTaskManager;
|
|
11737
12229
|
enableCoordinatorMode;
|
|
11738
12230
|
forkDisabled;
|
|
11739
12231
|
memoryManager;
|
|
@@ -11763,6 +12255,7 @@ var AgentHandleImpl = class {
|
|
|
11763
12255
|
this.hookEngine = agentHandleImpl.hookEngine;
|
|
11764
12256
|
this.recoveryState = agentHandleImpl.recoveryState;
|
|
11765
12257
|
this.teamManager = agentHandleImpl.teamManager;
|
|
12258
|
+
this.backgroundTaskManager = agentHandleImpl.backgroundTaskManager;
|
|
11766
12259
|
this.enableCoordinatorMode = agentHandleImpl.enableCoordinatorMode;
|
|
11767
12260
|
this.forkDisabled = agentHandleImpl.forkDisabled;
|
|
11768
12261
|
this.memoryManager = agentHandleImpl.memoryManager;
|
|
@@ -11810,7 +12303,10 @@ var AgentHandleImpl = class {
|
|
|
11810
12303
|
const section = this.skillCatalog ? buildSkillSection(this.skillCatalog, this.workDir) : "";
|
|
11811
12304
|
return section && !this.conv.hasReminderContaining(section) ? section : "";
|
|
11812
12305
|
},
|
|
11813
|
-
notificationFn: () =>
|
|
12306
|
+
notificationFn: () => [
|
|
12307
|
+
...this.teamManager.drainLeads(),
|
|
12308
|
+
...this.backgroundTaskManager.drainNotifications().map(formatAgentTaskNotification)
|
|
12309
|
+
],
|
|
11814
12310
|
onPermissionRequest: callbacks.onPermissionRequest,
|
|
11815
12311
|
onLoopComplete: (conv) => {
|
|
11816
12312
|
const summary = conv.getMessages().slice(-40).map((m) => `[${m.role}]: ${contentToText(m.content)}`).filter((s) => s.length > 12).join("\n");
|
|
@@ -11831,6 +12327,8 @@ var AgentHandleImpl = class {
|
|
|
11831
12327
|
}
|
|
11832
12328
|
abort() {
|
|
11833
12329
|
this.abortController?.abort();
|
|
12330
|
+
void this.backgroundTaskManager.stopAll();
|
|
12331
|
+
void this.teamManager.stopAll();
|
|
11834
12332
|
}
|
|
11835
12333
|
};
|
|
11836
12334
|
async function createRemoteAgent(opts) {
|
|
@@ -11841,9 +12339,9 @@ async function createRemoteAgent(opts) {
|
|
|
11841
12339
|
mcpServers: mcpConfigs,
|
|
11842
12340
|
enableCoordinatorMode,
|
|
11843
12341
|
forkDisabled,
|
|
11844
|
-
askUser
|
|
12342
|
+
askUser,
|
|
12343
|
+
sessionId = newSessionId()
|
|
11845
12344
|
} = opts;
|
|
11846
|
-
const sessionId = newSessionId();
|
|
11847
12345
|
const fileHistory = new FileHistory(workDir, sessionId);
|
|
11848
12346
|
const fileStateCache = new FileStateCache();
|
|
11849
12347
|
const registry = buildToolRegistry(workDir, sessionId);
|
|
@@ -11859,7 +12357,7 @@ async function createRemoteAgent(opts) {
|
|
|
11859
12357
|
conv.injectLongTermMemory(instructions, memReminder);
|
|
11860
12358
|
const hookErr = validate(hookConfigs ?? []);
|
|
11861
12359
|
if (hookErr) {
|
|
11862
|
-
|
|
12360
|
+
log35.warn({ message: hookErr.message }, "hook validation warning");
|
|
11863
12361
|
}
|
|
11864
12362
|
const hookEngine = new HookEngine(hookConfigs ?? []);
|
|
11865
12363
|
const catalog = new SkillCatalog();
|
|
@@ -11881,8 +12379,8 @@ async function createRemoteAgent(opts) {
|
|
|
11881
12379
|
if (!client) {
|
|
11882
12380
|
throw new Error("no llm client (provider not initialized)");
|
|
11883
12381
|
}
|
|
11884
|
-
const { PermissionChecker: PC } = await import("./checker-
|
|
11885
|
-
const { Agent: AgentClass } = await import("./agent-
|
|
12382
|
+
const { PermissionChecker: PC } = await import("./checker-UPY4CXUL.js");
|
|
12383
|
+
const { Agent: AgentClass } = await import("./agent-NNYCUIOF.js");
|
|
11886
12384
|
const subConv = new ConversationManager();
|
|
11887
12385
|
subConv.addUserMessage(prompt);
|
|
11888
12386
|
const subAgent = new AgentClass({
|
|
@@ -11909,7 +12407,9 @@ async function createRemoteAgent(opts) {
|
|
|
11909
12407
|
}
|
|
11910
12408
|
};
|
|
11911
12409
|
registry.register(new LoadSkillTool(catalog, skillHost, skillForkHost));
|
|
11912
|
-
|
|
12410
|
+
if (askUser) {
|
|
12411
|
+
registry.register(new AskUserQuestionTool(askUser));
|
|
12412
|
+
}
|
|
11913
12413
|
const teamRunAgentFactory = (registry2, teamChecker, memberWorkDir = workDir) => (task, onEvent, abortSignal) => spawnSubagent(
|
|
11914
12414
|
BUILTIN_AGENTS[0],
|
|
11915
12415
|
task,
|
|
@@ -11924,10 +12424,11 @@ async function createRemoteAgent(opts) {
|
|
|
11924
12424
|
{ abortSignal }
|
|
11925
12425
|
);
|
|
11926
12426
|
const teamManager = new TeamManager(workDir);
|
|
12427
|
+
const backgroundTaskManager = new TaskManager();
|
|
11927
12428
|
registry.register(new TeamCreateTool(teamManager));
|
|
11928
12429
|
registry.register(new SendMessageTool(teamManager));
|
|
11929
12430
|
registry.register(new TeamDeleteTool(teamManager));
|
|
11930
|
-
registry.register(new TaskStopTool(teamManager));
|
|
12431
|
+
registry.register(new TaskStopTool(teamManager, backgroundTaskManager));
|
|
11931
12432
|
registry.register(new SyntheticOutputTool());
|
|
11932
12433
|
const agentTool = new AgentTool(
|
|
11933
12434
|
workDir,
|
|
@@ -11990,7 +12491,8 @@ async function createRemoteAgent(opts) {
|
|
|
11990
12491
|
}
|
|
11991
12492
|
}
|
|
11992
12493
|
return output || "[No output]";
|
|
11993
|
-
}
|
|
12494
|
+
},
|
|
12495
|
+
backgroundTaskManager
|
|
11994
12496
|
);
|
|
11995
12497
|
agentTool.forkDisabled = forkDisabled ?? false;
|
|
11996
12498
|
agentTool.setTeamManager(teamManager, teamRunAgentFactory, provider.base_url);
|
|
@@ -12015,7 +12517,7 @@ async function createRemoteAgent(opts) {
|
|
|
12015
12517
|
}
|
|
12016
12518
|
}
|
|
12017
12519
|
for (const { serverName, error } of result.errors) {
|
|
12018
|
-
|
|
12520
|
+
log35.error({ serverName, error }, "MCP server connection error");
|
|
12019
12521
|
}
|
|
12020
12522
|
if (result.tools.length > 0) {
|
|
12021
12523
|
decideAndApply(registry, provider.base_url, getContextWindow(provider));
|
|
@@ -12036,6 +12538,7 @@ async function createRemoteAgent(opts) {
|
|
|
12036
12538
|
hookEngine,
|
|
12037
12539
|
recoveryState: new RecoveryState(),
|
|
12038
12540
|
teamManager,
|
|
12541
|
+
backgroundTaskManager,
|
|
12039
12542
|
forkDisabled,
|
|
12040
12543
|
enableCoordinatorMode,
|
|
12041
12544
|
memoryManager,
|
|
@@ -12115,7 +12618,7 @@ var RemoteServer = class {
|
|
|
12115
12618
|
streaming = false;
|
|
12116
12619
|
compactController = null;
|
|
12117
12620
|
turnCount = 0;
|
|
12118
|
-
eventLogger = new AgentEventLogger(
|
|
12621
|
+
eventLogger = new AgentEventLogger(log35);
|
|
12119
12622
|
// Pending permission/ask-user requests waiting for WS client responses
|
|
12120
12623
|
pendingPermissions = /* @__PURE__ */ new Map();
|
|
12121
12624
|
pendingAsks = /* @__PURE__ */ new Map();
|
|
@@ -12172,12 +12675,12 @@ var RemoteServer = class {
|
|
|
12172
12675
|
try {
|
|
12173
12676
|
raw = JSON.parse(data.toString("utf-8"));
|
|
12174
12677
|
} catch {
|
|
12175
|
-
|
|
12678
|
+
log35.warn("received non-JSON WebSocket message");
|
|
12176
12679
|
return;
|
|
12177
12680
|
}
|
|
12178
12681
|
const parsed = WsInboundSchema.safeParse(raw);
|
|
12179
12682
|
if (!parsed.success) {
|
|
12180
|
-
|
|
12683
|
+
log35.warn("received malformed WS message");
|
|
12181
12684
|
return;
|
|
12182
12685
|
}
|
|
12183
12686
|
void this.handleWsMessage(parsed.data);
|
|
@@ -12231,7 +12734,7 @@ var RemoteServer = class {
|
|
|
12231
12734
|
break;
|
|
12232
12735
|
}
|
|
12233
12736
|
default:
|
|
12234
|
-
|
|
12737
|
+
log35.warn({ type: msg.type }, "unknown WS message type");
|
|
12235
12738
|
}
|
|
12236
12739
|
}
|
|
12237
12740
|
/**
|
|
@@ -12258,7 +12761,7 @@ var RemoteServer = class {
|
|
|
12258
12761
|
});
|
|
12259
12762
|
return this.agentHandle;
|
|
12260
12763
|
} catch (err) {
|
|
12261
|
-
|
|
12764
|
+
log35.error({ err }, "failed to initialize agent");
|
|
12262
12765
|
this.broadcast({
|
|
12263
12766
|
type: "error",
|
|
12264
12767
|
data: {
|
|
@@ -12319,7 +12822,7 @@ var RemoteServer = class {
|
|
|
12319
12822
|
});
|
|
12320
12823
|
}
|
|
12321
12824
|
} catch (err) {
|
|
12322
|
-
|
|
12825
|
+
log35.error({ err }, "agent stream error");
|
|
12323
12826
|
this.broadcast({
|
|
12324
12827
|
type: "error",
|
|
12325
12828
|
data: { message: err instanceof Error ? err.message : String(err) }
|
|
@@ -12476,7 +12979,7 @@ var RemoteServer = class {
|
|
|
12476
12979
|
});
|
|
12477
12980
|
}
|
|
12478
12981
|
} catch (err) {
|
|
12479
|
-
|
|
12982
|
+
log35.error({ err }, "agent stream error in prompt command");
|
|
12480
12983
|
this.broadcast({
|
|
12481
12984
|
type: "error",
|
|
12482
12985
|
data: { message: err instanceof Error ? err.message : String(err) }
|
|
@@ -12697,7 +13200,7 @@ Explore the codebase and design your approach.`
|
|
|
12697
13200
|
});
|
|
12698
13201
|
}
|
|
12699
13202
|
} catch (err) {
|
|
12700
|
-
|
|
13203
|
+
log35.error({ err }, "agent stream error in plan command");
|
|
12701
13204
|
this.broadcast({
|
|
12702
13205
|
type: "error",
|
|
12703
13206
|
data: { message: err instanceof Error ? err.message : String(err) }
|
|
@@ -12865,7 +13368,7 @@ Explore the codebase and design your approach.`
|
|
|
12865
13368
|
forkDisabled: this.opts.forkDisabled
|
|
12866
13369
|
});
|
|
12867
13370
|
} catch (err) {
|
|
12868
|
-
|
|
13371
|
+
log35.warn({ err }, "agent init deferred -- will retry on first message");
|
|
12869
13372
|
this.agentHandle = null;
|
|
12870
13373
|
}
|
|
12871
13374
|
return new Promise((resolve12, reject) => {
|
|
@@ -12919,7 +13422,7 @@ import { randomUUID as randomUUID2 } from "crypto";
|
|
|
12919
13422
|
import {
|
|
12920
13423
|
mkdirSync as mkdirSync16,
|
|
12921
13424
|
writeFileSync as writeFileSync15,
|
|
12922
|
-
readFileSync as
|
|
13425
|
+
readFileSync as readFileSync23,
|
|
12923
13426
|
lstatSync,
|
|
12924
13427
|
realpathSync,
|
|
12925
13428
|
openSync as openSync2,
|
|
@@ -12929,7 +13432,7 @@ import {
|
|
|
12929
13432
|
} from "fs";
|
|
12930
13433
|
import { join as join27, resolve as resolve10 } from "path";
|
|
12931
13434
|
import yaml7 from "js-yaml";
|
|
12932
|
-
var
|
|
13435
|
+
var log36 = createChildLogger({ module: "skills" });
|
|
12933
13436
|
var InstallSkillTool = class {
|
|
12934
13437
|
constructor(workDir, catalog, onInstalled) {
|
|
12935
13438
|
this.workDir = workDir;
|
|
@@ -12993,7 +13496,7 @@ var InstallSkillTool = class {
|
|
|
12993
13496
|
clearTimeout(timer);
|
|
12994
13497
|
}
|
|
12995
13498
|
} else {
|
|
12996
|
-
content =
|
|
13499
|
+
content = readFileSync23(resolve10(this.workDir, source), "utf-8");
|
|
12997
13500
|
}
|
|
12998
13501
|
const parsed = parseSkillFile(content);
|
|
12999
13502
|
if (!parsed) {
|
|
@@ -13053,7 +13556,7 @@ ${parsed.body}
|
|
|
13053
13556
|
isError: false
|
|
13054
13557
|
};
|
|
13055
13558
|
} catch (err) {
|
|
13056
|
-
|
|
13559
|
+
log36.error({ err }, "skills operation failed");
|
|
13057
13560
|
return {
|
|
13058
13561
|
output: `Error installing skill: ${asErrorString(err)}`,
|
|
13059
13562
|
isError: true
|
|
@@ -13062,57 +13565,6 @@ ${parsed.body}
|
|
|
13062
13565
|
}
|
|
13063
13566
|
};
|
|
13064
13567
|
|
|
13065
|
-
// src/subagent/task-manager.ts
|
|
13066
|
-
var TaskManager = class {
|
|
13067
|
-
tasks = /* @__PURE__ */ new Map();
|
|
13068
|
-
nextId = 1;
|
|
13069
|
-
create(name, runner, cancel) {
|
|
13070
|
-
const id = String(this.nextId++);
|
|
13071
|
-
const task = {
|
|
13072
|
-
id,
|
|
13073
|
-
name,
|
|
13074
|
-
status: "running",
|
|
13075
|
-
output: "",
|
|
13076
|
-
cancel
|
|
13077
|
-
};
|
|
13078
|
-
this.tasks.set(id, task);
|
|
13079
|
-
runner().then((output) => {
|
|
13080
|
-
task.status = "completed";
|
|
13081
|
-
task.output = output;
|
|
13082
|
-
}).catch((err) => {
|
|
13083
|
-
task.status = "failed";
|
|
13084
|
-
task.output = `Error: ${asErrorString(err)}`;
|
|
13085
|
-
});
|
|
13086
|
-
return task;
|
|
13087
|
-
}
|
|
13088
|
-
get(id) {
|
|
13089
|
-
return this.tasks.get(id);
|
|
13090
|
-
}
|
|
13091
|
-
list() {
|
|
13092
|
-
return [...this.tasks.values()];
|
|
13093
|
-
}
|
|
13094
|
-
/** Abort a running task; returns whether a task was actually stopped. Returns false for tasks that have already finished. */
|
|
13095
|
-
stop(id) {
|
|
13096
|
-
const task = this.tasks.get(id);
|
|
13097
|
-
if (task?.status === "running") {
|
|
13098
|
-
task.cancel();
|
|
13099
|
-
task.status = "cancelled";
|
|
13100
|
-
task.output = "Stopped by user";
|
|
13101
|
-
return true;
|
|
13102
|
-
}
|
|
13103
|
-
return false;
|
|
13104
|
-
}
|
|
13105
|
-
drainNotifications() {
|
|
13106
|
-
const completed = [];
|
|
13107
|
-
for (const task of this.tasks.values()) {
|
|
13108
|
-
if (task.status !== "running") {
|
|
13109
|
-
completed.push(task);
|
|
13110
|
-
}
|
|
13111
|
-
}
|
|
13112
|
-
return completed;
|
|
13113
|
-
}
|
|
13114
|
-
};
|
|
13115
|
-
|
|
13116
13568
|
// src/tools/is-diff-tool.ts
|
|
13117
13569
|
function isDiffTool(toolName) {
|
|
13118
13570
|
return toolName === "EditFile";
|
|
@@ -13351,20 +13803,20 @@ import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
|
13351
13803
|
// src/vscode/ide-client.ts
|
|
13352
13804
|
import { setTimeout as delay2 } from "timers/promises";
|
|
13353
13805
|
import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
|
|
13354
|
-
import { z as
|
|
13806
|
+
import { z as z21 } from "zod";
|
|
13355
13807
|
|
|
13356
13808
|
// src/vscode/lockfile.ts
|
|
13357
13809
|
import { readdir as readdir2, readFile as readFile6 } from "fs/promises";
|
|
13358
13810
|
import { homedir as homedir10 } from "os";
|
|
13359
13811
|
import { join as join28, resolve as resolve11, sep as sep2 } from "path";
|
|
13360
|
-
import { z as
|
|
13361
|
-
var
|
|
13362
|
-
var LockfileSchema =
|
|
13363
|
-
workspaceFolders:
|
|
13364
|
-
pid:
|
|
13365
|
-
ideName:
|
|
13366
|
-
transport:
|
|
13367
|
-
authToken:
|
|
13812
|
+
import { z as z20 } from "zod";
|
|
13813
|
+
var log37 = createChildLogger({ module: "vscode" });
|
|
13814
|
+
var LockfileSchema = z20.object({
|
|
13815
|
+
workspaceFolders: z20.array(z20.string()).optional(),
|
|
13816
|
+
pid: z20.number().optional(),
|
|
13817
|
+
ideName: z20.string().optional(),
|
|
13818
|
+
transport: z20.enum(["ws", "sse"]).optional(),
|
|
13819
|
+
authToken: z20.string().optional()
|
|
13368
13820
|
});
|
|
13369
13821
|
function ideLockDir() {
|
|
13370
13822
|
const configDir = process.env.CLAUDE_CONFIG_DIR ?? join28(homedir10(), ".claude");
|
|
@@ -13400,7 +13852,7 @@ async function readLockfile(dir, filename) {
|
|
|
13400
13852
|
pid: parsed.pid
|
|
13401
13853
|
};
|
|
13402
13854
|
} catch (err) {
|
|
13403
|
-
|
|
13855
|
+
log37.error({ err, filename }, "failed to read IDE lockfile");
|
|
13404
13856
|
return null;
|
|
13405
13857
|
}
|
|
13406
13858
|
}
|
|
@@ -13516,13 +13968,13 @@ var WebSocketTransport = class {
|
|
|
13516
13968
|
};
|
|
13517
13969
|
|
|
13518
13970
|
// src/vscode/ide-client.ts
|
|
13519
|
-
var
|
|
13520
|
-
var AtMentionedSchema =
|
|
13521
|
-
method:
|
|
13522
|
-
params:
|
|
13523
|
-
filePath:
|
|
13524
|
-
lineStart:
|
|
13525
|
-
lineEnd:
|
|
13971
|
+
var log38 = createChildLogger({ module: "vscode" });
|
|
13972
|
+
var AtMentionedSchema = z21.object({
|
|
13973
|
+
method: z21.literal("at_mentioned"),
|
|
13974
|
+
params: z21.object({
|
|
13975
|
+
filePath: z21.string(),
|
|
13976
|
+
lineStart: z21.number().optional(),
|
|
13977
|
+
lineEnd: z21.number().optional()
|
|
13526
13978
|
})
|
|
13527
13979
|
});
|
|
13528
13980
|
function inIdeTerminal() {
|
|
@@ -13558,7 +14010,7 @@ async function connectToIde(opts) {
|
|
|
13558
14010
|
} catch (err) {
|
|
13559
14011
|
await client.close().catch(() => void 0);
|
|
13560
14012
|
if (!opts.signal?.aborted) {
|
|
13561
|
-
|
|
14013
|
+
log38.error({ err, url: ide.url }, "failed to connect to IDE extension");
|
|
13562
14014
|
}
|
|
13563
14015
|
return null;
|
|
13564
14016
|
}
|
|
@@ -13584,7 +14036,7 @@ async function connectToIde(opts) {
|
|
|
13584
14036
|
params: { pid: process.pid }
|
|
13585
14037
|
});
|
|
13586
14038
|
} catch (err) {
|
|
13587
|
-
|
|
14039
|
+
log38.error({ err }, "failed to send ide_connected notification");
|
|
13588
14040
|
}
|
|
13589
14041
|
return {
|
|
13590
14042
|
ideName: ide.ideName,
|
|
@@ -13593,7 +14045,7 @@ async function connectToIde(opts) {
|
|
|
13593
14045
|
try {
|
|
13594
14046
|
await client.close();
|
|
13595
14047
|
} catch (err) {
|
|
13596
|
-
|
|
14048
|
+
log38.error({ err }, "failed to close IDE client");
|
|
13597
14049
|
}
|
|
13598
14050
|
}
|
|
13599
14051
|
};
|
|
@@ -13709,10 +14161,11 @@ async function runPrintMode(args) {
|
|
|
13709
14161
|
registry.register(new EditFileTool());
|
|
13710
14162
|
registry.register(new ToolSearchTool(registry));
|
|
13711
14163
|
const teamManager = new TeamManager(workDir);
|
|
14164
|
+
const backgroundTaskManager = new TaskManager();
|
|
13712
14165
|
registry.register(new TeamCreateTool(teamManager));
|
|
13713
14166
|
registry.register(new SendMessageTool(teamManager));
|
|
13714
14167
|
registry.register(new TeamDeleteTool(teamManager));
|
|
13715
|
-
registry.register(new TaskStopTool(teamManager));
|
|
14168
|
+
registry.register(new TaskStopTool(teamManager, backgroundTaskManager));
|
|
13716
14169
|
registry.register(new SyntheticOutputTool());
|
|
13717
14170
|
registry.register(new McpCallTool(registry));
|
|
13718
14171
|
const agentTool = new AgentTool(
|
|
@@ -13753,7 +14206,8 @@ async function runPrintMode(args) {
|
|
|
13753
14206
|
abortSignal: context?.abortSignal,
|
|
13754
14207
|
onPermissionRequest: context?.onPermissionRequest
|
|
13755
14208
|
}
|
|
13756
|
-
)
|
|
14209
|
+
),
|
|
14210
|
+
backgroundTaskManager
|
|
13757
14211
|
);
|
|
13758
14212
|
agentTool.forkDisabled = !forkEnabled(cfg);
|
|
13759
14213
|
agentTool.setTeamManager(
|
|
@@ -13801,8 +14255,11 @@ async function runPrintMode(args) {
|
|
|
13801
14255
|
contextWindow: getContextWindow(provider),
|
|
13802
14256
|
maxOutput: getMaxOutputTokens(provider),
|
|
13803
14257
|
instructions: loadInstructions(workDir),
|
|
13804
|
-
//
|
|
13805
|
-
notificationFn: () =>
|
|
14258
|
+
// Completion reports are drained each turn as system reminders delivered to the Lead.
|
|
14259
|
+
notificationFn: () => [
|
|
14260
|
+
...teamManager.drainLeads(),
|
|
14261
|
+
...backgroundTaskManager.drainNotifications().map(formatAgentTaskNotification)
|
|
14262
|
+
],
|
|
13806
14263
|
toolFilter: coordinatorToolFilter(cfg.enable_coordinator_mode ?? false),
|
|
13807
14264
|
coordinatorActiveFn: () => coordinatorActive(cfg.enable_coordinator_mode ?? false)
|
|
13808
14265
|
});
|
|
@@ -13854,10 +14311,21 @@ Error: ${event.error.message}`);
|
|
|
13854
14311
|
break;
|
|
13855
14312
|
}
|
|
13856
14313
|
}
|
|
14314
|
+
await backgroundTaskManager.waitAll();
|
|
14315
|
+
const backgroundNotifications = backgroundTaskManager.drainNotifications();
|
|
13857
14316
|
const durationMs = Date.now() - startTime;
|
|
13858
14317
|
if (args.outputFormat === "text" && resultText && !resultText.endsWith("\n")) {
|
|
13859
14318
|
process.stdout.write("\n");
|
|
13860
14319
|
}
|
|
14320
|
+
for (const task of backgroundNotifications) {
|
|
14321
|
+
const notification = formatAgentTaskNotification(task);
|
|
14322
|
+
if (args.outputFormat === "stream-json") {
|
|
14323
|
+
console.log(JSON.stringify({ type: "task_notification", notification }));
|
|
14324
|
+
} else {
|
|
14325
|
+
process.stdout.write(`${notification}
|
|
14326
|
+
`);
|
|
14327
|
+
}
|
|
14328
|
+
}
|
|
13861
14329
|
if (args.outputFormat === "stream-json") {
|
|
13862
14330
|
const resultLine = {
|
|
13863
14331
|
type: "result",
|
|
@@ -13870,7 +14338,8 @@ Error: ${event.error.message}`);
|
|
|
13870
14338
|
console.log(JSON.stringify(resultLine));
|
|
13871
14339
|
}
|
|
13872
14340
|
} finally {
|
|
13873
|
-
await
|
|
14341
|
+
await backgroundTaskManager.stopAll();
|
|
14342
|
+
await teamManager.stopAll();
|
|
13874
14343
|
if (mcpManager) {
|
|
13875
14344
|
try {
|
|
13876
14345
|
await mcpManager.disconnectAll();
|
|
@@ -13943,6 +14412,23 @@ function recordError(context, error) {
|
|
|
13943
14412
|
const stack = error instanceof Error ? error.stack ?? error.message : String(error);
|
|
13944
14413
|
record(`crash [${context}] ${stack}`);
|
|
13945
14414
|
}
|
|
14415
|
+
var TERMINAL_GONE_CODES = /* @__PURE__ */ new Set(["EIO", "EPIPE", "ERR_STREAM_DESTROYED"]);
|
|
14416
|
+
function isTerminalGone(err) {
|
|
14417
|
+
if (err === null || typeof err !== "object" && typeof err !== "function") {
|
|
14418
|
+
return false;
|
|
14419
|
+
}
|
|
14420
|
+
const code = Reflect.get(err, "code");
|
|
14421
|
+
return typeof code === "string" && TERMINAL_GONE_CODES.has(code);
|
|
14422
|
+
}
|
|
14423
|
+
var terminalGoneRecorded = false;
|
|
14424
|
+
function exitForTerminalGone(context, error) {
|
|
14425
|
+
if (!terminalGoneRecorded) {
|
|
14426
|
+
terminalGoneRecorded = true;
|
|
14427
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
14428
|
+
record(`terminal closed [${context}] ${detail}`);
|
|
14429
|
+
}
|
|
14430
|
+
process.exit(0);
|
|
14431
|
+
}
|
|
13946
14432
|
var exitRecorded = false;
|
|
13947
14433
|
function recordExit(code) {
|
|
13948
14434
|
if (exitRecorded) {
|
|
@@ -13953,15 +14439,35 @@ function recordExit(code) {
|
|
|
13953
14439
|
}
|
|
13954
14440
|
function recover() {
|
|
13955
14441
|
record(`start pid=${String(process.pid)}`);
|
|
14442
|
+
const onStdioError = (err) => {
|
|
14443
|
+
if (isTerminalGone(err)) {
|
|
14444
|
+
exitForTerminalGone("stdio", err);
|
|
14445
|
+
}
|
|
14446
|
+
throw err;
|
|
14447
|
+
};
|
|
14448
|
+
process.stdout.on("error", onStdioError);
|
|
14449
|
+
process.stderr.on("error", onStdioError);
|
|
13956
14450
|
process.on("uncaughtException", (err) => {
|
|
14451
|
+
if (isTerminalGone(err)) {
|
|
14452
|
+
exitForTerminalGone("uncaught exception", err);
|
|
14453
|
+
}
|
|
13957
14454
|
recordError("uncaught exception", err);
|
|
13958
14455
|
logger.fatal({ err }, "uncaught exception");
|
|
13959
|
-
|
|
14456
|
+
captureTelemetryError(err, "uncaught exception");
|
|
14457
|
+
void shutdownTelemetry().finally(() => {
|
|
14458
|
+
process.exit(1);
|
|
14459
|
+
});
|
|
13960
14460
|
});
|
|
13961
14461
|
process.on("unhandledRejection", (reason) => {
|
|
14462
|
+
if (isTerminalGone(reason)) {
|
|
14463
|
+
exitForTerminalGone("unhandled rejection", reason);
|
|
14464
|
+
}
|
|
13962
14465
|
recordError("unhandled rejection", reason);
|
|
13963
14466
|
logger.fatal({ err: reason }, "unhandled rejection");
|
|
13964
|
-
|
|
14467
|
+
captureTelemetryError(reason, "unhandled rejection");
|
|
14468
|
+
void shutdownTelemetry().finally(() => {
|
|
14469
|
+
process.exit(1);
|
|
14470
|
+
});
|
|
13965
14471
|
});
|
|
13966
14472
|
process.on("exit", (code) => {
|
|
13967
14473
|
closeLogger();
|
|
@@ -14005,7 +14511,7 @@ function parseTeammateFlags(args) {
|
|
|
14005
14511
|
}
|
|
14006
14512
|
var ShutdownPrefix = "[shutdown]";
|
|
14007
14513
|
var LeadName = "lead";
|
|
14008
|
-
var
|
|
14514
|
+
var log39 = createChildLogger({ module: "teammate" });
|
|
14009
14515
|
function isShutdownRequest2(msg) {
|
|
14010
14516
|
return msg.text.trimStart().startsWith(ShutdownPrefix);
|
|
14011
14517
|
}
|
|
@@ -14050,13 +14556,13 @@ async function buildTeammateRegistry(opts) {
|
|
|
14050
14556
|
}
|
|
14051
14557
|
}
|
|
14052
14558
|
for (const { serverName, error } of result.errors) {
|
|
14053
|
-
|
|
14559
|
+
log39.error({ serverName, error }, "MCP connection error");
|
|
14054
14560
|
}
|
|
14055
14561
|
if (result.tools.length > 0 && opts.contextWindow) {
|
|
14056
14562
|
decideAndApply(registry, opts.baseUrl ?? "", opts.contextWindow);
|
|
14057
14563
|
}
|
|
14058
14564
|
} catch (err) {
|
|
14059
|
-
|
|
14565
|
+
log39.error({ err }, "MCP setup failed");
|
|
14060
14566
|
}
|
|
14061
14567
|
}
|
|
14062
14568
|
return registry;
|
|
@@ -14135,7 +14641,7 @@ ${body}`);
|
|
|
14135
14641
|
console.log("--- Task complete ---");
|
|
14136
14642
|
break;
|
|
14137
14643
|
case "error":
|
|
14138
|
-
|
|
14644
|
+
log39.error({ err: event.error }, "agent error");
|
|
14139
14645
|
throw event.error;
|
|
14140
14646
|
}
|
|
14141
14647
|
}
|
|
@@ -14153,7 +14659,7 @@ ${body}`);
|
|
|
14153
14659
|
if (event.type === "stream_text") {
|
|
14154
14660
|
process.stdout.write(event.text);
|
|
14155
14661
|
} else if (event.type === "error") {
|
|
14156
|
-
|
|
14662
|
+
log39.error({ err: event.error }, "agent error");
|
|
14157
14663
|
throw event.error;
|
|
14158
14664
|
}
|
|
14159
14665
|
}
|
|
@@ -14164,7 +14670,7 @@ ${body}`);
|
|
|
14164
14670
|
try {
|
|
14165
14671
|
await mcpManager.disconnectAll();
|
|
14166
14672
|
} catch (err) {
|
|
14167
|
-
|
|
14673
|
+
log39.error({ err }, "MCP cleanup failed");
|
|
14168
14674
|
}
|
|
14169
14675
|
}
|
|
14170
14676
|
process.off("exit", closeLogger);
|
|
@@ -14173,6 +14679,7 @@ ${body}`);
|
|
|
14173
14679
|
}
|
|
14174
14680
|
export {
|
|
14175
14681
|
ASYNC_AGENT_ALLOWED_TOOLS,
|
|
14682
|
+
ActiveToolSchema,
|
|
14176
14683
|
Agent,
|
|
14177
14684
|
AgentEventLogger,
|
|
14178
14685
|
AgentProgressSchema,
|
|
@@ -14223,6 +14730,7 @@ export {
|
|
|
14223
14730
|
LLMError,
|
|
14224
14731
|
ListTeamsTool,
|
|
14225
14732
|
LoadSkillTool,
|
|
14733
|
+
MACOS_SNIPPET,
|
|
14226
14734
|
MAIN_AGENT_ONLY_TOOLS,
|
|
14227
14735
|
MAX_DIMENSION_PX,
|
|
14228
14736
|
MAX_HISTORY_ENTRIES,
|
|
@@ -14270,6 +14778,7 @@ export {
|
|
|
14270
14778
|
SHUTDOWN_PREFIX,
|
|
14271
14779
|
SKIP_DIRS,
|
|
14272
14780
|
SUBAGENT_DISALLOWED_TOOLS,
|
|
14781
|
+
SUBAGENT_INTERRUPTED_MARKER,
|
|
14273
14782
|
SUMMARY_INSTRUCTIONS,
|
|
14274
14783
|
SeatbeltSandbox,
|
|
14275
14784
|
SendMessageTool,
|
|
@@ -14304,6 +14813,10 @@ export {
|
|
|
14304
14813
|
ToolActivitySchema,
|
|
14305
14814
|
ToolRegistry,
|
|
14306
14815
|
ToolSearchTool,
|
|
14816
|
+
WEB_FETCH_DESCRIPTION,
|
|
14817
|
+
WINDOWS_CSHARP_SNIPPET,
|
|
14818
|
+
WINDOWS_PWSH_INCLUDES_CSHARP_SNIPPET,
|
|
14819
|
+
WINDOWS_PWSH_SNIPPET,
|
|
14307
14820
|
WRITE_FILE_DESCRIPTION,
|
|
14308
14821
|
WebSocketTransport,
|
|
14309
14822
|
WriteFileTool,
|
|
@@ -14340,10 +14853,12 @@ export {
|
|
|
14340
14853
|
canonicalPath,
|
|
14341
14854
|
clampThinkingLevel,
|
|
14342
14855
|
cleanExpiredSessions,
|
|
14856
|
+
clearActiveTools,
|
|
14343
14857
|
clipboardImageFileName,
|
|
14344
14858
|
cloneRegistryForFork,
|
|
14345
14859
|
closeLogger,
|
|
14346
14860
|
coerceBySchema,
|
|
14861
|
+
compactPath,
|
|
14347
14862
|
computeCompactThreshold,
|
|
14348
14863
|
computeKeepStartIndex,
|
|
14349
14864
|
connectToIde,
|
|
@@ -14390,6 +14905,7 @@ export {
|
|
|
14390
14905
|
filterToolsForAgent,
|
|
14391
14906
|
forceCompact,
|
|
14392
14907
|
forkEnabled,
|
|
14908
|
+
formatAgentTaskNotification,
|
|
14393
14909
|
formatInteractionSummary,
|
|
14394
14910
|
formatShellOutput,
|
|
14395
14911
|
formatTokens,
|
|
@@ -14423,9 +14939,11 @@ export {
|
|
|
14423
14939
|
isSafeCommand,
|
|
14424
14940
|
isShutdownRequest,
|
|
14425
14941
|
isSpillReadback,
|
|
14942
|
+
isTerminalGone,
|
|
14426
14943
|
isToolResultContentBlock,
|
|
14427
14944
|
isValidThinkingLevel,
|
|
14428
14945
|
listSessions,
|
|
14946
|
+
listTeamNames,
|
|
14429
14947
|
load,
|
|
14430
14948
|
loadAgentDefinitions,
|
|
14431
14949
|
loadConfig,
|
|
@@ -14474,7 +14992,10 @@ export {
|
|
|
14474
14992
|
recordError,
|
|
14475
14993
|
recordExit,
|
|
14476
14994
|
recordTokens,
|
|
14995
|
+
recordToolResult,
|
|
14996
|
+
recordToolStart,
|
|
14477
14997
|
recordToolUse,
|
|
14998
|
+
recordTurnComplete,
|
|
14478
14999
|
recover,
|
|
14479
15000
|
removeAgentWorktree,
|
|
14480
15001
|
removeMcpTools,
|