tarsk 0.5.41 → 0.5.42

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 (82) hide show
  1. package/dist/index.js +1697 -493
  2. package/dist/public/assets/{account-view-D-dJ0y-D.js → account-view-BAm4Az95.js} +1 -1
  3. package/dist/public/assets/api-C44iPrsZ.js +1 -0
  4. package/dist/public/assets/browser-tab-DV6mFCbu.js +1 -0
  5. package/dist/public/assets/commit-dialog-DYdmiA4p.js +1 -0
  6. package/dist/public/assets/context-menu-BVP2PUoS.js +1 -0
  7. package/dist/public/assets/create-repo-dialog-DrGJUrGO.js +1 -0
  8. package/dist/public/assets/{dialogs-config-B-LZ4nOb.js → dialogs-config-BPzX42HH.js} +14 -14
  9. package/dist/public/assets/diff-view-BnNtDcmG.js +3 -0
  10. package/dist/public/assets/explorer-tab-view-OGWG1dH9.js +2 -0
  11. package/dist/public/assets/explorer-tree--Ho2K8-i.js +1 -0
  12. package/dist/public/assets/explorer-view-Dw0Kyxb8.js +1 -0
  13. package/dist/public/assets/git-history-dialog-BpOpNbze.js +1 -0
  14. package/dist/public/assets/git-ops-button-DKOfNm4s.js +2 -0
  15. package/dist/public/assets/history-view-DwSqsTGI.js +9 -0
  16. package/dist/public/assets/index-BdLH7zyV.css +1 -0
  17. package/dist/public/assets/index-BqaiWs7x.js +88 -0
  18. package/dist/public/assets/mcp-server-card-BbAUiI-X.js +1 -0
  19. package/dist/public/assets/merged-pr-dialog-Cvo93bRH.js +1 -0
  20. package/dist/public/assets/onboarding-BmBbv0At.js +1 -0
  21. package/dist/public/assets/project-settings-view-D-8Ua1Tu.js +1 -0
  22. package/dist/public/assets/providers-list-view-Gm13Fagd.js +1 -0
  23. package/dist/public/assets/pull-request-dialog-BkzdP5F2.js +1 -0
  24. package/dist/public/assets/pull-with-changes-dialog-DQU7jHYL.js +1 -0
  25. package/dist/public/assets/push-before-pr-dialog-DII3gUVw.js +1 -0
  26. package/dist/public/assets/radio-group-DkDRaQdF.js +1 -0
  27. package/dist/public/assets/react-vendor-BmFalJ0W.js +16 -0
  28. package/dist/public/assets/settings-general-view-Br89Vrbv.js +1 -0
  29. package/dist/public/assets/{settings-instructions-view-C57XGLha.js → settings-instructions-view-ByxlAb9U.js} +1 -1
  30. package/dist/public/assets/settings-list-DSrSoTv7.js +1 -0
  31. package/dist/public/assets/settings-mcp-servers-view-U_la7C8z.js +5 -0
  32. package/dist/public/assets/{settings-models-skeleton-ClrbJy_p.js → settings-models-skeleton-C5XrzM39.js} +1 -1
  33. package/dist/public/assets/settings-models-view-obQa4doG.js +1 -0
  34. package/dist/public/assets/settings-rules-view-Cd9WHEAE.js +8 -0
  35. package/dist/public/assets/settings-skills-view-B81njz98.js +2 -0
  36. package/dist/public/assets/settings-slash-commands-view-Ct8WC1Z0.js +1 -0
  37. package/dist/public/assets/settings-subagents-view-BwNTnGkz.js +2 -0
  38. package/dist/public/assets/{settings-system-prompt-view-Dl66VFaj.js → settings-system-prompt-view-C4uLn68y.js} +1 -1
  39. package/dist/public/assets/settings-view-DQW_9pnm.js +2 -0
  40. package/dist/public/assets/skeleton-HsHVnOKO.js +1 -0
  41. package/dist/public/assets/{terminal-panel-BGQxckfH.js → terminal-panel-wOltBu70.js} +2 -2
  42. package/dist/public/assets/{ui-components-C4RrfJEJ.js → ui-components-CaeQlGTd.js} +1 -1
  43. package/dist/public/assets/{utils-B7FQXlI6.js → utils-5mrXWXCs.js} +1 -1
  44. package/dist/public/assets/web-Gjw6klhQ.js +1 -0
  45. package/dist/public/assets/web-NsuCXAqU.js +1 -0
  46. package/dist/public/browser-preview-rpc.js +484 -0
  47. package/dist/public/index.html +7 -7
  48. package/package.json +1 -1
  49. package/dist/public/assets/api-DJaJqkc6.js +0 -1
  50. package/dist/public/assets/browser-tab-D7wEj-BD.js +0 -1
  51. package/dist/public/assets/commit-dialog-DdhGDH4F.js +0 -1
  52. package/dist/public/assets/context-menu-asf2g-KX.js +0 -1
  53. package/dist/public/assets/create-repo-dialog-Bltp6PKZ.js +0 -1
  54. package/dist/public/assets/diff-view-Bi545EPj.js +0 -3
  55. package/dist/public/assets/explorer-tab-view-B-P555GE.js +0 -2
  56. package/dist/public/assets/explorer-tree-CyXhVrI7.js +0 -1
  57. package/dist/public/assets/explorer-view-BAHDhIGN.js +0 -1
  58. package/dist/public/assets/git-history-dialog-Bci_iQmi.js +0 -1
  59. package/dist/public/assets/git-ops-button-1lum9QXI.js +0 -2
  60. package/dist/public/assets/history-view-CAkN8PCo.js +0 -9
  61. package/dist/public/assets/index-BLO68CQl.js +0 -69
  62. package/dist/public/assets/index-jIBJk8xl.css +0 -1
  63. package/dist/public/assets/mcp-server-card-DQUpkDFV.js +0 -1
  64. package/dist/public/assets/merged-pr-dialog-tHPrJ2CK.js +0 -1
  65. package/dist/public/assets/onboarding-Bbvb7kO4.js +0 -1
  66. package/dist/public/assets/project-settings-view-BRkHOq_q.js +0 -1
  67. package/dist/public/assets/providers-list-view-Dex879vv.js +0 -1
  68. package/dist/public/assets/pull-request-dialog-WHdmrW83.js +0 -1
  69. package/dist/public/assets/pull-with-changes-dialog-Ck3OwINV.js +0 -1
  70. package/dist/public/assets/push-before-pr-dialog-Bvqvz04U.js +0 -1
  71. package/dist/public/assets/radio-group-B0xvu5B9.js +0 -1
  72. package/dist/public/assets/react-vendor-D8PTA4EX.js +0 -16
  73. package/dist/public/assets/settings-general-view-lUxshNA9.js +0 -1
  74. package/dist/public/assets/settings-list-CHGKmGl_.js +0 -1
  75. package/dist/public/assets/settings-mcp-servers-view-DpqkhrgB.js +0 -5
  76. package/dist/public/assets/settings-models-view-QPEdnibD.js +0 -1
  77. package/dist/public/assets/settings-rules-view-WJU--cRq.js +0 -8
  78. package/dist/public/assets/settings-skills-view-mgHy4G_g.js +0 -2
  79. package/dist/public/assets/settings-slash-commands-view-LB5tVqy1.js +0 -1
  80. package/dist/public/assets/settings-subagents-view-QR2qlA_y.js +0 -2
  81. package/dist/public/assets/settings-view-BlVJv4Pz.js +0 -2
  82. package/dist/public/assets/skeleton-K-fVduHt.js +0 -1
package/dist/index.js CHANGED
@@ -50,7 +50,7 @@ import { homedir } from "node:os";
50
50
  import { join } from "node:path";
51
51
  import { spawn, spawnSync, execSync } from "child_process";
52
52
  function delay(ms) {
53
- return new Promise((resolve6) => setTimeout(resolve6, ms));
53
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
54
54
  }
