@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.
Files changed (75) hide show
  1. package/README.md +64 -0
  2. package/dist/acp-5WRTN2LL.js +14 -0
  3. package/dist/agent-3CAPJCPD.js +4 -0
  4. package/dist/anthropic-FXRRA63E.js +4 -0
  5. package/dist/{bwrap-TPE6G4BM.js → bwrap-ZTS67W2K.js} +1 -1
  6. package/dist/checker-3O7QF7XV.js +4 -0
  7. package/dist/chunk-2U4IWZ6E.js +4 -0
  8. package/dist/chunk-35X3GW25.js +4 -0
  9. package/dist/{chunk-NERVCUWE.js → chunk-4C3KJLJV.js} +1 -1
  10. package/dist/chunk-4HSQBZBI.js +4 -0
  11. package/dist/{chunk-JMF2DWGY.js → chunk-4PQYUHSN.js} +1 -1
  12. package/dist/chunk-6I3HEMIE.js +4 -0
  13. package/dist/{chunk-TKQ5T5VO.js → chunk-7JQHFZRD.js} +2 -2
  14. package/dist/{chunk-S35E7OYL.js → chunk-C5WLP3DH.js} +1 -1
  15. package/dist/chunk-FUKVR3XO.js +608 -0
  16. package/dist/chunk-FZORZF22.js +16 -0
  17. package/dist/chunk-G2TE2QZM.js +4 -0
  18. package/dist/chunk-I7XJSLRI.js +4 -0
  19. package/dist/{chunk-2EV72T5X.js → chunk-IKQTIDRL.js} +1 -1
  20. package/dist/chunk-IZ7QOBSB.js +4 -0
  21. package/dist/chunk-LC7ZWLIH.js +4 -0
  22. package/dist/chunk-LHSUYZO5.js +4 -0
  23. package/dist/chunk-M4D66APS.js +67 -0
  24. package/dist/chunk-NAAAN2TD.js +60 -0
  25. package/dist/chunk-NCZR3AGA.js +65 -0
  26. package/dist/{server-QMMIYNXW.js → chunk-PXQD6GWP.js} +15 -15
  27. package/dist/{chunk-FPQ2LBL4.js → chunk-QQVDL4QT.js} +1 -1
  28. package/dist/{chunk-TCQ4EB4G.js → chunk-TNF3EHFY.js} +1 -1
  29. package/dist/chunk-XGUH7SGV.js +4 -0
  30. package/dist/chunk-YIKI3EPG.js +150 -0
  31. package/dist/chunk-YKWIZE5D.js +4 -0
  32. package/dist/{devtools-OJ75EP3T.js → devtools-WJM4UVVT.js} +1 -1
  33. package/dist/esm-46WSO26W.js +4 -0
  34. package/dist/esm-4DBD7ONK.js +229 -0
  35. package/dist/esm-6EDGM6QS.js +4 -0
  36. package/dist/esm-D6BB3FBR.js +4 -0
  37. package/dist/esm-P2KEXEEK.js +4 -0
  38. package/dist/esm-XYKZYVLV.js +4 -0
  39. package/dist/esm-YJVVI2QX.js +4 -0
  40. package/dist/getMachineId-bsd-NBOQ3GRQ.js +4 -0
  41. package/dist/getMachineId-darwin-6VBNSUFR.js +5 -0
  42. package/dist/getMachineId-linux-RQ33NIOO.js +4 -0
  43. package/dist/getMachineId-unsupported-IWOYPUHZ.js +4 -0
  44. package/dist/getMachineId-win-ITT3WXP6.js +4 -0
  45. package/dist/lib/{agent-3OEC4SOB.js → agent-NNYCUIOF.js} +1 -1
  46. package/dist/lib/{checker-QNMXPZ7C.js → checker-UPY4CXUL.js} +1 -1
  47. package/dist/lib/{chunk-UH5M7FDP.js → chunk-AFYPVA5C.js} +252 -20
  48. package/dist/lib/{chunk-SOOSTGHI.js → chunk-CFYAZWDA.js} +8 -2
  49. package/dist/lib/chunk-GTHYQZRL.js +31 -0
  50. package/dist/lib/{chunk-4BPKIIQD.js → chunk-LDWFI6MR.js} +6 -4
  51. package/dist/lib/index.d.ts +229 -152
  52. package/dist/lib/index.js +1096 -575
  53. package/dist/lib/providers-UJEWP7C7.js +419 -0
  54. package/dist/lib/{tool-filter-IMIU7CW2.js → tool-filter-BFEINZIA.js} +1 -1
  55. package/dist/main.js +189 -181
  56. package/dist/{node-GXMM4JG3.js → node-A6KBFKOV.js} +1 -1
  57. package/dist/{openai-M7B2TITO.js → openai-WPEICAZ4.js} +1 -1
  58. package/dist/providers-WNYZUMZ3.js +6 -0
  59. package/dist/{seatbelt-3GM2YGXV.js → seatbelt-PSPJ5LBR.js} +1 -1
  60. package/dist/server-YPBLBRDN.js +4 -0
  61. package/dist/src-3A5BCUSZ.js +4 -0
  62. package/dist/src-CRYFVH7Y.js +12 -0
  63. package/dist/src-Y77IB66J.js +4 -0
  64. package/dist/src-ZCW4GCG2.js +4 -0
  65. package/dist/{tool-filter-7LLVAHD5.js → tool-filter-X6IYME2K.js} +1 -1
  66. package/dist/turndown.es-WNOSVZF6.js +83 -0
  67. package/package.json +30 -4
  68. package/dist/agent-2DUTUJFX.js +0 -4
  69. package/dist/anthropic-KR5MXB5I.js +0 -4
  70. package/dist/checker-RKAWOOXC.js +0 -4
  71. package/dist/chunk-4YVJP2CO.js +0 -4
  72. package/dist/chunk-BWWNI2FK.js +0 -121
  73. package/dist/chunk-LTFPTWX4.js +0 -612
  74. package/dist/chunk-TW7RHCDS.js +0 -150
  75. 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-UH5M7FDP.js";
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-SOOSTGHI.js";
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-4BPKIIQD.js";
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
- // raw-import:/Users/hangtiancheng/github/swifty-code/apps/swifty/src/tools/snippets/macos.swift
1221
- var macos_default = `import AppKit
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, "\\\\": 42,
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
- // raw-import:/Users/hangtiancheng/github/swifty-code/apps/swifty/src/tools/snippets/win32-script.ps1
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
- // raw-import:/Users/hangtiancheng/github/swifty-code/apps/swifty/src/tools/snippets/windows.cs
1421
- var windows_default = 'using System;\nusing System.Drawing;\nusing System.Runtime.InteropServices;\nusing System.Windows.Forms;\n\npublic static class SwiftyComputer\n{\n [StructLayout(LayoutKind.Sequential)]\n private struct INPUT { public uint type; public InputUnion value; }\n [StructLayout(LayoutKind.Explicit)]\n private struct InputUnion { [FieldOffset(0)] public KEYBDINPUT keyboard; }\n [StructLayout(LayoutKind.Sequential)]\n private struct KEYBDINPUT\n {\n public ushort virtualKey;\n public ushort scanCode;\n public uint flags;\n public uint time;\n public UIntPtr extraInfo;\n }\n\n [DllImport("user32.dll")]\n private static extern void mouse_event(uint flags, uint dx, uint dy, int data, UIntPtr extraInfo);\n [DllImport("user32.dll", SetLastError = true)]\n private static extern uint SendInput(uint count, INPUT[] inputs, int size);\n\n private static void SendKeyboard(ushort virtualKey, ushort scanCode, uint flags)\n {\n var inputs = new[] {\n new INPUT {\n type = 1,\n value = new InputUnion {\n keyboard = new KEYBDINPUT {\n virtualKey = virtualKey,\n scanCode = scanCode,\n flags = flags,\n extraInfo = UIntPtr.Zero\n }\n }\n }\n };\n if (SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT))) != 1)\n {\n throw new InvalidOperationException("SendInput failed.");\n }\n }\n\n public static void Move(int x, int y) { Cursor.Position = new Point(x, y); }\n public static Point Position() { return Cursor.Position; }\n public static void Mouse(uint flags, int data = 0) { mouse_event(flags, 0, 0, data, UIntPtr.Zero); }\n public static void Key(int virtualKey, bool down) { SendKeyboard((ushort)virtualKey, 0, down ? 0u : 2u); }\n public static void Text(string text)\n {\n foreach (char value in text)\n {\n SendKeyboard(0, value, 4u);\n SendKeyboard(0, value, 6u);\n }\n }\n}\n';
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
- // raw-import:/Users/hangtiancheng/github/swifty-code/apps/swifty/src/tools/snippets/windows.ps1
1424
- var windows_default2 = `$ErrorActionPreference = 'Stop'
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
- \${WINDOWS.CS}
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(path2) {
1545
- return mediaType[extname(path2).toLowerCase()] ?? null;
1604
+ function getMediaType(path) {
1605
+ return mediaType[extname(path).toLowerCase()] ?? null;
1546
1606
  }
1547
- function isImagePath(path2) {
1548
- return getMediaType(path2) !== null;
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, macos_default, "utf8");
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", WINDOWS_POWERSHELL],
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 path2 = action.path ?? [];
2470
- if (path2.length < 2) {
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(path2[0].x), String(path2[0].y)]);
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 path2.slice(1)) {
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", win32_script_default],
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 newContent = replaceAll ? content.replaceAll(oldString, newString) : content.replace(oldString, newString);
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(path2) {
2964
+ async function pathExists(path) {
2905
2965
  try {
2906
- await access(path2);
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(path2, branch, gitRoot) {
3104
- await execFileAsync("git", ["worktree", "remove", "--", path2], {
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(path2, headCommit) {
3169
+ async function hasWorktreeChanges(path, headCommit) {
3110
3170
  try {
3111
3171
  const { stdout: status } = await execFileAsync("git", ["status", "--porcelain"], {
3112
- cwd: path2
3172
+ cwd: path
3113
3173
  });
3114
3174
  if (status.trim()) {
3115
3175
  return true;
3116
3176
  }
3117
- const currentHead = await readWorktreeHeadSha(path2) || (await execFileAsync("git", ["rev-parse", "HEAD"], { cwd: path2 })).stdout.trim();
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 path2 = strArg(args, "path");
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 (!path2 || !branch || !gitRoot) {
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: ${path2}
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(path2, headCommit);
3481
+ const hasChanges = await hasWorktreeChanges(path, headCommit);
3422
3482
  if (!hasChanges) {
3423
3483
  try {
3424
- await removeAgentWorktree(path2, branch, gitRoot);
3484
+ await removeAgentWorktree(path, branch, gitRoot);
3425
3485
  return {
3426
- output: `Worktree cleaned up (no changes): ${path2}`,
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: ${path2}
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 log10 = createChildLogger({ module: "tools" });
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
- log10.error({ err }, "tool operation failed");
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 log11 = createChildLogger({ module: "code-review" });
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
- log11.error({ str }, "code-review operation failed");
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 log12 = createChildLogger({ module: "code-review" });
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
- log12.error({ err }, "code-review operation failed");
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 log13 = createChildLogger({ module: "teams" });
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
- log13.error({ err }, "teams operation failed");
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
- log13.error({ err }, "teams operation failed");
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 log14 = createChildLogger({ module: "code-review" });
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
- log14.error({ err }, "code-review operation failed");
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 log15 = createChildLogger({ module: "commands" });
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
- log15.error({ err }, "commands operation failed");
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(path2) {
5852
- if (!existsSync7(path2)) {
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(path2, "utf-8"));
6119
+ raw = yaml3.load(readFileSync7(path, "utf-8"));
5858
6120
  } catch {
5859
- throw new Error(`Unable to read existing ${path2}; it has not been changed.`);
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(path2, raw) {
5864
- const directory = dirname5(path2);
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, path2);
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 path2 = globalConfigPath();
5883
- const config = readConfigRaw(path2);
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(path2, { ...config, providers: stored });
5909
- return { provider, providers, replaced, path: path2 };
6170
+ writeConfigAtomic(path, { ...config, providers: stored });
6171
+ return { provider, providers, replaced, path };
5910
6172
  }
5911
6173
  function persistThinkingLevel(baseUrl, level) {
5912
- const path2 = globalConfigPath();
5913
- const config = readConfigRaw(path2);
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 ${path2}.`);
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(path2, { ...config, providers });
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 log16 = createChildLogger({ module: "tui" });
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
- log16.error({ err }, "TUI operation failed");
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
- log16.error({ err }, "TUI operation failed");
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
- log16.error({ err }, "TUI operation failed");
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 log17 = createChildLogger({ module: "file-history" });
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(path2) {
6098
- const absPath = resolve7(path2);
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
- log17.error({ err }, "file-history operation failed");
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
- log17.error({ err }, "file-history operation failed");
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
- log17.error({ err }, "file-history operation failed");
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
- log17.error({ err: err2 }, "file-history operation failed");
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
- log17.error({ err }, "file-history operation failed");
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
- log17.error({ err }, "file-history operation failed");
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 log18 = createChildLogger({ module: "history" });
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
- log18.error({ err }, "parse history line failed");
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
- log18.error({ err: err2 }, "load history failed");
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 log19 = createChildLogger({ module: "hooks" });
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
- log19.error({ err }, "hooks operation failed");
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
- log19.error({ err }, "hooks operation failed");
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
- log19.error({ err }, "hooks operation failed");
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
- log19.error({ err }, "hooks operation failed");
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
- log19.error({ err }, "hooks operation failed");
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
- log19.error({ err }, "hooks operation failed");
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 path2 = join14(dir, clipboardImageFileName(bytes));
6619
- await writeFile3(path2, bytes);
6620
- return path2;
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 path2 = url.pathname.replace(/\/+$/, "");
6858
- path2 = path2.replace(/\/(?:chat\/completions|completions|responses|messages|models)$/, "");
6859
- if (!path2 || protocol === "anthropic" && !path2.endsWith("/v1")) {
6860
- path2 += "/v1";
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 = `${path2}/models`;
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
- log20.warn({ err }, "mcp image dropped (too large to fit the API limit)");
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
- log20.error({ err }, "mcp operation failed");
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 log21 = createChildLogger({ module: "mcp" });
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
- log21.error({ err }, "mcp operation failed");
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 readFileSync12,
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 path2 = canonicalPath(resolve8(this.memoryWorkDir, requested));
7393
- const insideMemory = this.memoryRoots.some((root) => isPathWithin(canonicalPath(root), path2));
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(path2) === ".md" ? { effect: "allow", reason: "Memory file update" } : {
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), path2) ? { effect: "allow", reason: "Memory task read" } : { effect: "deny", reason: "Read outside the memory task's scope" };
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 log22 = createChildLogger({ module: "file-state-cache" });
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
- log22.error({ err }, "file state cache operation failed");
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
- log22.error({ err }, "file state cache operation failed");
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 log23 = createChildLogger({ module: "memory" });
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 path2 = lockPath(memDir);
7575
- if (!existsSync10(path2)) {
7815
+ const path = lockPath(memDir);
7816
+ if (!existsSync10(path)) {
7576
7817
  return 0;
7577
7818
  }
7578
7819
  try {
7579
- return statSync8(path2).mtimeMs;
7820
+ return statSync8(path).mtimeMs;
7580
7821
  } catch {
7581
7822
  return 0;
7582
7823
  }
7583
7824
  }
7584
7825
  function tryAcquireLock(memDir) {
7585
- const path2 = lockPath(memDir);
7826
+ const path = lockPath(memDir);
7586
7827
  let mtimeMs;
7587
7828
  let holderPid;
7588
- if (existsSync10(path2)) {
7829
+ if (existsSync10(path)) {
7589
7830
  try {
7590
- mtimeMs = statSync8(path2).mtimeMs;
7591
- const raw = readFileSync12(path2, "utf-8").trim();
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
- log23.error({ err }, "failed to read consolidation lock file");
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(path2, String(process.pid));
7847
+ writeFileSync7(path, String(process.pid));
7607
7848
  try {
7608
- const verify = readFileSync12(path2, "utf-8").trim();
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 path2 = lockPath(memDir);
7859
+ const path = lockPath(memDir);
7619
7860
  try {
7620
7861
  if (priorMtime === 0) {
7621
- unlinkSync3(path2);
7862
+ unlinkSync3(path);
7622
7863
  return;
7623
7864
  }
7624
- writeFileSync7(path2, "");
7865
+ writeFileSync7(path, "");
7625
7866
  const t = priorMtime / 1e3;
7626
- utimesSync(path2, t, t);
7867
+ utimesSync(path, t, t);
7627
7868
  } catch (err) {
7628
- log23.error({ err }, "failed to rollback consolidation lock");
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 readFileSync14, existsSync as existsSync12, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
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 readFileSync13,
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 z11, { parse as parse4 } from "zod";
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 log24 = createChildLogger({ module: "memory" });
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 = z11.object({
7754
- selected_memories: z11.array(z11.string())
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(readFileSync13(fullPath, "utf-8"));
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
- log24.error({ err, path: fullPath }, "memory operation failed");
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
- log24.error({ err, path: dir }, "memory operation failed");
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
- log24.error({ err }, "memory operation failed");
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 (readFileSync13(indexPath, "utf-8") === content) {
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
- log24.error({ err }, "memory operation failed");
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
- log24.error({ err }, "memory operation failed");
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
- log24.error({ err, path: dir }, "memory operation failed");
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 = readFileSync13(mem.path, "utf-8");
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 path2 = m.filePath || m.filename;
8292
+ const path = m.filePath || m.filename;
8052
8293
  if (m.description) {
8053
- lines.push(`- ${scope}${tag}${path2} (${ts}): ${m.description}`);
8294
+ lines.push(`- ${scope}${tag}${path} (${ts}): ${m.description}`);
8054
8295
  } else {
8055
- lines.push(`- ${scope}${tag}${path2} (${ts})`);
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 = z11.looseObject({
8076
- name: z11.string().optional(),
8077
- description: z11.string().optional(),
8078
- type: z11.string().optional(),
8079
- metadata: z11.looseObject({
8080
- type: z11.string().optional()
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 = readFileSync14(join18(dir, file), "utf-8");
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 readFileSync15, existsSync as existsSync13 } from "fs";
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 log25 = createChildLogger({ module: "memory" });
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
- log25.error({ err }, "memory operation failed");
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
- log25.error({ err }, "memory operation failed");
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
- log25.error({ err }, "memory operation failed");
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 = readFileSync15(abs, "utf-8");
8654
+ data = readFileSync14(abs, "utf-8");
8414
8655
  } catch (err) {
8415
- log25.error({ err }, "memory operation failed");
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
- log25.error({ err }, "memory operation failed");
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 = readFileSync15(abs, "utf-8");
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
- log25.error({ err }, "memory operation failed");
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
- log25.error({ err }, "memory operation failed");
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
- log25.error({ err }, "memory operation failed");
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
- log25.error({ err }, "memory operation failed");
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, even with run_in_background (a tool restriction, not async execution). Persistent async teammates require TeamCreate and Agent team_name, with SendMessage for follow-ups. Worktrees isolate changes but do not merge them.
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 log26 = createChildLogger({ module: "prompt" });
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
- log26.error({ err }, "prompt operation failed");
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(log39) {
8743
- this.log = log39;
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 readFileSync23, existsSync as existsSync20, statSync as statSync11 } from "fs";
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 z20 from "zod";
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 readFileSync16, existsSync as existsSync14 } from "fs";
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 z12, { parse as parse5 } from "zod";
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 log27 = createChildLogger({ module: "subagent" });
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 = readFileSync16(join20(dir, file), "utf-8");
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
- log27.error({ err }, "subagent operation failed");
9171
+ log28.error({ err }, "subagent operation failed");
8936
9172
  continue;
8937
9173
  }
8938
9174
  }
8939
9175
  }
8940
- var YamlFrontmatterSchema3 = z12.looseObject({
8941
- name: z12.string(),
8942
- description: z12.string().optional(),
8943
- tools: z12.array(z12.string()).optional(),
8944
- disallowed_tools: z12.array(z12.string()).optional(),
8945
- system_prompt: z12.string().optional(),
8946
- max_turns: z12.number().optional(),
8947
- model: z12.string().optional(),
8948
- background: z12.boolean().optional(),
8949
- isolation: z12.literal("worktree").optional()
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
- log27.error({ err }, "subagent operation failed");
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 log28 = createChildLogger({ module: "teams" });
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
- let name = requested;
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(name, void 0, {
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
- const t = this.mgr.get(team) ?? this.mgr.create(team);
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
- log28.error({ err }, "teams operation failed");
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 log29 = createChildLogger({ module: "subagent" });
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: "Apply background-agent tool restrictions. One-shot calls currently wait and return their result inline; use team_name for persistent asynchronous teammates.",
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
- One-shot calls return results inline, including run_in_background calls (that flag restricts tools). Use team_name for persistent asynchronous teammates and SendMessage for their follow-up assignments. Do not predict results before receiving them.
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
- try {
9842
- const output = await this.spawnHandler(
9843
- definition,
9844
- effectivePrompt,
9845
- background || !!definition.background,
9846
- modelOverride,
9847
- workDirOverride,
9848
- ctx
9849
- );
9850
- return {
9851
- output: workDirOverride ? `${output}
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
- isError: false
9855
- };
9856
- } catch (err) {
9857
- return {
9858
- output: `Agent error: ${asErrorString(err)}${workDirOverride ? `
10219
+ isError: false
10220
+ };
10221
+ } catch (err) {
10222
+ return {
10223
+ output: `Agent error: ${asErrorString(err)}${workDirOverride ? `
9859
10224
  Worktree retained at: ${workDirOverride}` : ""}`,
9860
- isError: true
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
- const team = this.teamManager.get(teamName) ?? this.teamManager.create(teamName, void 0, {
9876
- leadAgentId: "lead",
9877
- description
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(memberName, teammatePrompt, runAgent, checker, this.teamProviderBaseUrl);
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-IMIU7CW2.js");
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
- log29.error({ err }, "subagent operation failed");
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" : ""}[Interrupted]`;
10481
+ return `${output}${output ? "\n\n" : ""}${SUBAGENT_INTERRUPTED_MARKER}`;
10071
10482
  }
10072
10483
  return output || "[No output]";
10073
10484
  case "error":
10074
- return output ? `${output}
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. Pass the teammate name as it appears in the from= field of a team-notification. Use this when you sent a teammate in the wrong direction \u2014 for example when the user changes requirements after you launched it.";
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
- if (!name) {
10131
- return { output: "Error: teammate is required", isError: true };
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 readFileSync21, rmSync } from "fs";
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 readFileSync17,
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 z13, { safeParse as safeParse3 } from "zod";
10184
- var log30 = createChildLogger({ module: "teams" });
10185
- var FileMailMessageSchema = z13.object({
10186
- from: z13.string(),
10187
- text: z13.string(),
10188
- timestamp: z13.string(),
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: z13.boolean().optional(),
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: z13.string().optional(),
10200
- requestId: z13.string().optional(),
10201
- approve: z13.boolean().optional()
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 = z13.looseObject({
10208
- errno: z13.number().optional(),
10209
- code: z13.string().optional(),
10210
- path: z13.string().optional(),
10211
- syscall: z13.string().optional()
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
- log30.error({ err }, "teams operation failed");
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
- log30.error({ err: err2 }, "teams operation failed");
10670
+ log31.error({ err: err2 }, "teams operation failed");
10242
10671
  }
10243
10672
  }
10244
10673
  } catch (err2) {
10245
- log30.error({ err: err2 }, "teams operation failed");
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
- log30.error({ err }, "teams operation failed");
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(readFileSync17(this.filePath, "utf-8"));
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
- log30.error({ err }, "teams operation failed");
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 z14 } from "zod";
10371
- var ToolActivitySchema = z14.object({
10372
- toolName: z14.string(),
10373
- input: z14.record(z14.string(), z14.unknown()),
10374
- activityDescription: z14.string()
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 AgentProgressSchema = z14.object({
10378
- toolUseCount: z14.number(),
10379
- tokenCount: z14.number(),
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: z14.array(ToolActivitySchema)
10816
+ recentActivities: z13.array(ToolActivitySchema)
10382
10817
  // circular buffer, max 5
10383
10818
  });
10384
- var TeammateUIStateSchema = z14.object({
10385
- name: z14.string(),
10386
- teamName: z14.string(),
10387
- status: z14.enum(["running", "idle", "completed", "failed", "stopped"]),
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
- startTime: z14.number(),
10390
- spinnerVerb: z14.string(),
10391
- lastMessage: z14.string().optional()
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: desc
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 = inputTokens + outputTokens;
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 readFileSync18, writeFileSync as writeFileSync11, mkdirSync as mkdirSync11, existsSync as existsSync16 } from "fs";
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 z15, { parse as parse6 } from "zod";
10459
- var SerializedTaskSchema = z15.object({
10460
- id: z15.string(),
10461
- title: z15.string(),
10462
- description: z15.string().default(""),
10463
- status: z15.string().default("pending"),
10464
- assignee: z15.string().default(""),
10465
- blocks: z15.array(z15.string()).default([]),
10466
- blocked_by: z15.array(z15.string()).default([]),
10467
- created_by: z15.string().default("")
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 = z15.object({
10470
- next_id: z15.number().int().positive().default(1),
10471
- tasks: z15.array(SerializedTaskSchema).default([])
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(path2) {
10478
- this.path = path2;
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(readFileSync18(this.path, "utf-8"));
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 readFileSync19, writeFileSync as writeFileSync12 } from "fs";
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 z16 from "zod";
10598
- var TeamMemberEntrySchema = z16.object({
10599
- agentId: z16.string(),
10600
- name: z16.string(),
10601
- agentType: z16.string().optional(),
10602
- model: z16.string().optional(),
10603
- joinedAt: z16.number(),
10604
- worktreePath: z16.string().optional(),
10605
- backendType: z16.string().optional(),
10606
- isActive: z16.boolean().optional()
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 = z16.object({
10609
- name: z16.string(),
10610
- description: z16.string().optional(),
10611
- createdAt: z16.number(),
10612
- leadAgentId: z16.string(),
10613
- members: z16.array(TeamMemberEntrySchema)
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 path2 = teamConfigPath(name);
10629
- if (!existsSync17(path2)) {
11088
+ const path = teamConfigPath(name);
11089
+ if (!existsSync17(path)) {
10630
11090
  return null;
10631
11091
  }
10632
11092
  try {
10633
- const raw = JSON.parse(readFileSync19(path2, "utf-8"));
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 path2 = teamConfigPath(name);
10642
- mkdirSync12(dirname8(path2), { recursive: true });
10643
- writeFileSync12(path2, JSON.stringify(file, null, 2), "utf-8");
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 readFileSync20, writeFileSync as writeFileSync13, existsSync as existsSync18 } from "fs";
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 z17, { parse as parse7 } from "zod";
10652
- var log31 = createChildLogger({ module: "teams" });
10653
- var TranscriptToolUseSchema = z17.object({
10654
- tool_use_id: z17.string(),
10655
- tool_name: z17.string(),
10656
- arguments: z17.record(z17.string(), z17.unknown())
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 = z17.object({
10659
- tool_use_id: z17.string(),
10660
- content: z17.string(),
10661
- is_error: z17.boolean().optional()
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 = z17.object({
10664
- role: z17.string(),
10665
- content: z17.string().optional(),
10666
- tool_uses: z17.array(TranscriptToolUseSchema).optional(),
10667
- tool_results: z17.array(TranscriptToolResultSchema).optional()
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 path2 = join23(dir, `${agentId}.json`);
11160
+ const path = join23(dir, `${agentId}.json`);
10701
11161
  const data = serializeConversation(conversation);
10702
- writeFileSync13(path2, JSON.stringify(data, null, 2), "utf-8");
10703
- return path2;
11162
+ writeFileSync13(path, JSON.stringify(data, null, 2), "utf-8");
11163
+ return path;
10704
11164
  }
10705
11165
  function loadTranscript(workDir, teamName, agentId) {
10706
- const path2 = join23(transcriptDir(workDir, teamName), `${agentId}.json`);
10707
- if (!existsSync18(path2)) {
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(readFileSync20(path2, "utf-8"));
10712
- const parsed = parse7(z17.array(TranscriptEntrySchema), raw);
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
- log31.error({ err }, "teams operation failed");
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 log32 = createChildLogger({ module: "teams" });
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
- if (event.toolName && event.args) {
11027
- recordToolUse(uiState.progress, event.toolName, event.args);
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
- if (event.usage) {
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 "stream_text":
11036
- if (event.text) {
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 = "completed";
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
- log32.error({ err }, "teams operation failed");
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
- log32.error({ err }, "teams operation failed");
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 = readFileSync21(getOrCreatePlanPath(this.workDir), "utf-8");
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
- for (const member of this.members.values()) {
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 readFileSync22, writeFileSync as writeFileSync14, mkdirSync as mkdirSync15, existsSync as existsSync19 } from "fs";
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 z18, { parse as parse8 } from "zod";
11335
- var log33 = createChildLogger({ module: "todo" });
11336
- var StoredTaskStatusSchema = z18.enum(["pending", "in_progress", "completed"]);
11337
- var StoredTaskSchema = z18.object({
11338
- id: z18.string(),
11339
- subject: z18.string(),
11340
- description: z18.string(),
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: z18.string().optional(),
11343
- activeForm: z18.string().optional(),
11344
- blocks: z18.array(z18.string()),
11345
- blockedBy: z18.array(z18.string()),
11346
- metadata: z18.record(z18.string(), z18.unknown())
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 = readFileSync22(this.filePath, "utf-8");
11850
+ const data = readFileSync21(this.filePath, "utf-8");
11360
11851
  const raw = JSON.parse(data);
11361
- const parsed = parse8(z18.array(StoredTaskSchema), raw);
11852
+ const parsed = parse8(z17.array(StoredTaskSchema), raw);
11362
11853
  return parsed;
11363
11854
  } catch (err) {
11364
- log33.error({ err }, "todo operation failed");
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 z19 } from "zod";
11480
- var QuestionOptionSchema = z19.object({
11481
- label: z19.string(),
11482
- description: z19.string().optional()
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 = z19.object({
11485
- question: z19.string(),
11486
- header: z19.string(),
11487
- options: z19.array(QuestionOptionSchema),
11488
- multiSelect: z19.boolean()
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(z19.object({ questions: z19.array(QuestionSchema) }), args);
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 log34 = createChildLogger({ module: "remote" });
11683
- var WsInboundSchema = z20.object({
11684
- type: z20.string(),
11685
- data: z20.unknown()
12173
+ var log35 = createChildLogger({ module: "remote" });
12174
+ var WsInboundSchema = z19.object({
12175
+ type: z19.string(),
12176
+ data: z19.unknown()
11686
12177
  });
11687
- var UserMessageSchema = z20.object({
11688
- content: z20.string()
12178
+ var UserMessageSchema = z19.object({
12179
+ content: z19.string()
11689
12180
  });
11690
- var PermissionResponseSchema = z20.object({
11691
- id: z20.string(),
11692
- response: z20.enum(["allow", "deny", "allowAlways"])
12181
+ var PermissionResponseSchema = z19.object({
12182
+ id: z19.string(),
12183
+ response: z19.enum(["allow", "deny", "allowAlways"])
11693
12184
  });
11694
- var AskUserResponseSchema = z20.object({
11695
- id: z20.string(),
11696
- answers: z20.record(z20.string(), z20.string())
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(path2) {
11710
- const cleanPath = normalize(path2).replace(/^(\.\.[/\\])+/, "");
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 = readFileSync23(fullPath);
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: () => this.teamManager.drainLeads(),
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
- log34.warn({ message: hookErr.message }, "hook validation warning");
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-QNMXPZ7C.js");
11885
- const { Agent: AgentClass } = await import("./agent-3OEC4SOB.js");
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
- registry.register(new AskUserQuestionTool(askUser));
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
- log34.error({ serverName, error }, "MCP server connection error");
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(log34);
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
- log34.warn("received non-JSON WebSocket message");
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
- log34.warn("received malformed WS message");
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
- log34.warn({ type: msg.type }, "unknown WS message type");
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
- log34.error({ err }, "failed to initialize agent");
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
- log34.error({ err }, "agent stream error");
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
- log34.error({ err }, "agent stream error in prompt command");
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
- log34.error({ err }, "agent stream error in plan command");
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
- log34.warn({ err }, "agent init deferred -- will retry on first message");
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 readFileSync24,
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 log35 = createChildLogger({ module: "skills" });
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 = readFileSync24(resolve10(this.workDir, source), "utf-8");
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
- log35.error({ err }, "skills operation failed");
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 z22 } from "zod";
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 z21 } from "zod";
13361
- var log36 = createChildLogger({ module: "vscode" });
13362
- var LockfileSchema = z21.object({
13363
- workspaceFolders: z21.array(z21.string()).optional(),
13364
- pid: z21.number().optional(),
13365
- ideName: z21.string().optional(),
13366
- transport: z21.enum(["ws", "sse"]).optional(),
13367
- authToken: z21.string().optional()
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
- log36.error({ err, filename }, "failed to read IDE lockfile");
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 log37 = createChildLogger({ module: "vscode" });
13520
- var AtMentionedSchema = z22.object({
13521
- method: z22.literal("at_mentioned"),
13522
- params: z22.object({
13523
- filePath: z22.string(),
13524
- lineStart: z22.number().optional(),
13525
- lineEnd: z22.number().optional()
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
- log37.error({ err, url: ide.url }, "failed to connect to IDE extension");
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
- log37.error({ err }, "failed to send ide_connected notification");
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
- log37.error({ err }, "failed to close IDE client");
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
- // Teammate completion reports land in the Lead's inbox; drained each turn as a system-reminder delivered to the Lead
13805
- notificationFn: () => teamManager.drainLeads(),
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 Promise.allSettled(teamManager.list().map((team) => team.stopAll()));
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
- process.exit(1);
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
- process.exit(1);
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 log38 = createChildLogger({ module: "teammate" });
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
- log38.error({ serverName, error }, "MCP connection error");
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
- log38.error({ err }, "MCP setup failed");
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
- log38.error({ err: event.error }, "agent error");
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
- log38.error({ err: event.error }, "agent error");
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
- log38.error({ err }, "MCP cleanup failed");
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,