55
55
  function mergePath(parentPath, shellPath) {
56
56
  const parent = (parentPath ?? "").split(":").filter(Boolean);
@@ -1296,6 +1296,7 @@ function parseGeneratedImagePayload(value) {
1296
1296
  function stripGeneratedImagePayload(payload) {
1297
1297
  const next = { ...payload };
1298
1298
  delete next.imageData;
1299
+ delete next.image_data;
1299
1300
  if (typeof next.imageUrl === "string" && isDataImageUrl(next.imageUrl)) {
1300
1301
  delete next.imageUrl;
1301
1302
  }
@@ -1317,12 +1318,13 @@ function stripGeneratedImageText(text) {
1317
1318
  return text;
1318
1319
  }
1319
1320
  }
1321
+ var IMAGE_RESULT_TOOL_NAMES = /* @__PURE__ */ new Set(["generate_image", "find_images", "browser"]);
1320
1322
  function sanitizeToolResultBlock(block, collected) {
1321
1323
  if (block.type !== "tool-result") {
1322
1324
  return block;
1323
1325
  }
1324
1326
  const toolName = typeof block.toolName === "string" ? block.toolName : "";
1325
- if (toolName !== "generate_image") {
1327
+ if (!IMAGE_RESULT_TOOL_NAMES.has(toolName)) {
1326
1328
  return block;
1327
1329
  }
1328
1330
  const blockRefs = parseGeneratedImagePayload(block);
@@ -1467,7 +1469,7 @@ function extractGeneratedImageFromEvents(events) {
1467
1469
  const parsed = JSON.parse(event.content.trim());
1468
1470
  const blocks = Array.isArray(parsed) ? parsed : [parsed];
1469
1471
  for (const block of blocks) {
1470
- if (block && typeof block === "object" && "type" in block && block.type === "tool-result" && block.toolName === "generate_image") {
1472
+ if (block && typeof block === "object" && "type" in block && block.type === "tool-result" && IMAGE_RESULT_TOOL_NAMES.has(block.toolName)) {
1471
1473
  const refs = parseGeneratedImagePayload(block);
1472
1474
  if (refs?.imageUrl) {
1473
1475
  collected.imageUrl = refs.imageUrl;
@@ -1490,11 +1492,29 @@ function extractGeneratedImageFromEvents(events) {
1490
1492
  return collected;
1491
1493
  }
1492
1494
 
1495
+ // ../shared/dist/image-url.js
1496
+ function isAbsoluteHttpOrDataUrl(url) {
1497
+ return !url || url.startsWith("data:") || /^https?:\/\//i.test(url);
1498
+ }
1499
+ function buildThreadExplorerMediaUrl(threadId, projectPath, origin = "http://localhost:641") {
1500
+ const base = origin.replace(/\/$/, "");
1501
+ return `${base}/api/threads/${encodeURIComponent(threadId)}/explorer/media?path=${encodeURIComponent(projectPath)}`;
1502
+ }
1503
+ function resolveAbsoluteImageUrl(imageUrl, baseUrl) {
1504
+ if (isAbsoluteHttpOrDataUrl(imageUrl)) {
1505
+ return imageUrl;
1506
+ }
1507
+ const origin = baseUrl.replace(/\/$/, "");
1508
+ const path7 = imageUrl.startsWith("/") ? imageUrl : `/${imageUrl}`;
1509
+ return `${origin}${path7}`;
1510
+ }
1511
+
1493
1512
  // ../shared/dist/tool-display.js
1494
1513
  var toolDisplayRegistry = [
1495
1514
  { name: "agent", displayName: "Subagent", activeDisplayName: "Subagent" },
1496
1515
  { name: "ask_user", displayName: "Ask", activeDisplayName: "Asking" },
1497
1516
  { name: "bash", displayName: "Ran", activeDisplayName: "Running" },
1517
+ { name: "browser", displayName: "Browser", activeDisplayName: "Browsing" },
1498
1518
  { name: "code_search", displayName: "Code Search", activeDisplayName: "Searching" },
1499
1519
  { name: "edit", displayName: "Edit", activeDisplayName: "Editing" },
1500
1520
  { name: "execute_skill_script", displayName: "Run Script", activeDisplayName: "Running Script" },
@@ -1642,9 +1662,27 @@ function getExplorerFileMediaType(path7, name) {
1642
1662
  return null;
1643
1663
  }
1644
1664
 
1665
+ // ../shared/dist/browser-tool.js
1666
+ var BROWSER_HOST_ACTIONS = [
1667
+ "reload",
1668
+ "go_back",
1669
+ "go_forward",
1670
+ "get_state",
1671
+ "set_viewport"
1672
+ ];
1673
+ function isBrowserHostAction(action) {
1674
+ return BROWSER_HOST_ACTIONS.includes(action);
1675
+ }
1676
+
1677
+ // ../shared/dist/tmp-images.js
1678
+ var TARSK_TMP_IMAGES_DIR = ".tarsk/tmp-images";
1679
+ function buildTmpImagePath(filename) {
1680
+ return `${TARSK_TMP_IMAGES_DIR}/${filename}`;
1681
+ }
1682
+
1645
1683
  // src/server.ts
1646
1684
  import fs3 from "fs";
1647
- import { Hono as Hono26 } from "hono";
1685
+ import { Hono as Hono27 } from "hono";
1648
1686
  import { cors } from "hono/cors";
1649
1687
  import open3 from "open";
1650
1688
  import path5 from "path";
@@ -2453,7 +2491,7 @@ var bashSchema = Type.Object({
2453
2491
  });
2454
2492
  var defaultBashOperations = {
2455
2493
  exec: (command, cwd, { onData, signal, timeout, env }) => {
2456
- return new Promise((resolve6, reject) => {
2494
+ return new Promise((resolve7, reject) => {
2457
2495
  const { shell, args: args2 } = getShellConfig();
2458
2496
  if (!existsSync3(cwd)) {
2459
2497
  reject(new Error(`Working directory does not exist: ${cwd}`));
@@ -2502,7 +2540,7 @@ var defaultBashOperations = {
2502
2540
  reject(new Error(`timeout:${timeout}`));
2503
2541
  return;
2504
2542
  }
2505
- resolve6({ exitCode: code });
2543
+ resolve7({ exitCode: code });
2506
2544
  });
2507
2545
  });
2508
2546
  }
@@ -2525,7 +2563,7 @@ ${command}` : command;
2525
2563
  if (shellPermissionGate) {
2526
2564
  await shellPermissionGate.ensureAllowed(toolCallId, resolvedCommand, "bash", signal);
2527
2565
  }
2528
- return new Promise((resolve6, reject) => {
2566
+ return new Promise((resolve7, reject) => {
2529
2567
  let tempFilePath;
2530
2568
  let tempFileStream;
2531
2569
  let totalBytes = 0;
@@ -2594,7 +2632,7 @@ ${command}` : command;
2594
2632
  Command exited with code ${exitCode}`;
2595
2633
  reject(new Error(outputText));
2596
2634
  } else {
2597
- resolve6({ content: [{ type: "text", text: outputText }], details });
2635
+ resolve7({ content: [{ type: "text", text: outputText }], details });
2598
2636
  }
2599
2637
  }).catch((err) => {
2600
2638
  tarskDebugLog(`[ai] bash-end: ${resolvedCommand}`);
@@ -2750,7 +2788,7 @@ function generateDiffString(oldContent, newContent, contextLines = 4) {
2750
2788
 
2751
2789
  // src/tools/tool-helpers.ts
2752
2790
  async function withAbortSignal(signal, operation) {
2753
- return new Promise((resolve6, reject) => {
2791
+ return new Promise((resolve7, reject) => {
2754
2792
  if (signal?.aborted) {
2755
2793
  reject(new Error("Operation aborted"));
2756
2794
  return;
@@ -2769,7 +2807,7 @@ async function withAbortSignal(signal, operation) {
2769
2807
  const abortCheck = () => aborted;
2770
2808
  operation(abortCheck).then((result) => {
2771
2809
  cleanup();
2772
- if (!aborted) resolve6(result);
2810
+ if (!aborted) resolve7(result);
2773
2811
  }).catch((error) => {
2774
2812
  cleanup();
2775
2813
  if (!aborted) reject(error);
@@ -2892,7 +2930,7 @@ function createFindTool(cwd, options) {
2892
2930
  description: `Search for files by glob pattern. Returns matching file paths relative to the search directory. Output truncated to ${DEFAULT_LIMIT} results or ${DEFAULT_MAX_BYTES / 1024}KB.`,
2893
2931
  parameters: findSchema,
2894
2932
  execute: async (_toolCallId, { pattern, path: searchDir, limit }, signal) => {
2895
- return new Promise((resolve6, reject) => {
2933
+ return new Promise((resolve7, reject) => {
2896
2934
  if (signal?.aborted) {
2897
2935
  reject(new Error("Operation aborted"));
2898
2936
  return;
@@ -2925,7 +2963,7 @@ function createFindTool(cwd, options) {
2925
2963
  });
2926
2964
  signal?.removeEventListener("abort", onAbort);
2927
2965
  if (results.length === 0) {
2928
- resolve6({
2966
+ resolve7({
2929
2967
  content: [{ type: "text", text: "No files found matching pattern" }],
2930
2968
  details: void 0
2931
2969
  });
@@ -2956,7 +2994,7 @@ function createFindTool(cwd, options) {
2956
2994
  if (notices.length > 0) resultOutput += `
2957
2995
 
2958
2996
  [${notices.join(". ")}]`;
2959
- resolve6({
2997
+ resolve7({
2960
2998
  content: [{ type: "text", text: resultOutput }],
2961
2999
  details: Object.keys(details).length > 0 ? details : void 0
2962
3000
  });
@@ -2981,7 +3019,7 @@ import path2 from "path";
2981
3019
  // src/tools/resolve-bin.ts
2982
3020
  init_utils();
2983
3021
  function resolveBin(name) {
2984
- return new Promise((resolve6) => {
3022
+ return new Promise((resolve7) => {
2985
3023
  const cmd = process.platform === "win32" ? "where" : "which";
2986
3024
  const args2 = process.platform === "win32" ? [name + ".exe", name] : [name];
2987
3025
  try {
@@ -2989,13 +3027,13 @@ function resolveBin(name) {
2989
3027
  if (result.status === 0 && result.stdout) {
2990
3028
  const first = (typeof result.stdout === "string" ? result.stdout : result.stdout?.toString() || "").trim().split(/\r?\n/)[0]?.trim();
2991
3029
  if (first) {
2992
- resolve6(first);
3030
+ resolve7(first);
2993
3031
  return;
2994
3032
  }
2995
3033
  }
2996
3034
  } catch {
2997
3035
  }
2998
- resolve6(null);
3036
+ resolve7(null);
2999
3037
  });
3000
3038
  }
3001
3039
 
@@ -3034,7 +3072,7 @@ function createSimpleGrepTool(cwd, options) {
3034
3072
  description: `Search file contents for a pattern. Returns matching lines with file paths and line numbers. Respects .gitignore. Output truncated to ${DEFAULT_LIMIT2} matches or ${DEFAULT_MAX_BYTES / 1024}KB. Long lines truncated to ${GREP_MAX_LINE_LENGTH} chars.`,
3035
3073
  parameters: grepSchema,
3036
3074
  execute: async (_toolCallId, { pattern, path: searchDir, glob: glob3, ignoreCase, literal, context, limit }, signal) => {
3037
- return new Promise((resolve6, reject) => {
3075
+ return new Promise((resolve7, reject) => {
3038
3076
  if (signal?.aborted) {
3039
3077
  reject(new Error("Operation aborted"));
3040
3078
  return;
@@ -3183,7 +3221,7 @@ function createSimpleGrepTool(cwd, options) {
3183
3221
  }
3184
3222
  if (matchCount === 0) {
3185
3223
  settle(
3186
- () => resolve6({
3224
+ () => resolve7({
3187
3225
  content: [{ type: "text", text: "No matches found" }],
3188
3226
  details: void 0
3189
3227
  })
@@ -3219,7 +3257,7 @@ function createSimpleGrepTool(cwd, options) {
3219
3257
 
3220
3258
  [${notices.join(". ")}]`;
3221
3259
  settle(
3222
- () => resolve6({
3260
+ () => resolve7({
3223
3261
  content: [{ type: "text", text: output }],
3224
3262
  details: Object.keys(details).length > 0 ? details : void 0
3225
3263
  })
@@ -4246,7 +4284,7 @@ function createLsTool(cwd, options) {
4246
4284
  description: `List directory contents. Returns entries sorted alphabetically, with '/' suffix for directories. Output truncated to ${DEFAULT_LIMIT4} entries or ${DEFAULT_MAX_BYTES / 1024}KB.`,
4247
4285
  parameters: lsSchema,
4248
4286
  execute: async (_toolCallId, { path: dirPathArg, limit }, signal) => {
4249
- return new Promise((resolve6, reject) => {
4287
+ return new Promise((resolve7, reject) => {
4250
4288
  if (signal?.aborted) {
4251
4289
  reject(new Error("Operation aborted"));
4252
4290
  return;
@@ -4294,7 +4332,7 @@ function createLsTool(cwd, options) {
4294
4332
  }
4295
4333
  signal?.removeEventListener("abort", onAbort);
4296
4334
  if (results.length === 0) {
4297
- resolve6({
4335
+ resolve7({
4298
4336
  content: [{ type: "text", text: "(empty directory)" }],
4299
4337
  details: void 0
4300
4338
  });
@@ -4318,7 +4356,7 @@ function createLsTool(cwd, options) {
4318
4356
  if (notices.length > 0) output += `
4319
4357
 
4320
4358
  [${notices.join(". ")}]`;
4321
- resolve6({
4359
+ resolve7({
4322
4360
  content: [{ type: "text", text: output }],
4323
4361
  details: Object.keys(details).length > 0 ? details : void 0
4324
4362
  });
@@ -4444,7 +4482,7 @@ function createWriteTool(cwd, options) {
4444
4482
  validatePathWithinCwd(absolutePath, cwd);
4445
4483
  const dir = dirname(absolutePath);
4446
4484
  return new Promise(
4447
- (resolve6, reject) => {
4485
+ (resolve7, reject) => {
4448
4486
  if (signal?.aborted) {
4449
4487
  reject(new Error("Operation aborted"));
4450
4488
  return;
@@ -4462,7 +4500,7 @@ function createWriteTool(cwd, options) {
4462
4500
  await ops.writeFile(absolutePath, content);
4463
4501
  if (aborted) return;
4464
4502
  if (signal) signal.removeEventListener("abort", onAbort);
4465
- resolve6({
4503
+ resolve7({
4466
4504
  content: [
4467
4505
  { type: "text", text: `Successfully wrote ${content.length} bytes to ${path7}` }
4468
4506
  ],
@@ -4514,7 +4552,7 @@ async function executeScript(scriptPath, args2, cwd, timeout = 30) {
4514
4552
  if (!interpreter) {
4515
4553
  throw new Error(`Unsupported script type: ${scriptPath}`);
4516
4554
  }
4517
- return new Promise((resolve6, reject) => {
4555
+ return new Promise((resolve7, reject) => {
4518
4556
  const child = spawnProcess(interpreter.command, [...interpreter.args, ...args2], {
4519
4557
  cwd,
4520
4558
  timeout: timeout * 1e3
@@ -4546,7 +4584,7 @@ async function executeScript(scriptPath, args2, cwd, timeout = 30) {
4546
4584
  reject(new Error(`Script execution timed out after ${timeout}s`));
4547
4585
  return;
4548
4586
  }
4549
- resolve6({ stdout, stderr, exitCode: code });
4587
+ resolve7({ stdout, stderr, exitCode: code });
4550
4588
  });
4551
4589
  });
4552
4590
  }
@@ -4825,9 +4863,9 @@ function createAskUserTool() {
4825
4863
  if (signal?.aborted) {
4826
4864
  throw new Error("Operation aborted");
4827
4865
  }
4828
- const answer = await new Promise((resolve6, reject) => {
4866
+ const answer = await new Promise((resolve7, reject) => {
4829
4867
  pendingQuestions.set(toolCallId, {
4830
- resolve: resolve6,
4868
+ resolve: resolve7,
4831
4869
  reject,
4832
4870
  question,
4833
4871
  options,
@@ -6555,8 +6593,46 @@ async function executeGenerateImage(options) {
6555
6593
 
6556
6594
  // src/tools/find-images.ts
6557
6595
  import { Type as Type15 } from "@sinclair/typebox";
6596
+ import { join as join9 } from "path";
6597
+
6598
+ // src/core/server-origin.ts
6599
+ function getServerOrigin() {
6600
+ const port = process.env.PORT ?? "641";
6601
+ return `http://localhost:${port}`;
6602
+ }
6603
+
6604
+ // src/tools/tool-image-storage.ts
6558
6605
  import { mkdir as fsMkdir3, writeFile as fsWriteFile4 } from "fs/promises";
6559
6606
  import { dirname as dirname3 } from "path";
6607
+ function decodeBase64ImageData(imageData) {
6608
+ const binary = atob(imageData);
6609
+ const bytes = new Uint8Array(binary.length);
6610
+ for (let i = 0; i < binary.length; i++) {
6611
+ bytes[i] = binary.charCodeAt(i);
6612
+ }
6613
+ return bytes;
6614
+ }
6615
+ async function saveToolImage(options) {
6616
+ const bytes = decodeBase64ImageData(options.imageData);
6617
+ const absolutePath = resolveToCwd(options.relativePath, options.cwd);
6618
+ validatePathWithinCwd(absolutePath, options.cwd);
6619
+ await fsMkdir3(dirname3(absolutePath), { recursive: true });
6620
+ await fsWriteFile4(absolutePath, bytes);
6621
+ const saved = {
6622
+ savedPath: options.relativePath,
6623
+ sizeInBytes: bytes.length
6624
+ };
6625
+ if (options.threadId && options.serverOrigin) {
6626
+ saved.imageUrl = buildThreadExplorerMediaUrl(
6627
+ options.threadId,
6628
+ options.relativePath,
6629
+ options.serverOrigin
6630
+ );
6631
+ }
6632
+ return saved;
6633
+ }
6634
+
6635
+ // src/tools/find-images.ts
6560
6636
  var findImagesSchema = Type15.Object({
6561
6637
  query: Type15.String({ description: "Search term for the image (e.g. 'sunset over mountains')" }),
6562
6638
  size: Type15.Optional(
@@ -6578,15 +6654,36 @@ var findImagesSchema = Type15.Object({
6578
6654
  ),
6579
6655
  save_to_file: Type15.Optional(
6580
6656
  Type15.String({
6581
- description: "File path relative to the project root to save the image to (e.g. 'assets/hero.jpg'). Parent directories are created automatically. If omitted the image is returned as base64 data only."
6657
+ description: "File path relative to the project root to save the image to (e.g. 'assets/hero.jpg'). Parent directories are created automatically. If omitted the image is saved under .tarsk/tmp-images/."
6582
6658
  })
6583
6659
  )
6584
6660
  });
6585
- function createFindImagesTool(cwd) {
6661
+ function extensionForContentType(contentType) {
6662
+ if (contentType.includes("png")) {
6663
+ return ".png";
6664
+ }
6665
+ if (contentType.includes("webp")) {
6666
+ return ".webp";
6667
+ }
6668
+ if (contentType.includes("gif")) {
6669
+ return ".gif";
6670
+ }
6671
+ if (contentType.includes("jpeg") || contentType.includes("jpg")) {
6672
+ return ".jpg";
6673
+ }
6674
+ return ".jpg";
6675
+ }
6676
+ function defaultSavePath(query, contentType) {
6677
+ const slug = query.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "image";
6678
+ const id = Math.random().toString(36).slice(2, 8);
6679
+ return buildTmpImagePath(`${slug}-${id}${extensionForContentType(contentType)}`);
6680
+ }
6681
+ function createFindImagesTool(options = {}) {
6682
+ const { cwd, threadId, serverOrigin = getServerOrigin() } = options;
6586
6683
  return {
6587
6684
  name: "find_images",
6588
6685
  label: "find_images",
6589
- description: "Find a stock image matching a search query. Returns the image as base64 data and optionally saves it to a file. Use this when the user needs an image and the user has not requested it be generated.",
6686
+ description: "Find a stock image matching a search query. Returns a URL served by the Tarsk API and optionally saves the image to a file. Use this when the user needs an image and the user has not requested it be generated.",
6590
6687
  parameters: findImagesSchema,
6591
6688
  execute: async (_toolCallId, { query, size, save_to_file }, signal) => {
6592
6689
  return withAbortSignal(signal, async (isAborted) => {
@@ -6607,52 +6704,889 @@ function createFindImagesTool(cwd) {
6607
6704
  };
6608
6705
  }
6609
6706
  if (isAborted()) return { content: [], details: void 0 };
6610
- let savedPath;
6611
- if (cwd && save_to_file) {
6612
- try {
6613
- const absolutePath = resolveToCwd(save_to_file, cwd);
6707
+ if (!cwd) {
6708
+ return {
6709
+ content: [
6710
+ {
6711
+ type: "text",
6712
+ text: "Image search succeeded but no project directory is available to save the image."
6713
+ }
6714
+ ],
6715
+ details: void 0
6716
+ };
6717
+ }
6718
+ const relativePath = save_to_file ?? defaultSavePath(query, contentType);
6719
+ let saved;
6720
+ try {
6721
+ if (save_to_file) {
6722
+ const absolutePath = resolveToCwd(relativePath, cwd);
6614
6723
  validatePathWithinCwd(absolutePath, cwd);
6615
- const binary = atob(imageData);
6616
- const bytes = new Uint8Array(binary.length);
6617
- for (let i = 0; i < binary.length; i++) {
6618
- bytes[i] = binary.charCodeAt(i);
6619
- }
6620
- await fsMkdir3(dirname3(absolutePath), { recursive: true });
6621
- await fsWriteFile4(absolutePath, bytes);
6622
- savedPath = save_to_file;
6623
- console.log(`[find_images] Saved image to ${absolutePath}`);
6624
- } catch (saveError) {
6625
- const errMsg = saveError instanceof Error ? saveError.message : String(saveError);
6626
- return {
6627
- content: [
6628
- {
6629
- type: "text",
6630
- text: `Image was found but failed to save to "${save_to_file}": ${errMsg}`
6631
- }
6632
- ],
6633
- details: { imageData, contentType }
6634
- };
6635
6724
  }
6725
+ saved = await saveToolImage({
6726
+ imageData,
6727
+ cwd,
6728
+ relativePath,
6729
+ threadId,
6730
+ serverOrigin
6731
+ });
6732
+ console.log(`[find_images] Saved image to ${join9(cwd, saved.savedPath)}`);
6733
+ } catch (saveError) {
6734
+ const errMsg = saveError instanceof Error ? saveError.message : String(saveError);
6735
+ return {
6736
+ content: [
6737
+ {
6738
+ type: "text",
6739
+ text: `Image was found but failed to save to "${relativePath}": ${errMsg}`
6740
+ }
6741
+ ],
6742
+ details: void 0
6743
+ };
6744
+ }
6745
+ const resultPayload = {
6746
+ contentType,
6747
+ savedPath: saved.savedPath,
6748
+ sizeInBytes: saved.sizeInBytes
6749
+ };
6750
+ if (saved.imageUrl) {
6751
+ resultPayload.imageUrl = saved.imageUrl;
6752
+ }
6753
+ return {
6754
+ content: [{ type: "text", text: buildGenerateImageToolText(resultPayload) }],
6755
+ details: saved.imageUrl ? { imageUrl: saved.imageUrl } : void 0
6756
+ };
6757
+ });
6758
+ }
6759
+ };
6760
+ }
6761
+
6762
+ // src/tools/browser.ts
6763
+ import { Type as Type16 } from "@sinclair/typebox";
6764
+
6765
+ // src/core/browser-preview-bridge.ts
6766
+ async function invokePreviewApi(webview, method, params) {
6767
+ if (!webview.rpc?.request?.evaluateJavascriptWithResponse) {
6768
+ throw new Error("Browser preview webview RPC is not available");
6769
+ }
6770
+ const paramsLiteral = JSON.stringify(params ?? null);
6771
+ const script = `return (async () => {
6772
+ const api = window.__tarskBrowserPreview;
6773
+ if (!api) {
6774
+ throw new Error("Browser preview API is not available");
6775
+ }
6776
+ const fn = api[${JSON.stringify(method)}];
6777
+ if (typeof fn !== "function") {
6778
+ throw new Error("Unknown browser preview API method: ${method}");
6779
+ }
6780
+ return await fn.call(api, ${paramsLiteral});
6781
+ })()`;
6782
+ return webview.rpc.request.evaluateJavascriptWithResponse({ script });
6783
+ }
6784
+
6785
+ // src/core/browser-native-screenshot.ts
6786
+ import { execFile } from "node:child_process";
6787
+ import { randomUUID as randomUUID3 } from "node:crypto";
6788
+ import { readFile as readFile5, unlink } from "node:fs/promises";
6789
+ import { tmpdir as tmpdir2 } from "node:os";
6790
+ import { join as join10 } from "node:path";
6791
+ import { setTimeout as sleep } from "node:timers/promises";
6792
+ import { promisify } from "node:util";
6793
+
6794
+ // src/core/browser-view-access.ts
6795
+ var electrobunModule = null;
6796
+ var electrobunLoadAttempted = false;
6797
+ async function loadElectrobunBrowserView() {
6798
+ if (electrobunLoadAttempted) {
6799
+ return electrobunModule;
6800
+ }
6801
+ electrobunLoadAttempted = true;
6802
+ try {
6803
+ electrobunModule = await import("electrobun/bun");
6804
+ } catch {
6805
+ electrobunModule = null;
6806
+ }
6807
+ return electrobunModule;
6808
+ }
6809
+ function getBrowserViewById(webviewId) {
6810
+ if (!electrobunModule) {
6811
+ return null;
6812
+ }
6813
+ return electrobunModule.BrowserView.getById(webviewId) ?? null;
6814
+ }
6815
+ function getBrowserWindowById(windowId) {
6816
+ if (!electrobunModule) {
6817
+ return null;
6818
+ }
6819
+ return electrobunModule.BrowserWindow.getById(windowId) ?? null;
6820
+ }
6821
+ function waitForBrowserEvent(webview, eventName, timeoutMs) {
6822
+ return new Promise((resolve7, reject) => {
6823
+ const timeout = setTimeout(() => {
6824
+ reject(new Error(`Timed out waiting for ${eventName} after ${timeoutMs}ms`));
6825
+ }, timeoutMs);
6826
+ webview.on(eventName, () => {
6827
+ clearTimeout(timeout);
6828
+ resolve7();
6829
+ });
6830
+ });
6831
+ }
6832
+
6833
+ // src/core/browser-native-screenshot.ts
6834
+ var execFileAsync = promisify(execFile);
6835
+ var NATIVE_FOCUS_DELAY_MS = 75;
6836
+ var PNG_SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
6837
+ var SCREEN_RECORDING_HINT = "Grant Screen Recording permission to Tarsk in System Settings \u2192 Privacy & Security \u2192 Screen Recording, then restart Tarsk.";
6838
+ function parsePngDimensions(bytes) {
6839
+ if (bytes.length < 24 || !bytes.subarray(0, 8).equals(PNG_SIGNATURE)) {
6840
+ return null;
6841
+ }
6842
+ return {
6843
+ width: bytes.readUInt32BE(16),
6844
+ height: bytes.readUInt32BE(20)
6845
+ };
6846
+ }
6847
+ function pngHasImageData(bytes) {
6848
+ return bytes.includes(Buffer.from("IDAT")) && bytes.includes(Buffer.from("IEND"));
6849
+ }
6850
+ function parseJpegDimensions(bytes) {
6851
+ if (bytes.length < 4 || bytes[0] !== 255 || bytes[1] !== 216) {
6852
+ return null;
6853
+ }
6854
+ let offset = 2;
6855
+ while (offset + 9 < bytes.length) {
6856
+ if (bytes[offset] !== 255) {
6857
+ return null;
6858
+ }
6859
+ const marker = bytes[offset + 1];
6860
+ if (marker === 217) {
6861
+ break;
6862
+ }
6863
+ const segmentLength = bytes.readUInt16BE(offset + 2);
6864
+ if (segmentLength < 2 || offset + 2 + segmentLength > bytes.length) {
6865
+ return null;
6866
+ }
6867
+ const isStartOfFrame = marker === 192 || marker === 193 || marker === 194 || marker === 195;
6868
+ if (isStartOfFrame) {
6869
+ return {
6870
+ height: bytes.readUInt16BE(offset + 5),
6871
+ width: bytes.readUInt16BE(offset + 7)
6872
+ };
6873
+ }
6874
+ offset += 2 + segmentLength;
6875
+ }
6876
+ return null;
6877
+ }
6878
+ function isTruncatedCapture(bytes, width, height) {
6879
+ if (width >= 64 || height >= 64) {
6880
+ return bytes.length < 1024;
6881
+ }
6882
+ return bytes.length < 68;
6883
+ }
6884
+ function validateCapturedImage(bytes, format) {
6885
+ if (bytes.length === 0) {
6886
+ return { error: "macOS screencapture produced an empty image file." };
6887
+ }
6888
+ if (format === "png") {
6889
+ const dimensions2 = parsePngDimensions(bytes);
6890
+ if (!dimensions2) {
6891
+ return { error: "macOS screencapture produced an invalid PNG file." };
6892
+ }
6893
+ if (!pngHasImageData(bytes) || isTruncatedCapture(bytes, dimensions2.width, dimensions2.height)) {
6894
+ return {
6895
+ error: `macOS screencapture wrote a truncated PNG (${bytes.length} bytes for ${dimensions2.width}x${dimensions2.height}). ` + SCREEN_RECORDING_HINT
6896
+ };
6897
+ }
6898
+ return { ...dimensions2, format: "png" };
6899
+ }
6900
+ const dimensions = parseJpegDimensions(bytes);
6901
+ if (!dimensions) {
6902
+ return { error: "macOS screencapture produced an invalid JPEG file." };
6903
+ }
6904
+ if (isTruncatedCapture(bytes, dimensions.width, dimensions.height)) {
6905
+ return {
6906
+ error: `macOS screencapture wrote a truncated JPEG (${bytes.length} bytes for ${dimensions.width}x${dimensions.height}). ` + SCREEN_RECORDING_HINT
6907
+ };
6908
+ }
6909
+ return { ...dimensions, format: "jpeg" };
6910
+ }
6911
+ function isValidRect(rect) {
6912
+ return Number.isFinite(rect.x) && Number.isFinite(rect.y) && rect.width > 0 && rect.height > 0;
6913
+ }
6914
+ async function resolveViewFrame(view) {
6915
+ if (view.getFrame) {
6916
+ return await view.getFrame();
6917
+ }
6918
+ if (view.frame) {
6919
+ return view.frame;
6920
+ }
6921
+ return { x: 0, y: 0, width: 0, height: 0 };
6922
+ }
6923
+ async function captureMacOSRect(options) {
6924
+ const formatFlag = options.format === "jpeg" ? "jpg" : "png";
6925
+ const region = `${Math.round(options.rect.x)},${Math.round(options.rect.y)},${Math.round(options.rect.width)},${Math.round(options.rect.height)}`;
6926
+ await execFileAsync("screencapture", ["-x", "-t", formatFlag, "-R", region, options.path]);
6927
+ }
6928
+ async function resolveCaptureRect(webviewId) {
6929
+ const view = getBrowserViewById(webviewId);
6930
+ if (!view) {
6931
+ throw new Error(`Browser preview webview ${webviewId} is not available`);
6932
+ }
6933
+ const window = getBrowserWindowById(view.windowId);
6934
+ if (!window) {
6935
+ throw new Error(`Electrobun window ${view.windowId} is not available for screenshot capture`);
6936
+ }
6937
+ const windowFrame = window.getFrame();
6938
+ const viewFrame = await resolveViewFrame(view);
6939
+ if (!isValidRect(windowFrame)) {
6940
+ throw new Error(`Window ${window.id} does not expose a usable frame for screenshots`);
6941
+ }
6942
+ if (!isValidRect(viewFrame)) {
6943
+ throw new Error(`View ${view.id} does not expose a usable frame for screenshots`);
6944
+ }
6945
+ return {
6946
+ rect: {
6947
+ x: windowFrame.x + viewFrame.x,
6948
+ y: windowFrame.y + viewFrame.y,
6949
+ width: viewFrame.width,
6950
+ height: viewFrame.height
6951
+ },
6952
+ viewId: view.id,
6953
+ windowId: window.id
6954
+ };
6955
+ }
6956
+ async function captureNativeBrowserScreenshot(webviewId, format) {
6957
+ if (process.platform !== "darwin") {
6958
+ return {
6959
+ ok: false,
6960
+ reason: "Native Electrobun screenshots are only supported on macOS."
6961
+ };
6962
+ }
6963
+ const outputPath = join10(
6964
+ tmpdir2(),
6965
+ `tarsk-browser-screenshot-${randomUUID3()}.${format === "jpeg" ? "jpg" : "png"}`
6966
+ );
6967
+ try {
6968
+ const target = await resolveCaptureRect(webviewId);
6969
+ const window = getBrowserWindowById(target.windowId);
6970
+ window?.focus();
6971
+ await sleep(NATIVE_FOCUS_DELAY_MS);
6972
+ await captureMacOSRect({
6973
+ format,
6974
+ path: outputPath,
6975
+ rect: target.rect
6976
+ });
6977
+ const imageBytes = await readFile5(outputPath);
6978
+ const validated = validateCapturedImage(imageBytes, format);
6979
+ if ("error" in validated) {
6980
+ return {
6981
+ ok: false,
6982
+ reason: validated.error
6983
+ };
6984
+ }
6985
+ return {
6986
+ ok: true,
6987
+ result: {
6988
+ format: validated.format,
6989
+ width: validated.width,
6990
+ height: validated.height,
6991
+ image_data: imageBytes.toString("base64"),
6992
+ capture_method: "native-screencapture",
6993
+ window_id: target.windowId,
6994
+ view_id: target.viewId
6995
+ }
6996
+ };
6997
+ } catch (error) {
6998
+ const message = error instanceof Error ? error.message : String(error);
6999
+ return {
7000
+ ok: false,
7001
+ reason: `${message} Native screenshots require a visible on-screen preview. ${SCREEN_RECORDING_HINT}`
7002
+ };
7003
+ } finally {
7004
+ await unlink(outputPath).catch(() => void 0);
7005
+ }
7006
+ }
7007
+
7008
+ // src/tools/browser.ts
7009
+ var browserSchema = Type16.Object({
7010
+ action: Type16.Union(
7011
+ [
7012
+ Type16.Literal("open_url"),
7013
+ Type16.Literal("evaluate_javascript"),
7014
+ Type16.Literal("find_in_page"),
7015
+ Type16.Literal("stop_find_in_page"),
7016
+ Type16.Literal("reload"),
7017
+ Type16.Literal("go_back"),
7018
+ Type16.Literal("go_forward"),
7019
+ Type16.Literal("get_state"),
7020
+ Type16.Literal("screenshot"),
7021
+ Type16.Literal("set_viewport"),
7022
+ Type16.Literal("snapshot"),
7023
+ Type16.Literal("click"),
7024
+ Type16.Literal("fill"),
7025
+ Type16.Literal("press"),
7026
+ Type16.Literal("hover"),
7027
+ Type16.Literal("wait_for"),
7028
+ Type16.Literal("get_console_logs")
7029
+ ],
7030
+ {
7031
+ description: "Browser action. Navigation: open_url, reload, go_back, go_forward, get_state. Inspection: snapshot, screenshot, get_console_logs, find_in_page, stop_find_in_page. Interaction: click, fill, press, hover, wait_for, evaluate_javascript. Layout: set_viewport."
7032
+ }
7033
+ ),
7034
+ url: Type16.Optional(
7035
+ Type16.String({
7036
+ description: "URL to open in the Browser tab preview (required for open_url)."
7037
+ })
7038
+ ),
7039
+ script: Type16.Optional(
7040
+ Type16.String({
7041
+ description: "JavaScript to execute in the preview page context (required for evaluate_javascript)."
7042
+ })
7043
+ ),
7044
+ text: Type16.Optional(
7045
+ Type16.String({
7046
+ description: "Text to find in the page (required for find_in_page)."
7047
+ })
7048
+ ),
7049
+ forward: Type16.Optional(
7050
+ Type16.Boolean({
7051
+ description: "Search forward through matches for find_in_page (default true)."
7052
+ })
7053
+ ),
7054
+ match_case: Type16.Optional(
7055
+ Type16.Boolean({
7056
+ description: "Case-sensitive search for find_in_page (default false)."
7057
+ })
7058
+ ),
7059
+ wait_for_load_ms: Type16.Optional(
7060
+ Type16.Number({
7061
+ description: "Milliseconds to wait for the dom-ready event after navigation (default 5000, max 30000)."
7062
+ })
7063
+ ),
7064
+ preset: Type16.Optional(
7065
+ Type16.Union([Type16.Literal("desktop"), Type16.Literal("tablet"), Type16.Literal("mobile")], {
7066
+ description: "Viewport preset for set_viewport."
7067
+ })
7068
+ ),
7069
+ format: Type16.Optional(
7070
+ Type16.Union([Type16.Literal("png"), Type16.Literal("jpeg")], {
7071
+ description: "Image format for screenshot (default png)."
7072
+ })
7073
+ ),
7074
+ ref: Type16.Optional(
7075
+ Type16.String({
7076
+ description: "Element ref from snapshot for click, fill, or hover (e.g. @e1)."
7077
+ })
7078
+ ),
7079
+ value: Type16.Optional(
7080
+ Type16.String({
7081
+ description: "Value to type for fill."
7082
+ })
7083
+ ),
7084
+ key: Type16.Optional(
7085
+ Type16.String({
7086
+ description: "Key to press for press (e.g. Enter, Tab, Escape)."
7087
+ })
7088
+ ),
7089
+ selector: Type16.Optional(
7090
+ Type16.String({
7091
+ description: "CSS selector to wait for in wait_for."
7092
+ })
7093
+ ),
7094
+ timeout_ms: Type16.Optional(
7095
+ Type16.Number({
7096
+ description: "Timeout in milliseconds for wait_for (default 10000, max 60000)."
7097
+ })
7098
+ ),
7099
+ interactive_only: Type16.Optional(
7100
+ Type16.Boolean({
7101
+ description: "Only include interactive elements in snapshot (default false)."
7102
+ })
7103
+ ),
7104
+ max_depth: Type16.Optional(
7105
+ Type16.Number({
7106
+ description: "Maximum DOM depth for snapshot (default 15)."
7107
+ })
7108
+ ),
7109
+ since_ms: Type16.Optional(
7110
+ Type16.Number({
7111
+ description: "Only return console logs from the last N milliseconds for get_console_logs."
7112
+ })
7113
+ ),
7114
+ levels: Type16.Optional(
7115
+ Type16.Array(Type16.String(), {
7116
+ description: 'Console levels to include for get_console_logs (e.g. ["error", "warn"]).'
7117
+ })
7118
+ ),
7119
+ clear: Type16.Optional(
7120
+ Type16.Boolean({
7121
+ description: "Clear buffered console logs after reading for get_console_logs (default false)."
7122
+ })
7123
+ )
7124
+ });
7125
+ var pendingBrowserTasks = /* @__PURE__ */ new Map();
7126
+ var bufferedWebviewIds = /* @__PURE__ */ new Map();
7127
+ var pendingBrowserActionResults = /* @__PURE__ */ new Map();
7128
+ var bufferedBrowserActionResults = /* @__PURE__ */ new Map();
7129
+ var DEFAULT_WAIT_FOR_LOAD_MS = 5e3;
7130
+ var MAX_WAIT_FOR_LOAD_MS = 3e4;
7131
+ var DEFAULT_WEBVIEW_READY_TIMEOUT_MS = 2e4;
7132
+ var DEFAULT_WAIT_FOR_TIMEOUT_MS = 1e4;
7133
+ var MAX_WAIT_FOR_TIMEOUT_MS = 6e4;
7134
+ var DEFAULT_BROWSER_ACTION_TIMEOUT_MS = 2e4;
7135
+ function submitBrowserWebviewReady(toolCallId, webviewId) {
7136
+ const pending = pendingBrowserTasks.get(toolCallId);
7137
+ if (pending) {
7138
+ pending.resolve(webviewId);
7139
+ pendingBrowserTasks.delete(toolCallId);
7140
+ return true;
7141
+ }
7142
+ bufferedWebviewIds.set(toolCallId, webviewId);
7143
+ return true;
7144
+ }
7145
+ function submitBrowserActionResult(toolCallId, result) {
7146
+ const pending = pendingBrowserActionResults.get(toolCallId);
7147
+ if (pending) {
7148
+ pending.resolve(result);
7149
+ pendingBrowserActionResults.delete(toolCallId);
7150
+ return true;
7151
+ }
7152
+ bufferedBrowserActionResults.set(toolCallId, result);
7153
+ return true;
7154
+ }
7155
+ function cancelPendingBrowserTask(toolCallId) {
7156
+ const pending = pendingBrowserTasks.get(toolCallId);
7157
+ if (!pending) return false;
7158
+ pending.reject(new Error("Browser tool execution cancelled"));
7159
+ pendingBrowserTasks.delete(toolCallId);
7160
+ return true;
7161
+ }
7162
+ function cancelPendingBrowserActionResult(toolCallId) {
7163
+ const pending = pendingBrowserActionResults.get(toolCallId);
7164
+ if (!pending) return false;
7165
+ pending.reject(new Error("Browser tool execution cancelled"));
7166
+ pendingBrowserActionResults.delete(toolCallId);
7167
+ return true;
7168
+ }
7169
+ function normalizeUrl(url) {
7170
+ const trimmed = url.trim();
7171
+ if (!trimmed) {
7172
+ throw new Error("url must not be empty");
7173
+ }
7174
+ if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) {
7175
+ return trimmed;
7176
+ }
7177
+ return `http://${trimmed}`;
7178
+ }
7179
+ function waitForPreviewWebview(toolCallId, signal, timeoutMs = DEFAULT_WEBVIEW_READY_TIMEOUT_MS) {
7180
+ const buffered = bufferedWebviewIds.get(toolCallId);
7181
+ if (buffered !== void 0) {
7182
+ bufferedWebviewIds.delete(toolCallId);
7183
+ return Promise.resolve(buffered);
7184
+ }
7185
+ return new Promise((resolve7, reject) => {
7186
+ if (signal?.aborted) {
7187
+ reject(new Error("Operation aborted"));
7188
+ return;
7189
+ }
7190
+ let timeoutHandle = null;
7191
+ const pending = pendingBrowserTasks.get(toolCallId);
7192
+ const onAbort = () => {
7193
+ cancelPendingBrowserTask(toolCallId);
7194
+ };
7195
+ function cleanup() {
7196
+ signal?.removeEventListener("abort", onAbort);
7197
+ if (timeoutHandle) {
7198
+ clearTimeout(timeoutHandle);
7199
+ }
7200
+ }
7201
+ const wrappedResolve = (webviewId) => {
7202
+ cleanup();
7203
+ resolve7(webviewId);
7204
+ };
7205
+ const wrappedReject = (error) => {
7206
+ cleanup();
7207
+ reject(error);
7208
+ };
7209
+ if (pending) {
7210
+ pending.resolve = wrappedResolve;
7211
+ pending.reject = wrappedReject;
7212
+ } else {
7213
+ pendingBrowserTasks.set(toolCallId, {
7214
+ resolve: wrappedResolve,
7215
+ reject: wrappedReject
7216
+ });
7217
+ }
7218
+ signal?.addEventListener("abort", onAbort, { once: true });
7219
+ timeoutHandle = setTimeout(() => {
7220
+ if (!pendingBrowserTasks.has(toolCallId)) return;
7221
+ pendingBrowserTasks.delete(toolCallId);
7222
+ wrappedReject(
7223
+ new Error(
7224
+ `browser tool timed out after ${timeoutMs}ms waiting for the Browser tab preview webview`
7225
+ )
7226
+ );
7227
+ }, timeoutMs);
7228
+ });
7229
+ }
7230
+ function waitForBrowserActionResult(toolCallId, signal, timeoutMs = DEFAULT_BROWSER_ACTION_TIMEOUT_MS) {
7231
+ const buffered = bufferedBrowserActionResults.get(toolCallId);
7232
+ if (buffered) {
7233
+ bufferedBrowserActionResults.delete(toolCallId);
7234
+ return Promise.resolve(buffered);
7235
+ }
7236
+ return new Promise((resolve7, reject) => {
7237
+ if (signal?.aborted) {
7238
+ reject(new Error("Operation aborted"));
7239
+ return;
7240
+ }
7241
+ let timeoutHandle = null;
7242
+ const onAbort = () => {
7243
+ cancelPendingBrowserActionResult(toolCallId);
7244
+ };
7245
+ function cleanup() {
7246
+ signal?.removeEventListener("abort", onAbort);
7247
+ if (timeoutHandle) {
7248
+ clearTimeout(timeoutHandle);
7249
+ }
7250
+ }
7251
+ const wrappedResolve = (result) => {
7252
+ cleanup();
7253
+ resolve7(result);
7254
+ };
7255
+ const wrappedReject = (error) => {
7256
+ cleanup();
7257
+ reject(error);
7258
+ };
7259
+ pendingBrowserActionResults.set(toolCallId, {
7260
+ resolve: wrappedResolve,
7261
+ reject: wrappedReject
7262
+ });
7263
+ signal?.addEventListener("abort", onAbort, { once: true });
7264
+ timeoutHandle = setTimeout(() => {
7265
+ if (!pendingBrowserActionResults.has(toolCallId)) return;
7266
+ pendingBrowserActionResults.delete(toolCallId);
7267
+ wrappedReject(
7268
+ new Error(
7269
+ `browser tool timed out after ${timeoutMs}ms waiting for the Browser tab host action`
7270
+ )
7271
+ );
7272
+ }, timeoutMs);
7273
+ });
7274
+ }
7275
+ function logBrowserPreviewFailure(action, webviewId, webview, error, context) {
7276
+ console.error(`[browser] ${action} failed`, {
7277
+ webview_id: webviewId,
7278
+ current_url: webview.url,
7279
+ ...context,
7280
+ error: error instanceof Error ? error.message : String(error),
7281
+ stack: error instanceof Error ? error.stack : void 0
7282
+ });
7283
+ }
7284
+ async function executeBrowserAction(webviewId, input) {
7285
+ const webview = getBrowserViewById(webviewId);
7286
+ if (!webview) {
7287
+ throw new Error(`Browser preview webview ${webviewId} is not available`);
7288
+ }
7289
+ switch (input.action) {
7290
+ case "open_url": {
7291
+ if (!input.url?.trim()) {
7292
+ throw new Error("url is required for open_url");
7293
+ }
7294
+ const url = normalizeUrl(input.url);
7295
+ const waitMs = Math.min(
7296
+ Math.max(input.wait_for_load_ms ?? DEFAULT_WAIT_FOR_LOAD_MS, 0),
7297
+ MAX_WAIT_FOR_LOAD_MS
7298
+ );
7299
+ const domReadyPromise = waitForBrowserEvent(webview, "dom-ready", waitMs);
7300
+ webview.loadURL(url);
7301
+ let domReady = false;
7302
+ try {
7303
+ await domReadyPromise;
7304
+ domReady = true;
7305
+ } catch {
7306
+ }
7307
+ return {
7308
+ action: "open_url",
7309
+ url,
7310
+ webview_id: webviewId,
7311
+ current_url: webview.url,
7312
+ dom_ready: domReady
7313
+ };
7314
+ }
7315
+ case "evaluate_javascript": {
7316
+ if (!input.script?.trim()) {
7317
+ throw new Error("script is required for evaluate_javascript");
7318
+ }
7319
+ if (!webview.rpc?.request?.evaluateJavascriptWithResponse) {
7320
+ throw new Error("Browser preview webview RPC is not available for JavaScript execution");
7321
+ }
7322
+ const result = await webview.rpc.request.evaluateJavascriptWithResponse({
7323
+ script: input.script
7324
+ });
7325
+ return {
7326
+ action: "evaluate_javascript",
7327
+ webview_id: webviewId,
7328
+ result
7329
+ };
7330
+ }
7331
+ case "find_in_page": {
7332
+ if (!input.text?.trim()) {
7333
+ throw new Error("text is required for find_in_page");
7334
+ }
7335
+ webview.findInPage(input.text, {
7336
+ forward: input.forward ?? true,
7337
+ matchCase: input.match_case ?? false
7338
+ });
7339
+ return {
7340
+ action: "find_in_page",
7341
+ webview_id: webviewId,
7342
+ text: input.text,
7343
+ forward: input.forward ?? true,
7344
+ match_case: input.match_case ?? false
7345
+ };
7346
+ }
7347
+ case "stop_find_in_page": {
7348
+ webview.stopFindInPage();
7349
+ return {
7350
+ action: "stop_find_in_page",
7351
+ webview_id: webviewId
7352
+ };
7353
+ }
7354
+ case "snapshot": {
7355
+ const result = await invokePreviewApi(webview, "snapshot", {
7356
+ interactive_only: input.interactive_only ?? false,
7357
+ max_depth: input.max_depth ?? 15
7358
+ });
7359
+ return {
7360
+ action: "snapshot",
7361
+ webview_id: webviewId,
7362
+ ...typeof result === "object" && result !== null ? result : { result }
7363
+ };
7364
+ }
7365
+ case "click": {
7366
+ if (!input.ref?.trim()) {
7367
+ throw new Error("ref is required for click");
7368
+ }
7369
+ const result = await invokePreviewApi(webview, "click", { ref: input.ref });
7370
+ return {
7371
+ action: "click",
7372
+ webview_id: webviewId,
7373
+ ref: input.ref,
7374
+ result
7375
+ };
7376
+ }
7377
+ case "fill": {
7378
+ if (!input.ref?.trim()) {
7379
+ throw new Error("ref is required for fill");
7380
+ }
7381
+ if (input.value === void 0) {
7382
+ throw new Error("value is required for fill");
7383
+ }
7384
+ const result = await invokePreviewApi(webview, "fill", {
7385
+ ref: input.ref,
7386
+ value: input.value
7387
+ });
7388
+ return {
7389
+ action: "fill",
7390
+ webview_id: webviewId,
7391
+ ref: input.ref,
7392
+ value: input.value,
7393
+ result
7394
+ };
7395
+ }
7396
+ case "press": {
7397
+ if (!input.key?.trim()) {
7398
+ throw new Error("key is required for press");
7399
+ }
7400
+ const result = await invokePreviewApi(webview, "press", { key: input.key });
7401
+ return {
7402
+ action: "press",
7403
+ webview_id: webviewId,
7404
+ key: input.key,
7405
+ result
7406
+ };
7407
+ }
7408
+ case "hover": {
7409
+ if (!input.ref?.trim()) {
7410
+ throw new Error("ref is required for hover");
7411
+ }
7412
+ const result = await invokePreviewApi(webview, "hover", { ref: input.ref });
7413
+ return {
7414
+ action: "hover",
7415
+ webview_id: webviewId,
7416
+ ref: input.ref,
7417
+ result
7418
+ };
7419
+ }
7420
+ case "wait_for": {
7421
+ if (!input.selector?.trim() && !input.text?.trim()) {
7422
+ throw new Error("selector or text is required for wait_for");
7423
+ }
7424
+ const timeoutMs = Math.min(
7425
+ Math.max(input.timeout_ms ?? DEFAULT_WAIT_FOR_TIMEOUT_MS, 0),
7426
+ MAX_WAIT_FOR_TIMEOUT_MS
7427
+ );
7428
+ const result = await invokePreviewApi(webview, "waitFor", {
7429
+ selector: input.selector,
7430
+ text: input.text,
7431
+ timeout_ms: timeoutMs
7432
+ });
7433
+ return {
7434
+ action: "wait_for",
7435
+ webview_id: webviewId,
7436
+ selector: input.selector,
7437
+ text: input.text,
7438
+ timeout_ms: timeoutMs,
7439
+ result
7440
+ };
7441
+ }
7442
+ case "get_console_logs": {
7443
+ const result = await invokePreviewApi(webview, "getConsoleLogs", {
7444
+ since_ms: input.since_ms ?? 0,
7445
+ levels: input.levels,
7446
+ clear: input.clear ?? false
7447
+ });
7448
+ return {
7449
+ action: "get_console_logs",
7450
+ webview_id: webviewId,
7451
+ ...typeof result === "object" && result !== null ? result : { result }
7452
+ };
7453
+ }
7454
+ case "screenshot": {
7455
+ const format = input.format ?? "png";
7456
+ const nativeCapture = await captureNativeBrowserScreenshot(webviewId, format);
7457
+ if (nativeCapture.ok) {
7458
+ return {
7459
+ action: "screenshot",
7460
+ webview_id: webviewId,
7461
+ ...nativeCapture.result
7462
+ };
7463
+ }
7464
+ try {
7465
+ const result = await invokePreviewApi(webview, "takeScreenshot", { format });
7466
+ return {
7467
+ action: "screenshot",
7468
+ webview_id: webviewId,
7469
+ capture_method: "svg-foreign-object",
7470
+ native_capture_unavailable: nativeCapture.reason,
7471
+ ...typeof result === "object" && result !== null ? result : { result }
7472
+ };
7473
+ } catch (error) {
7474
+ logBrowserPreviewFailure("screenshot", webviewId, webview, error, {
7475
+ format,
7476
+ capture_method: "svg-foreign-object",
7477
+ native_capture_unavailable: nativeCapture.reason,
7478
+ troubleshooting: "Native macOS capture uses screencapture on the visible preview rect. If native capture fails, grant Screen Recording permission to Tarsk and restart the app. SVG fallback can fail on large third-party pages. Check tarsk-logs.txt for [browser-preview] takeScreenshot failed entries."
7479
+ });
7480
+ throw error;
7481
+ }
7482
+ }
7483
+ default:
7484
+ throw new Error(`Unsupported browser action: ${String(input.action)}`);
7485
+ }
7486
+ }
7487
+ function formatBrowserResult(payload) {
7488
+ if (payload.action === "screenshot") {
7489
+ const display = stripScreenshotPayload(payload);
7490
+ return {
7491
+ text: buildGenerateImageToolText(display),
7492
+ details: payload
7493
+ };
7494
+ }
7495
+ return {
7496
+ text: JSON.stringify(payload, null, 2),
7497
+ details: payload
7498
+ };
7499
+ }
7500
+ function stripScreenshotPayload(payload) {
7501
+ const display = {};
7502
+ for (const [key, value] of Object.entries(payload)) {
7503
+ if (key === "image_data") continue;
7504
+ if (typeof value === "string" || typeof value === "number") {
7505
+ display[key] = value;
7506
+ }
7507
+ }
7508
+ return display;
7509
+ }
7510
+ async function persistScreenshotResult(payload, options) {
7511
+ const imageData = payload.image_data;
7512
+ if (typeof imageData !== "string" || !imageData) {
7513
+ return payload;
7514
+ }
7515
+ const sizeInBytes = decodeBase64ImageData(imageData).length;
7516
+ const { image_data: _removed, ...rest } = payload;
7517
+ if (!options.cwd) {
7518
+ return { ...rest, sizeInBytes };
7519
+ }
7520
+ const format = payload.format === "jpeg" ? "jpeg" : "png";
7521
+ const extension = format === "jpeg" ? "jpg" : "png";
7522
+ const id = Math.random().toString(36).slice(2, 8);
7523
+ const relativePath = buildTmpImagePath(`screenshot-${id}.${extension}`);
7524
+ const saved = await saveToolImage({
7525
+ imageData,
7526
+ cwd: options.cwd,
7527
+ relativePath,
7528
+ threadId: options.threadId,
7529
+ serverOrigin: options.serverOrigin
7530
+ });
7531
+ return {
7532
+ ...rest,
7533
+ savedPath: saved.savedPath,
7534
+ sizeInBytes: saved.sizeInBytes,
7535
+ ...saved.imageUrl ? { imageUrl: saved.imageUrl } : {}
7536
+ };
7537
+ }
7538
+ var BROWSER_TOOL_DESCRIPTION = "Control the in-app Browser. Use to read information from websites \u2014 open_url then snapshot for page content and structure, get_state for URL/title, find_in_page to locate text, evaluate_javascript to extract data, or screenshot for visual reference. Also use for interacting with pages: click, fill, press, hover, wait_for. Actions: open_url, reload, go_back, go_forward, get_state, set_viewport, screenshot, snapshot, click, fill, press, hover, wait_for, get_console_logs, evaluate_javascript, find_in_page, stop_find_in_page. Use snapshot to get @eN element refs, then click/fill/hover by ref. The Browser tab opens automatically. Start the dev server first when testing localhost apps.";
7539
+ function createBrowserTool(options = {}) {
7540
+ const serverOrigin = options.serverOrigin ?? getServerOrigin();
7541
+ return {
7542
+ name: "browser",
7543
+ label: "browser",
7544
+ description: BROWSER_TOOL_DESCRIPTION,
7545
+ parameters: browserSchema,
7546
+ execute: async (toolCallId, input, signal) => {
7547
+ return withAbortSignal(signal, async () => {
7548
+ const electrobun = await loadElectrobunBrowserView();
7549
+ if (!electrobun) {
7550
+ throw new Error(
7551
+ "browser tool is only available in the Tarsk desktop app (Electrobun), not in server-only mode"
7552
+ );
7553
+ }
7554
+ if (isBrowserHostAction(input.action)) {
7555
+ const result2 = await waitForBrowserActionResult(toolCallId, signal);
7556
+ const formatted2 = formatBrowserResult(result2);
7557
+ return {
7558
+ content: [{ type: "text", text: formatted2.text }],
7559
+ details: formatted2.details
7560
+ };
6636
7561
  }
6637
- const resultPayload = { imageData, contentType };
6638
- if (savedPath) resultPayload.savedPath = savedPath;
7562
+ const webviewId = await waitForPreviewWebview(toolCallId, signal);
7563
+ let result = await executeBrowserAction(webviewId, input);
7564
+ if (input.action === "screenshot") {
7565
+ result = await persistScreenshotResult(result, {
7566
+ cwd: options.cwd,
7567
+ threadId: options.threadId,
7568
+ serverOrigin
7569
+ });
7570
+ }
7571
+ const formatted = formatBrowserResult(result);
6639
7572
  return {
6640
- content: [{ type: "text", text: JSON.stringify(resultPayload) }],
6641
- details: { imageData, contentType }
7573
+ content: [{ type: "text", text: formatted.text }],
7574
+ details: typeof formatted.details.imageUrl === "string" ? { imageUrl: formatted.details.imageUrl } : formatted.details
6642
7575
  };
6643
7576
  });
6644
7577
  }
6645
7578
  };
6646
7579
  }
7580
+ var browserTool = createBrowserTool();
6647
7581
 
6648
7582
  // src/tools/execute-browser-js.ts
6649
7583
  init_tarsk_debug();
6650
- import { Type as Type16 } from "@sinclair/typebox";
6651
- var runWebWorkerSchema = Type16.Object({
6652
- intention: Type16.String({
7584
+ import { Type as Type17 } from "@sinclair/typebox";
7585
+ var runWebWorkerSchema = Type17.Object({
7586
+ intention: Type17.String({
6653
7587
  description: "A short description of what this code execution is trying to accomplish."
6654
7588
  }),
6655
- code: Type16.String({
7589
+ code: Type17.String({
6656
7590
  description: "The javascript code to execute in browser context(not nodejs). The environment provides an async function `callTool(toolName, params)` which allows calling any of the other available tools by name. callTool always returns `{ text: string | null, error: string | null }`. On success `text` contains the result and `error` is null. On failure `error` contains the error message and `text` is null. Example: `const r = await callTool('read', { path: 'foo.ts' }); if (r.error) return r.error; return r.text;`."
6657
7591
  })
6658
7592
  });
@@ -6699,11 +7633,11 @@ function createRunWebWorkerTool(options) {
6699
7633
  if (signal?.aborted) {
6700
7634
  throw new Error("Operation aborted");
6701
7635
  }
6702
- const result = await new Promise((resolve6, reject) => {
7636
+ const result = await new Promise((resolve7, reject) => {
6703
7637
  const earlyResult = bufferedResults.get(toolCallId);
6704
7638
  if (earlyResult !== void 0) {
6705
7639
  bufferedResults.delete(toolCallId);
6706
- resolve6(earlyResult);
7640
+ resolve7(earlyResult);
6707
7641
  return;
6708
7642
  }
6709
7643
  let timeoutHandle = null;
@@ -6718,7 +7652,7 @@ function createRunWebWorkerTool(options) {
6718
7652
  }
6719
7653
  const wrappedResolve = (value) => {
6720
7654
  cleanup();
6721
- resolve6(value);
7655
+ resolve7(value);
6722
7656
  };
6723
7657
  const wrappedReject = (error) => {
6724
7658
  cleanup();
@@ -6786,7 +7720,7 @@ function looksLikeMissingReturn(code) {
6786
7720
  var runWebWorkerTool = createRunWebWorkerTool();
6787
7721
 
6788
7722
  // src/tools/fetch.ts
6789
- import { Type as Type17 } from "@sinclair/typebox";
7723
+ import { Type as Type18 } from "@sinclair/typebox";
6790
7724
 
6791
7725
  // src/features/web-fetch/content-processor.ts
6792
7726
  import { completeSimple as completeSimple2 } from "@earendil-works/pi-ai";
@@ -6817,7 +7751,7 @@ function runCommandTimed(stepName, command, args2, cwd) {
6817
7751
  let tFirstIo = null;
6818
7752
  let tExit = null;
6819
7753
  logSubprocessMark(scope, stepName, "spawn-call", 0);
6820
- return new Promise((resolve6, reject) => {
7754
+ return new Promise((resolve7, reject) => {
6821
7755
  const proc = spawnProcess(command, args2, { cwd });
6822
7756
  proc.on("spawn", () => {
6823
7757
  tSpawnEvent = Date.now();
@@ -6856,7 +7790,7 @@ function runCommandTimed(stepName, command, args2, cwd) {
6856
7790
  console.log(
6857
7791
  `${GITOPS_TIMING_PREFIX} subprocess ${scope} step=${stepName} summary total=${tClose - t0}ms` + (preSpawnMs !== null ? ` pre-spawn=${preSpawnMs}ms` : "") + (processActiveMs !== null ? ` process-active=${processActiveMs}ms` : "") + (exitToCloseMs !== null ? ` exit-to-close=${exitToCloseMs}ms` : "") + ` cmd=${command} ${args2.join(" ")}`
6858
7792
  );
6859
- resolve6({ code, stdout, stderr });
7793
+ resolve7({ code, stdout, stderr });
6860
7794
  });
6861
7795
  proc.on("error", (error) => {
6862
7796
  logSubprocessMark(scope, stepName, "spawn-error", Date.now() - t0);
@@ -6889,13 +7823,13 @@ function startEventLoopLagMonitor(threadId) {
6889
7823
 
6890
7824
  // src/features/git/git.utils.ts
6891
7825
  import { existsSync as existsSync8, readFileSync as readFileSync3, statSync as statSync5 } from "fs";
6892
- import { isAbsolute as isAbsolute2, normalize as normalize2, resolve, join as join10 } from "path";
7826
+ import { isAbsolute as isAbsolute2, normalize as normalize2, resolve, join as join12 } from "path";
6893
7827
 
6894
7828
  // src/core/paths.ts
6895
- import { join as join9 } from "path";
7829
+ import { join as join11 } from "path";
6896
7830
  import { homedir as homedir4 } from "os";
6897
- var APP_SUPPORT_DIR = join9(homedir4(), "Library", "Application Support", "Tarsk");
6898
- var DATA_DIR = join9(APP_SUPPORT_DIR, "data");
7831
+ var APP_SUPPORT_DIR = join11(homedir4(), "Library", "Application Support", "Tarsk");
7832
+ var DATA_DIR = join11(APP_SUPPORT_DIR, "data");
6899
7833
  function getDataDir() {
6900
7834
  return DATA_DIR;
6901
7835
  }
@@ -7163,7 +8097,7 @@ async function getCurrentBranch(gitRoot) {
7163
8097
  ]);
7164
8098
  return result.stdout.trim();
7165
8099
  }
7166
- return new Promise((resolve6) => {
8100
+ return new Promise((resolve7) => {
7167
8101
  const proc = spawnProcess("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: gitRoot });
7168
8102
  let out = "";
7169
8103
  if (proc.stdout) {
@@ -7172,9 +8106,9 @@ async function getCurrentBranch(gitRoot) {
7172
8106
  });
7173
8107
  }
7174
8108
  proc.on("close", () => {
7175
- resolve6(out.trim());
8109
+ resolve7(out.trim());
7176
8110
  });
7177
- proc.on("error", () => resolve6(""));
8111
+ proc.on("error", () => resolve7(""));
7178
8112
  });
7179
8113
  }
7180
8114
  async function getGitDiff(gitRoot) {
@@ -7251,7 +8185,7 @@ async function getUntrackedFilesDiff(gitRoot) {
7251
8185
  const parts = [];
7252
8186
  const maxFileSize = 1e5;
7253
8187
  for (const relPath of untrackedPaths) {
7254
- const fullPath = join10(gitRoot, relPath);
8188
+ const fullPath = join12(gitRoot, relPath);
7255
8189
  if (!existsSync8(fullPath)) continue;
7256
8190
  try {
7257
8191
  if (statSync5(fullPath).isDirectory()) continue;
@@ -7284,7 +8218,7 @@ async function getGitStatus(gitRoot) {
7284
8218
  ]);
7285
8219
  return result.stdout;
7286
8220
  }
7287
- return new Promise((resolve6) => {
8221
+ return new Promise((resolve7) => {
7288
8222
  const proc = spawnProcess("git", ["status", "--porcelain", "--untracked-files=all"], {
7289
8223
  cwd: gitRoot
7290
8224
  });
@@ -7294,15 +8228,15 @@ async function getGitStatus(gitRoot) {
7294
8228
  out += d.toString();
7295
8229
  });
7296
8230
  }
7297
- proc.on("close", () => resolve6(out));
7298
- proc.on("error", () => resolve6(""));
8231
+ proc.on("close", () => resolve7(out));
8232
+ proc.on("error", () => resolve7(""));
7299
8233
  });
7300
8234
  }
7301
8235
  function runGit(stepName, gitRoot, args2) {
7302
8236
  if (isGitSubprocessTimingEnabled()) {
7303
8237
  return runGitCommandTimed(stepName, gitRoot, args2);
7304
8238
  }
7305
- return new Promise((resolve6) => {
8239
+ return new Promise((resolve7) => {
7306
8240
  const proc = spawnProcess("git", args2, { cwd: gitRoot });
7307
8241
  let stdout = "";
7308
8242
  let stderr = "";
@@ -7316,8 +8250,8 @@ function runGit(stepName, gitRoot, args2) {
7316
8250
  stderr += d.toString();
7317
8251
  });
7318
8252
  }
7319
- proc.on("close", (code) => resolve6({ code, stdout, stderr }));
7320
- proc.on("error", () => resolve6({ code: -1, stdout, stderr }));
8253
+ proc.on("close", (code) => resolve7({ code, stdout, stderr }));
8254
+ proc.on("error", () => resolve7({ code: -1, stdout, stderr }));
7321
8255
  });
7322
8256
  }
7323
8257
  async function getStatusPorcelainV2(gitRoot) {
@@ -7415,7 +8349,7 @@ async function fetchRemoteRefs(gitRoot, refs) {
7415
8349
  await runGit("fetch-remote-refs", gitRoot, ["fetch", "--no-tags", "origin", ...refs]);
7416
8350
  }
7417
8351
  function hasUnpushedCommits(gitRoot, currentBranch) {
7418
- return new Promise((resolve6) => {
8352
+ return new Promise((resolve7) => {
7419
8353
  const proc = spawnProcess("git", ["log", `origin/${currentBranch}..HEAD`, "--oneline"], {
7420
8354
  cwd: gitRoot
7421
8355
  });
@@ -7426,13 +8360,13 @@ function hasUnpushedCommits(gitRoot, currentBranch) {
7426
8360
  });
7427
8361
  }
7428
8362
  proc.on("close", () => {
7429
- resolve6(out.trim().length > 0);
8363
+ resolve7(out.trim().length > 0);
7430
8364
  });
7431
- proc.on("error", () => resolve6(false));
8365
+ proc.on("error", () => resolve7(false));
7432
8366
  });
7433
8367
  }
7434
8368
  function hasCommitsAheadOfDefault(gitRoot, defaultBranch) {
7435
- return new Promise((resolve6) => {
8369
+ return new Promise((resolve7) => {
7436
8370
  const proc = spawnProcess("git", ["rev-list", "--count", `origin/${defaultBranch}..HEAD`], {
7437
8371
  cwd: gitRoot
7438
8372
  });
@@ -7444,12 +8378,12 @@ function hasCommitsAheadOfDefault(gitRoot, defaultBranch) {
7444
8378
  }
7445
8379
  proc.on("close", (code) => {
7446
8380
  if (code === 0) {
7447
- resolve6(parseInt(out.trim(), 10) > 0);
8381
+ resolve7(parseInt(out.trim(), 10) > 0);
7448
8382
  } else {
7449
- resolve6(false);
8383
+ resolve7(false);
7450
8384
  }
7451
8385
  });
7452
- proc.on("error", () => resolve6(false));
8386
+ proc.on("error", () => resolve7(false));
7453
8387
  });
7454
8388
  }
7455
8389
  async function getRemoteOrigin(gitRoot) {
@@ -7464,7 +8398,7 @@ async function getRemoteOrigin(gitRoot) {
7464
8398
  }
7465
8399
  return "";
7466
8400
  }
7467
- return new Promise((resolve6) => {
8401
+ return new Promise((resolve7) => {
7468
8402
  const proc = spawnProcess("git", ["remote", "get-url", "origin"], { cwd: gitRoot });
7469
8403
  let out = "";
7470
8404
  if (proc.stdout) {
@@ -7474,12 +8408,12 @@ async function getRemoteOrigin(gitRoot) {
7474
8408
  }
7475
8409
  proc.on("close", (code) => {
7476
8410
  if (code === 0) {
7477
- resolve6(out.trim());
8411
+ resolve7(out.trim());
7478
8412
  } else {
7479
- resolve6("");
8413
+ resolve7("");
7480
8414
  }
7481
8415
  });
7482
- proc.on("error", () => resolve6(""));
8416
+ proc.on("error", () => resolve7(""));
7483
8417
  });
7484
8418
  }
7485
8419
  async function getGitSyncStatus(gitRoot, branch, options) {
@@ -7487,20 +8421,20 @@ async function getGitSyncStatus(gitRoot, branch, options) {
7487
8421
  console.log(`[git-status] Checking sync status for branch: ${branch}`);
7488
8422
  if (fetchRemote) {
7489
8423
  console.log(`[git-status] Fetching from origin...`);
7490
- await new Promise((resolve6) => {
8424
+ await new Promise((resolve7) => {
7491
8425
  const fetchProc = spawnProcess("git", ["fetch", "origin"], { cwd: gitRoot });
7492
8426
  fetchProc.on("close", () => {
7493
8427
  console.log(`[git-status] \u2713 Fetch completed`);
7494
- resolve6();
8428
+ resolve7();
7495
8429
  });
7496
8430
  fetchProc.on("error", () => {
7497
8431
  console.log(`[git-status] Fetch error (continuing anyway)`);
7498
- resolve6();
8432
+ resolve7();
7499
8433
  });
7500
8434
  });
7501
8435
  }
7502
8436
  console.log(`[git-status] Checking status against remote tracking branch...`);
7503
- return new Promise((resolve6) => {
8437
+ return new Promise((resolve7) => {
7504
8438
  const statusProc = spawnProcess("git", ["status", "-sb"], { cwd: gitRoot });
7505
8439
  let statusOut = "";
7506
8440
  let statusErr = "";
@@ -7520,49 +8454,49 @@ async function getGitSyncStatus(gitRoot, branch, options) {
7520
8454
  const firstLine = output.split("\n")[0] ?? "";
7521
8455
  if (!firstLine.includes(branch)) {
7522
8456
  console.log(`[git-status] Branch name not found in status output, returning 'Up to date'`);
7523
- resolve6("Up to date");
8457
+ resolve7("Up to date");
7524
8458
  return;
7525
8459
  }
7526
8460
  if (!firstLine.includes("...")) {
7527
8461
  console.log(`[git-status] No upstream tracking branch detected, returning 'Up to date'`);
7528
- resolve6("Up to date");
8462
+ resolve7("Up to date");
7529
8463
  return;
7530
8464
  }
7531
8465
  if (firstLine.includes("ahead") && firstLine.includes("behind")) {
7532
8466
  console.log(`[git-status] \u2713 Status: Diverged`);
7533
- resolve6("Diverged");
8467
+ resolve7("Diverged");
7534
8468
  } else if (firstLine.includes("behind")) {
7535
8469
  console.log(`[git-status] \u2713 Status: Behind`);
7536
- resolve6("Behind");
8470
+ resolve7("Behind");
7537
8471
  } else if (firstLine.includes("ahead")) {
7538
8472
  console.log(`[git-status] \u2713 Status: Ahead`);
7539
- resolve6("Ahead");
8473
+ resolve7("Ahead");
7540
8474
  } else {
7541
8475
  console.log(`[git-status] \u2713 Status: Up to date`);
7542
- resolve6("Up to date");
8476
+ resolve7("Up to date");
7543
8477
  }
7544
8478
  });
7545
8479
  statusProc.on("error", () => {
7546
8480
  console.log(`[git-status] Error getting status, defaulting to 'Up to date'`);
7547
- resolve6("Up to date");
8481
+ resolve7("Up to date");
7548
8482
  });
7549
8483
  });
7550
8484
  }
7551
8485
  async function hasRemoteBranch(gitRoot, branch) {
7552
- return new Promise((resolve6) => {
8486
+ return new Promise((resolve7) => {
7553
8487
  const proc = spawnProcess("git", ["rev-parse", "--verify", `origin/${branch}`], {
7554
8488
  cwd: gitRoot
7555
8489
  });
7556
8490
  proc.on("close", (code) => {
7557
- resolve6(code === 0);
8491
+ resolve7(code === 0);
7558
8492
  });
7559
8493
  proc.on("error", () => {
7560
- resolve6(false);
8494
+ resolve7(false);
7561
8495
  });
7562
8496
  });
7563
8497
  }
7564
8498
  function getDefaultBranch(gitRoot) {
7565
- return new Promise((resolve6) => {
8499
+ return new Promise((resolve7) => {
7566
8500
  const proc = spawnProcess("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
7567
8501
  cwd: gitRoot
7568
8502
  });
@@ -7575,12 +8509,12 @@ function getDefaultBranch(gitRoot) {
7575
8509
  proc.on("close", (code) => {
7576
8510
  if (code === 0 && out.trim()) {
7577
8511
  const match = out.trim().match(/refs\/remotes\/origin\/(.+)/);
7578
- resolve6(match ? match[1] : null);
8512
+ resolve7(match ? match[1] : null);
7579
8513
  } else {
7580
- resolve6(null);
8514
+ resolve7(null);
7581
8515
  }
7582
8516
  });
7583
- proc.on("error", () => resolve6(null));
8517
+ proc.on("error", () => resolve7(null));
7584
8518
  });
7585
8519
  }
7586
8520
  async function findDefaultBranch(gitRoot) {
@@ -7600,7 +8534,7 @@ async function resolvePrCommitLogRange(gitRoot, baseBranch, currentBranch) {
7600
8534
  }
7601
8535
  async function getPrBranchDiff(gitRoot, baseBranch) {
7602
8536
  const diffRange = await hasRemoteBranch(gitRoot, baseBranch) ? `origin/${baseBranch}...HEAD` : `${baseBranch}...HEAD`;
7603
- return new Promise((resolve6) => {
8537
+ return new Promise((resolve7) => {
7604
8538
  const proc = spawnProcess("git", ["diff", diffRange], { cwd: gitRoot });
7605
8539
  let out = "";
7606
8540
  if (proc.stdout) {
@@ -7608,13 +8542,13 @@ async function getPrBranchDiff(gitRoot, baseBranch) {
7608
8542
  out += d.toString();
7609
8543
  });
7610
8544
  }
7611
- proc.on("close", () => resolve6(out.trim()));
7612
- proc.on("error", () => resolve6(""));
8545
+ proc.on("close", () => resolve7(out.trim()));
8546
+ proc.on("error", () => resolve7(""));
7613
8547
  });
7614
8548
  }
7615
8549
  async function getCommitLog(gitRoot, baseBranch, currentBranch) {
7616
8550
  const range = await resolvePrCommitLogRange(gitRoot, baseBranch, currentBranch);
7617
- return new Promise((resolve6) => {
8551
+ return new Promise((resolve7) => {
7618
8552
  const proc = spawnProcess("git", ["log", "--oneline", range], {
7619
8553
  cwd: gitRoot
7620
8554
  });
@@ -7624,13 +8558,13 @@ async function getCommitLog(gitRoot, baseBranch, currentBranch) {
7624
8558
  out += d.toString();
7625
8559
  });
7626
8560
  }
7627
- proc.on("close", () => resolve6(out.trim()));
7628
- proc.on("error", () => resolve6(""));
8561
+ proc.on("close", () => resolve7(out.trim()));
8562
+ proc.on("error", () => resolve7(""));
7629
8563
  });
7630
8564
  }
7631
8565
  async function getDetailedCommits(gitRoot, baseBranch, currentBranch) {
7632
8566
  const range = await resolvePrCommitLogRange(gitRoot, baseBranch, currentBranch);
7633
- return new Promise((resolve6) => {
8567
+ return new Promise((resolve7) => {
7634
8568
  const proc = spawnProcess("git", ["log", range, "--format=%h %s%n%b%n---"], { cwd: gitRoot });
7635
8569
  let out = "";
7636
8570
  if (proc.stdout) {
@@ -7639,14 +8573,14 @@ async function getDetailedCommits(gitRoot, baseBranch, currentBranch) {
7639
8573
  });
7640
8574
  }
7641
8575
  proc.on("close", () => {
7642
- resolve6(out.length > 2e3 ? out.substring(0, 2e3) + "\n...(more commits)" : out);
8576
+ resolve7(out.length > 2e3 ? out.substring(0, 2e3) + "\n...(more commits)" : out);
7643
8577
  });
7644
- proc.on("error", () => resolve6(""));
8578
+ proc.on("error", () => resolve7(""));
7645
8579
  });
7646
8580
  }
7647
8581
  function getGitLog(gitRoot, limit) {
7648
8582
  return new Promise(
7649
- (resolve6, reject) => {
8583
+ (resolve7, reject) => {
7650
8584
  const proc = spawnProcess(
7651
8585
  "git",
7652
8586
  ["log", "--oneline", "-n", limit.toString(), "--format=%H|%s|%an|%ai"],
@@ -7676,7 +8610,7 @@ function getGitLog(gitRoot, limit) {
7676
8610
  date: parts[3] || ""
7677
8611
  };
7678
8612
  });
7679
- resolve6(parsed);
8613
+ resolve7(parsed);
7680
8614
  } else {
7681
8615
  reject(new Error(err || "Failed to get git log"));
7682
8616
  }
@@ -7686,27 +8620,27 @@ function getGitLog(gitRoot, limit) {
7686
8620
  );
7687
8621
  }
7688
8622
  function stageAllChanges(gitRoot) {
7689
- return new Promise((resolve6, reject) => {
8623
+ return new Promise((resolve7, reject) => {
7690
8624
  const proc = spawnProcess("git", ["add", "-A"], { cwd: gitRoot });
7691
8625
  proc.on("close", (code) => {
7692
- if (code === 0) resolve6();
8626
+ if (code === 0) resolve7();
7693
8627
  else reject(new Error("Failed to stage changes"));
7694
8628
  });
7695
8629
  proc.on("error", reject);
7696
8630
  });
7697
8631
  }
7698
8632
  function commitChanges(gitRoot, message) {
7699
- return new Promise((resolve6, reject) => {
8633
+ return new Promise((resolve7, reject) => {
7700
8634
  const proc = spawnProcess("git", ["commit", "-m", message], { cwd: gitRoot });
7701
8635
  proc.on("close", (code) => {
7702
- if (code === 0) resolve6();
8636
+ if (code === 0) resolve7();
7703
8637
  else reject(new Error("Failed to commit changes"));
7704
8638
  });
7705
8639
  proc.on("error", reject);
7706
8640
  });
7707
8641
  }
7708
8642
  function pushToOrigin(gitRoot, currentBranch) {
7709
- return new Promise((resolve6, reject) => {
8643
+ return new Promise((resolve7, reject) => {
7710
8644
  console.log(`[git-push] Executing: git push -u origin ${currentBranch}`);
7711
8645
  const proc = spawnProcess("git", ["push", "-u", "origin", currentBranch], { cwd: gitRoot });
7712
8646
  let err = "";
@@ -7727,7 +8661,7 @@ function pushToOrigin(gitRoot, currentBranch) {
7727
8661
  console.log(`[git-push] git push exited with code: ${code}`);
7728
8662
  if (code === 0) {
7729
8663
  console.log(`[git-push] \u2713 Push completed successfully`);
7730
- resolve6();
8664
+ resolve7();
7731
8665
  } else {
7732
8666
  console.log(`[git-push] \u2717 Push failed with error: ${err || "Unknown error"}`);
7733
8667
  reject(new Error(err || "Failed to push changes"));
@@ -7742,7 +8676,7 @@ function pushToOrigin(gitRoot, currentBranch) {
7742
8676
  });
7743
8677
  }
7744
8678
  function fetchFromOrigin(gitRoot) {
7745
- return new Promise((resolve6, reject) => {
8679
+ return new Promise((resolve7, reject) => {
7746
8680
  const proc = spawnProcess("git", ["fetch", "origin"], { cwd: gitRoot });
7747
8681
  let err = "";
7748
8682
  if (proc.stderr) {
@@ -7751,14 +8685,14 @@ function fetchFromOrigin(gitRoot) {
7751
8685
  });
7752
8686
  }
7753
8687
  proc.on("close", (code) => {
7754
- if (code === 0) resolve6();
8688
+ if (code === 0) resolve7();
7755
8689
  else reject(new Error(err || "Failed to fetch from origin"));
7756
8690
  });
7757
8691
  proc.on("error", reject);
7758
8692
  });
7759
8693
  }
7760
8694
  function pullFromOrigin(gitRoot) {
7761
- return new Promise((resolve6, reject) => {
8695
+ return new Promise((resolve7, reject) => {
7762
8696
  const proc = spawnProcess("git", ["pull"], { cwd: gitRoot });
7763
8697
  let err = "";
7764
8698
  if (proc.stderr) {
@@ -7767,14 +8701,14 @@ function pullFromOrigin(gitRoot) {
7767
8701
  });
7768
8702
  }
7769
8703
  proc.on("close", (code) => {
7770
- if (code === 0) resolve6();
8704
+ if (code === 0) resolve7();
7771
8705
  else reject(new Error(err || "Failed to pull from origin"));
7772
8706
  });
7773
8707
  proc.on("error", reject);
7774
8708
  });
7775
8709
  }
7776
8710
  function createGitHubRepo(gitRoot, repoName, description, isPrivate) {
7777
- return new Promise((resolve6, reject) => {
8711
+ return new Promise((resolve7, reject) => {
7778
8712
  const args2 = ["repo", "create"];
7779
8713
  if (repoName) {
7780
8714
  args2.push(repoName);
@@ -7805,7 +8739,7 @@ function createGitHubRepo(gitRoot, repoName, description, isPrivate) {
7805
8739
  if (code === 0) {
7806
8740
  invalidateRepoInfoCache(gitRoot);
7807
8741
  const urlMatch = out.match(/https:\/\/[^\s]+/);
7808
- resolve6(urlMatch ? urlMatch[0] : out.trim());
8742
+ resolve7(urlMatch ? urlMatch[0] : out.trim());
7809
8743
  } else {
7810
8744
  reject(new Error(err || "Failed to create repository"));
7811
8745
  }
@@ -7817,7 +8751,7 @@ function createGitHubRepo(gitRoot, repoName, description, isPrivate) {
7817
8751
  });
7818
8752
  }
7819
8753
  function createPullRequest(gitRoot, title, description) {
7820
- return new Promise((resolve6, reject) => {
8754
+ return new Promise((resolve7, reject) => {
7821
8755
  const args2 = ["pr", "create", "--title", title ?? "", "--body", description ?? ""];
7822
8756
  console.log(`[git-create-pr] Executing: gh ${args2.join(" ")}`);
7823
8757
  const proc = spawnProcess("gh", args2, { cwd: gitRoot });
@@ -7842,7 +8776,7 @@ function createPullRequest(gitRoot, title, description) {
7842
8776
  const urlMatch = out.match(/https:\/\/[^\s]+/);
7843
8777
  const prUrl = urlMatch ? urlMatch[0] : out.trim();
7844
8778
  console.log(`[git-create-pr] \u2713 PR URL extracted: ${prUrl}`);
7845
- resolve6(prUrl);
8779
+ resolve7(prUrl);
7846
8780
  } else {
7847
8781
  console.log(`[git-create-pr] \u2717 gh pr create failed with error: ${err || "Unknown error"}`);
7848
8782
  reject(new Error(err || "Failed to create PR"));
@@ -7881,7 +8815,7 @@ async function getPullRequestStatus(gitRoot) {
7881
8815
  }
7882
8816
  throw new Error(result.stderr || "Failed to check PR status");
7883
8817
  }
7884
- return new Promise((resolve6, reject) => {
8818
+ return new Promise((resolve7, reject) => {
7885
8819
  const args2 = ["pr", "view", "--json", "url,state"];
7886
8820
  const proc = spawnProcess("gh", args2, { cwd: gitRoot });
7887
8821
  let out = "";
@@ -7900,7 +8834,7 @@ async function getPullRequestStatus(gitRoot) {
7900
8834
  if (code === 0) {
7901
8835
  try {
7902
8836
  const prData = JSON.parse(out.trim());
7903
- resolve6({
8837
+ resolve7({
7904
8838
  exists: true,
7905
8839
  url: prData.url,
7906
8840
  state: prData.state
@@ -7910,7 +8844,7 @@ async function getPullRequestStatus(gitRoot) {
7910
8844
  }
7911
8845
  } else {
7912
8846
  if (err.includes("no pull requests") || err.includes("not found")) {
7913
- resolve6({ exists: false });
8847
+ resolve7({ exists: false });
7914
8848
  } else {
7915
8849
  reject(new Error(err || "Failed to check PR status"));
7916
8850
  }
@@ -8097,7 +9031,7 @@ function buildCrossHostRedirectResult(redirectUrl) {
8097
9031
 
8098
9032
  // src/features/web-fetch/web-fetch.client.ts
8099
9033
  import { mkdir, writeFile as writeFile2 } from "fs/promises";
8100
- import { join as join11, extname as extname3 } from "path";
9034
+ import { join as join13, extname as extname3 } from "path";
8101
9035
  import { createHash as createHash2 } from "crypto";
8102
9036
 
8103
9037
  // src/features/web-fetch/web-fetch-cache.ts
@@ -8202,7 +9136,7 @@ async function readResponseBody(response, signal) {
8202
9136
  return { body, bytes: body.length };
8203
9137
  }
8204
9138
  async function saveBinaryContent(body, contentType, url) {
8205
- const downloadsDir = join11(getDataDir(), "fetch-downloads");
9139
+ const downloadsDir = join13(getDataDir(), "fetch-downloads");
8206
9140
  await mkdir(downloadsDir, { recursive: true });
8207
9141
  const hash = createHash2("sha256").update(url).digest("hex").slice(0, 12);
8208
9142
  let extension = extname3(new URL(url).pathname);
@@ -8217,7 +9151,7 @@ async function saveBinaryContent(body, contentType, url) {
8217
9151
  if (!extension) {
8218
9152
  extension = ".bin";
8219
9153
  }
8220
- const filePath = join11(downloadsDir, `${hash}${extension}`);
9154
+ const filePath = join13(downloadsDir, `${hash}${extension}`);
8221
9155
  await writeFile2(filePath, body);
8222
9156
  return filePath;
8223
9157
  }
@@ -8378,9 +9312,9 @@ function truncateFetchUrl(url, maxLength = 80) {
8378
9312
  }
8379
9313
 
8380
9314
  // src/tools/fetch.ts
8381
- var fetchSchema = Type17.Object({
8382
- url: Type17.String({ description: "Fully qualified URL to fetch (http or https)" }),
8383
- prompt: Type17.String({
9315
+ var fetchSchema = Type18.Object({
9316
+ url: Type18.String({ description: "Fully qualified URL to fetch (http or https)" }),
9317
+ prompt: Type18.String({
8384
9318
  description: "What to extract or summarize from the page content"
8385
9319
  })
8386
9320
  });
@@ -8477,7 +9411,7 @@ function createFetchTool(options) {
8477
9411
  }
8478
9412
 
8479
9413
  // src/tools/web-search.ts
8480
- import { Type as Type18 } from "@sinclair/typebox";
9414
+ import { Type as Type19 } from "@sinclair/typebox";
8481
9415
 
8482
9416
  // src/features/web-search/web-search.client.ts
8483
9417
  var WEB_SEARCH_API = "https://api.webnative.dev/websearch";
@@ -8547,37 +9481,37 @@ async function searchWeb(options) {
8547
9481
  }
8548
9482
 
8549
9483
  // src/tools/web-search.ts
8550
- var searchTypeSchema = Type18.Union([
8551
- Type18.Literal("auto"),
8552
- Type18.Literal("fast"),
8553
- Type18.Literal("instant"),
8554
- Type18.Literal("deep-lite"),
8555
- Type18.Literal("deep"),
8556
- Type18.Literal("deep-reasoning")
9484
+ var searchTypeSchema = Type19.Union([
9485
+ Type19.Literal("auto"),
9486
+ Type19.Literal("fast"),
9487
+ Type19.Literal("instant"),
9488
+ Type19.Literal("deep-lite"),
9489
+ Type19.Literal("deep"),
9490
+ Type19.Literal("deep-reasoning")
8557
9491
  ]);
8558
- var webSearchSchema = Type18.Object({
8559
- query: Type18.String({
9492
+ var webSearchSchema = Type19.Object({
9493
+ query: Type19.String({
8560
9494
  description: "Natural language search query (e.g. 'Next.js route handler authentication example')"
8561
9495
  }),
8562
- numResults: Type18.Optional(
8563
- Type18.Number({
9496
+ numResults: Type19.Optional(
9497
+ Type19.Number({
8564
9498
  description: "Number of results to return (1-100, default 10)",
8565
9499
  minimum: 1,
8566
9500
  maximum: 100
8567
9501
  })
8568
9502
  ),
8569
- type: Type18.Optional(
8570
- Type18.Union([searchTypeSchema], {
9503
+ type: Type19.Optional(
9504
+ Type19.Union([searchTypeSchema], {
8571
9505
  description: "Search depth/latency tradeoff. Use auto (default) for most queries; fast/instant for low latency; deep variants for research."
8572
9506
  })
8573
9507
  ),
8574
- includeDomains: Type18.Optional(
8575
- Type18.Array(Type18.String(), {
9508
+ includeDomains: Type19.Optional(
9509
+ Type19.Array(Type19.String(), {
8576
9510
  description: "Only return results from these domains (e.g. ['docs.github.com', 'nodejs.org'])"
8577
9511
  })
8578
9512
  ),
8579
- excludeDomains: Type18.Optional(
8580
- Type18.Array(Type18.String(), {
9513
+ excludeDomains: Type19.Optional(
9514
+ Type19.Array(Type19.String(), {
8581
9515
  description: "Exclude results from these domains"
8582
9516
  })
8583
9517
  )
@@ -8651,13 +9585,13 @@ function createWebSearchTool() {
8651
9585
  }
8652
9586
 
8653
9587
  // src/tools/tool-search.ts
8654
- import { Type as Type19 } from "@sinclair/typebox";
8655
- var toolSearchSchema = Type19.Object({
8656
- query: Type19.String({
9588
+ import { Type as Type20 } from "@sinclair/typebox";
9589
+ var toolSearchSchema = Type20.Object({
9590
+ query: Type20.String({
8657
9591
  description: 'Search query to find tools. Use "select:name1,name2" to fetch exact tools by name, or keywords to search by description.'
8658
9592
  }),
8659
- max_results: Type19.Optional(
8660
- Type19.Number({
9593
+ max_results: Type20.Optional(
9594
+ Type20.Number({
8661
9595
  description: "Maximum number of results to return (default: 5)"
8662
9596
  })
8663
9597
  )
@@ -8785,6 +9719,7 @@ var toolRegistry = [
8785
9719
  { name: "agent", enabled: true, displayName: "Subagent", activeDisplayName: "Subagent" },
8786
9720
  { name: "ask_user", enabled: true, displayName: "Ask", activeDisplayName: "Asking" },
8787
9721
  { name: "bash", enabled: true, displayName: "Ran", activeDisplayName: "Running" },
9722
+ { name: "browser", enabled: true, displayName: "Browser", activeDisplayName: "Browsing" },
8788
9723
  {
8789
9724
  name: "code_search",
8790
9725
  enabled: false,
@@ -8971,7 +9906,7 @@ var WebFetchPermissionGate = class {
8971
9906
  wildcardOptions: buildWildcardOptions2(normalized)
8972
9907
  };
8973
9908
  this.config.onPermissionRequired?.(request);
8974
- return new Promise((resolve6, reject) => {
9909
+ return new Promise((resolve7, reject) => {
8975
9910
  const cleanup = () => {
8976
9911
  pendingPermissions.delete(toolCallId);
8977
9912
  signal?.removeEventListener("abort", onAbort);
@@ -8989,7 +9924,7 @@ var WebFetchPermissionGate = class {
8989
9924
  pendingPermissions.set(toolCallId, {
8990
9925
  resolve: (decision) => {
8991
9926
  cleanup();
8992
- void this.applyDecision(decision, normalized).then(() => resolve6(decision));
9927
+ void this.applyDecision(decision, normalized).then(() => resolve7(decision));
8993
9928
  },
8994
9929
  reject: (error) => {
8995
9930
  cleanup();
@@ -9135,7 +10070,10 @@ async function createCodingTools(cwd, options) {
9135
10070
  if (options?.metadataManager && isToolEnabled("generate_image")) {
9136
10071
  coreTools.push(createGenerateImageTool({ metadataManager: options.metadataManager, cwd }));
9137
10072
  }
9138
- const optionalTools = filterEnabledTools([createFindImagesTool(cwd)]);
10073
+ const optionalTools = filterEnabledTools([
10074
+ createFindImagesTool({ cwd, threadId: options?.threadId }),
10075
+ createBrowserTool({ cwd, threadId: options?.threadId })
10076
+ ]);
9139
10077
  if (options?.skills && options.skills.length > 0) {
9140
10078
  optionalTools.push(
9141
10079
  ...filterEnabledTools([
@@ -9214,7 +10152,7 @@ function createAllTools(cwd, options) {
9214
10152
  });
9215
10153
  }
9216
10154
  if (isToolEnabled("find_images")) {
9217
- tools.find_images = createFindImagesTool(cwd);
10155
+ tools.find_images = createFindImagesTool({ cwd, threadId: options?.threadId });
9218
10156
  }
9219
10157
  if (options?.threadId) {
9220
10158
  for (const [name, tool] of Object.entries(tools)) {
@@ -9248,10 +10186,10 @@ async function* loadCodingToolsWithMcpStatus(cwd, options) {
9248
10186
  if (completed) {
9249
10187
  break;
9250
10188
  }
9251
- await new Promise((resolve6) => {
9252
- notify = resolve6;
10189
+ await new Promise((resolve7) => {
10190
+ notify = resolve7;
9253
10191
  if (completed) {
9254
- resolve6();
10192
+ resolve7();
9255
10193
  }
9256
10194
  });
9257
10195
  notify = null;
@@ -9263,7 +10201,7 @@ async function* loadCodingToolsWithMcpStatus(cwd, options) {
9263
10201
  }
9264
10202
 
9265
10203
  // src/tools/agent-tool.ts
9266
- import { Type as Type20 } from "@sinclair/typebox";
10204
+ import { Type as Type21 } from "@sinclair/typebox";
9267
10205
 
9268
10206
  // src/agent/agent.subagent-executor.ts
9269
10207
  import { Agent } from "@earendil-works/pi-agent-core";
@@ -9381,7 +10319,7 @@ var EventQueue = class {
9381
10319
  content: resultContent,
9382
10320
  isError: event.isError
9383
10321
  };
9384
- if (event.toolName === "generate_image") {
10322
+ if (event.toolName === "generate_image" || event.toolName === "find_images" || event.toolName === "browser") {
9385
10323
  toolResultBlock.content = stripGeneratedImageText(resultContent);
9386
10324
  const details = event.result?.details;
9387
10325
  if (details?.imageUrl) {
@@ -9588,8 +10526,8 @@ async function executeSubagent(options) {
9588
10526
  const e = eventQueue.shift();
9589
10527
  if (e) onEvent(e);
9590
10528
  } else {
9591
- await new Promise((resolve6) => {
9592
- eventQueue.setResolver(resolve6);
10529
+ await new Promise((resolve7) => {
10530
+ eventQueue.setResolver(resolve7);
9593
10531
  });
9594
10532
  }
9595
10533
  }
@@ -9613,15 +10551,15 @@ async function executeSubagent(options) {
9613
10551
  }
9614
10552
 
9615
10553
  // src/tools/agent-tool.ts
9616
- var agentToolSchema = Type20.Object({
9617
- prompt: Type20.String({ description: "The task or question for the subagent to work on" }),
9618
- agentName: Type20.Optional(
9619
- Type20.String({
10554
+ var agentToolSchema = Type21.Object({
10555
+ prompt: Type21.String({ description: "The task or question for the subagent to work on" }),
10556
+ agentName: Type21.Optional(
10557
+ Type21.String({
9620
10558
  description: "Name of a defined agent (from AGENT.md) to use. If omitted, a general-purpose subagent is created."
9621
10559
  })
9622
10560
  ),
9623
- description: Type20.Optional(
9624
- Type20.String({
10561
+ description: Type21.Optional(
10562
+ Type21.String({
9625
10563
  description: "A short (3-5 word) label describing what the subagent will do"
9626
10564
  })
9627
10565
  )
@@ -9732,7 +10670,7 @@ import { readFileSync as readFileSync5 } from "fs";
9732
10670
 
9733
10671
  // src/project-analyzer.ts
9734
10672
  import { readFileSync as readFileSync4, existsSync as existsSync9 } from "fs";
9735
- import { join as join12 } from "path";
10673
+ import { join as join14 } from "path";
9736
10674
  var ProjectAnalyzer = class {
9737
10675
  projectPath;
9738
10676
  constructor(projectPath = process.cwd()) {
@@ -9743,7 +10681,7 @@ var ProjectAnalyzer = class {
9743
10681
  return this.generateDescription(info);
9744
10682
  }
9745
10683
  getProjectInfo() {
9746
- const packageJsonPath = join12(this.projectPath, "package.json");
10684
+ const packageJsonPath = join14(this.projectPath, "package.json");
9747
10685
  const info = { description: "" };
9748
10686
  if (existsSync9(packageJsonPath)) {
9749
10687
  const packageJson = JSON.parse(readFileSync4(packageJsonPath, "utf-8"));
@@ -9785,15 +10723,15 @@ var ProjectAnalyzer = class {
9785
10723
  }
9786
10724
  }
9787
10725
  detectBuildTool(_scripts = {}, deps, info) {
9788
- if (deps.vite || existsSync9(join12(this.projectPath, "vite.config.js")) || existsSync9(join12(this.projectPath, "vite.config.ts"))) {
10726
+ if (deps.vite || existsSync9(join14(this.projectPath, "vite.config.js")) || existsSync9(join14(this.projectPath, "vite.config.ts"))) {
9789
10727
  info.buildTool = "Vite";
9790
10728
  return;
9791
10729
  }
9792
- if (deps.webpack || existsSync9(join12(this.projectPath, "webpack.config.js"))) {
10730
+ if (deps.webpack || existsSync9(join14(this.projectPath, "webpack.config.js"))) {
9793
10731
  info.buildTool = "Webpack";
9794
10732
  return;
9795
10733
  }
9796
- if (deps.rollup || existsSync9(join12(this.projectPath, "rollup.config.js"))) {
10734
+ if (deps.rollup || existsSync9(join14(this.projectPath, "rollup.config.js"))) {
9797
10735
  info.buildTool = "Rollup";
9798
10736
  return;
9799
10737
  }
@@ -9843,40 +10781,40 @@ var ProjectAnalyzer = class {
9843
10781
  info.uiLibraries = [...new Set(uiLibs)].filter(Boolean);
9844
10782
  }
9845
10783
  detectProjectType(info) {
9846
- if (existsSync9(join12(this.projectPath, ".xcodeproj")) || existsSync9(join12(this.projectPath, ".xcworkspace")) || existsSync9(join12(this.projectPath, "project.pbxproj"))) {
10784
+ if (existsSync9(join14(this.projectPath, ".xcodeproj")) || existsSync9(join14(this.projectPath, ".xcworkspace")) || existsSync9(join14(this.projectPath, "project.pbxproj"))) {
9847
10785
  info.projectType = "Xcode";
9848
- } else if (existsSync9(join12(this.projectPath, "build.gradle")) || existsSync9(join12(this.projectPath, "build.gradle.kts")) || existsSync9(join12(this.projectPath, "app/build.gradle")) || existsSync9(join12(this.projectPath, "settings.gradle"))) {
10786
+ } else if (existsSync9(join14(this.projectPath, "build.gradle")) || existsSync9(join14(this.projectPath, "build.gradle.kts")) || existsSync9(join14(this.projectPath, "app/build.gradle")) || existsSync9(join14(this.projectPath, "settings.gradle"))) {
9849
10787
  info.projectType = "Android Studio";
9850
- } else if (existsSync9(join12(this.projectPath, "pubspec.yaml"))) {
10788
+ } else if (existsSync9(join14(this.projectPath, "pubspec.yaml"))) {
9851
10789
  info.projectType = "Flutter";
9852
- } else if (existsSync9(join12(this.projectPath, "go.mod"))) {
10790
+ } else if (existsSync9(join14(this.projectPath, "go.mod"))) {
9853
10791
  info.projectType = "Go";
9854
- } else if (existsSync9(join12(this.projectPath, "Cargo.toml"))) {
10792
+ } else if (existsSync9(join14(this.projectPath, "Cargo.toml"))) {
9855
10793
  info.projectType = "Rust";
9856
- } else if (existsSync9(join12(this.projectPath, "requirements.txt")) || existsSync9(join12(this.projectPath, "pyproject.toml")) || existsSync9(join12(this.projectPath, "setup.py"))) {
10794
+ } else if (existsSync9(join14(this.projectPath, "requirements.txt")) || existsSync9(join14(this.projectPath, "pyproject.toml")) || existsSync9(join14(this.projectPath, "setup.py"))) {
9857
10795
  info.projectType = "Python";
9858
- } else if (existsSync9(join12(this.projectPath, "Gemfile"))) {
10796
+ } else if (existsSync9(join14(this.projectPath, "Gemfile"))) {
9859
10797
  info.projectType = "Ruby";
9860
- } else if (existsSync9(join12(this.projectPath, "composer.json"))) {
10798
+ } else if (existsSync9(join14(this.projectPath, "composer.json"))) {
9861
10799
  info.projectType = "PHP";
9862
- } else if (existsSync9(join12(this.projectPath, "pom.xml")) || existsSync9(join12(this.projectPath, "build.xml"))) {
10800
+ } else if (existsSync9(join14(this.projectPath, "pom.xml")) || existsSync9(join14(this.projectPath, "build.xml"))) {
9863
10801
  info.projectType = "Java";
9864
- } else if (existsSync9(join12(this.projectPath, ".csproj")) || existsSync9(join12(this.projectPath, "project.json"))) {
10802
+ } else if (existsSync9(join14(this.projectPath, ".csproj")) || existsSync9(join14(this.projectPath, "project.json"))) {
9865
10803
  info.projectType = ".NET";
9866
10804
  }
9867
10805
  }
9868
10806
  detectPackageManager(info) {
9869
- if (existsSync9(join12(this.projectPath, "bun.lockb"))) {
10807
+ if (existsSync9(join14(this.projectPath, "bun.lockb"))) {
9870
10808
  info.packageManager = "Bun";
9871
- } else if (existsSync9(join12(this.projectPath, "bun.lock"))) {
10809
+ } else if (existsSync9(join14(this.projectPath, "bun.lock"))) {
9872
10810
  info.packageManager = "Bun";
9873
- } else if (existsSync9(join12(this.projectPath, "pnpm-lock.yaml"))) {
10811
+ } else if (existsSync9(join14(this.projectPath, "pnpm-lock.yaml"))) {
9874
10812
  info.packageManager = "pnpm";
9875
- } else if (existsSync9(join12(this.projectPath, "yarn.lock"))) {
10813
+ } else if (existsSync9(join14(this.projectPath, "yarn.lock"))) {
9876
10814
  info.packageManager = "Yarn";
9877
- } else if (existsSync9(join12(this.projectPath, "package-lock.json"))) {
10815
+ } else if (existsSync9(join14(this.projectPath, "package-lock.json"))) {
9878
10816
  info.packageManager = "npm";
9879
- } else if (existsSync9(join12(this.projectPath, "npm-shrinkwrap.json"))) {
10817
+ } else if (existsSync9(join14(this.projectPath, "npm-shrinkwrap.json"))) {
9880
10818
  info.packageManager = "npm";
9881
10819
  }
9882
10820
  }
@@ -9938,8 +10876,8 @@ function analyzeProject(projectPath) {
9938
10876
  }
9939
10877
 
9940
10878
  // src/features/rules/rules.manager.ts
9941
- import { readdir as readdir3, readFile as readFile5 } from "fs/promises";
9942
- import { join as join13, relative as relative4 } from "path";
10879
+ import { readdir as readdir3, readFile as readFile6 } from "fs/promises";
10880
+ import { join as join15, relative as relative4 } from "path";
9943
10881
  import { existsSync as existsSync10 } from "fs";
9944
10882
  import { homedir as homedir5 } from "os";
9945
10883
  function parseRuleFrontmatter(markdown) {
@@ -9983,10 +10921,10 @@ function parseRuleFrontmatter(markdown) {
9983
10921
  return { metadata, content };
9984
10922
  }
9985
10923
  function getGlobalRulesDir() {
9986
- return join13(homedir5(), ".agents", "rules");
10924
+ return join15(homedir5(), ".agents", "rules");
9987
10925
  }
9988
10926
  function getProjectRulesDir(threadPath) {
9989
- return join13(threadPath, ".agents", "rules");
10927
+ return join15(threadPath, ".agents", "rules");
9990
10928
  }
9991
10929
  var RuleManager = class {
9992
10930
  /**
@@ -10019,13 +10957,13 @@ var RuleManager = class {
10019
10957
  try {
10020
10958
  const entries = await readdir3(dir, { withFileTypes: true });
10021
10959
  for (const entry of entries) {
10022
- const fullPath = join13(dir, entry.name);
10960
+ const fullPath = join15(dir, entry.name);
10023
10961
  if (entry.isDirectory()) {
10024
10962
  const subRules = await this.loadRulesFromDir(fullPath, threadPath, scope);
10025
10963
  rules.push(...subRules);
10026
10964
  } else if (entry.isFile() && (entry.name.endsWith(".md") || entry.name.endsWith(".mdc"))) {
10027
10965
  try {
10028
- const fileContent = await readFile5(fullPath, "utf-8");
10966
+ const fileContent = await readFile6(fullPath, "utf-8");
10029
10967
  const { metadata, content } = parseRuleFrontmatter(fileContent);
10030
10968
  const ruleName = entry.name.replace(/\.(md|mdc)$/, "");
10031
10969
  const relativePath = relative4(threadPath, fullPath);
@@ -10527,7 +11465,7 @@ var ShellPermissionGate = class {
10527
11465
  wildcardOptions: buildWildcardOptions(command)
10528
11466
  };
10529
11467
  this.config.onPermissionRequired?.(request);
10530
- return new Promise((resolve6, reject) => {
11468
+ return new Promise((resolve7, reject) => {
10531
11469
  const cleanup = () => {
10532
11470
  pendingPermissions2.delete(toolCallId);
10533
11471
  signal?.removeEventListener("abort", onAbort);
@@ -10545,7 +11483,7 @@ var ShellPermissionGate = class {
10545
11483
  pendingPermissions2.set(toolCallId, {
10546
11484
  resolve: (decision, pattern) => {
10547
11485
  cleanup();
10548
- void this.applyDecision(decision, command, pattern).then(() => resolve6(decision));
11486
+ void this.applyDecision(decision, command, pattern).then(() => resolve7(decision));
10549
11487
  },
10550
11488
  reject: (error) => {
10551
11489
  cleanup();
@@ -10974,8 +11912,8 @@ ${userPrompt}`;
10974
11912
  if (eventQueue.length > 0) {
10975
11913
  yield eventQueue.shift();
10976
11914
  } else {
10977
- await new Promise((resolve6) => {
10978
- eventQueue.setResolver(resolve6);
11915
+ await new Promise((resolve7) => {
11916
+ eventQueue.setResolver(resolve7);
10979
11917
  });
10980
11918
  }
10981
11919
  }
@@ -11131,14 +12069,14 @@ var ProcessingStateManagerImpl = class {
11131
12069
 
11132
12070
  // src/core/env-manager.ts
11133
12071
  import { promises as fs } from "fs";
11134
- import { join as join14 } from "path";
12072
+ import { join as join16 } from "path";
11135
12073
  function updateRuntimeEnv(values) {
11136
12074
  for (const [key, value] of Object.entries(values)) {
11137
12075
  process.env[key] = value;
11138
12076
  }
11139
12077
  }
11140
12078
  async function updateEnvFile(keyNames) {
11141
- const envPath = join14(process.cwd(), ".env");
12079
+ const envPath = join16(process.cwd(), ".env");
11142
12080
  let content = "";
11143
12081
  try {
11144
12082
  content = await fs.readFile(envPath, "utf-8");
@@ -11167,7 +12105,7 @@ async function updateEnvFile(keyNames) {
11167
12105
  await fs.writeFile(envPath, newLines.join("\n") + "\n", "utf-8");
11168
12106
  }
11169
12107
  async function readEnvFile() {
11170
- const envPath = join14(process.cwd(), ".env");
12108
+ const envPath = join16(process.cwd(), ".env");
11171
12109
  const envMap = {};
11172
12110
  try {
11173
12111
  const content = await fs.readFile(envPath, "utf-8");
@@ -11190,7 +12128,7 @@ async function readEnvFile() {
11190
12128
  import { Hono } from "hono";
11191
12129
 
11192
12130
  // src/agent/agent-run-guard.ts
11193
- import { randomUUID as randomUUID3 } from "crypto";
12131
+ import { randomUUID as randomUUID4 } from "crypto";
11194
12132
  var DEFAULT_LONG_RUNNING_TURN_LIMIT = 50;
11195
12133
  var LONG_RUNNING_CONFIRMATION_QUESTION = "The agent has been working for a long time. Do you want to continue?";
11196
12134
  var LONG_RUNNING_CONFIRMATION_OPTIONS = ["Yes", "No"];
@@ -11232,7 +12170,7 @@ function shouldPromptForLongRunningConfirmation(state) {
11232
12170
  return state.turnCount >= state.maximumTurnCount;
11233
12171
  }
11234
12172
  function createLongRunningConfirmationRequest(maximumTurnCount) {
11235
- const toolCallId = `long-running-confirmation-${randomUUID3()}`;
12173
+ const toolCallId = `long-running-confirmation-${randomUUID4()}`;
11236
12174
  const event = {
11237
12175
  type: "long_running_confirmation",
11238
12176
  prompt: {
@@ -11258,7 +12196,7 @@ function createLongRunningConfirmationRequest(maximumTurnCount) {
11258
12196
  toolCallId,
11259
12197
  event,
11260
12198
  waitForAnswer: (signal) => {
11261
- return new Promise((resolve6, reject) => {
12199
+ return new Promise((resolve7, reject) => {
11262
12200
  const resolvers = getLongRunningResolvers();
11263
12201
  const cleanup = () => {
11264
12202
  resolvers.delete(toolCallId);
@@ -11281,7 +12219,7 @@ function createLongRunningConfirmationRequest(maximumTurnCount) {
11281
12219
  resolvers.set(toolCallId, {
11282
12220
  resolve: (answer) => {
11283
12221
  cleanup();
11284
- resolve6(answer);
12222
+ resolve7(answer);
11285
12223
  },
11286
12224
  reject: (error) => {
11287
12225
  cleanup();
@@ -11608,11 +12546,11 @@ function createWebFetchPermissionRoutes() {
11608
12546
  import { Hono as Hono4 } from "hono";
11609
12547
 
11610
12548
  // src/features/chat/chat-post.route.ts
11611
- import { randomUUID as randomUUID6 } from "crypto";
12549
+ import { randomUUID as randomUUID7 } from "crypto";
11612
12550
 
11613
12551
  // src/features/skills/skills.manager.ts
11614
- import { readdir as readdir4, readFile as readFile6 } from "fs/promises";
11615
- import { join as join15 } from "path";
12552
+ import { readdir as readdir4, readFile as readFile7 } from "fs/promises";
12553
+ import { join as join17 } from "path";
11616
12554
  import { existsSync as existsSync11 } from "fs";
11617
12555
  import { homedir as homedir6 } from "os";
11618
12556
  function parseFrontmatter(markdown) {
@@ -11660,10 +12598,10 @@ function parseFrontmatter(markdown) {
11660
12598
  return { metadata, content };
11661
12599
  }
11662
12600
  function getGlobalSkillsDir() {
11663
- return join15(homedir6(), ".agents", "skills");
12601
+ return join17(homedir6(), ".agents", "skills");
11664
12602
  }
11665
12603
  function getProjectSkillsDir(threadPath) {
11666
- return join15(threadPath, ".agents", "skills");
12604
+ return join17(threadPath, ".agents", "skills");
11667
12605
  }
11668
12606
  function validateSkillName(name) {
11669
12607
  if (!name || name.length === 0 || name.length > 64) {
@@ -11716,14 +12654,14 @@ var SkillManager = class {
11716
12654
  for (const entry of entries) {
11717
12655
  if (!entry.isDirectory()) continue;
11718
12656
  const skillDirName = entry.name;
11719
- const skillPath = join15(dir, skillDirName);
11720
- const skillFilePath = join15(skillPath, "SKILL.md");
12657
+ const skillPath = join17(dir, skillDirName);
12658
+ const skillFilePath = join17(skillPath, "SKILL.md");
11721
12659
  if (!existsSync11(skillFilePath)) {
11722
12660
  console.warn(`Skipping skill directory ${skillDirName}: SKILL.md not found`);
11723
12661
  continue;
11724
12662
  }
11725
12663
  try {
11726
- const fileContent = await readFile6(skillFilePath, "utf-8");
12664
+ const fileContent = await readFile7(skillFilePath, "utf-8");
11727
12665
  const { metadata, content } = parseFrontmatter(fileContent);
11728
12666
  if (!metadata.name) {
11729
12667
  console.warn(`Skipping skill in ${skillDirName}: missing 'name' in frontmatter`);
@@ -11896,8 +12834,8 @@ async function activateSkills(allSkills, taskDescription, thread, options) {
11896
12834
  }
11897
12835
 
11898
12836
  // src/features/agents/agents.manager.ts
11899
- import { readdir as readdir5, readFile as readFile7 } from "fs/promises";
11900
- import { join as join16 } from "path";
12837
+ import { readdir as readdir5, readFile as readFile8 } from "fs/promises";
12838
+ import { join as join18 } from "path";
11901
12839
  import { existsSync as existsSync12 } from "fs";
11902
12840
  import { homedir as homedir7 } from "os";
11903
12841
  function parseFrontmatter2(markdown) {
@@ -11955,10 +12893,10 @@ function validateDescription2(description) {
11955
12893
  return !!description && description.length > 0 && description.length <= 1024;
11956
12894
  }
11957
12895
  function getGlobalAgentsDir() {
11958
- return join16(homedir7(), ".agents", "agents");
12896
+ return join18(homedir7(), ".agents", "agents");
11959
12897
  }
11960
12898
  function getProjectAgentsDir(threadPath) {
11961
- return join16(threadPath, ".agents", "agents");
12899
+ return join18(threadPath, ".agents", "agents");
11962
12900
  }
11963
12901
  var AgentsManager = class {
11964
12902
  /**
@@ -11990,14 +12928,14 @@ var AgentsManager = class {
11990
12928
  for (const entry of entries) {
11991
12929
  if (!entry.isDirectory()) continue;
11992
12930
  const agentDirName = entry.name;
11993
- const agentPath = join16(dir, agentDirName);
11994
- const agentFilePath = join16(agentPath, "AGENT.md");
12931
+ const agentPath = join18(dir, agentDirName);
12932
+ const agentFilePath = join18(agentPath, "AGENT.md");
11995
12933
  if (!existsSync12(agentFilePath)) {
11996
12934
  console.warn(`[agents] Skipping agent directory ${agentDirName}: AGENT.md not found`);
11997
12935
  continue;
11998
12936
  }
11999
12937
  try {
12000
- const fileContent = await readFile7(agentFilePath, "utf-8");
12938
+ const fileContent = await readFile8(agentFilePath, "utf-8");
12001
12939
  const { metadata, content } = parseFrontmatter2(fileContent);
12002
12940
  if (!metadata.name) {
12003
12941
  console.warn(
@@ -12180,12 +13118,12 @@ function extractAssistantContent(events, fallback) {
12180
13118
 
12181
13119
  // src/features/chat/chat-post.route.ts
12182
13120
  init_database();
12183
- import { readFile as readFile8, unlink } from "fs/promises";
12184
- import { join as join17 } from "path";
13121
+ import { readFile as readFile9, unlink as unlink2 } from "fs/promises";
13122
+ import { join as join19 } from "path";
12185
13123
 
12186
13124
  // src/features/project-todos/project-todos.database.ts
12187
13125
  init_database();
12188
- import { randomUUID as randomUUID4 } from "crypto";
13126
+ import { randomUUID as randomUUID5 } from "crypto";
12189
13127
  function serializeTodo(row) {
12190
13128
  return {
12191
13129
  ...row,
@@ -12210,7 +13148,7 @@ async function getTodoById(db, todoId) {
12210
13148
  }
12211
13149
  async function insertTodo(projectId, title, description) {
12212
13150
  const db = await getDatabase();
12213
- const id = randomUUID4();
13151
+ const id = randomUUID5();
12214
13152
  const now = (/* @__PURE__ */ new Date()).toISOString();
12215
13153
  await db.execute({
12216
13154
  sql: `INSERT INTO project_todos (id, projectId, title, description, status, working, createdAt, updatedAt)
@@ -12332,7 +13270,7 @@ async function invalidateGitStatusCache(db, threadId) {
12332
13270
 
12333
13271
  // src/features/account/account-get-info.route.ts
12334
13272
  init_database();
12335
- import { randomUUID as randomUUID5 } from "crypto";
13273
+ import { randomUUID as randomUUID6 } from "crypto";
12336
13274
  var KEY_BALANCE = "PromptBalance";
12337
13275
  var KEY_VERIFICATION = "PromptBalanceVerification";
12338
13276
  var KEY_PLAN = "Plan";
@@ -12378,7 +13316,7 @@ async function getOrCreateClientReferenceId() {
12378
13316
  if (existing !== null && existing !== void 0) {
12379
13317
  return existing;
12380
13318
  }
12381
- const id = randomUUID5();
13319
+ const id = randomUUID6();
12382
13320
  await setState(db, KEY_CLIENT_REFERENCE_ID, id);
12383
13321
  return id;
12384
13322
  }
@@ -12659,7 +13597,7 @@ async function deserializeProject(db, row) {
12659
13597
  // src/features/chat/chat-post.route.ts
12660
13598
  init_utils();
12661
13599
  function runValidationScript(script, cwd) {
12662
- return new Promise((resolve6) => {
13600
+ return new Promise((resolve7) => {
12663
13601
  const child = spawnShell(script, { cwd, stdio: ["pipe", "pipe", "pipe"] });
12664
13602
  let output = "";
12665
13603
  const stdoutDecoder = new TextDecoder();
@@ -12673,12 +13611,12 @@ function runValidationScript(script, cwd) {
12673
13611
  child.on("close", (code) => {
12674
13612
  output += stdoutDecoder.decode();
12675
13613
  output += stderrDecoder.decode();
12676
- resolve6({ exitCode: code ?? 1, output: output.trim() });
13614
+ resolve7({ exitCode: code ?? 1, output: output.trim() });
12677
13615
  });
12678
13616
  child.on("error", (err) => {
12679
13617
  output += stdoutDecoder.decode();
12680
13618
  output += stderrDecoder.decode();
12681
- resolve6({
13619
+ resolve7({
12682
13620
  exitCode: 1,
12683
13621
  output: `${output}${output ? "\n" : ""}${err.message}`.trim()
12684
13622
  });
@@ -12795,7 +13733,7 @@ async function postChatMessage(c, threadManager, agentExecutor, conversationMana
12795
13733
  }
12796
13734
  let conversationId = thread.currentConversationId;
12797
13735
  if (!conversationId) {
12798
- conversationId = randomUUID6();
13736
+ conversationId = randomUUID7();
12799
13737
  const db2 = await getDatabase();
12800
13738
  await db2.execute({
12801
13739
  sql: "DELETE FROM todos WHERE threadId = ?",
@@ -13105,7 +14043,7 @@ ${result.output}`;
13105
14043
  };
13106
14044
  processingStateManager.pushEvent(threadId, iterationEvent);
13107
14045
  yield iterationEvent;
13108
- const newConversationId = randomUUID6();
14046
+ const newConversationId = randomUUID7();
13109
14047
  await threadManager.updateThread(threadId, {
13110
14048
  currentConversationId: newConversationId
13111
14049
  });
@@ -13197,11 +14135,11 @@ ${result.output}`;
13197
14135
  try {
13198
14136
  const todo = await getTodoByThreadId(db2, threadId);
13199
14137
  if (todo && todo.status === "Plan") {
13200
- const planFilePath = join17(threadPath, `${todo.id}-plan.md`);
14138
+ const planFilePath = join19(threadPath, `${todo.id}-plan.md`);
13201
14139
  try {
13202
- const planContent = await readFile8(planFilePath, "utf-8");
14140
+ const planContent = await readFile9(planFilePath, "utf-8");
13203
14141
  await updateTodo(db2, todo.id, { description: planContent.trim() });
13204
- await unlink(planFilePath);
14142
+ await unlink2(planFilePath);
13205
14143
  } catch {
13206
14144
  }
13207
14145
  }
@@ -13538,7 +14476,7 @@ ${summary}`;
13538
14476
  }
13539
14477
 
13540
14478
  // src/features/chat/chat-delete.route.ts
13541
- import { randomUUID as randomUUID7 } from "crypto";
14479
+ import { randomUUID as randomUUID8 } from "crypto";
13542
14480
  init_database();
13543
14481
  async function deleteChat(c, threadManager) {
13544
14482
  try {
@@ -13560,7 +14498,7 @@ async function deleteChat(c, threadManager) {
13560
14498
  sql: "DELETE FROM todos WHERE threadId = ?",
13561
14499
  args: [threadId]
13562
14500
  });
13563
- const newConversationId = randomUUID7();
14501
+ const newConversationId = randomUUID8();
13564
14502
  await threadManager.updateThread(threadId, { currentConversationId: newConversationId });
13565
14503
  return successResponse(
13566
14504
  c,
@@ -13589,7 +14527,7 @@ function subscribeToChatStream(c, processingStateManager) {
13589
14527
  return c.json({ error: "Thread is not currently processing" }, 404);
13590
14528
  }
13591
14529
  return stream2(c, async (writer) => {
13592
- let resolve6 = null;
14530
+ let resolve7 = null;
13593
14531
  const queue = [];
13594
14532
  let done = false;
13595
14533
  const replay = processingStateManager.getBufferedEvents(threadId).slice();
@@ -13600,18 +14538,18 @@ function subscribeToChatStream(c, processingStateManager) {
13600
14538
  } else {
13601
14539
  queue.push(event);
13602
14540
  }
13603
- if (resolve6) {
13604
- resolve6();
13605
- resolve6 = null;
14541
+ if (resolve7) {
14542
+ resolve7();
14543
+ resolve7 = null;
13606
14544
  }
13607
14545
  });
13608
14546
  const finishedBeforeSubscribe = !processingStateManager.isProcessing(threadId);
13609
14547
  writer.onAbort(() => {
13610
14548
  done = true;
13611
14549
  unsubscribe();
13612
- if (resolve6) {
13613
- resolve6();
13614
- resolve6 = null;
14550
+ if (resolve7) {
14551
+ resolve7();
14552
+ resolve7 = null;
13615
14553
  }
13616
14554
  });
13617
14555
  try {
@@ -13636,7 +14574,7 @@ function subscribeToChatStream(c, processingStateManager) {
13636
14574
  }
13637
14575
  if (!done) {
13638
14576
  await new Promise((r) => {
13639
- resolve6 = r;
14577
+ resolve7 = r;
13640
14578
  });
13641
14579
  }
13642
14580
  }
@@ -13684,10 +14622,10 @@ function createChatRoutes(threadManager, agentExecutor, conversationManager, pro
13684
14622
  init_database();
13685
14623
 
13686
14624
  // src/features/conversations/conversations.database.ts
13687
- import { randomUUID as randomUUID8 } from "crypto";
14625
+ import { randomUUID as randomUUID9 } from "crypto";
13688
14626
  async function insertMessage(db, threadId, conversationId, message, model, attachments, planMode, imageMode, ralphMode, checkpointRef) {
13689
14627
  try {
13690
- const messageId = randomUUID8();
14628
+ const messageId = randomUUID9();
13691
14629
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
13692
14630
  await db.execute(
13693
14631
  `
@@ -15986,17 +16924,17 @@ async function getOnboardingStatus(c, metadataManager) {
15986
16924
  init_utils();
15987
16925
  async function getGitCheck(c) {
15988
16926
  try {
15989
- const gitInstalled = await new Promise((resolve6) => {
16927
+ const gitInstalled = await new Promise((resolve7) => {
15990
16928
  const proc = spawnProcess("git", ["--version"]);
15991
16929
  let _err = "";
15992
16930
  proc.stderr?.on("data", (d) => {
15993
16931
  _err += d.toString();
15994
16932
  });
15995
16933
  proc.on("close", (code) => {
15996
- resolve6(code === 0);
16934
+ resolve7(code === 0);
15997
16935
  });
15998
16936
  proc.on("error", () => {
15999
- resolve6(false);
16937
+ resolve7(false);
16000
16938
  });
16001
16939
  });
16002
16940
  return c.json({ gitInstalled });
@@ -16030,13 +16968,13 @@ async function postOnboardingReset(c, metadataManager) {
16030
16968
 
16031
16969
  // src/features/onboarding/onboarding-get-recommended-models.route.ts
16032
16970
  import { readFileSync as readFileSync6, existsSync as existsSync13 } from "fs";
16033
- import { join as join18, dirname as dirname4 } from "path";
16971
+ import { join as join20, dirname as dirname4 } from "path";
16034
16972
  import { fileURLToPath } from "url";
16035
16973
  function getRecommendedModelsFilePath() {
16036
16974
  const moduleDir = dirname4(fileURLToPath(import.meta.url));
16037
16975
  const candidates = [
16038
- join18(moduleDir, "../../../recommended-models.txt"),
16039
- join18(moduleDir, "recommended-models.txt")
16976
+ join20(moduleDir, "../../../recommended-models.txt"),
16977
+ join20(moduleDir, "recommended-models.txt")
16040
16978
  ];
16041
16979
  for (const candidate of candidates) {
16042
16980
  if (existsSync13(candidate)) {
@@ -16691,12 +17629,12 @@ async function handleCheckRunning(c, projectManager) {
16691
17629
  }
16692
17630
 
16693
17631
  // src/core/run-command-detector.ts
16694
- import { readFile as readFile9 } from "fs/promises";
16695
- import { join as join19 } from "path";
17632
+ import { readFile as readFile10 } from "fs/promises";
17633
+ import { join as join21 } from "path";
16696
17634
  async function detectPackageManager(projectPath) {
16697
17635
  try {
16698
- const packageJsonPath = join19(projectPath, "package.json");
16699
- const content = await readFile9(packageJsonPath, "utf-8");
17636
+ const packageJsonPath = join21(projectPath, "package.json");
17637
+ const content = await readFile10(packageJsonPath, "utf-8");
16700
17638
  const packageJson = JSON.parse(content);
16701
17639
  if (packageJson.packageManager) {
16702
17640
  const pm = packageJson.packageManager.split("@")[0];
@@ -16711,8 +17649,8 @@ async function detectPackageManager(projectPath) {
16711
17649
  }
16712
17650
  async function detectRunScripts(projectPath) {
16713
17651
  try {
16714
- const packageJsonPath = join19(projectPath, "package.json");
16715
- const content = await readFile9(packageJsonPath, "utf-8");
17652
+ const packageJsonPath = join21(projectPath, "package.json");
17653
+ const content = await readFile10(packageJsonPath, "utf-8");
16716
17654
  const packageJson = JSON.parse(content);
16717
17655
  const scripts = packageJson.scripts ?? {};
16718
17656
  return {
@@ -16757,8 +17695,8 @@ async function suggestRunCommand(projectPath) {
16757
17695
  }
16758
17696
 
16759
17697
  // src/features/projects/projects-package-scripts.route.ts
16760
- import { readFile as readFile10 } from "fs/promises";
16761
- import { join as join20 } from "path";
17698
+ import { readFile as readFile11 } from "fs/promises";
17699
+ import { join as join22 } from "path";
16762
17700
  import { glob } from "glob";
16763
17701
  function formatScriptName(scriptName) {
16764
17702
  return scriptName.replace(/[-:_]/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
@@ -16786,8 +17724,8 @@ async function findPackageScripts(projectPath) {
16786
17724
  const packageManager = await detectPackageManager(projectPath);
16787
17725
  for (const filePath of packageJsonFiles) {
16788
17726
  try {
16789
- const fullPath = join20(projectPath, filePath);
16790
- const content = await readFile10(fullPath, "utf-8");
17727
+ const fullPath = join22(projectPath, filePath);
17728
+ const content = await readFile11(fullPath, "utf-8");
16791
17729
  const packageJson = JSON.parse(content);
16792
17730
  if (packageJson.scripts) {
16793
17731
  for (const [scriptName] of Object.entries(packageJson.scripts)) {
@@ -16834,8 +17772,8 @@ async function handleGetPackageScripts(c, projectManager) {
16834
17772
  }
16835
17773
 
16836
17774
  // src/features/projects/projects-ai-files.route.ts
16837
- import { readdir as readdir6, readFile as readFile11, writeFile as writeFile3, mkdir as mkdir2, access as access2, rm } from "fs/promises";
16838
- import { join as join21, extname as extname4, basename } from "path";
17775
+ import { readdir as readdir6, readFile as readFile12, writeFile as writeFile3, mkdir as mkdir2, access as access2, rm } from "fs/promises";
17776
+ import { join as join23, extname as extname4, basename } from "path";
16839
17777
  import { existsSync as existsSync14 } from "fs";
16840
17778
  var IGNORED_DIRS = /* @__PURE__ */ new Set([
16841
17779
  ".git",
@@ -16854,8 +17792,8 @@ async function buildFullTree(dirPath, relativeDirPath) {
16854
17792
  try {
16855
17793
  const entries = await readdir6(dirPath, { withFileTypes: true });
16856
17794
  for (const entry of entries) {
16857
- const entryRelPath = join21(relativeDirPath, entry.name);
16858
- const entryAbsPath = join21(dirPath, entry.name);
17795
+ const entryRelPath = join23(relativeDirPath, entry.name);
17796
+ const entryAbsPath = join23(dirPath, entry.name);
16859
17797
  if (entry.isDirectory()) {
16860
17798
  const children = await buildFullTree(entryAbsPath, entryRelPath);
16861
17799
  nodes.push({
@@ -16883,8 +17821,8 @@ async function buildMarkdownFilteredTree(dirPath, relativeDirPath) {
16883
17821
  try {
16884
17822
  const entries = await readdir6(dirPath, { withFileTypes: true });
16885
17823
  for (const entry of entries) {
16886
- const entryRelPath = join21(relativeDirPath, entry.name);
16887
- const entryAbsPath = join21(dirPath, entry.name);
17824
+ const entryRelPath = join23(relativeDirPath, entry.name);
17825
+ const entryAbsPath = join23(dirPath, entry.name);
16888
17826
  if (entry.isDirectory()) {
16889
17827
  if (IGNORED_DIRS.has(entry.name)) continue;
16890
17828
  const children = await buildMarkdownFilteredTree(entryAbsPath, entryRelPath);
@@ -16919,8 +17857,8 @@ async function buildAIFileTree(projectPath) {
16919
17857
  { key: "agents", label: "Agents" }
16920
17858
  ];
16921
17859
  for (const { key, label } of agentsFolders) {
16922
- const relPath = join21(".agents", key);
16923
- const absPath = join21(projectPath, relPath);
17860
+ const relPath = join23(".agents", key);
17861
+ const absPath = join23(projectPath, relPath);
16924
17862
  const exists = existsSync14(absPath);
16925
17863
  if (exists) {
16926
17864
  const children = await buildFullTree(absPath, relPath);
@@ -16955,7 +17893,7 @@ async function buildAIFileTree(projectPath) {
16955
17893
  if (entry.name === ".agents" || entry.name === "AGENTS.md" || IGNORED_DIRS.has(entry.name))
16956
17894
  continue;
16957
17895
  const entryRelPath = entry.name;
16958
- const entryAbsPath = join21(projectPath, entry.name);
17896
+ const entryAbsPath = join23(projectPath, entry.name);
16959
17897
  if (entry.isFile() && MARKDOWN_EXTS.has(extname4(entry.name))) {
16960
17898
  nodes.push({
16961
17899
  id: entryRelPath,
@@ -16982,7 +17920,7 @@ async function buildAIFileTree(projectPath) {
16982
17920
  }
16983
17921
  function validateFilePath(projectPath, filePath) {
16984
17922
  if (!filePath) return null;
16985
- const abs = join21(projectPath, filePath);
17923
+ const abs = join23(projectPath, filePath);
16986
17924
  if (!abs.startsWith(projectPath + "/") && abs !== projectPath) return null;
16987
17925
  return abs;
16988
17926
  }
@@ -17069,7 +18007,7 @@ Links to important documentation, tools, or references.
17069
18007
  } catch {
17070
18008
  return c.json({ error: { code: "NOT_FOUND", message: `File not found: ${filePath}` } }, 404);
17071
18009
  }
17072
- const content = await readFile11(absPath, "utf-8");
18010
+ const content = await readFile12(absPath, "utf-8");
17073
18011
  return c.json({ content, path: filePath });
17074
18012
  } catch (error) {
17075
18013
  return errorResponse(
@@ -17108,7 +18046,7 @@ async function handleSaveAIFile(c, projectManager) {
17108
18046
  if (!absPath) {
17109
18047
  return c.json({ error: { code: "BAD_REQUEST", message: "Invalid file path" } }, 400);
17110
18048
  }
17111
- const parentDir = join21(absPath, "..");
18049
+ const parentDir = join23(absPath, "..");
17112
18050
  await mkdir2(parentDir, { recursive: true });
17113
18051
  await writeFile3(absPath, content, "utf-8");
17114
18052
  return c.json({ success: true, path: filePath });
@@ -17208,10 +18146,10 @@ async function handleCreateSkill(c, projectManager) {
17208
18146
  if (!project) {
17209
18147
  return errorResponse(c, ErrorCodes.PROJECT_NOT_FOUND, `Project not found: ${projectId}`, 404);
17210
18148
  }
17211
- const skillRelPath = join21(".agents", "skills", name);
17212
- const skillAbsPath = join21(project.path, skillRelPath);
17213
- const skillFileRelPath = join21(skillRelPath, "SKILL.md");
17214
- const skillFileAbsPath = join21(skillAbsPath, "SKILL.md");
18149
+ const skillRelPath = join23(".agents", "skills", name);
18150
+ const skillAbsPath = join23(project.path, skillRelPath);
18151
+ const skillFileRelPath = join23(skillRelPath, "SKILL.md");
18152
+ const skillFileAbsPath = join23(skillAbsPath, "SKILL.md");
17215
18153
  if (existsSync14(skillAbsPath)) {
17216
18154
  return c.json(
17217
18155
  { error: { code: "CONFLICT", message: `Skill '${name}' already exists` } },
@@ -17283,11 +18221,14 @@ async function handleGetProjectEnvVars(c, projectManager) {
17283
18221
  return errorResponse(c, ErrorCodes.PROJECT_NOT_FOUND, `Project not found: ${projectId}`, 404);
17284
18222
  }
17285
18223
  const db = await getDatabase();
17286
- const rows = await listProjectEnvVarNames(db, projectId);
17287
- const vars = rows.map((row) => ({
17288
- name: row.name,
17289
- hasValue: true
17290
- }));
18224
+ const rows = await listProjectEnvVars(db, projectId);
18225
+ const vars = await Promise.all(
18226
+ rows.map(async (row) => ({
18227
+ name: row.name,
18228
+ hasValue: true,
18229
+ value: await decryptProviderKey(row.encryptedValue)
18230
+ }))
18231
+ );
17291
18232
  return c.json({ vars });
17292
18233
  } catch (error) {
17293
18234
  return errorResponse(
@@ -17457,7 +18398,7 @@ function createProjectRoutes(projectManager, threadManager) {
17457
18398
 
17458
18399
  // src/features/projects/projects.manager.ts
17459
18400
  init_utils();
17460
- import { join as join24 } from "path";
18401
+ import { join as join26 } from "path";
17461
18402
  import { realpathSync as realpathSync2 } from "fs";
17462
18403
  import { rm as rm4 } from "fs/promises";
17463
18404
 
@@ -17524,13 +18465,13 @@ var ProcessManager = class extends EventEmitter {
17524
18465
  }
17525
18466
  };
17526
18467
  const waitForQueue = () => {
17527
- return new Promise((resolve6) => {
18468
+ return new Promise((resolve7) => {
17528
18469
  const queue = this.queues.get(projectId);
17529
18470
  if (queue && queue.length > 0) {
17530
- resolve6();
18471
+ resolve7();
17531
18472
  } else {
17532
18473
  const res = this.resolvers.get(projectId);
17533
- if (res) res.push(resolve6);
18474
+ if (res) res.push(resolve7);
17534
18475
  }
17535
18476
  });
17536
18477
  };
@@ -17678,14 +18619,14 @@ var ProcessManager = class extends EventEmitter {
17678
18619
 
17679
18620
  // src/features/projects/projects.creator.ts
17680
18621
  init_utils();
17681
- import { randomUUID as randomUUID9 } from "crypto";
17682
- import { basename as basename2, join as join23, relative as relative5 } from "path";
18622
+ import { randomUUID as randomUUID10 } from "crypto";
18623
+ import { basename as basename2, join as join25, relative as relative5 } from "path";
17683
18624
  import { access as access3, mkdir as mkdir4, rm as rm3, stat as stat3, readdir as readdir8 } from "fs/promises";
17684
18625
 
17685
18626
  // src/features/scaffold/scaffold.runner.ts
17686
18627
  init_utils();
17687
18628
  import { existsSync as existsSync15 } from "fs";
17688
- import { join as join22 } from "path";
18629
+ import { join as join24 } from "path";
17689
18630
  import { mkdir as mkdir3, readdir as readdir7, stat as stat2, rename, rm as rm2, writeFile as writeFile4 } from "fs/promises";
17690
18631
  import { createInterface as createInterface2 } from "readline";
17691
18632
 
@@ -18407,7 +19348,7 @@ function loadCatalog() {
18407
19348
  }
18408
19349
  async function writeAgentsMd(projectPath, agentsMd) {
18409
19350
  if (!agentsMd) return;
18410
- const agentsPath = join22(projectPath, "AGENTS.md");
19351
+ const agentsPath = join24(projectPath, "AGENTS.md");
18411
19352
  if (existsSync15(agentsPath)) return;
18412
19353
  await writeFile4(agentsPath, agentsMd, "utf-8");
18413
19354
  }
@@ -18565,7 +19506,7 @@ async function* runCommandStreaming(cwd, command) {
18565
19506
  }
18566
19507
  }
18567
19508
  async function* createScaffoldedProjectStreaming(options) {
18568
- const projectPath = join22(options.parentDir, options.threadId);
19509
+ const projectPath = join24(options.parentDir, options.threadId);
18569
19510
  if (!existsSync15(options.parentDir)) {
18570
19511
  yield {
18571
19512
  type: "result",
@@ -18655,7 +19596,7 @@ async function* createScaffoldedProjectStreaming(options) {
18655
19596
  }
18656
19597
  try {
18657
19598
  const projectName2 = getProjectName(options.projectName);
18658
- const projectSubDir = join22(projectPath, projectName2);
19599
+ const projectSubDir = join24(projectPath, projectName2);
18659
19600
  try {
18660
19601
  const subDirStat = await stat2(projectSubDir);
18661
19602
  if (subDirStat.isDirectory()) {
@@ -18665,8 +19606,8 @@ async function* createScaffoldedProjectStreaming(options) {
18665
19606
  };
18666
19607
  const items = await readdir7(projectSubDir);
18667
19608
  for (const item of items) {
18668
- const oldPath = join22(projectSubDir, item);
18669
- const newPath = join22(projectPath, item);
19609
+ const oldPath = join24(projectSubDir, item);
19610
+ const newPath = join24(projectPath, item);
18670
19611
  await rename(oldPath, newPath);
18671
19612
  }
18672
19613
  await rm2(projectSubDir, { recursive: true, force: true });
@@ -18864,14 +19805,14 @@ var ProjectCreator = class {
18864
19805
  }
18865
19806
  let initialThreadId;
18866
19807
  try {
18867
- const projectId = randomUUID9();
19808
+ const projectId = randomUUID10();
18868
19809
  const existingProjects = await this.metadataManager.loadProjects();
18869
19810
  const existingNames = existingProjects.map((p) => p.name);
18870
19811
  const projectName = this.ensureUniqueProjectName(
18871
19812
  this.deriveProjectName(gitUrl),
18872
19813
  existingNames
18873
19814
  );
18874
- initialThreadId = randomUUID9();
19815
+ initialThreadId = randomUUID10();
18875
19816
  const initialThreadTitle = generateRandomThreadName();
18876
19817
  const baseFolderName = this.toFolderName(this.deriveProjectName(gitUrl));
18877
19818
  if (!baseFolderName) {
@@ -18882,7 +19823,7 @@ var ProjectCreator = class {
18882
19823
  return;
18883
19824
  }
18884
19825
  const uniqueFolderName = await this.ensureUniqueFolderName(baseFolderName);
18885
- const firstThreadPath = join23(this.rootFolder, uniqueFolderName);
19826
+ const firstThreadPath = join25(this.rootFolder, uniqueFolderName);
18886
19827
  this.processingStateManager?.setProcessing(initialThreadId);
18887
19828
  yield {
18888
19829
  type: "progress",
@@ -19020,8 +19961,8 @@ var ProjectCreator = class {
19020
19961
  };
19021
19962
  return;
19022
19963
  }
19023
- const projectId = randomUUID9();
19024
- const initialThreadId = randomUUID9();
19964
+ const projectId = randomUUID10();
19965
+ const initialThreadId = randomUUID10();
19025
19966
  const initialThreadTitle = generateRandomThreadName();
19026
19967
  this.processingStateManager?.setProcessing(initialThreadId);
19027
19968
  try {
@@ -19132,15 +20073,15 @@ var ProjectCreator = class {
19132
20073
  };
19133
20074
  return;
19134
20075
  }
19135
- const projectId = randomUUID9();
19136
- const initialThreadId = randomUUID9();
20076
+ const projectId = randomUUID10();
20077
+ const initialThreadId = randomUUID10();
19137
20078
  const initialThreadTitle = generateRandomThreadName();
19138
20079
  let folderPath;
19139
20080
  let projectMetadataSaved = false;
19140
20081
  this.processingStateManager?.setProcessing(initialThreadId);
19141
20082
  try {
19142
20083
  const uniqueFolderName = await this.ensureUniqueFolderName(folderName);
19143
- folderPath = join23(this.rootFolder, uniqueFolderName);
20084
+ folderPath = join25(this.rootFolder, uniqueFolderName);
19144
20085
  yield {
19145
20086
  type: "progress",
19146
20087
  message: `Creating folder "${uniqueFolderName}"...`,
@@ -19227,7 +20168,7 @@ var ProjectCreator = class {
19227
20168
  return name;
19228
20169
  }
19229
20170
  generateThreadPath(_projectId, threadId) {
19230
- return join23(this.rootFolder, threadId);
20171
+ return join25(this.rootFolder, threadId);
19231
20172
  }
19232
20173
  toFolderName(name) {
19233
20174
  return name.trim().toLowerCase().replace(/[\\/]+/g, " ").replace(/[^a-z0-9._ -]+/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
@@ -19243,7 +20184,7 @@ var ProjectCreator = class {
19243
20184
  async ensureUniqueFolderName(baseFolderName) {
19244
20185
  let candidate = baseFolderName;
19245
20186
  let counter = 1;
19246
- while (await this.folderExists(join23(this.rootFolder, candidate))) {
20187
+ while (await this.folderExists(join25(this.rootFolder, candidate))) {
19247
20188
  candidate = `${baseFolderName}-${counter}`;
19248
20189
  counter++;
19249
20190
  }
@@ -19275,8 +20216,8 @@ var ProjectCreator = class {
19275
20216
  yield { type: "error", message: `Cannot access folder: ${folderPath}` };
19276
20217
  return;
19277
20218
  }
19278
- const projectId = randomUUID9();
19279
- const initialThreadId = randomUUID9();
20219
+ const projectId = randomUUID10();
20220
+ const initialThreadId = randomUUID10();
19280
20221
  const initialThreadTitle = generateRandomThreadName();
19281
20222
  this.processingStateManager?.setProcessing(initialThreadId);
19282
20223
  try {
@@ -19397,19 +20338,19 @@ var ProjectCreator = class {
19397
20338
  }
19398
20339
  async findAllPackageJsonFiles(rootPath, currentPath, setupScripts) {
19399
20340
  try {
19400
- await access3(join23(currentPath, "package.json"));
20341
+ await access3(join25(currentPath, "package.json"));
19401
20342
  const relativePath = relative5(rootPath, currentPath);
19402
20343
  let installCommand = "";
19403
20344
  try {
19404
- await access3(join23(currentPath, "yarn.lock"));
20345
+ await access3(join25(currentPath, "yarn.lock"));
19405
20346
  installCommand = "yarn install";
19406
20347
  } catch {
19407
20348
  try {
19408
- await access3(join23(currentPath, "bun.lock"));
20349
+ await access3(join25(currentPath, "bun.lock"));
19409
20350
  installCommand = "bun install";
19410
20351
  } catch {
19411
20352
  try {
19412
- await access3(join23(currentPath, "pnpm-lock.yaml"));
20353
+ await access3(join25(currentPath, "pnpm-lock.yaml"));
19413
20354
  installCommand = "pnpm install";
19414
20355
  } catch {
19415
20356
  installCommand = "npm install";
@@ -19426,7 +20367,7 @@ var ProjectCreator = class {
19426
20367
  try {
19427
20368
  const entries = await readdir8(currentPath);
19428
20369
  for (const entry of entries) {
19429
- const entryPath = join23(currentPath, entry);
20370
+ const entryPath = join25(currentPath, entry);
19430
20371
  try {
19431
20372
  const stats = await stat3(entryPath);
19432
20373
  if (stats.isDirectory() && !entry.startsWith(".") && entry !== "node_modules") {
@@ -19772,7 +20713,7 @@ var ProjectManagerImpl = class {
19772
20713
  const session = this.terminalSessionManager.getOrCreateSession(threadId, thread.path);
19773
20714
  let workingDir;
19774
20715
  if (cwd) {
19775
- workingDir = cwd.startsWith("/") ? cwd : join24(thread.path, cwd);
20716
+ workingDir = cwd.startsWith("/") ? cwd : join26(thread.path, cwd);
19776
20717
  this.terminalSessionManager.updateWorkingDirectory(threadId, workingDir);
19777
20718
  } else {
19778
20719
  workingDir = session.currentWorkingDirectory;
@@ -19828,7 +20769,7 @@ ___CWD___%s
19828
20769
  if (outputBuffer.length > 0) {
19829
20770
  yield outputBuffer.shift();
19830
20771
  } else {
19831
- await new Promise((resolve6) => setTimeout(resolve6, 50));
20772
+ await new Promise((resolve7) => setTimeout(resolve7, 50));
19832
20773
  }
19833
20774
  }
19834
20775
  }
@@ -20563,8 +21504,8 @@ function createScaffoldRoutes(projectManager) {
20563
21504
  }
20564
21505
 
20565
21506
  // src/features/slash-commands/slash-commands.manager.ts
20566
- import { readdir as readdir9, readFile as readFile12, mkdir as mkdir5, writeFile as writeFile5, unlink as unlink2 } from "fs/promises";
20567
- import { join as join25, basename as basename3, extname as extname5, relative as relative6 } from "path";
21507
+ import { readdir as readdir9, readFile as readFile13, mkdir as mkdir5, writeFile as writeFile5, unlink as unlink3 } from "fs/promises";
21508
+ import { join as join27, basename as basename3, extname as extname5, relative as relative6 } from "path";
20568
21509
  import { existsSync as existsSync16 } from "fs";
20569
21510
  import { homedir as homedir8 } from "os";
20570
21511
  function slugify(filename) {
@@ -20610,10 +21551,10 @@ function parseFrontmatter3(markdown) {
20610
21551
  return { metadata, content };
20611
21552
  }
20612
21553
  function getGlobalCommandsDir() {
20613
- return join25(homedir8(), ".agents", "commands");
21554
+ return join27(homedir8(), ".agents", "commands");
20614
21555
  }
20615
21556
  function getProjectCommandsDir(threadPath) {
20616
- return join25(threadPath, ".agents", "commands");
21557
+ return join27(threadPath, ".agents", "commands");
20617
21558
  }
20618
21559
  var SlashCommandManager = class _SlashCommandManager {
20619
21560
  /**
@@ -20686,8 +21627,8 @@ var SlashCommandManager = class _SlashCommandManager {
20686
21627
  const files = await readdir9(dir);
20687
21628
  for (const file of files) {
20688
21629
  if (!file.endsWith(".md")) continue;
20689
- const filePath = join25(dir, file);
20690
- const fileContent = await readFile12(filePath, "utf-8");
21630
+ const filePath = join27(dir, file);
21631
+ const fileContent = await readFile13(filePath, "utf-8");
20691
21632
  const { metadata, content } = parseFrontmatter3(fileContent);
20692
21633
  const nameWithoutExt = basename3(file, extname5(file));
20693
21634
  const commandName = slugify(nameWithoutExt);
@@ -20721,7 +21662,7 @@ var SlashCommandManager = class _SlashCommandManager {
20721
21662
  await mkdir5(dir, { recursive: true });
20722
21663
  }
20723
21664
  const filename = `${name}.md`;
20724
- const filePath = join25(dir, filename);
21665
+ const filePath = join27(dir, filename);
20725
21666
  if (existsSync16(filePath)) {
20726
21667
  throw new Error(`Command already exists: ${name}`);
20727
21668
  }
@@ -20786,7 +21727,7 @@ var SlashCommandManager = class _SlashCommandManager {
20786
21727
  if (!existsSync16(filePath)) {
20787
21728
  throw new Error(`Command file not found: ${filePath}`);
20788
21729
  }
20789
- await unlink2(filePath);
21730
+ await unlink3(filePath);
20790
21731
  }
20791
21732
  /**
20792
21733
  * Get a specific command by name
@@ -21315,8 +22256,8 @@ function createRuleRoutes(router, projectManager) {
21315
22256
 
21316
22257
  // src/features/threads/threads.manager.ts
21317
22258
  init_utils();
21318
- import { randomUUID as randomUUID10 } from "crypto";
21319
- import { join as join26 } from "path";
22259
+ import { randomUUID as randomUUID11 } from "crypto";
22260
+ import { join as join28 } from "path";
21320
22261
  import { execSync as execSync3 } from "child_process";
21321
22262
  import { rm as rm5, stat as stat4, mkdir as mkdir6 } from "fs/promises";
21322
22263
  init_database();
@@ -21382,7 +22323,7 @@ var ThreadManagerImpl = class {
21382
22323
  } catch {
21383
22324
  await mkdir6(project.path, { recursive: true });
21384
22325
  }
21385
- threadId = randomUUID10();
22326
+ threadId = randomUUID11();
21386
22327
  const threadPath = this.generateThreadPath(projectId, threadId);
21387
22328
  const existingThreads = await this.metadataManager.loadThreads();
21388
22329
  const existingTitles = new Set(
@@ -21706,8 +22647,8 @@ var ThreadManagerImpl = class {
21706
22647
  isDone = true;
21707
22648
  resolveNext();
21708
22649
  });
21709
- const waitForData = () => new Promise((resolve6) => {
21710
- resolveNext = resolve6;
22650
+ const waitForData = () => new Promise((resolve7) => {
22651
+ resolveNext = resolve7;
21711
22652
  });
21712
22653
  while (!isDone || outputQueue.length > 0 || errorQueue.length > 0) {
21713
22654
  if (outputQueue.length > 0) {
@@ -21740,7 +22681,7 @@ var ThreadManagerImpl = class {
21740
22681
  * - 7.4 - THE CLI SHALL ensure each Thread clone is stored in a unique directory path
21741
22682
  */
21742
22683
  generateThreadPath(_projectId, threadId) {
21743
- return join26(this.rootFolder, threadId);
22684
+ return join28(this.rootFolder, threadId);
21744
22685
  }
21745
22686
  /**
21746
22687
  * Generates a thread title if not provided
@@ -21770,7 +22711,7 @@ var ThreadManagerImpl = class {
21770
22711
  if (!thread) {
21771
22712
  throw new Error(`Thread not found: ${threadId}`);
21772
22713
  }
21773
- return new Promise((resolve6, reject) => {
22714
+ return new Promise((resolve7, reject) => {
21774
22715
  const gitProcess = spawnProcess(
21775
22716
  "git",
21776
22717
  ["ls-files", "--cached", "--others", "--exclude-standard"],
@@ -21789,7 +22730,7 @@ var ThreadManagerImpl = class {
21789
22730
  gitProcess.on("close", (code) => {
21790
22731
  if (code === 0) {
21791
22732
  const files = output.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
21792
- resolve6(files);
22733
+ resolve7(files);
21793
22734
  } else {
21794
22735
  reject(new Error(`git ls-files failed with code ${code}: ${errorOutput}`));
21795
22736
  }
@@ -21933,14 +22874,14 @@ async function handleDeleteThread(c, threadManager) {
21933
22874
  }
21934
22875
 
21935
22876
  // src/core/project-inspector.ts
21936
- import { readFile as readFile13, readdir as readdir10 } from "fs/promises";
22877
+ import { readFile as readFile14, readdir as readdir10 } from "fs/promises";
21937
22878
  import { existsSync as existsSync17 } from "fs";
21938
- import { join as join27, basename as basename4, relative as relative7 } from "path";
22879
+ import { join as join29, basename as basename4, relative as relative7 } from "path";
21939
22880
  import { glob as glob2 } from "glob";
21940
22881
 
21941
22882
  // src/features/project-scripts/project-scripts.database.ts
21942
22883
  init_database();
21943
- import { randomUUID as randomUUID11 } from "crypto";
22884
+ import { randomUUID as randomUUID12 } from "crypto";
21944
22885
  async function getScriptsByProject(db, projectId) {
21945
22886
  const result = await db.execute({
21946
22887
  sql: `SELECT id, projectId, workspace, name, command, friendlyName, updatedAt
@@ -21957,7 +22898,7 @@ async function upsertProjectScripts(projectId, scripts) {
21957
22898
  sql: `INSERT OR REPLACE INTO project_scripts (id, projectId, workspace, name, command, friendlyName, updatedAt)
21958
22899
  VALUES (?, ?, ?, ?, ?, ?, ?)`,
21959
22900
  args: [
21960
- randomUUID11(),
22901
+ randomUUID12(),
21961
22902
  projectId,
21962
22903
  script.workspace,
21963
22904
  script.name,
@@ -21979,13 +22920,13 @@ async function inspectProject(projectPath, projectId) {
21979
22920
  return scripts;
21980
22921
  }
21981
22922
  async function detectPackageManager2(projectPath) {
21982
- if (existsSync17(join27(projectPath, "bun.lockb")) || existsSync17(join27(projectPath, "bun.lock"))) {
22923
+ if (existsSync17(join29(projectPath, "bun.lockb")) || existsSync17(join29(projectPath, "bun.lock"))) {
21983
22924
  return "bun";
21984
22925
  }
21985
- if (existsSync17(join27(projectPath, "pnpm-lock.yaml"))) {
22926
+ if (existsSync17(join29(projectPath, "pnpm-lock.yaml"))) {
21986
22927
  return "pnpm";
21987
22928
  }
21988
- if (existsSync17(join27(projectPath, "yarn.lock"))) {
22929
+ if (existsSync17(join29(projectPath, "yarn.lock"))) {
21989
22930
  return "yarn";
21990
22931
  }
21991
22932
  try {
@@ -22004,8 +22945,8 @@ async function detectPackageManager2(projectPath) {
22004
22945
  return "npm";
22005
22946
  }
22006
22947
  async function detectMonoRepoType(projectPath) {
22007
- const hasPnpmWorkspace = existsSync17(join27(projectPath, "pnpm-workspace.yaml"));
22008
- if (existsSync17(join27(projectPath, "nx.json"))) {
22948
+ const hasPnpmWorkspace = existsSync17(join29(projectPath, "pnpm-workspace.yaml"));
22949
+ if (existsSync17(join29(projectPath, "nx.json"))) {
22009
22950
  return "nx";
22010
22951
  }
22011
22952
  const pkg = await readPackageJson(projectPath);
@@ -22019,10 +22960,10 @@ async function detectMonoRepoType(projectPath) {
22019
22960
  if (hasPnpmWorkspace) {
22020
22961
  return "pnpm";
22021
22962
  }
22022
- if (existsSync17(join27(projectPath, "lerna.json"))) {
22963
+ if (existsSync17(join29(projectPath, "lerna.json"))) {
22023
22964
  return "lerna";
22024
22965
  }
22025
- if (existsSync17(join27(projectPath, "turbo.json"))) {
22966
+ if (existsSync17(join29(projectPath, "turbo.json"))) {
22026
22967
  return "turbo";
22027
22968
  }
22028
22969
  const folderWorkspaces = await detectFolderBasedWorkspaces(projectPath);
@@ -22063,8 +23004,8 @@ async function resolvePackageJsonWorkspaces(projectPath) {
22063
23004
  });
22064
23005
  const workspaces = [];
22065
23006
  for (const folder of folders) {
22066
- const absPath = join27(projectPath, folder);
22067
- if (existsSync17(join27(absPath, "package.json"))) {
23007
+ const absPath = join29(projectPath, folder);
23008
+ if (existsSync17(join29(absPath, "package.json"))) {
22068
23009
  workspaces.push({
22069
23010
  name: basename4(folder),
22070
23011
  folder: absPath,
@@ -22079,12 +23020,12 @@ async function resolvePackageJsonWorkspaces(projectPath) {
22079
23020
  return workspaces;
22080
23021
  }
22081
23022
  async function resolvePnpmWorkspaces(projectPath) {
22082
- const yamlPath = join27(projectPath, "pnpm-workspace.yaml");
23023
+ const yamlPath = join29(projectPath, "pnpm-workspace.yaml");
22083
23024
  if (!existsSync17(yamlPath)) {
22084
23025
  return [{ name: "root", folder: projectPath, relativePath: "." }];
22085
23026
  }
22086
23027
  try {
22087
- const yaml = await readFile13(yamlPath, "utf-8");
23028
+ const yaml = await readFile14(yamlPath, "utf-8");
22088
23029
  const patterns = [];
22089
23030
  for (const line of yaml.split("\n")) {
22090
23031
  const trimmed = line.trim();
@@ -22105,8 +23046,8 @@ async function resolvePnpmWorkspaces(projectPath) {
22105
23046
  });
22106
23047
  const workspaces = [];
22107
23048
  for (const folder of folders) {
22108
- const absPath = join27(projectPath, folder);
22109
- if (existsSync17(join27(absPath, "package.json"))) {
23049
+ const absPath = join29(projectPath, folder);
23050
+ if (existsSync17(join29(absPath, "package.json"))) {
22110
23051
  const wsPkg = await readPackageJson(absPath);
22111
23052
  workspaces.push({
22112
23053
  name: wsPkg?.name ?? basename4(folder),
@@ -22125,12 +23066,12 @@ async function resolvePnpmWorkspaces(projectPath) {
22125
23066
  }
22126
23067
  }
22127
23068
  async function resolveLernaWorkspaces(projectPath) {
22128
- const lernaPath = join27(projectPath, "lerna.json");
23069
+ const lernaPath = join29(projectPath, "lerna.json");
22129
23070
  if (!existsSync17(lernaPath)) {
22130
23071
  return [{ name: "root", folder: projectPath, relativePath: "." }];
22131
23072
  }
22132
23073
  try {
22133
- const content = await readFile13(lernaPath, "utf-8");
23074
+ const content = await readFile14(lernaPath, "utf-8");
22134
23075
  const lerna = JSON.parse(content);
22135
23076
  const patterns = lerna.packages ?? ["packages/*"];
22136
23077
  const folders = await glob2(patterns, {
@@ -22139,8 +23080,8 @@ async function resolveLernaWorkspaces(projectPath) {
22139
23080
  });
22140
23081
  const workspaces = [];
22141
23082
  for (const folder of folders) {
22142
- const absPath = join27(projectPath, folder);
22143
- if (existsSync17(join27(absPath, "package.json"))) {
23083
+ const absPath = join29(projectPath, folder);
23084
+ if (existsSync17(join29(absPath, "package.json"))) {
22144
23085
  const wsPkg = await readPackageJson(absPath);
22145
23086
  workspaces.push({
22146
23087
  name: wsPkg?.name ?? basename4(folder),
@@ -22160,17 +23101,17 @@ async function resolveLernaWorkspaces(projectPath) {
22160
23101
  }
22161
23102
  async function resolveNxWorkspaces(projectPath) {
22162
23103
  const workspaces = [];
22163
- const workspaceJsonPath = join27(projectPath, "workspace.json");
23104
+ const workspaceJsonPath = join29(projectPath, "workspace.json");
22164
23105
  if (existsSync17(workspaceJsonPath)) {
22165
23106
  try {
22166
- const content = await readFile13(workspaceJsonPath, "utf-8");
23107
+ const content = await readFile14(workspaceJsonPath, "utf-8");
22167
23108
  const wsJson = JSON.parse(content);
22168
23109
  for (const [name, value] of Object.entries(wsJson.projects ?? {})) {
22169
23110
  const folder = typeof value === "string" ? value : value.root;
22170
23111
  if (folder) {
22171
23112
  workspaces.push({
22172
23113
  name,
22173
- folder: join27(projectPath, folder),
23114
+ folder: join29(projectPath, folder),
22174
23115
  relativePath: folder
22175
23116
  });
22176
23117
  }
@@ -22185,13 +23126,13 @@ async function resolveNxWorkspaces(projectPath) {
22185
23126
  });
22186
23127
  for (const file of projectJsonFiles) {
22187
23128
  try {
22188
- const content = await readFile13(join27(projectPath, file), "utf-8");
23129
+ const content = await readFile14(join29(projectPath, file), "utf-8");
22189
23130
  const project = JSON.parse(content);
22190
23131
  if (project.name) {
22191
23132
  const folder = file.replace(/\/project\.json$/, "");
22192
23133
  workspaces.push({
22193
23134
  name: project.name,
22194
- folder: join27(projectPath, folder),
23135
+ folder: join29(projectPath, folder),
22195
23136
  relativePath: folder
22196
23137
  });
22197
23138
  }
@@ -22199,15 +23140,15 @@ async function resolveNxWorkspaces(projectPath) {
22199
23140
  }
22200
23141
  }
22201
23142
  if (workspaces.length > 0) return workspaces;
22202
- const appsDir = join27(projectPath, "apps");
23143
+ const appsDir = join29(projectPath, "apps");
22203
23144
  if (existsSync17(appsDir)) {
22204
23145
  const entries = await readdir10(appsDir, { withFileTypes: true });
22205
23146
  for (const entry of entries) {
22206
23147
  if (entry.isDirectory() && !entry.name.startsWith(".")) {
22207
- const folder = join27("apps", entry.name);
23148
+ const folder = join29("apps", entry.name);
22208
23149
  workspaces.push({
22209
23150
  name: entry.name,
22210
- folder: join27(projectPath, folder),
23151
+ folder: join29(projectPath, folder),
22211
23152
  relativePath: folder
22212
23153
  });
22213
23154
  }
@@ -22241,13 +23182,13 @@ async function detectFolderBasedWorkspaces(projectPath) {
22241
23182
  const results = [];
22242
23183
  for (const entry of entries) {
22243
23184
  if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules" && entry.name !== "dist" && entry.name !== "build") {
22244
- const pkgPath = join27(projectPath, entry.name, "package.json");
23185
+ const pkgPath = join29(projectPath, entry.name, "package.json");
22245
23186
  if (existsSync17(pkgPath)) {
22246
23187
  try {
22247
- const content = await readFile13(pkgPath, "utf-8");
23188
+ const content = await readFile14(pkgPath, "utf-8");
22248
23189
  const pkg = JSON.parse(content);
22249
23190
  if (pkg.dependencies || pkg.devDependencies || pkg.scripts) {
22250
- results.push({ name: entry.name, absPath: join27(projectPath, entry.name) });
23191
+ results.push({ name: entry.name, absPath: join29(projectPath, entry.name) });
22251
23192
  }
22252
23193
  } catch {
22253
23194
  }
@@ -22303,7 +23244,7 @@ function pmRunCommand(packageManager, scriptName) {
22303
23244
  }
22304
23245
  async function readPackageJson(dir) {
22305
23246
  try {
22306
- const content = await readFile13(join27(dir, "package.json"), "utf-8");
23247
+ const content = await readFile14(join29(dir, "package.json"), "utf-8");
22307
23248
  return JSON.parse(content);
22308
23249
  } catch {
22309
23250
  return null;
@@ -22495,8 +23436,8 @@ async function handleListThreadFiles(c, threadManager) {
22495
23436
  }
22496
23437
 
22497
23438
  // src/features/threads/threads-explorer.route.ts
22498
- import { readdir as readdir11, readFile as readFile14, rename as rename2, writeFile as writeFile6, mkdir as mkdir7, rm as rm6, access as access4 } from "fs/promises";
22499
- import { join as join28 } from "path";
23439
+ import { readdir as readdir11, readFile as readFile15, rename as rename2, writeFile as writeFile6, mkdir as mkdir7, rm as rm6, access as access4 } from "fs/promises";
23440
+ import { join as join30 } from "path";
22500
23441
  import { spawn as spawn2 } from "child_process";
22501
23442
  var Utils3 = null;
22502
23443
  var utilsLoaded3 = false;
@@ -22511,7 +23452,7 @@ async function loadUtils3() {
22511
23452
  }
22512
23453
  async function getIgnoredPaths(repoPath, relativePaths) {
22513
23454
  if (relativePaths.length === 0) return /* @__PURE__ */ new Set();
22514
- return new Promise((resolve6) => {
23455
+ return new Promise((resolve7) => {
22515
23456
  const proc = spawn2("git", ["check-ignore", "--stdin"], { cwd: repoPath });
22516
23457
  let output = "";
22517
23458
  proc.stdout?.on("data", (d) => {
@@ -22523,16 +23464,16 @@ async function getIgnoredPaths(repoPath, relativePaths) {
22523
23464
  const ignored = new Set(
22524
23465
  output.split("\n").map((l) => l.trim()).filter(Boolean)
22525
23466
  );
22526
- resolve6(ignored);
23467
+ resolve7(ignored);
22527
23468
  });
22528
- proc.on("error", () => resolve6(/* @__PURE__ */ new Set()));
23469
+ proc.on("error", () => resolve7(/* @__PURE__ */ new Set()));
22529
23470
  });
22530
23471
  }
22531
23472
  async function listExplorerEntriesRecursive(rootPath, currentPath, relativePath) {
22532
23473
  const dirents = await readdir11(currentPath, { withFileTypes: true });
22533
23474
  const isRoot = relativePath.length === 0;
22534
23475
  const entries = dirents.filter((d) => !(isRoot && d.name === ".git") && d.name !== "node_modules").map((d) => {
22535
- const entryPath = relativePath ? join28(relativePath, d.name) : d.name;
23476
+ const entryPath = relativePath ? join30(relativePath, d.name) : d.name;
22536
23477
  return {
22537
23478
  name: d.name,
22538
23479
  type: d.isDirectory() ? "folder" : "file",
@@ -22544,7 +23485,7 @@ async function listExplorerEntriesRecursive(rootPath, currentPath, relativePath)
22544
23485
  });
22545
23486
  const nestedEntries = await Promise.all(
22546
23487
  entries.filter((entry) => entry.type === "folder").map(
22547
- (entry) => listExplorerEntriesRecursive(rootPath, join28(rootPath, entry.path), entry.path)
23488
+ (entry) => listExplorerEntriesRecursive(rootPath, join30(rootPath, entry.path), entry.path)
22548
23489
  )
22549
23490
  );
22550
23491
  return [...entries, ...nestedEntries.flat()];
@@ -22580,7 +23521,7 @@ async function handleListThreadExplorerDir(c, threadManager) {
22580
23521
  const entries = recursive ? await listExplorerEntriesRecursive(thread.path, absDir, queryPath) : dirents.filter((d) => !(!queryPath && d.name === ".git")).map((d) => ({
22581
23522
  name: d.name,
22582
23523
  type: d.isDirectory() ? "folder" : "file",
22583
- path: queryPath ? join28(queryPath, d.name) : d.name
23524
+ path: queryPath ? join30(queryPath, d.name) : d.name
22584
23525
  })).sort((a, b) => {
22585
23526
  if (a.type !== b.type) return a.type === "folder" ? -1 : 1;
22586
23527
  return a.name.localeCompare(b.name);
@@ -22726,7 +23667,7 @@ async function handleCreateExplorerFile(c, threadManager) {
22726
23667
  absParent = validated;
22727
23668
  }
22728
23669
  await mkdir7(absParent, { recursive: true });
22729
- await writeFile6(join28(absParent, name), "", "utf-8");
23670
+ await writeFile6(join30(absParent, name), "", "utf-8");
22730
23671
  const relPath = dirPath ? `${dirPath}/${name}` : name;
22731
23672
  return c.json({ success: true, path: relPath });
22732
23673
  } catch (error) {
@@ -22768,7 +23709,7 @@ async function handleGetExplorerMedia(c, threadManager) {
22768
23709
  }
22769
23710
  const ext = getFileExtension(filePath);
22770
23711
  const contentType = MEDIA_MIME_TYPES[ext] ?? "application/octet-stream";
22771
- const data = await readFile14(absPath);
23712
+ const data = await readFile15(absPath);
22772
23713
  return new Response(data, {
22773
23714
  headers: {
22774
23715
  "Content-Type": contentType,
@@ -22818,7 +23759,7 @@ async function handleCreateExplorerFolder(c, threadManager) {
22818
23759
  }
22819
23760
  absParent = validated;
22820
23761
  }
22821
- await mkdir7(join28(absParent, name), { recursive: true });
23762
+ await mkdir7(join30(absParent, name), { recursive: true });
22822
23763
  const relPath = dirPath ? `${dirPath}/${name}` : name;
22823
23764
  return c.json({ success: true, path: relPath });
22824
23765
  } catch (error) {
@@ -22991,13 +23932,13 @@ async function handleFixComments(c, threadManager) {
22991
23932
  }
22992
23933
 
22993
23934
  // src/features/threads/threads-ai-files.route.ts
22994
- import { readFile as readFile15, writeFile as writeFile8, mkdir as mkdir9, access as access5, rm as rm7, stat as stat5 } from "fs/promises";
22995
- import { join as join30 } from "path";
23935
+ import { readFile as readFile16, writeFile as writeFile8, mkdir as mkdir9, access as access5, rm as rm7, stat as stat5 } from "fs/promises";
23936
+ import { join as join32 } from "path";
22996
23937
  import { existsSync as existsSync18 } from "fs";
22997
23938
 
22998
23939
  // src/features/git/git-download-folder.ts
22999
23940
  import { mkdir as mkdir8, writeFile as writeFile7 } from "fs/promises";
23000
- import { join as join29, dirname as dirname5 } from "path";
23941
+ import { join as join31, dirname as dirname5 } from "path";
23001
23942
  async function downloadGithubFolder(repoUrl, srcPath, destPath, options = {}) {
23002
23943
  const { token, ref } = options;
23003
23944
  const match = repoUrl.replace(/\.git$/, "").match(/github\.com\/([^/]+)\/([^/]+)/);
@@ -23033,7 +23974,7 @@ async function downloadGithubFolder(repoUrl, srcPath, destPath, options = {}) {
23033
23974
  await Promise.all(
23034
23975
  files.map(async (file) => {
23035
23976
  const relativePath = file.path.slice(prefix.length);
23036
- const localPath = join29(destPath, relativePath);
23977
+ const localPath = join31(destPath, relativePath);
23037
23978
  await mkdir8(dirname5(localPath), { recursive: true });
23038
23979
  const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${refParam}/${file.path}`;
23039
23980
  const fileRes = await fetch(rawUrl, { headers });
@@ -23151,7 +24092,7 @@ Links to important documentation, tools, or references.
23151
24092
  400
23152
24093
  );
23153
24094
  }
23154
- const content = await readFile15(absPath, "utf-8");
24095
+ const content = await readFile16(absPath, "utf-8");
23155
24096
  return c.json({ content, path: filePath });
23156
24097
  } catch (error) {
23157
24098
  return errorResponse(
@@ -23190,7 +24131,7 @@ async function handleSaveThreadAIFile(c, threadManager) {
23190
24131
  if (!absPath) {
23191
24132
  return c.json({ error: { code: "BAD_REQUEST", message: "Invalid file path" } }, 400);
23192
24133
  }
23193
- const parentDir = join30(absPath, "..");
24134
+ const parentDir = join32(absPath, "..");
23194
24135
  await mkdir9(parentDir, { recursive: true });
23195
24136
  await writeFile8(absPath, content, "utf-8");
23196
24137
  return c.json({ success: true, path: filePath });
@@ -23279,10 +24220,10 @@ async function handleCreateThreadAgent(c, threadManager) {
23279
24220
  if (!thread) {
23280
24221
  return errorResponse(c, ErrorCodes.THREAD_NOT_FOUND, `Thread not found: ${threadId}`, 404);
23281
24222
  }
23282
- const agentRelPath = join30(".agents", "agents", name);
23283
- const agentAbsPath = join30(thread.path, agentRelPath);
23284
- const agentFileRelPath = join30(agentRelPath, "AGENT.md");
23285
- const agentFileAbsPath = join30(agentAbsPath, "AGENT.md");
24223
+ const agentRelPath = join32(".agents", "agents", name);
24224
+ const agentAbsPath = join32(thread.path, agentRelPath);
24225
+ const agentFileRelPath = join32(agentRelPath, "AGENT.md");
24226
+ const agentFileAbsPath = join32(agentAbsPath, "AGENT.md");
23286
24227
  if (existsSync18(agentAbsPath)) {
23287
24228
  return c.json(
23288
24229
  { error: { code: "CONFLICT", message: `Agent '${name}' already exists` } },
@@ -23349,10 +24290,10 @@ async function handleCreateThreadSkill(c, threadManager) {
23349
24290
  if (!thread) {
23350
24291
  return errorResponse(c, ErrorCodes.THREAD_NOT_FOUND, `Thread not found: ${threadId}`, 404);
23351
24292
  }
23352
- const skillRelPath = join30(".agents", "skills", name);
23353
- const skillAbsPath = join30(thread.path, skillRelPath);
23354
- const skillFileRelPath = join30(skillRelPath, "SKILL.md");
23355
- const skillFileAbsPath = join30(skillAbsPath, "SKILL.md");
24293
+ const skillRelPath = join32(".agents", "skills", name);
24294
+ const skillAbsPath = join32(thread.path, skillRelPath);
24295
+ const skillFileRelPath = join32(skillRelPath, "SKILL.md");
24296
+ const skillFileAbsPath = join32(skillAbsPath, "SKILL.md");
23356
24297
  if (existsSync18(skillAbsPath)) {
23357
24298
  return c.json(
23358
24299
  { error: { code: "CONFLICT", message: `Skill '${name}' already exists` } },
@@ -23483,7 +24424,7 @@ var ScriptProcessManager = class {
23483
24424
  if (outputBuffer.length > 0) {
23484
24425
  yield outputBuffer.shift();
23485
24426
  } else {
23486
- await new Promise((resolve6) => setTimeout(resolve6, 50));
24427
+ await new Promise((resolve7) => setTimeout(resolve7, 50));
23487
24428
  }
23488
24429
  }
23489
24430
  } finally {
@@ -23817,7 +24758,7 @@ var GitManagerImpl = class {
23817
24758
  yield event;
23818
24759
  }
23819
24760
  } else {
23820
- await new Promise((resolve6) => setTimeout(resolve6, 10));
24761
+ await new Promise((resolve7) => setTimeout(resolve7, 10));
23821
24762
  }
23822
24763
  }
23823
24764
  if (processError) {
@@ -23880,7 +24821,7 @@ var GitManagerImpl = class {
23880
24821
  * @returns true if the branch exists, false otherwise
23881
24822
  */
23882
24823
  async checkBranchExists(repoPath, branchName) {
23883
- return new Promise((resolve6) => {
24824
+ return new Promise((resolve7) => {
23884
24825
  const gitProcess = spawnProcess(
23885
24826
  "git",
23886
24827
  ["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`],
@@ -23889,10 +24830,10 @@ var GitManagerImpl = class {
23889
24830
  }
23890
24831
  );
23891
24832
  gitProcess.on("close", (code) => {
23892
- resolve6(code === 0);
24833
+ resolve7(code === 0);
23893
24834
  });
23894
24835
  gitProcess.on("error", () => {
23895
- resolve6(false);
24836
+ resolve7(false);
23896
24837
  });
23897
24838
  });
23898
24839
  }
@@ -23950,7 +24891,7 @@ var GitManagerImpl = class {
23950
24891
  yield event;
23951
24892
  }
23952
24893
  } else {
23953
- await new Promise((resolve6) => setTimeout(resolve6, 10));
24894
+ await new Promise((resolve7) => setTimeout(resolve7, 10));
23954
24895
  }
23955
24896
  }
23956
24897
  if (processError) {
@@ -24006,7 +24947,7 @@ import { Hono as Hono13 } from "hono";
24006
24947
  // src/features/git/git-user.route.ts
24007
24948
  init_utils();
24008
24949
  function readGitConfigValue(key) {
24009
- return new Promise((resolve6) => {
24950
+ return new Promise((resolve7) => {
24010
24951
  const proc = spawnProcess("git", ["config", key]);
24011
24952
  let out = "";
24012
24953
  proc.stdout?.on("data", (d) => {
@@ -24014,12 +24955,12 @@ function readGitConfigValue(key) {
24014
24955
  });
24015
24956
  proc.on("close", (code) => {
24016
24957
  if (code === 0) {
24017
- resolve6(out.trim());
24958
+ resolve7(out.trim());
24018
24959
  } else {
24019
- resolve6("");
24960
+ resolve7("");
24020
24961
  }
24021
24962
  });
24022
- proc.on("error", () => resolve6(""));
24963
+ proc.on("error", () => resolve7(""));
24023
24964
  });
24024
24965
  }
24025
24966
  async function gitUserHandler(c) {
@@ -24064,14 +25005,14 @@ async function gitStatusHandler(c, metadataManager) {
24064
25005
  400
24065
25006
  );
24066
25007
  }
24067
- const { hasChanges, changedFilesCount } = await new Promise((resolve6) => {
25008
+ const { hasChanges, changedFilesCount } = await new Promise((resolve7) => {
24068
25009
  getGitStatus(gitRoot).then((statusOutput) => {
24069
25010
  const lines = statusOutput.trim().split("\n").filter((line) => line.length > 0);
24070
- resolve6({
25011
+ resolve7({
24071
25012
  hasChanges: lines.length > 0,
24072
25013
  changedFilesCount: lines.length
24073
25014
  });
24074
- }).catch(() => resolve6({ hasChanges: false, changedFilesCount: 0 }));
25015
+ }).catch(() => resolve7({ hasChanges: false, changedFilesCount: 0 }));
24075
25016
  });
24076
25017
  const currentBranch = await getCurrentBranch(gitRoot);
24077
25018
  const hasUnpushed = await hasUnpushedCommits(gitRoot, currentBranch);
@@ -24138,15 +25079,26 @@ async function gitDiffHandler(c, metadataManager) {
24138
25079
  let oldContent = "";
24139
25080
  let newContent = "";
24140
25081
  const hunks = [];
25082
+ const isBinary = getExplorerFileMediaType(file.path) !== null;
25083
+ if (isBinary) {
25084
+ filesWithDiff.push({
25085
+ ...file,
25086
+ oldContent: "",
25087
+ newContent: "",
25088
+ isBinary: true,
25089
+ hunks: []
25090
+ });
25091
+ continue;
25092
+ }
24141
25093
  if (file.status === "deleted") {
24142
- const content = await new Promise((resolve6) => {
25094
+ const content = await new Promise((resolve7) => {
24143
25095
  const proc = spawnProcess("git", ["show", `HEAD:${file.path}`], { cwd: gitRoot });
24144
25096
  let out = "";
24145
25097
  proc.stdout?.on("data", (d) => {
24146
25098
  out += d.toString();
24147
25099
  });
24148
- proc.on("close", () => resolve6(out));
24149
- proc.on("error", () => resolve6(""));
25100
+ proc.on("close", () => resolve7(out));
25101
+ proc.on("error", () => resolve7(""));
24150
25102
  });
24151
25103
  oldContent = content;
24152
25104
  } else if (file.status === "added" && file.path.startsWith("(new)")) {
@@ -24156,7 +25108,7 @@ async function gitDiffHandler(c, metadataManager) {
24156
25108
  newContent = "";
24157
25109
  }
24158
25110
  } else {
24159
- const diffOutput = await new Promise((resolve6) => {
25111
+ const diffOutput = await new Promise((resolve7) => {
24160
25112
  const proc = spawnProcess(
24161
25113
  "git",
24162
25114
  file.status === "added" ? ["diff", "--cached", "--", file.path] : ["diff", "HEAD", "--", file.path],
@@ -24166,21 +25118,21 @@ async function gitDiffHandler(c, metadataManager) {
24166
25118
  proc.stdout?.on("data", (d) => {
24167
25119
  out += d.toString();
24168
25120
  });
24169
- proc.on("close", () => resolve6(out));
24170
- proc.on("error", () => resolve6(""));
25121
+ proc.on("close", () => resolve7(out));
25122
+ proc.on("error", () => resolve7(""));
24171
25123
  });
24172
25124
  const lines = diffOutput.split("\n");
24173
25125
  let currentHunk = null;
24174
25126
  let oldLineNum = 0;
24175
25127
  let newLineNum = 0;
24176
- const oldContentOutput = await new Promise((resolve6) => {
25128
+ const oldContentOutput = await new Promise((resolve7) => {
24177
25129
  const proc = spawnProcess("git", ["show", `HEAD:${file.path}`], { cwd: gitRoot });
24178
25130
  let out = "";
24179
25131
  proc.stdout?.on("data", (d) => {
24180
25132
  out += d.toString();
24181
25133
  });
24182
- proc.on("close", () => resolve6(out));
24183
- proc.on("error", () => resolve6(""));
25134
+ proc.on("close", () => resolve7(out));
25135
+ proc.on("error", () => resolve7(""));
24184
25136
  });
24185
25137
  oldContent = oldContentOutput;
24186
25138
  try {
@@ -24237,6 +25189,7 @@ async function gitDiffHandler(c, metadataManager) {
24237
25189
  ...file,
24238
25190
  oldContent,
24239
25191
  newContent,
25192
+ isBinary: false,
24240
25193
  hunks
24241
25194
  });
24242
25195
  }
@@ -25016,7 +25969,7 @@ function sanitizeBranchName(name) {
25016
25969
  return name.trim().toLowerCase().replace(/\s+/g, "-").replace(/[^a-zA-Z0-9\-_/.]/g, "").replace(/^[/.]+|[/.]+$/g, "").replace(/-+/g, "-") || "branch";
25017
25970
  }
25018
25971
  function checkBranchExists(gitRoot, branchName) {
25019
- return new Promise((resolve6) => {
25972
+ return new Promise((resolve7) => {
25020
25973
  const proc = spawnProcess(
25021
25974
  "git",
25022
25975
  ["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`],
@@ -25025,15 +25978,15 @@ function checkBranchExists(gitRoot, branchName) {
25025
25978
  }
25026
25979
  );
25027
25980
  proc.on("close", (code) => {
25028
- resolve6(code === 0);
25981
+ resolve7(code === 0);
25029
25982
  });
25030
25983
  proc.on("error", () => {
25031
- resolve6(false);
25984
+ resolve7(false);
25032
25985
  });
25033
25986
  });
25034
25987
  }
25035
25988
  function createAndCheckoutBranch(gitRoot, branchName) {
25036
- return new Promise((resolve6, reject) => {
25989
+ return new Promise((resolve7, reject) => {
25037
25990
  const proc = spawnProcess("git", ["checkout", "-b", branchName], {
25038
25991
  cwd: gitRoot
25039
25992
  });
@@ -25045,7 +25998,7 @@ function createAndCheckoutBranch(gitRoot, branchName) {
25045
25998
  }
25046
25999
  proc.on("close", (code) => {
25047
26000
  if (code === 0) {
25048
- resolve6();
26001
+ resolve7();
25049
26002
  } else {
25050
26003
  reject(new Error(err || `Failed to create branch "${branchName}"`));
25051
26004
  }
@@ -25138,7 +26091,7 @@ function sanitizeBranchName2(name) {
25138
26091
  return name.trim().toLowerCase().replace(/\s+/g, "-").replace(/[^a-zA-Z0-9\-_/.]/g, "").replace(/^[/.]+|[/.]+$/g, "").replace(/-+/g, "-") || "branch";
25139
26092
  }
25140
26093
  function checkBranchExists2(gitRoot, branchName) {
25141
- return new Promise((resolve6) => {
26094
+ return new Promise((resolve7) => {
25142
26095
  const proc = spawnProcess(
25143
26096
  "git",
25144
26097
  ["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`],
@@ -25147,16 +26100,16 @@ function checkBranchExists2(gitRoot, branchName) {
25147
26100
  }
25148
26101
  );
25149
26102
  proc.on("close", (code) => {
25150
- resolve6(code === 0);
26103
+ resolve7(code === 0);
25151
26104
  });
25152
26105
  proc.on("error", () => {
25153
- resolve6(false);
26106
+ resolve7(false);
25154
26107
  });
25155
26108
  });
25156
26109
  }
25157
26110
  function createAndCheckoutBranch2(gitRoot, branchName, baseBranch) {
25158
26111
  const args2 = baseBranch ? ["checkout", "-b", branchName, `origin/${baseBranch}`] : ["checkout", "-b", branchName];
25159
- return new Promise((resolve6, reject) => {
26112
+ return new Promise((resolve7, reject) => {
25160
26113
  const proc = spawnProcess("git", args2, {
25161
26114
  cwd: gitRoot
25162
26115
  });
@@ -25168,7 +26121,7 @@ function createAndCheckoutBranch2(gitRoot, branchName, baseBranch) {
25168
26121
  }
25169
26122
  proc.on("close", (code) => {
25170
26123
  if (code === 0) {
25171
- resolve6();
26124
+ resolve7();
25172
26125
  } else {
25173
26126
  reject(new Error(err || `Failed to create branch "${branchName}"`));
25174
26127
  }
@@ -25283,14 +26236,14 @@ async function gitSyncBranchHandler(c, metadataManager) {
25283
26236
  );
25284
26237
  }
25285
26238
  const { spawnProcess: spawnProcess2 } = await Promise.resolve().then(() => (init_utils(), utils_exports));
25286
- return new Promise((resolve6) => {
26239
+ return new Promise((resolve7) => {
25287
26240
  console.log(`[sync-branch] Checking branch status...`);
25288
26241
  const statusProc = spawnProcess2("git", ["status", "-sb"], { cwd: gitRoot });
25289
26242
  let statusOutput = "";
25290
26243
  statusProc.on("close", (statusCode) => {
25291
26244
  if (statusCode !== 0) {
25292
26245
  console.log(`[sync-branch] Failed to get git status: code ${statusCode}`);
25293
- resolve6(
26246
+ resolve7(
25294
26247
  c.json(
25295
26248
  {
25296
26249
  error: "Failed to check git status",
@@ -25324,7 +26277,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
25324
26277
  pushProc.on("close", (pushCode) => {
25325
26278
  if (pushCode !== 0) {
25326
26279
  console.log(`[sync-branch] Failed to push commits: code ${pushCode}`);
25327
- resolve6(
26280
+ resolve7(
25328
26281
  c.json(
25329
26282
  {
25330
26283
  error: `Failed to push commits before sync: ${pushErr || pushOut}`,
@@ -25342,7 +26295,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
25342
26295
  console.log(
25343
26296
  `[sync-branch] \u2717 Push process error: ${error instanceof Error ? error.message : String(error)}`
25344
26297
  );
25345
- resolve6(
26298
+ resolve7(
25346
26299
  c.json(
25347
26300
  {
25348
26301
  error: `Failed to push commits: ${error instanceof Error ? error.message : String(error)}`
@@ -25370,7 +26323,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
25370
26323
  console.log(
25371
26324
  `[sync-branch] \u2717 Status process error: ${error instanceof Error ? error.message : String(error)}`
25372
26325
  );
25373
- resolve6(
26326
+ resolve7(
25374
26327
  c.json(
25375
26328
  {
25376
26329
  error: `Failed to check git status: ${error instanceof Error ? error.message : String(error)}`
@@ -25385,7 +26338,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
25385
26338
  configProc.on("close", (configCode) => {
25386
26339
  if (configCode !== 0) {
25387
26340
  console.log(`[sync-branch] Failed to configure git: code ${configCode}`);
25388
- resolve6(
26341
+ resolve7(
25389
26342
  c.json(
25390
26343
  {
25391
26344
  error: "Failed to configure git pull strategy",
@@ -25419,7 +26372,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
25419
26372
  if (code === 0) {
25420
26373
  const message = currentBranch === defaultBranch ? `Successfully pulled latest changes from origin/${defaultBranch}` : `Successfully synced ${currentBranch} with ${defaultBranch}`;
25421
26374
  console.log(`[sync-branch] \u2713 ${message}`);
25422
- resolve6(
26375
+ resolve7(
25423
26376
  c.json({
25424
26377
  success: true,
25425
26378
  message,
@@ -25430,7 +26383,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
25430
26383
  } else {
25431
26384
  console.log(`[sync-branch] \u2717 Failed to sync with exit code ${code}`);
25432
26385
  console.log(`[sync-branch] Error output: ${err || out}`);
25433
- resolve6(
26386
+ resolve7(
25434
26387
  c.json(
25435
26388
  {
25436
26389
  error: `Failed to sync with ${defaultBranch}: ${err || out}`,
@@ -25445,7 +26398,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
25445
26398
  console.log(
25446
26399
  `[sync-branch] \u2717 Process error: ${error instanceof Error ? error.message : String(error)}`
25447
26400
  );
25448
- resolve6(
26401
+ resolve7(
25449
26402
  c.json(
25450
26403
  {
25451
26404
  error: `Failed to sync branch: ${error instanceof Error ? error.message : String(error)}`
@@ -25459,7 +26412,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
25459
26412
  console.log(
25460
26413
  `[sync-branch] \u2717 Config process error: ${error instanceof Error ? error.message : String(error)}`
25461
26414
  );
25462
- resolve6(
26415
+ resolve7(
25463
26416
  c.json(
25464
26417
  {
25465
26418
  error: `Failed to configure git: ${error instanceof Error ? error.message : String(error)}`
@@ -25480,7 +26433,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
25480
26433
  // src/features/git/git-list-branches.route.ts
25481
26434
  init_utils();
25482
26435
  function listLocalBranches(gitRoot) {
25483
- return new Promise((resolve6) => {
26436
+ return new Promise((resolve7) => {
25484
26437
  const proc = spawnProcess("git", ["branch", "--format=%(refname:short)"], { cwd: gitRoot });
25485
26438
  let out = "";
25486
26439
  if (proc.stdout) {
@@ -25490,9 +26443,9 @@ function listLocalBranches(gitRoot) {
25490
26443
  }
25491
26444
  proc.on("close", () => {
25492
26445
  const branches = out.split("\n").map((b) => b.trim()).filter(Boolean);
25493
- resolve6(branches);
26446
+ resolve7(branches);
25494
26447
  });
25495
- proc.on("error", () => resolve6([]));
26448
+ proc.on("error", () => resolve7([]));
25496
26449
  });
25497
26450
  }
25498
26451
  async function gitListBranchesHandler(c, metadataManager) {
@@ -25524,7 +26477,7 @@ async function gitListBranchesHandler(c, metadataManager) {
25524
26477
  // src/features/git/git-checkout-branch.route.ts
25525
26478
  init_utils();
25526
26479
  function checkoutBranch(gitRoot, branchName) {
25527
- return new Promise((resolve6, reject) => {
26480
+ return new Promise((resolve7, reject) => {
25528
26481
  const proc = spawnProcess("git", ["checkout", branchName], { cwd: gitRoot });
25529
26482
  let err = "";
25530
26483
  if (proc.stderr) {
@@ -25534,7 +26487,7 @@ function checkoutBranch(gitRoot, branchName) {
25534
26487
  }
25535
26488
  proc.on("close", (code) => {
25536
26489
  if (code === 0) {
25537
- resolve6();
26490
+ resolve7();
25538
26491
  } else {
25539
26492
  reject(new Error(err.trim() || `Failed to checkout branch "${branchName}"`));
25540
26493
  }
@@ -25584,7 +26537,7 @@ import { unlinkSync } from "fs";
25584
26537
  import { resolve as resolve5 } from "path";
25585
26538
  init_utils();
25586
26539
  function runGit2(args2, cwd) {
25587
- return new Promise((resolve6, reject) => {
26540
+ return new Promise((resolve7, reject) => {
25588
26541
  const proc = spawnProcess("git", args2, { cwd });
25589
26542
  let out = "";
25590
26543
  let err = "";
@@ -25595,7 +26548,7 @@ function runGit2(args2, cwd) {
25595
26548
  err += d.toString();
25596
26549
  });
25597
26550
  proc.on("close", (code) => {
25598
- if (code === 0) resolve6(out.trim());
26551
+ if (code === 0) resolve7(out.trim());
25599
26552
  else reject(new Error(err.trim() || `git ${args2[0]} failed with code ${code}`));
25600
26553
  });
25601
26554
  proc.on("error", reject);
@@ -25647,7 +26600,7 @@ async function gitRevertFileHandler(c, metadataManager) {
25647
26600
  // src/features/git/git-checkpoint.route.ts
25648
26601
  init_utils();
25649
26602
  function stashWithLabel(gitRoot, label) {
25650
- return new Promise((resolve6, reject) => {
26603
+ return new Promise((resolve7, reject) => {
25651
26604
  const proc = spawnProcess("git", ["stash", "push", "--include-untracked", "-m", label], {
25652
26605
  cwd: gitRoot
25653
26606
  });
@@ -25667,7 +26620,7 @@ function stashWithLabel(gitRoot, label) {
25667
26620
  if (code === 0) {
25668
26621
  const nothingSaved = out.includes("No local changes to save");
25669
26622
  if (nothingSaved) {
25670
- resolve6(false);
26623
+ resolve7(false);
25671
26624
  return;
25672
26625
  }
25673
26626
  const apply = spawnProcess("git", ["stash", "apply", "--index"], { cwd: gitRoot });
@@ -25679,7 +26632,7 @@ function stashWithLabel(gitRoot, label) {
25679
26632
  }
25680
26633
  apply.on("close", (applyCode) => {
25681
26634
  if (applyCode === 0) {
25682
- resolve6(true);
26635
+ resolve7(true);
25683
26636
  } else {
25684
26637
  reject(new Error(applyErr || "Failed to re-apply stash after checkpoint"));
25685
26638
  }
@@ -25728,7 +26681,7 @@ async function gitCheckpointHandler(c, metadataManager) {
25728
26681
  // src/features/git/git-checkpoint-restore.route.ts
25729
26682
  init_utils();
25730
26683
  function listStashes(gitRoot) {
25731
- return new Promise((resolve6, reject) => {
26684
+ return new Promise((resolve7, reject) => {
25732
26685
  const proc = spawnProcess("git", ["stash", "list", "--format=%gd %s"], { cwd: gitRoot });
25733
26686
  let out = "";
25734
26687
  let err = "";
@@ -25752,18 +26705,18 @@ function listStashes(gitRoot) {
25752
26705
  if (!match) return null;
25753
26706
  return { index: parseInt(match[1], 10), label: match[2].trim() };
25754
26707
  }).filter((e) => e !== null);
25755
- resolve6(entries);
26708
+ resolve7(entries);
25756
26709
  });
25757
26710
  proc.on("error", reject);
25758
26711
  });
25759
26712
  }
25760
26713
  function discardWorkingChanges(gitRoot) {
25761
- return new Promise((resolve6, reject) => {
26714
+ return new Promise((resolve7, reject) => {
25762
26715
  const proc = spawnProcess("git", ["checkout", "--", "."], { cwd: gitRoot });
25763
26716
  proc.on("close", () => {
25764
26717
  const clean = spawnProcess("git", ["clean", "-fd"], { cwd: gitRoot });
25765
26718
  clean.on("close", (code) => {
25766
- if (code === 0) resolve6();
26719
+ if (code === 0) resolve7();
25767
26720
  else reject(new Error("Failed to clean working tree"));
25768
26721
  });
25769
26722
  clean.on("error", reject);
@@ -25772,7 +26725,7 @@ function discardWorkingChanges(gitRoot) {
25772
26725
  });
25773
26726
  }
25774
26727
  function applyStash(gitRoot, index) {
25775
- return new Promise((resolve6, reject) => {
26728
+ return new Promise((resolve7, reject) => {
25776
26729
  const proc = spawnProcess("git", ["stash", "apply", `stash@{${index}}`], { cwd: gitRoot });
25777
26730
  let err = "";
25778
26731
  if (proc.stderr) {
@@ -25781,7 +26734,7 @@ function applyStash(gitRoot, index) {
25781
26734
  });
25782
26735
  }
25783
26736
  proc.on("close", (code) => {
25784
- if (code === 0) resolve6();
26737
+ if (code === 0) resolve7();
25785
26738
  else reject(new Error(err || "Failed to apply stash"));
25786
26739
  });
25787
26740
  proc.on("error", reject);
@@ -25825,6 +26778,175 @@ async function gitCheckpointRestoreHandler(c, metadataManager) {
25825
26778
  }
25826
26779
  }
25827
26780
 
26781
+ // src/features/git/git-rename-branch.route.ts
26782
+ init_utils();
26783
+ function sanitizeBranchName3(name) {
26784
+ if (!name || typeof name !== "string") {
26785
+ return "";
26786
+ }
26787
+ return name.trim().toLowerCase().replace(/\s+/g, "-").replace(/[^a-zA-Z0-9\-_/.]/g, "").replace(/^[/.]+|[/.]+$/g, "").replace(/-+/g, "-") || "";
26788
+ }
26789
+ function checkBranchExists3(gitRoot, branchName) {
26790
+ return new Promise((resolve7) => {
26791
+ const proc = spawnProcess(
26792
+ "git",
26793
+ ["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`],
26794
+ {
26795
+ cwd: gitRoot
26796
+ }
26797
+ );
26798
+ proc.on("close", (code) => {
26799
+ resolve7(code === 0);
26800
+ });
26801
+ proc.on("error", () => {
26802
+ resolve7(false);
26803
+ });
26804
+ });
26805
+ }
26806
+ function renameBranch(gitRoot, newBranchName) {
26807
+ return new Promise((resolve7, reject) => {
26808
+ const proc = spawnProcess("git", ["branch", "-m", newBranchName], {
26809
+ cwd: gitRoot
26810
+ });
26811
+ let err = "";
26812
+ if (proc.stderr) {
26813
+ proc.stderr.on("data", (d) => {
26814
+ err += d.toString();
26815
+ });
26816
+ }
26817
+ proc.on("close", (code) => {
26818
+ if (code === 0) {
26819
+ resolve7();
26820
+ } else {
26821
+ reject(new Error(err || `Failed to rename branch to "${newBranchName}"`));
26822
+ }
26823
+ });
26824
+ proc.on("error", reject);
26825
+ });
26826
+ }
26827
+ async function gitRenameBranchHandler(c, metadataManager) {
26828
+ try {
26829
+ const threadId = c.req.param("threadId");
26830
+ const thread = await metadataManager.loadThreads().then((threads2) => threads2.find((t) => t.id === threadId));
26831
+ if (!thread) {
26832
+ return c.json({ error: "Thread not found" }, 404);
26833
+ }
26834
+ const repoPath = thread.path;
26835
+ if (!repoPath) {
26836
+ return c.json({ error: "Thread path not found" }, 404);
26837
+ }
26838
+ const body = await c.req.json().catch(() => ({}));
26839
+ const rawName = body.newBranchName?.trim();
26840
+ if (!rawName) {
26841
+ return c.json({ error: "newBranchName is required" }, 400);
26842
+ }
26843
+ const newBranchName = sanitizeBranchName3(rawName);
26844
+ if (!newBranchName) {
26845
+ return c.json({ error: "Invalid branch name" }, 400);
26846
+ }
26847
+ const absolutePath = resolveThreadPath(repoPath);
26848
+ let gitRoot;
26849
+ try {
26850
+ gitRoot = await getGitRoot(absolutePath);
26851
+ } catch {
26852
+ return c.json({ error: `Path is not a git repository: ${absolutePath}` }, 400);
26853
+ }
26854
+ const branchExists = await checkBranchExists3(gitRoot, newBranchName);
26855
+ if (branchExists) {
26856
+ return c.json({ error: `Branch "${newBranchName}" already exists` }, 409);
26857
+ }
26858
+ await renameBranch(gitRoot, newBranchName);
26859
+ const threads = await metadataManager.loadThreads();
26860
+ const threadIndex = threads.findIndex((t) => t.id === threadId);
26861
+ if (threadIndex !== -1) {
26862
+ threads[threadIndex].currentBranch = newBranchName;
26863
+ await metadataManager.saveThreads(threads);
26864
+ }
26865
+ return c.json({ success: true, branchName: newBranchName });
26866
+ } catch (error) {
26867
+ const message = error instanceof Error ? error.message : "Failed to rename branch";
26868
+ return c.json({ error: message }, 500);
26869
+ }
26870
+ }
26871
+
26872
+ // src/features/git/git-media.route.ts
26873
+ import { readFileSync as readFileSync8, existsSync as existsSync23 } from "fs";
26874
+ import { resolve as resolve6 } from "path";
26875
+ import { spawn as spawn3 } from "child_process";
26876
+ function spawnBinary(command, args2, cwd) {
26877
+ return new Promise((resolve7, reject) => {
26878
+ const proc = spawn3(command, args2, { cwd });
26879
+ const chunks = [];
26880
+ proc.stdout?.on("data", (d) => {
26881
+ chunks.push(Buffer.from(d));
26882
+ });
26883
+ proc.on("close", (code) => {
26884
+ if (code === 0) {
26885
+ resolve7(Buffer.concat(chunks));
26886
+ } else {
26887
+ reject(new Error(`Process exited with code ${code}`));
26888
+ }
26889
+ });
26890
+ proc.on("error", reject);
26891
+ });
26892
+ }
26893
+ async function gitMediaHandler(c, metadataManager) {
26894
+ try {
26895
+ const threadId = c.req.param("threadId");
26896
+ const filePath = c.req.query("path");
26897
+ const version = c.req.query("version");
26898
+ if (!threadId) {
26899
+ return c.json({ error: "Thread ID is required" }, 400);
26900
+ }
26901
+ if (!filePath) {
26902
+ return c.json({ error: "path query parameter is required" }, 400);
26903
+ }
26904
+ if (version !== "old" && version !== "new") {
26905
+ return c.json({ error: "version query parameter must be 'old' or 'new'" }, 400);
26906
+ }
26907
+ const thread = await metadataManager.loadThreads().then((threads) => threads.find((t) => t.id === threadId));
26908
+ if (!thread) {
26909
+ return c.json({ error: "Thread not found" }, 404);
26910
+ }
26911
+ const repoPath = thread.path;
26912
+ if (!repoPath) {
26913
+ return c.json({ error: "Thread path not found" }, 404);
26914
+ }
26915
+ const absolutePath = resolveThreadPath(repoPath);
26916
+ let gitRoot;
26917
+ try {
26918
+ gitRoot = await getGitRoot(absolutePath);
26919
+ } catch {
26920
+ return c.json({ error: `Path is not a git repository: ${absolutePath}` }, 400);
26921
+ }
26922
+ const ext = getFileExtension(filePath);
26923
+ const contentType = EXPLORER_MEDIA_MIME_TYPES[ext] ?? "application/octet-stream";
26924
+ let data;
26925
+ if (version === "old") {
26926
+ try {
26927
+ data = await spawnBinary("git", ["show", `HEAD:${filePath}`], gitRoot);
26928
+ } catch {
26929
+ return c.json({ error: `File not found in HEAD: ${filePath}` }, 404);
26930
+ }
26931
+ } else {
26932
+ const absFilePath = resolve6(gitRoot, filePath);
26933
+ if (!existsSync23(absFilePath)) {
26934
+ return c.json({ error: `File not found in working directory: ${filePath}` }, 404);
26935
+ }
26936
+ data = readFileSync8(absFilePath);
26937
+ }
26938
+ return new Response(data.buffer, {
26939
+ headers: {
26940
+ "Content-Type": contentType,
26941
+ "Cross-Origin-Resource-Policy": "cross-origin"
26942
+ }
26943
+ });
26944
+ } catch (error) {
26945
+ const message = error instanceof Error ? error.message : "Failed to get git media";
26946
+ return c.json({ error: message }, 500);
26947
+ }
26948
+ }
26949
+
25828
26950
  // src/features/git/git.routes.ts
25829
26951
  function createGitRoutes(metadataManager) {
25830
26952
  const router = new Hono13();
@@ -25847,6 +26969,9 @@ function createGitRoutes(metadataManager) {
25847
26969
  router.get("/diff/:threadId", async (c) => {
25848
26970
  return gitDiffHandler(c, metadataManager);
25849
26971
  });
26972
+ router.get("/media/:threadId", async (c) => {
26973
+ return gitMediaHandler(c, metadataManager);
26974
+ });
25850
26975
  router.post("/generate-commit-message/:threadId", async (c) => {
25851
26976
  return gitGenerateCommitMessageHandler(c, metadataManager);
25852
26977
  });
@@ -25911,6 +27036,11 @@ function createGitRoutes(metadataManager) {
25911
27036
  await invalidateGitStatusCache(db, c.req.param("threadId"));
25912
27037
  return gitCheckoutBranchHandler(c, metadataManager);
25913
27038
  });
27039
+ router.post("/rename-branch/:threadId", async (c) => {
27040
+ const db = await getDatabase();
27041
+ await invalidateGitStatusCache(db, c.req.param("threadId"));
27042
+ return gitRenameBranchHandler(c, metadataManager);
27043
+ });
25914
27044
  router.post("/revert-file/:threadId", async (c) => {
25915
27045
  const db = await getDatabase();
25916
27046
  await invalidateGitStatusCache(db, c.req.param("threadId"));
@@ -25966,8 +27096,8 @@ function createUpdateRoutes(updater) {
25966
27096
 
25967
27097
  // src/features/mcp/mcp.routes.ts
25968
27098
  import { Hono as Hono15 } from "hono";
25969
- import { readFile as readFile16, writeFile as writeFile9, mkdir as mkdir11, access as access6 } from "fs/promises";
25970
- import { join as join31, dirname as dirname7 } from "path";
27099
+ import { readFile as readFile17, writeFile as writeFile9, mkdir as mkdir11, access as access6 } from "fs/promises";
27100
+ import { join as join33, dirname as dirname7 } from "path";
25971
27101
 
25972
27102
  // src/features/mcp/mcp.popular.json
25973
27103
  var mcp_popular_default = [
@@ -26089,10 +27219,10 @@ var mcp_popular_default = [
26089
27219
  var MCP_CONFIG_PATHS2 = [".agents/mcp.json", "mcp.json"];
26090
27220
  async function readMCPConfig(projectPath) {
26091
27221
  for (const configPath of MCP_CONFIG_PATHS2) {
26092
- const fullPath = join31(projectPath, configPath);
27222
+ const fullPath = join33(projectPath, configPath);
26093
27223
  try {
26094
27224
  await access6(fullPath);
26095
- const content = await readFile16(fullPath, "utf-8");
27225
+ const content = await readFile17(fullPath, "utf-8");
26096
27226
  const config = JSON.parse(content);
26097
27227
  return { config, filePath: fullPath };
26098
27228
  } catch {
@@ -26103,7 +27233,7 @@ async function readMCPConfig(projectPath) {
26103
27233
  }
26104
27234
  async function writeMCPConfig(projectPath, config) {
26105
27235
  const existing = await readMCPConfig(projectPath);
26106
- const targetPath = existing?.filePath ?? join31(projectPath, ".agents/mcp.json");
27236
+ const targetPath = existing?.filePath ?? join33(projectPath, ".agents/mcp.json");
26107
27237
  await mkdir11(dirname7(targetPath), { recursive: true });
26108
27238
  await writeFile9(targetPath, JSON.stringify(config, null, 2), "utf-8");
26109
27239
  }
@@ -26519,8 +27649,8 @@ function getLocalNetworkAddresses() {
26519
27649
  }
26520
27650
 
26521
27651
  // src/core/server-bind-mode-store.ts
26522
- import { existsSync as existsSync23, mkdirSync as mkdirSync2, readFileSync as readFileSync8, writeFileSync } from "fs";
26523
- import { join as join32 } from "path";
27652
+ import { existsSync as existsSync24, mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync } from "fs";
27653
+ import { join as join34 } from "path";
26524
27654
 
26525
27655
  // src/core/server-bind-mode.ts
26526
27656
  var DEFAULT_SERVER_BIND_MODE = "local";
@@ -26532,16 +27662,16 @@ function getServerBindHostname(mode) {
26532
27662
  }
26533
27663
 
26534
27664
  // src/core/server-bind-mode-store.ts
26535
- var SERVER_BIND_MODE_FILE = join32(DATA_DIR, "server-bind-mode.json");
27665
+ var SERVER_BIND_MODE_FILE = join34(DATA_DIR, "server-bind-mode.json");
26536
27666
  function hasServerBindModeFile() {
26537
- return existsSync23(SERVER_BIND_MODE_FILE);
27667
+ return existsSync24(SERVER_BIND_MODE_FILE);
26538
27668
  }
26539
27669
  function readServerBindMode() {
26540
27670
  try {
26541
- if (!existsSync23(SERVER_BIND_MODE_FILE)) {
27671
+ if (!existsSync24(SERVER_BIND_MODE_FILE)) {
26542
27672
  return DEFAULT_SERVER_BIND_MODE;
26543
27673
  }
26544
- const raw = readFileSync8(SERVER_BIND_MODE_FILE, "utf8");
27674
+ const raw = readFileSync9(SERVER_BIND_MODE_FILE, "utf8");
26545
27675
  const parsed = JSON.parse(raw);
26546
27676
  return normalizeServerBindMode(parsed.serverBindMode);
26547
27677
  } catch {
@@ -26865,7 +27995,8 @@ async function clipboardWriteImage(c) {
26865
27995
  pngData[i] = binary.charCodeAt(i);
26866
27996
  }
26867
27997
  } else if (imageUrl) {
26868
- const res = await fetch(imageUrl);
27998
+ const fetchUrl2 = resolveAbsoluteImageUrl(imageUrl, new URL(c.req.url).origin);
27999
+ const res = await fetch(fetchUrl2);
26869
28000
  if (!res.ok) {
26870
28001
  return c.json({ error: `Failed to fetch image: ${res.status}` }, 400);
26871
28002
  }
@@ -26882,7 +28013,7 @@ async function clipboardWriteImage(c) {
26882
28013
  }
26883
28014
 
26884
28015
  // src/features/image/image-save.route.ts
26885
- import { join as join33 } from "path";
28016
+ import { join as join35 } from "path";
26886
28017
  var Utils5 = null;
26887
28018
  var utilsLoaded5 = false;
26888
28019
  async function loadUtils5() {
@@ -26910,7 +28041,8 @@ async function saveImage(c) {
26910
28041
  data[i] = binary.charCodeAt(i);
26911
28042
  }
26912
28043
  } else if (imageUrl) {
26913
- const res = await fetch(imageUrl);
28044
+ const fetchUrl2 = resolveAbsoluteImageUrl(imageUrl, new URL(c.req.url).origin);
28045
+ const res = await fetch(fetchUrl2);
26914
28046
  if (!res.ok) {
26915
28047
  return c.json({ error: `Failed to fetch image: ${res.status}` }, 400);
26916
28048
  }
@@ -26919,7 +28051,7 @@ async function saveImage(c) {
26919
28051
  return c.json({ error: "imageUrl or imageData is required" }, 400);
26920
28052
  }
26921
28053
  const name = filename ?? `generated-image-${Date.now()}.png`;
26922
- const savePath = join33(Utils5.paths.downloads, name);
28054
+ const savePath = join35(Utils5.paths.downloads, name);
26923
28055
  await Bun.write(savePath, data);
26924
28056
  return c.json({ success: true, path: savePath });
26925
28057
  } catch (error) {
@@ -27011,7 +28143,7 @@ function createBrowserJsRoutes(threadManager, projectManager, processingStateMan
27011
28143
  });
27012
28144
  }
27013
28145
  }) : void 0;
27014
- const tools = createAllTools(thread.path, { shellPermissionGate });
28146
+ const tools = createAllTools(thread.path, { shellPermissionGate, threadId });
27015
28147
  const tool = tools[toolName];
27016
28148
  if (!tool) {
27017
28149
  return errorResponse(c, ErrorCodes.INVALID_REQUEST, `Tool ${toolName} not found`, 404);
@@ -27027,8 +28159,79 @@ function createBrowserJsRoutes(threadManager, projectManager, processingStateMan
27027
28159
  return router;
27028
28160
  }
27029
28161
 
27030
- // src/features/voice-model/voice-model.routes.ts
28162
+ // src/features/browser/browser.routes.ts
27031
28163
  import { Hono as Hono23 } from "hono";
28164
+ function createBrowserRoutes() {
28165
+ const router = new Hono23();
28166
+ router.post("/ready", async (c) => {
28167
+ try {
28168
+ const body = await c.req.json();
28169
+ const { toolCallId, webviewId } = body;
28170
+ if (!toolCallId || typeof toolCallId !== "string") {
28171
+ return errorResponse(
28172
+ c,
28173
+ ErrorCodes.INVALID_REQUEST,
28174
+ "toolCallId is required and must be a string",
28175
+ 400
28176
+ );
28177
+ }
28178
+ if (typeof webviewId !== "number" || !Number.isFinite(webviewId)) {
28179
+ return errorResponse(
28180
+ c,
28181
+ ErrorCodes.INVALID_REQUEST,
28182
+ "webviewId is required and must be a number",
28183
+ 400
28184
+ );
28185
+ }
28186
+ submitBrowserWebviewReady(toolCallId, webviewId);
28187
+ return c.json({ success: true });
28188
+ } catch (error) {
28189
+ return errorResponse(
28190
+ c,
28191
+ ErrorCodes.REQUEST_PARSE_ERROR,
28192
+ "Failed to parse request body",
28193
+ 400,
28194
+ error instanceof Error ? error.message : String(error)
28195
+ );
28196
+ }
28197
+ });
28198
+ router.post("/action-result", async (c) => {
28199
+ try {
28200
+ const body = await c.req.json();
28201
+ const { toolCallId, result } = body;
28202
+ if (!toolCallId || typeof toolCallId !== "string") {
28203
+ return errorResponse(
28204
+ c,
28205
+ ErrorCodes.INVALID_REQUEST,
28206
+ "toolCallId is required and must be a string",
28207
+ 400
28208
+ );
28209
+ }
28210
+ if (!result || typeof result !== "object" || Array.isArray(result)) {
28211
+ return errorResponse(
28212
+ c,
28213
+ ErrorCodes.INVALID_REQUEST,
28214
+ "result is required and must be an object",
28215
+ 400
28216
+ );
28217
+ }
28218
+ submitBrowserActionResult(toolCallId, result);
28219
+ return c.json({ success: true });
28220
+ } catch (error) {
28221
+ return errorResponse(
28222
+ c,
28223
+ ErrorCodes.REQUEST_PARSE_ERROR,
28224
+ "Failed to parse request body",
28225
+ 400,
28226
+ error instanceof Error ? error.message : String(error)
28227
+ );
28228
+ }
28229
+ });
28230
+ return router;
28231
+ }
28232
+
28233
+ // src/features/voice-model/voice-model.routes.ts
28234
+ import { Hono as Hono24 } from "hono";
27032
28235
  var VOICE_MODEL_URLS = {
27033
28236
  default: "https://install.tarsk.io/voice-models/ggml-tiny.en.bin",
27034
28237
  tiny: "https://install.tarsk.io/voice-models/ggml-tiny.en-q5_1.bin"
@@ -27040,7 +28243,7 @@ function getVoiceModelUrl(model) {
27040
28243
  return VOICE_MODEL_URLS.default;
27041
28244
  }
27042
28245
  function createVoiceModelRoutes() {
27043
- const router = new Hono23();
28246
+ const router = new Hono24();
27044
28247
  router.get("/download", async (c) => {
27045
28248
  const selectedModel = c.req.query("model");
27046
28249
  const modelUrl = getVoiceModelUrl(selectedModel);
@@ -27081,9 +28284,9 @@ function createVoiceModelRoutes() {
27081
28284
  }
27082
28285
 
27083
28286
  // src/features/logs/logs.routes.ts
27084
- import { Hono as Hono24 } from "hono";
28287
+ import { Hono as Hono25 } from "hono";
27085
28288
  function createLogsRoutes() {
27086
- const router = new Hono24();
28289
+ const router = new Hono25();
27087
28290
  router.post("/", async (c) => {
27088
28291
  let body = {};
27089
28292
  try {
@@ -27121,11 +28324,11 @@ function createLogsRoutes() {
27121
28324
 
27122
28325
  // src/features/user-tasks/user-tasks.routes.ts
27123
28326
  init_database();
27124
- import { Hono as Hono25 } from "hono";
28327
+ import { Hono as Hono26 } from "hono";
27125
28328
 
27126
28329
  // src/features/user-tasks/user-tasks.database.ts
27127
28330
  init_database();
27128
- import { randomUUID as randomUUID12 } from "crypto";
28331
+ import { randomUUID as randomUUID13 } from "crypto";
27129
28332
  async function getUserTasksByThread(db, threadId) {
27130
28333
  const result = await db.execute({
27131
28334
  sql: `SELECT id, threadId, content, createdAt, updatedAt
@@ -27138,7 +28341,7 @@ async function getUserTasksByThread(db, threadId) {
27138
28341
  }
27139
28342
  async function insertUserTask(threadId, content) {
27140
28343
  const db = await getDatabase();
27141
- const id = randomUUID12();
28344
+ const id = randomUUID13();
27142
28345
  const now = (/* @__PURE__ */ new Date()).toISOString();
27143
28346
  await db.execute({
27144
28347
  sql: `INSERT INTO user_tasks (id, threadId, content, createdAt, updatedAt)
@@ -27162,7 +28365,7 @@ async function deleteUserTaskById(db, id) {
27162
28365
 
27163
28366
  // src/features/user-tasks/user-tasks.routes.ts
27164
28367
  function createUserTaskRoutes() {
27165
- const router = new Hono25();
28368
+ const router = new Hono26();
27166
28369
  router.get("/:threadId/user-tasks", async (c) => {
27167
28370
  const threadId = c.req.param("threadId");
27168
28371
  const db = await getDatabase();
@@ -27202,7 +28405,7 @@ async function startTarskServer(options) {
27202
28405
  async function startTarskServerInternal(options) {
27203
28406
  const { isDebug: isDebug2, publicDir: publicDirOverride } = options;
27204
28407
  const port = isDebug2 ? 462 : process.env.PORT ? parseInt(process.env.PORT) : 641;
27205
- const app = new Hono26();
28408
+ const app = new Hono27();
27206
28409
  app.use("/*", cors());
27207
28410
  app.use("/*", async (c, next) => {
27208
28411
  c.header("Cross-Origin-Opener-Policy", "same-origin");
@@ -27295,6 +28498,7 @@ async function startTarskServerInternal(options) {
27295
28498
  "/api/browser-js",
27296
28499
  createBrowserJsRoutes(threadManager, projectManager, processingStateManager)
27297
28500
  );
28501
+ app.route("/api/browser", createBrowserRoutes());
27298
28502
  app.route("/api/voice-model", createVoiceModelRoutes());
27299
28503
  app.route("/api/logs", createLogsRoutes());
27300
28504
  app.route("/api/update", createUpdateRoutes(options.updater));
@@ -27366,10 +28570,10 @@ async function listenForTarskServer(options) {
27366
28570
  hostname: options.hostname,
27367
28571
  port: options.port
27368
28572
  });
27369
- return new Promise((resolve6, reject) => {
28573
+ return new Promise((resolve7, reject) => {
27370
28574
  server.once("error", (error) => {
27371
28575
  if (error.code === "EADDRINUSE") {
27372
- resolve6(false);
28576
+ resolve7(false);
27373
28577
  return;
27374
28578
  }
27375
28579
  startPromise = null;
@@ -27377,7 +28581,7 @@ async function listenForTarskServer(options) {
27377
28581
  });
27378
28582
  function onListening() {
27379
28583
  options.onListening();
27380
- resolve6(true);
28584
+ resolve7(true);
27381
28585
  }
27382
28586
  if (options.hostname) {
27383
28587
  server.listen(options.port, options.hostname, onListening);